Skip to content

Commit d76c000

Browse files
committed
WIP - Create new EnterURLScreen for SharePoint, add SharePoint Icons and add SharePoint as CloudProvider
1 parent c3ca8a6 commit d76c000

33 files changed

+338
-25
lines changed

Cryptomator.xcodeproj/project.pbxproj

+17
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,10 @@
433433
74F5DC1F26DD036D00AFE989 /* StoreManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74F5DC1E26DD036D00AFE989 /* StoreManager.swift */; };
434434
74FC576125ADED030003ED27 /* VaultCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FC576025ADED030003ED27 /* VaultCell.swift */; };
435435
B330CB452CB5735300C21E03 /* UnauthorizedErrorViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B330CB442CB5735000C21E03 /* UnauthorizedErrorViewController.swift */; };
436+
B34C53262D142B1000F30FE9 /* EnterSharePointURLViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B34C53252D142B0700F30FE9 /* EnterSharePointURLViewController.swift */; };
437+
B34C53282D142B5800F30FE9 /* EnterSharePointURLViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B34C53272D142B5400F30FE9 /* EnterSharePointURLViewModel.swift */; };
438+
B34C532A2D142BA700F30FE9 /* SharePointURLSetting.swift in Sources */ = {isa = PBXBuildFile; fileRef = B34C53292D142B9200F30FE9 /* SharePointURLSetting.swift */; };
439+
B34C532C2D142BF600F30FE9 /* URLValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B34C532B2D142BE000F30FE9 /* URLValidator.swift */; };
436440
B3D19A442CB937C700CD18A5 /* FileProviderCoordinatorError.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3D19A432CB937BF00CD18A5 /* FileProviderCoordinatorError.swift */; };
437441
/* End PBXBuildFile section */
438442

@@ -1040,6 +1044,11 @@
10401044
74F5DC1E26DD036D00AFE989 /* StoreManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreManager.swift; sourceTree = "<group>"; };
10411045
74FC576025ADED030003ED27 /* VaultCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VaultCell.swift; sourceTree = "<group>"; };
10421046
B330CB442CB5735000C21E03 /* UnauthorizedErrorViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnauthorizedErrorViewController.swift; sourceTree = "<group>"; };
1047+
B34C53212D1355D900F30FE9 /* cloud-access-swift */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = "cloud-access-swift"; path = "../cloud-access-swift"; sourceTree = SOURCE_ROOT; };
1048+
B34C53252D142B0700F30FE9 /* EnterSharePointURLViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnterSharePointURLViewController.swift; sourceTree = "<group>"; };
1049+
B34C53272D142B5400F30FE9 /* EnterSharePointURLViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnterSharePointURLViewModel.swift; sourceTree = "<group>"; };
1050+
B34C53292D142B9200F30FE9 /* SharePointURLSetting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharePointURLSetting.swift; sourceTree = "<group>"; };
1051+
B34C532B2D142BE000F30FE9 /* URLValidator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLValidator.swift; sourceTree = "<group>"; };
10431052
B3D19A432CB937BF00CD18A5 /* FileProviderCoordinatorError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileProviderCoordinatorError.swift; sourceTree = "<group>"; };
10441053
/* End PBXFileReference section */
10451054

@@ -1378,6 +1387,10 @@
13781387
4A644B45267A3D21008CBB9A /* CreateNewVault */ = {
13791388
isa = PBXGroup;
13801389
children = (
1390+
B34C532B2D142BE000F30FE9 /* URLValidator.swift */,
1391+
B34C53292D142B9200F30FE9 /* SharePointURLSetting.swift */,
1392+
B34C53272D142B5400F30FE9 /* EnterSharePointURLViewModel.swift */,
1393+
B34C53252D142B0700F30FE9 /* EnterSharePointURLViewController.swift */,
13811394
4A644B4A267B4C08008CBB9A /* CreateNewVaultChooseFolderViewController.swift */,
13821395
4A53CC16267CDBFF00853BB3 /* CreateNewVaultChooseFolderViewModel.swift */,
13831396
4A644B4C267B55E4008CBB9A /* CreateNewVaultCoordinator.swift */,
@@ -2718,6 +2731,7 @@
27182731
4A6A521D268B7C8F006F7368 /* BaseNavigationController.swift in Sources */,
27192732
4AC005F127C3D80B006FFE87 /* PremiumManager.swift in Sources */,
27202733
4ADD2342267383BE00374E4E /* AddVaultSuccessViewModel.swift in Sources */,
2734+
B34C53262D142B1000F30FE9 /* EnterSharePointURLViewController.swift in Sources */,
27212735
4AB1D4F827D68026009060AB /* IAPHeaderView.swift in Sources */,
27222736
4A79E26926B16993008C9959 /* ActionButton.swift in Sources */,
27232737
4AF91CD925A722A600ACF01E /* VaultInfo.swift in Sources */,
@@ -2820,17 +2834,20 @@
28202834
4A0C07EB25AC832900B83211 /* VaultListPosition.swift in Sources */,
28212835
4A3D658226838991000DA764 /* OpenExistingLocalVaultViewModel.swift in Sources */,
28222836
4AC86270273598CC00E15BA5 /* UIViewController+ProgressHUDError.swift in Sources */,
2837+
B34C53282D142B5800F30FE9 /* EnterSharePointURLViewModel.swift in Sources */,
28232838
4AB1D4FD27D69BB2009060AB /* TrialCell.swift in Sources */,
28242839
4A2FD08225B5E2BA008565C8 /* VaultInstalling.swift in Sources */,
28252840
74C2BC5226E8FCD000BCAA03 /* PurchaseCoordinator.swift in Sources */,
28262841
4A53B6D32722F92D000DC367 /* MoveVaultViewModel.swift in Sources */,
28272842
4A4B7E4426B2B1A5009BFDB1 /* BindableTableViewCellViewModel.swift in Sources */,
2843+
B34C532C2D142BF600F30FE9 /* URLValidator.swift in Sources */,
28282844
4AED9A69286B303000352951 /* S3Authenticator+VC.swift in Sources */,
28292845
4ADBD35827284BAB00B19B5C /* MoveVaultViewController.swift in Sources */,
28302846
7408E6CD26779BCC00D7FAEA /* AboutViewModel.swift in Sources */,
28312847
4A8A6424286CA72B001F5EB9 /* DefaultShowEditAccountBehavior.swift in Sources */,
28322848
4AB1D4FF27D69C9A009060AB /* DisclosureCell.swift in Sources */,
28332849
4A7077FF278DC2ED00AEF4CE /* VaultKeepUnlockedViewController.swift in Sources */,
2850+
B34C532A2D142BA700F30FE9 /* SharePointURLSetting.swift in Sources */,
28342851
4A21B49C26BD68C2000D13DF /* UIControl+Publisher.swift in Sources */,
28352852
4AF91CD025A71C5800ACF01E /* UIImage+CloudProviderType.swift in Sources */,
28362853
4A4B7E4A26B2C071009BFDB1 /* ButtonCellViewModel.swift in Sources */,

Cryptomator/AddVault/CreateNewVault/CreateNewVaultCoordinator.swift

+26-6
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,21 @@ import CryptomatorCloudAccessCore
1111
import CryptomatorCommonCore
1212
import UIKit
1313

14-
class CreateNewVaultCoordinator: AccountListing, CloudChoosing, DefaultShowEditAccountBehavior, Coordinator {
14+
class CreateNewVaultCoordinator: AccountListing, CloudChoosing, DefaultShowEditAccountBehavior, Coordinator, SharePointURLSetting {
1515
var navigationController: UINavigationController
1616
var childCoordinators = [Coordinator]()
1717
weak var parentCoordinator: Coordinator?
1818

1919
private let vaultName: String
20+
private var currentSharePointAccount: AccountInfo?
2021

2122
init(navigationController: UINavigationController, vaultName: String) {
2223
self.navigationController = navigationController
2324
self.vaultName = vaultName
2425
}
2526

2627
func start() {
27-
let viewModel = ChooseCloudViewModel(clouds: [.localFileSystem(type: .iCloudDrive), .dropbox, .googleDrive, .oneDrive, .pCloud, .box, .webDAV(type: .custom), .s3(type: .custom), .localFileSystem(type: .custom)], headerTitle: LocalizedString.getValue("addVault.createNewVault.chooseCloud.header"))
28+
let viewModel = ChooseCloudViewModel(clouds: [.localFileSystem(type: .iCloudDrive), .dropbox, .googleDrive, .oneDrive, .sharePoint, .pCloud, .box, .webDAV(type: .custom), .s3(type: .custom), .localFileSystem(type: .custom)], headerTitle: LocalizedString.getValue("addVault.createNewVault.chooseCloud.header"))
2829
let chooseCloudVC = ChooseCloudViewController(viewModel: viewModel)
2930
chooseCloudVC.title = LocalizedString.getValue("addVault.createNewVault.title")
3031
chooseCloudVC.coordinator = self
@@ -44,15 +45,34 @@ class CreateNewVaultCoordinator: AccountListing, CloudChoosing, DefaultShowEditA
4445

4546
func showAddAccount(for cloudProviderType: CloudProviderType, from viewController: UIViewController) {
4647
let authenticator = CloudAuthenticator(accountManager: CloudProviderAccountDBManager.shared)
47-
authenticator.authenticate(cloudProviderType, from: viewController).then { account in
48+
authenticator.authenticate(cloudProviderType, from: viewController).then { _ in
49+
}
50+
}
51+
52+
func showEnterSharePointURL(for account: AccountInfo) {
53+
let viewModel = EnterSharePointURLViewModel(account: account)
54+
let enterURLVC = EnterSharePointURLViewController(viewModel: viewModel)
55+
enterURLVC.coordinator = self
56+
navigationController.pushViewController(enterURLVC, animated: true)
57+
}
58+
59+
func setSharePointURL(_ url: String) {
60+
guard let account = currentSharePointAccount else { return }
61+
do {
4862
let provider = try CloudProviderDBManager.shared.getProvider(with: account.accountUID)
49-
self.startFolderChooser(with: provider, account: account)
63+
startFolderChooser(with: provider, account: account.cloudProviderAccount)
64+
} catch {
65+
handleError(error, for: navigationController)
5066
}
5167
}
5268

5369
func selectedAccont(_ account: AccountInfo) throws {
54-
let provider = try CloudProviderDBManager.shared.getProvider(with: account.accountUID)
55-
startFolderChooser(with: provider, account: account.cloudProviderAccount)
70+
if account.cloudProviderType == .sharePoint {
71+
showEnterSharePointURL(for: account)
72+
} else {
73+
let provider = try CloudProviderDBManager.shared.getProvider(with: account.accountUID)
74+
startFolderChooser(with: provider, account: account.cloudProviderAccount)
75+
}
5676
}
5777

5878
private func startFolderChooser(with provider: CloudProvider, account: CloudProviderAccount) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
//
2+
//  EnterSharePointURLViewController.swift
3+
//  Cryptomator
4+
//
5+
//  Created by Majid Achhoud on 03.12.24.
6+
//
7+
8+
import Combine
9+
import CryptomatorCommonCore
10+
import UIKit
11+
12+
class EnterSharePointURLViewController: SingleSectionStaticUITableViewController {
13+
weak var coordinator: (SharePointURLSetting & Coordinator)?
14+
private var viewModel: EnterSharePointURLViewModelProtocol
15+
private var lastReturnButtonPressedSubscriber: AnyCancellable?
16+
init(viewModel: EnterSharePointURLViewModelProtocol) {
17+
self.viewModel = viewModel
18+
super.init(viewModel: viewModel)
19+
}
20+
21+
override func viewDidLoad() {
22+
super.viewDidLoad()
23+
let doneButton = UIBarButtonItem(title: LocalizedString.getValue("common.button.next"), style: .done, target: self, action: #selector(nextButtonClicked))
24+
navigationItem.rightBarButtonItem = doneButton
25+
lastReturnButtonPressedSubscriber = viewModel.lastReturnButtonPressed.sink { [weak self] in
26+
self?.lastReturnButtonPressedAction()
27+
}
28+
}
29+
30+
@objc func nextButtonClicked() {
31+
do {
32+
try coordinator?.setSharePointURL(viewModel.getValidatedSharePointURL())
33+
} catch {
34+
coordinator?.handleError(error, for: self)
35+
}
36+
}
37+
38+
func lastReturnButtonPressedAction() {
39+
nextButtonClicked()
40+
}
41+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
//
2+
// EnterSharePointURLViewModel.swift
3+
// Cryptomator
4+
//
5+
// Created by Majid Achhoud on 03.12.24.
6+
// Copyright © 2024 Skymatic GmbH. All rights reserved.
7+
//
8+
9+
import Combine
10+
import CryptomatorCommonCore
11+
import Foundation
12+
13+
protocol EnterSharePointURLViewModelProtocol: SingleSectionTableViewModel, ReturnButtonSupport {
14+
func getValidatedSharePointURL() throws -> String
15+
}
16+
17+
class EnterSharePointURLViewModel: SingleSectionTableViewModel, EnterSharePointURLViewModelProtocol {
18+
let account: AccountInfo
19+
init(account: AccountInfo) {
20+
self.account = account
21+
}
22+
23+
var lastReturnButtonPressed: AnyPublisher<Void, Never> {
24+
return setupReturnButtonSupport(for: [sharePointURLCellViewModel], subscribers: &subscribers)
25+
}
26+
27+
override var cells: [TableViewCellViewModel] {
28+
return [sharePointURLCellViewModel]
29+
}
30+
31+
override var title: String? {
32+
return LocalizedString.getValue("addVault.enterSharePointURL.title")
33+
}
34+
35+
let sharePointURLCellViewModel = TextFieldCellViewModel(
36+
type: .normal,
37+
placeholder: LocalizedString.getValue("addVault.enterSharePointURL.placeholder"),
38+
isInitialFirstResponder: true
39+
)
40+
var trimmedSharePointURL: String {
41+
return sharePointURLCellViewModel.input.value.trimmingCharacters(in: .whitespacesAndNewlines)
42+
}
43+
44+
private lazy var subscribers = Set<AnyCancellable>()
45+
func getValidatedSharePointURL() throws -> String {
46+
guard !trimmedSharePointURL.isEmpty else {
47+
throw EnterSharePointURLViewModelError.emptyURL
48+
}
49+
try URLValidator.validateSharePointURL(urlString: trimmedSharePointURL)
50+
return trimmedSharePointURL
51+
}
52+
53+
override func getHeaderTitle(for section: Int) -> String? {
54+
guard section == 0 else {
55+
return nil
56+
}
57+
return LocalizedString.getValue("addVault.enterSharePointURL.header.title")
58+
}
59+
}
60+
61+
enum EnterSharePointURLViewModelError: LocalizedError {
62+
case emptyURL
63+
case invalidURL
64+
var errorDescription: String? {
65+
switch self {
66+
case .emptyURL:
67+
return LocalizedString.getValue("addVault.enterSharePointURL.error.emptyURL")
68+
case .invalidURL:
69+
return LocalizedString.getValue("addVault.enterSharePointURL.error.invalidURL")
70+
}
71+
}
72+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
//
2+
// SharePointURLSetting.swift
3+
// Cryptomator
4+
//
5+
// Created by Majid Achhoud on 03.12.24.
6+
// Copyright © 2024 Skymatic GmbH. All rights reserved.
7+
//
8+
9+
import CryptomatorCommonCore
10+
import UIKit
11+
12+
protocol SharePointURLSetting: AnyObject {
13+
func setSharePointURL(_ url: String)
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//
2+
// URLValidator.swift
3+
// Cryptomator
4+
//
5+
// Created by Majid Achhoud on 03.12.24.
6+
// Copyright © 2024 Skymatic GmbH. All rights reserved.
7+
//
8+
9+
import CryptomatorCommonCore
10+
import Foundation
11+
12+
public enum URLValidatorError: Error, Equatable {
13+
case invalidURLFormat
14+
}
15+
16+
extension URLValidatorError: LocalizedError {
17+
public var errorDescription: String? {
18+
switch self {
19+
case .invalidURLFormat:
20+
return LocalizedString.getValue("addVault.enterSharePointURL.error.invalidURL")
21+
}
22+
}
23+
}
24+
25+
public enum URLValidator {
26+
public static func validateSharePointURL(urlString: String) throws {
27+
let pattern = #"^https:\/\/[a-zA-Z0-9\-]+\.sharepoint\.com\/sites\/[a-zA-Z0-9\-]+$"#
28+
let regex = try NSRegularExpression(pattern: pattern)
29+
let range = NSRange(location: 0, length: urlString.utf16.count)
30+
if regex.firstMatch(in: urlString, options: [], range: range) == nil {
31+
throw URLValidatorError.invalidURLFormat
32+
}
33+
}
34+
}

Cryptomator/AddVault/OpenExistingVault/OpenExistingVaultCoordinator.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ class OpenExistingVaultCoordinator: AccountListing, CloudChoosing, DefaultShowEd
2525
}
2626

2727
func start() {
28-
let viewModel = ChooseCloudViewModel(clouds: [.localFileSystem(type: .iCloudDrive), .dropbox, .googleDrive, .oneDrive, .pCloud, .box, .webDAV(type: .custom), .s3(type: .custom), .localFileSystem(type: .custom)], headerTitle: LocalizedString.getValue("addVault.openExistingVault.chooseCloud.header"))
28+
let viewModel = ChooseCloudViewModel(clouds: [.localFileSystem(type: .iCloudDrive), .dropbox, .googleDrive, .oneDrive, .sharePoint, .pCloud, .box, .webDAV(type: .custom), .s3(type: .custom), .localFileSystem(type: .custom)], headerTitle: LocalizedString.getValue("addVault.openExistingVault.chooseCloud.header"))
2929
let chooseCloudVC = ChooseCloudViewController(viewModel: viewModel)
3030
chooseCloudVC.title = LocalizedString.getValue("addVault.openExistingVault.title")
3131
chooseCloudVC.coordinator = self

Cryptomator/AppDelegate.swift

+5-5
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
4141
DropboxSetup.constants = DropboxSetup(appKey: CloudAccessSecrets.dropboxAppKey, sharedContainerIdentifier: nil, keychainService: CryptomatorConstants.mainAppBundleId, forceForegroundSession: true)
4242
GoogleDriveSetup.constants = GoogleDriveSetup(clientId: CloudAccessSecrets.googleDriveClientId, redirectURL: CloudAccessSecrets.googleDriveRedirectURL!, sharedContainerIdentifier: nil)
4343
do {
44-
let oneDriveConfiguration = MSALPublicClientApplicationConfig(clientId: CloudAccessSecrets.oneDriveClientId, redirectUri: CloudAccessSecrets.oneDriveRedirectURI, authority: nil)
45-
oneDriveConfiguration.cacheConfig.keychainSharingGroup = CryptomatorConstants.mainAppBundleId
46-
let oneDriveClientApplication = try MSALPublicClientApplication(configuration: oneDriveConfiguration)
47-
OneDriveSetup.constants = OneDriveSetup(clientApplication: oneDriveClientApplication, sharedContainerIdentifier: nil)
44+
let microsoftGraphConfiguration = MSALPublicClientApplicationConfig(clientId: CloudAccessSecrets.microsoftGraphClientId, redirectUri: CloudAccessSecrets.microsoftGraphRedirectURI, authority: nil)
45+
microsoftGraphConfiguration.cacheConfig.keychainSharingGroup = CryptomatorConstants.mainAppBundleId
46+
let microsoftGraphClientApplication = try MSALPublicClientApplication(configuration: microsoftGraphConfiguration)
47+
MicrosoftGraphSetup.constants = MicrosoftGraphSetup(clientApplication: microsoftGraphClientApplication, sharedContainerIdentifier: nil)
4848
} catch {
4949
DDLogError("Setting up OneDrive failed with error: \(error)")
5050
}
@@ -87,7 +87,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
8787
}
8888
} else if url.scheme == CloudAccessSecrets.googleDriveRedirectURLScheme {
8989
return GoogleDriveAuthenticator.currentAuthorizationFlow?.resumeExternalUserAgentFlow(with: url) ?? false
90-
} else if url.scheme == CloudAccessSecrets.oneDriveRedirectURIScheme {
90+
} else if url.scheme == CloudAccessSecrets.microsoftGraphRedirectURIScheme {
9191
return MSALPublicClientApplication.handleMSALResponse(url, sourceApplication: options[.sourceApplication] as? String)
9292
}
9393
return false

Cryptomator/Common/CloudAccountList/AccountListViewController.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ class AccountListViewController: ListViewController<AccountCellContent>, ASWebAu
142142

143143
private func supportsEditing(_ cloudProviderType: CloudProviderType) -> Bool {
144144
switch cloudProviderType {
145-
case .box, .dropbox, .googleDrive, .localFileSystem, .oneDrive, .pCloud:
145+
case .box, .dropbox, .googleDrive, .localFileSystem, .oneDrive, .sharePoint, .pCloud:
146146
return false
147147
case .s3, .webDAV:
148148
return true

Cryptomator/Common/CloudAccountList/AccountListViewModel.swift

+8
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,9 @@ class AccountListViewModel: AccountListViewModelProtocol {
8989
case .oneDrive:
9090
let credential = try OneDriveCredential(with: accountInfo.accountUID)
9191
return try createAccountCellContent(for: credential)
92+
case .sharePoint:
93+
let credential = try SharePointCredential(with: accountInfo.accountUID)
94+
return try createAccountCellContent(for: credential)
9295
case .pCloud:
9396
return createAccountCellContentPlaceholder()
9497
case .s3:
@@ -125,6 +128,11 @@ class AccountListViewModel: AccountListViewModelProtocol {
125128
return AccountCellContent(mainLabelText: username, detailLabelText: nil)
126129
}
127130

131+
func createAccountCellContent(for credential: SharePointCredential) throws -> AccountCellContent {
132+
let username = try credential.getUsername()
133+
return AccountCellContent(mainLabelText: username, detailLabelText: nil)
134+
}
135+
128136
func createAccountCellContent(for credential: PCloudCredential) -> Promise<AccountCellContent> {
129137
return credential.getUsername().then { username in
130138
AccountCellContent(mainLabelText: username, detailLabelText: nil)

0 commit comments

Comments
 (0)