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: MykSuite dashboard #1419

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
0bfdb52
chore: Switch to dashboard branch
adrien-coye Feb 3, 2025
cc5f4c7
feat: New account cell in parameters to display MyKSuiteStore view
adrien-coye Feb 3, 2025
d5bf784
feat(MyKSuiteStore): Add type to DI
adrien-coye Feb 4, 2025
42b1ff4
feat(MyKSuiteDashboardViewBridgeController): Reworked presentation
adrien-coye Feb 4, 2025
7eb32f9
feat(AccountManager): Refresh myKSuiteStore on login
adrien-coye Feb 4, 2025
b79e426
Merge branch 'converstion-quota-dropbox-view' into converstion-quota-…
adrien-coye Feb 4, 2025
cf4dddd
Merge branch 'converstion-quota-dropbox-view' into converstion-quota-…
adrien-coye Feb 4, 2025
6a61e1c
Merge branch 'converstion-quota-dropbox-view' into converstion-quota-…
adrien-coye Feb 5, 2025
86e4f6b
Merge branch 'converstion-quota-dropbox-view' into converstion-quota-…
adrien-coye Feb 5, 2025
7e26d48
feat(ParameterTableViewController): Grouped cell UI update
adrien-coye Feb 6, 2025
01eb8f8
Merge branch 'converstion-quota-dropbox-view' into converstion-quota-…
adrien-coye Feb 6, 2025
6a95017
Merge branch 'converstion-quota-dropbox-view' into converstion-quota-…
adrien-coye Feb 6, 2025
2547bb6
chore: Removed iOS 15 conditional code
adrien-coye Feb 6, 2025
b79df5f
Merge branch 'converstion-quota-dropbox-view' into converstion-quota-…
adrien-coye Feb 6, 2025
7d413e6
feat(): Modern sheet presentation for MyKSuiteBridgeViewController
adrien-coye Feb 7, 2025
6145d52
Merge branch 'converstion-quota-dropbox-view' into converstion-quota-…
adrien-coye Feb 7, 2025
e12ca58
feat(presentUpSaleSheet): Animate a dismiss before presenting
adrien-coye Feb 7, 2025
f5a3e18
Merge branch 'converstion-quota-dropbox' into converstion-quota-dropb…
adrien-coye Feb 7, 2025
b45d75e
Merge branch 'converstion-quota-dropbox-view' into converstion-quota-…
adrien-coye Feb 7, 2025
c377943
feat(ios-features): Added necessary libraries as a Framework
adrien-coye Feb 7, 2025
85f401b
Merge branch 'converstion-quota-dropbox-view' into converstion-quota-…
adrien-coye Feb 7, 2025
4350f73
Merge branch 'converstion-quota-dropbox' into converstion-quota-dropb…
adrien-coye Feb 10, 2025
7a5c735
Merge branch 'converstion-quota-dropbox-view' into converstion-quota-…
adrien-coye Feb 10, 2025
589d8c3
chore(MyKSuiteDashboardViewBridgeController): Bumped to lattest ios-f…
adrien-coye Feb 10, 2025
7bda3d9
Merge branch 'converstion-quota-dropbox-view' into converstion-quota-…
adrien-coye Feb 10, 2025
9c731c2
feat(ParameterTableViewController): Some i18n
adrien-coye Feb 10, 2025
4a8afa7
chore: PR Feedback
adrien-coye Feb 10, 2025
b6c90ae
chore: Remove conditional ios version dependent code
adrien-coye Feb 10, 2025
328685f
feat(ParameterTableViewController): Dynamic sections if mykSuite enabled
adrien-coye Feb 10, 2025
ab9ca45
fix(ParameterTableViewCell): Proper prepare for reuse implementation
adrien-coye Feb 10, 2025
a8e1b48
Merge branch 'converstion-quota-dropbox-view' into converstion-quota-…
adrien-coye Feb 11, 2025
4778b07
feat(ParameterTableViewController): Inter18n general section
adrien-coye Feb 11, 2025
52036f8
Merge branch 'converstion-quota-dropbox-view' into converstion-quota-…
adrien-coye Feb 11, 2025
ccd5237
fix: New ios-features build
adrien-coye Feb 12, 2025
592754a
chore: Bump core-ui
adrien-coye Feb 12, 2025
ed90be4
feat(AsyncImageView): A sample swUI view to display account image sti…
adrien-coye Feb 12, 2025
d02db04
Merge branch 'converstion-quota-dropbox-view' into converstion-quota-…
adrien-coye Feb 12, 2025
84d6398
feat(AccountManager): Call to updateMyKSuite is no longer breaking au…
adrien-coye Feb 12, 2025
3190449
fix(ParameterTableViewController): Correct rounded corner on cell
adrien-coye Feb 12, 2025
1d202d3
feat(ParameterTableViewCell): Cell has support for other fonts and ca…
adrien-coye Feb 12, 2025
639da2f
chore: Bump to ios-features 1.0.2
adrien-coye Feb 12, 2025
fda3c30
chore: Branding my kSuite plus with a plus logo not a plus plus
adrien-coye Feb 12, 2025
306d1ac
feat(ParameterTableViewController): Custom design table view section
adrien-coye Feb 12, 2025
9560e9d
chore: Remove force display of myksuite
adrien-coye Feb 12, 2025
7ef89a4
feat(AsyncImageView): Loading view with default color
adrien-coye Feb 12, 2025
cb56616
feat: Check for mykSuite availlable for a user
adrien-coye Feb 12, 2025
8aa93cd
chore: Bump ios-features
adrien-coye Feb 13, 2025
936a846
Merge branch 'converstion-quota-dropbox-view' into converstion-quota-…
adrien-coye Feb 13, 2025
696e972
Merge branch 'converstion-quota-dropbox-view' into converstion-quota-…
adrien-coye Feb 13, 2025
3b69216
refactor(ParameterTableViewController): Better async loading
adrien-coye Feb 13, 2025
8713b44
fix(ParameterTableViewController): Section title is now correct witho…
adrien-coye Feb 13, 2025
5633fcb
chore: Sonar
adrien-coye Feb 13, 2025
791a863
Merge branch 'converstion-quota-dropbox-view' into converstion-quota-…
adrien-coye Feb 13, 2025
ff5bd0f
refactor(ParameterTableViewController): Use a font with correct color…
adrien-coye Feb 14, 2025
02e132b
refactor(MyKSuiteDashboardViewBridgeController): Simplify constructor
adrien-coye Feb 14, 2025
5ecf009
feat(AccountManager): Update mykSuite on updateUser
adrien-coye Feb 14, 2025
d929584
refactor(ParameterTableViewController): Remove extraneous lazy DI for…
adrien-coye Feb 14, 2025
5e80f80
refactor(MenuViewController): Remove extraneous import
adrien-coye Feb 14, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions Tuist/Package.resolved
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"originHash" : "88f7f532a7e7657a65b78bfcaf4a729503a5cbc882035f94e3ccc805e73a8845",
"originHash" : "8861f96269cb0948c6a4f36939cb053cc533985c8abc8a6cd517110b6da6bc0f",
"pins" : [
{
"identity" : "alamofire",
Expand Down Expand Up @@ -96,8 +96,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/Infomaniak/ios-core-ui",
"state" : {
"revision" : "89873aff2c300f9f2966340b7b3e8832e88a95a6",
"version" : "18.0.1"
"revision" : "4d988c5e91d27db9ea3905242cb6c3a44df472d0",
"version" : "18.1.0"
}
},
{
Expand All @@ -114,8 +114,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/Infomaniak/ios-features",
"state" : {
"revision" : "cf6608e2d84d8f921fd00806689a13ad4a7d96ff",
"version" : "1.0.1"
"revision" : "c1b1a15d0fd6bf4622dc6f0d675e795b60c952f1",
"version" : "1.0.3"
}
},
{
Expand Down
4 changes: 2 additions & 2 deletions Tuist/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ let package = Package(
.package(url: "https://github.com/apple/swift-algorithms", .upToNextMajor(from: "1.2.0")),
.package(url: "https://github.com/Alamofire/Alamofire", .upToNextMajor(from: "5.2.2")),
.package(url: "https://github.com/Infomaniak/ios-core", .upToNextMajor(from: "15.0.0")),
.package(url: "https://github.com/Infomaniak/ios-core-ui", .upToNextMajor(from: "18.0.0")),
.package(url: "https://github.com/Infomaniak/ios-features", .upToNextMajor(from: "1.0.1")),
.package(url: "https://github.com/Infomaniak/ios-core-ui", .upToNextMajor(from: "18.1.0")),
.package(url: "https://github.com/Infomaniak/ios-features", .upToNextMajor(from: "1.0.3")),
.package(url: "https://github.com/Infomaniak/ios-login", .upToNextMajor(from: "7.2.0")),
.package(url: "https://github.com/Infomaniak/ios-dependency-injection", .upToNextMajor(from: "2.0.3")),
.package(url: "https://github.com/Infomaniak/swift-concurrency", .upToNextMajor(from: "1.0.0")),
Expand Down
192 changes: 181 additions & 11 deletions kDrive/UI/Controller/Menu/ParameterTableViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,13 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import InfomaniakCoreCommonUI
import InfomaniakCoreUIKit
import InfomaniakDI
import InfomaniakLogin
import kDriveCore
import kDriveResources
import MyKSuite
import Sentry
import UIKit

Expand All @@ -30,7 +33,46 @@ class ParameterTableViewController: BaseGroupedTableViewController {

let driveFileManager: DriveFileManager

private enum ParameterRow: CaseIterable {
lazy var packId = DrivePackId(rawValue: driveFileManager.drive.pack.name)

var mykSuiteEnabled: Bool = false

private enum ParameterSection: Int, CaseIterable {
case mykSuite
case general

func title(packId: DrivePackId?) -> String {
switch self {
case .mykSuite:
if packId == .myKSuite {
return "my kSuite"
adrien-coye marked this conversation as resolved.
Show resolved Hide resolved
} else if packId == .myKSuitePlus {
return "my kSuite+"
} else {
return ""
}
case .general:
return KDriveResourcesStrings.Localizable.settingsSectionGeneral
}
}
}

private enum MykSuiteParameterRow: CaseIterable {
case email
case mySubscription

var title: String {
switch self {
case .email:
@InjectService var accountManager: AccountManageable
return accountManager.currentAccount?.user.email ?? ""
case .mySubscription:
return MyKSuiteLocalizable.iosMyKSuiteDashboardSubscriptionButton
}
}
}

private enum GeneralParameterRow: CaseIterable {
case photos
case theme
case notifications
Expand Down Expand Up @@ -62,10 +104,6 @@ class ParameterTableViewController: BaseGroupedTableViewController {
}
}

private var tableContent: [ParameterRow] {
return ParameterRow.allCases
}

init(driveFileManager: DriveFileManager) {
self.driveFileManager = driveFileManager
super.init()
Expand All @@ -80,6 +118,7 @@ class ParameterTableViewController: BaseGroupedTableViewController {
tableView.register(cellView: ParameterWifiTableViewCell.self)

navigationItem.hideBackButtonText()
checkMykSuiteEnabledAndRefresh()
}

override func viewWillAppear(_ animated: Bool) {
Expand All @@ -105,20 +144,86 @@ class ParameterTableViewController: BaseGroupedTableViewController {
}
}

override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return nil
}

override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let currentSection: ParameterSection?
if mykSuiteEnabled {
currentSection = ParameterSection(rawValue: section)
} else {
currentSection = ParameterSection.general
}

guard let currentSection else { return nil }

let headerView = UIView()
headerView.backgroundColor = .clear

let label = IKLabel()
label.text = currentSection.title(packId: packId)
label.font = TextStyle.header3.font

label.translatesAutoresizingMaskIntoConstraints = false
headerView.addSubview(label)

NSLayoutConstraint.activate([
label.leadingAnchor.constraint(equalTo: headerView.leadingAnchor, constant: 24),
label.trailingAnchor.constraint(equalTo: headerView.trailingAnchor, constant: -24),
label.centerYAnchor.constraint(equalTo: headerView.centerYAnchor)
])

return headerView
}

override func numberOfSections(in tableView: UITableView) -> Int {
return 1
guard mykSuiteEnabled else {
return 1
}

return ParameterSection.allCases.count
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return tableContent.count
guard mykSuiteEnabled else {
return GeneralParameterRow.allCases.count
}

switch section {
case ParameterSection.mykSuite.rawValue:
return MykSuiteParameterRow.allCases.count
case ParameterSection.general.rawValue:
return GeneralParameterRow.allCases.count
default:
return 0
}
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let row = tableContent[indexPath.row]
guard mykSuiteEnabled else {
return generalCell(tableView, forRowAt: indexPath)
}

switch indexPath.section {
case ParameterSection.mykSuite.rawValue:
return mykSuiteCell(tableView, forRowAt: indexPath)
case ParameterSection.general.rawValue:
return generalCell(tableView, forRowAt: indexPath)
default:
fatalError("invalid indexPath: \(indexPath)")
}
}

private func generalCell(_ tableView: UITableView, forRowAt indexPath: IndexPath) -> UITableViewCell {
let row = GeneralParameterRow.allCases[indexPath.row]
switch row {
case .photos, .theme, .notifications:
let cell = tableView.dequeueReusableCell(type: ParameterTableViewCell.self, for: indexPath)
cell.initWithPositionAndShadow(isFirst: indexPath.row == 0, isLast: indexPath.row == tableContent.count - 1)
cell.initWithPositionAndShadow(
isFirst: indexPath.row == 0,
isLast: indexPath.row == GeneralParameterRow.allCases.count - 1
)
cell.titleLabel.text = row.title
if row == .photos {
cell.valueLabel.text = photoLibraryUploader.isSyncEnabled ? KDriveResourcesStrings.Localizable
Expand All @@ -140,16 +245,65 @@ class ParameterTableViewController: BaseGroupedTableViewController {
return cell
case .security, .storage, .about, .deleteAccount:
let cell = tableView.dequeueReusableCell(type: ParameterAboutTableViewCell.self, for: indexPath)
cell.initWithPositionAndShadow(isFirst: indexPath.row == 0, isLast: indexPath.row == tableContent.count - 1)
cell.initWithPositionAndShadow(
isFirst: indexPath.row == 0,
isLast: indexPath.row == GeneralParameterRow.allCases.count - 1
)
cell.titleLabel.text = row.title
return cell
}
}

private func mykSuiteCell(_ tableView: UITableView, forRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(type: ParameterTableViewCell.self, for: indexPath)
cell.initWithPositionAndShadow(
isFirst: indexPath.row == 0,
isLast: indexPath.row == MykSuiteParameterRow.allCases.count - 1
)

let row = MykSuiteParameterRow.allCases[indexPath.row]
switch row {
case .email:
cell.titleLabel.text = row.title
cell.titleLabel.font = TextStyle.body1.font
cell.selectionStyle = .none
case .mySubscription:
cell.titleLabel.text = row.title
}
return cell
}

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let row = tableContent[indexPath.row]
tableView.deselectRow(at: indexPath, animated: true)

guard mykSuiteEnabled else {
didSelectGeneralRowAt(indexPath: indexPath)
return
}

switch indexPath.section {
case ParameterSection.mykSuite.rawValue:
didSelectMykSuiteRowAt(indexPath: indexPath)
case ParameterSection.general.rawValue:
didSelectGeneralRowAt(indexPath: indexPath)
default:
return
}
}

private func didSelectMykSuiteRowAt(indexPath: IndexPath) {
let row = MykSuiteParameterRow.allCases[indexPath.row]
guard row == MykSuiteParameterRow.mySubscription else { return }
guard let currentAccount = accountManager.currentAccount else { return }
let dashboardViewController = MyKSuiteDashboardViewBridgeController.instantiate(
apiFetcher: driveFileManager.apiFetcher,
currentAccount: currentAccount
)
navigationController?.present(dashboardViewController, animated: true)
}

private func didSelectGeneralRowAt(indexPath: IndexPath) {
let row = GeneralParameterRow.allCases[indexPath.row]
switch row {
case .storage:
navigationController?.pushViewController(StorageTableViewController(style: .grouped), animated: true)
Expand All @@ -175,6 +329,22 @@ class ParameterTableViewController: BaseGroupedTableViewController {
navigationController?.present(deleteAccountViewController, animated: true)
}
}

private func checkMykSuiteEnabledAndRefresh() {
Task { @MainActor in
@InjectService var mykSuiteStore: MyKSuiteStore
let packIsMykSuite: Bool
if await mykSuiteStore.getMyKSuite(id: accountManager.currentUserId) != nil,
packId == .myKSuite || packId == .myKSuitePlus {
packIsMykSuite = true
} else {
packIsMykSuite = false
}

self.mykSuiteEnabled = packIsMykSuite
self.tableView.reloadData()
}
}
}

extension ParameterTableViewController: DeleteAccountDelegate {
Expand Down
44 changes: 44 additions & 0 deletions kDrive/UI/Controller/SwiftUI/AsyncImageView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
Infomaniak kDrive - iOS App
Copyright (C) 2025 Infomaniak Network SA

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import Foundation
import InfomaniakCore
import SwiftUI

struct AsyncImageView: View {
adrien-coye marked this conversation as resolved.
Show resolved Hide resolved
@State private var loadedImage: UIImage?
let currentAccount: Account

var body: some View {
VStack {
if let image = loadedImage {
Image(uiImage: image)
.resizable()
.scaledToFit()
.frame(width: 40, height: 40)
} else {
Circle()
.fill(KDriveAsset.loaderDefaultColor.swiftUIColor)
.frame(width: 40, height: 40)
}
}
.task {
loadedImage = await currentAccount.user.getAvatar()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
Infomaniak kDrive - iOS App
Copyright (C) 2025 Infomaniak Network SA

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import FloatingPanel
import InfomaniakCore
import InfomaniakCoreUIKit
import InfomaniakDI
import kDriveCore
import kDriveResources
import MyKSuite
import SwiftUI
import UIKit

enum MyKSuiteDashboardViewBridgeController {
static func instantiate(apiFetcher: DriveApiFetcher, currentAccount: Account) -> UIViewController {
let swiftUIView = MyKSuiteDashboardView(apiFetcher: apiFetcher, userId: currentAccount.userId) {
AsyncImageView(currentAccount: currentAccount)
}

return UIHostingController(rootView: swiftUIView)
}
}
Loading
Loading