Skip to content

Commit

Permalink
WIP - Create new EnterURLScreen for SharePoint, add SharePoint Icons …
Browse files Browse the repository at this point in the history
…and add SharePoint as CloudProvider
  • Loading branch information
iammajid committed Dec 23, 2024
1 parent c3ca8a6 commit d76c000
Show file tree
Hide file tree
Showing 33 changed files with 338 additions and 25 deletions.
17 changes: 17 additions & 0 deletions Cryptomator.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,10 @@
74F5DC1F26DD036D00AFE989 /* StoreManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74F5DC1E26DD036D00AFE989 /* StoreManager.swift */; };
74FC576125ADED030003ED27 /* VaultCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FC576025ADED030003ED27 /* VaultCell.swift */; };
B330CB452CB5735300C21E03 /* UnauthorizedErrorViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B330CB442CB5735000C21E03 /* UnauthorizedErrorViewController.swift */; };
B34C53262D142B1000F30FE9 /* EnterSharePointURLViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B34C53252D142B0700F30FE9 /* EnterSharePointURLViewController.swift */; };
B34C53282D142B5800F30FE9 /* EnterSharePointURLViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B34C53272D142B5400F30FE9 /* EnterSharePointURLViewModel.swift */; };
B34C532A2D142BA700F30FE9 /* SharePointURLSetting.swift in Sources */ = {isa = PBXBuildFile; fileRef = B34C53292D142B9200F30FE9 /* SharePointURLSetting.swift */; };
B34C532C2D142BF600F30FE9 /* URLValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B34C532B2D142BE000F30FE9 /* URLValidator.swift */; };
B3D19A442CB937C700CD18A5 /* FileProviderCoordinatorError.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3D19A432CB937BF00CD18A5 /* FileProviderCoordinatorError.swift */; };
/* End PBXBuildFile section */

Expand Down Expand Up @@ -1040,6 +1044,11 @@
74F5DC1E26DD036D00AFE989 /* StoreManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreManager.swift; sourceTree = "<group>"; };
74FC576025ADED030003ED27 /* VaultCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VaultCell.swift; sourceTree = "<group>"; };
B330CB442CB5735000C21E03 /* UnauthorizedErrorViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnauthorizedErrorViewController.swift; sourceTree = "<group>"; };
B34C53212D1355D900F30FE9 /* cloud-access-swift */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = "cloud-access-swift"; path = "../cloud-access-swift"; sourceTree = SOURCE_ROOT; };
B34C53252D142B0700F30FE9 /* EnterSharePointURLViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnterSharePointURLViewController.swift; sourceTree = "<group>"; };
B34C53272D142B5400F30FE9 /* EnterSharePointURLViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnterSharePointURLViewModel.swift; sourceTree = "<group>"; };
B34C53292D142B9200F30FE9 /* SharePointURLSetting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharePointURLSetting.swift; sourceTree = "<group>"; };
B34C532B2D142BE000F30FE9 /* URLValidator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLValidator.swift; sourceTree = "<group>"; };
B3D19A432CB937BF00CD18A5 /* FileProviderCoordinatorError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileProviderCoordinatorError.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

Expand Down Expand Up @@ -1378,6 +1387,10 @@
4A644B45267A3D21008CBB9A /* CreateNewVault */ = {
isa = PBXGroup;
children = (
B34C532B2D142BE000F30FE9 /* URLValidator.swift */,
B34C53292D142B9200F30FE9 /* SharePointURLSetting.swift */,
B34C53272D142B5400F30FE9 /* EnterSharePointURLViewModel.swift */,
B34C53252D142B0700F30FE9 /* EnterSharePointURLViewController.swift */,
4A644B4A267B4C08008CBB9A /* CreateNewVaultChooseFolderViewController.swift */,
4A53CC16267CDBFF00853BB3 /* CreateNewVaultChooseFolderViewModel.swift */,
4A644B4C267B55E4008CBB9A /* CreateNewVaultCoordinator.swift */,
Expand Down Expand Up @@ -2718,6 +2731,7 @@
4A6A521D268B7C8F006F7368 /* BaseNavigationController.swift in Sources */,
4AC005F127C3D80B006FFE87 /* PremiumManager.swift in Sources */,
4ADD2342267383BE00374E4E /* AddVaultSuccessViewModel.swift in Sources */,
B34C53262D142B1000F30FE9 /* EnterSharePointURLViewController.swift in Sources */,
4AB1D4F827D68026009060AB /* IAPHeaderView.swift in Sources */,
4A79E26926B16993008C9959 /* ActionButton.swift in Sources */,
4AF91CD925A722A600ACF01E /* VaultInfo.swift in Sources */,
Expand Down Expand Up @@ -2820,17 +2834,20 @@
4A0C07EB25AC832900B83211 /* VaultListPosition.swift in Sources */,
4A3D658226838991000DA764 /* OpenExistingLocalVaultViewModel.swift in Sources */,
4AC86270273598CC00E15BA5 /* UIViewController+ProgressHUDError.swift in Sources */,
B34C53282D142B5800F30FE9 /* EnterSharePointURLViewModel.swift in Sources */,
4AB1D4FD27D69BB2009060AB /* TrialCell.swift in Sources */,
4A2FD08225B5E2BA008565C8 /* VaultInstalling.swift in Sources */,
74C2BC5226E8FCD000BCAA03 /* PurchaseCoordinator.swift in Sources */,
4A53B6D32722F92D000DC367 /* MoveVaultViewModel.swift in Sources */,
4A4B7E4426B2B1A5009BFDB1 /* BindableTableViewCellViewModel.swift in Sources */,
B34C532C2D142BF600F30FE9 /* URLValidator.swift in Sources */,
4AED9A69286B303000352951 /* S3Authenticator+VC.swift in Sources */,
4ADBD35827284BAB00B19B5C /* MoveVaultViewController.swift in Sources */,
7408E6CD26779BCC00D7FAEA /* AboutViewModel.swift in Sources */,
4A8A6424286CA72B001F5EB9 /* DefaultShowEditAccountBehavior.swift in Sources */,
4AB1D4FF27D69C9A009060AB /* DisclosureCell.swift in Sources */,
4A7077FF278DC2ED00AEF4CE /* VaultKeepUnlockedViewController.swift in Sources */,
B34C532A2D142BA700F30FE9 /* SharePointURLSetting.swift in Sources */,
4A21B49C26BD68C2000D13DF /* UIControl+Publisher.swift in Sources */,
4AF91CD025A71C5800ACF01E /* UIImage+CloudProviderType.swift in Sources */,
4A4B7E4A26B2C071009BFDB1 /* ButtonCellViewModel.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,21 @@ import CryptomatorCloudAccessCore
import CryptomatorCommonCore
import UIKit

class CreateNewVaultCoordinator: AccountListing, CloudChoosing, DefaultShowEditAccountBehavior, Coordinator {
class CreateNewVaultCoordinator: AccountListing, CloudChoosing, DefaultShowEditAccountBehavior, Coordinator, SharePointURLSetting {
var navigationController: UINavigationController
var childCoordinators = [Coordinator]()
weak var parentCoordinator: Coordinator?

private let vaultName: String
private var currentSharePointAccount: AccountInfo?

init(navigationController: UINavigationController, vaultName: String) {
self.navigationController = navigationController
self.vaultName = vaultName
}

func start() {
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"))
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"))
let chooseCloudVC = ChooseCloudViewController(viewModel: viewModel)
chooseCloudVC.title = LocalizedString.getValue("addVault.createNewVault.title")
chooseCloudVC.coordinator = self
Expand All @@ -44,15 +45,34 @@ class CreateNewVaultCoordinator: AccountListing, CloudChoosing, DefaultShowEditA

func showAddAccount(for cloudProviderType: CloudProviderType, from viewController: UIViewController) {
let authenticator = CloudAuthenticator(accountManager: CloudProviderAccountDBManager.shared)
authenticator.authenticate(cloudProviderType, from: viewController).then { account in
authenticator.authenticate(cloudProviderType, from: viewController).then { _ in
}
}

func showEnterSharePointURL(for account: AccountInfo) {
let viewModel = EnterSharePointURLViewModel(account: account)
let enterURLVC = EnterSharePointURLViewController(viewModel: viewModel)
enterURLVC.coordinator = self
navigationController.pushViewController(enterURLVC, animated: true)
}

func setSharePointURL(_ url: String) {
guard let account = currentSharePointAccount else { return }
do {
let provider = try CloudProviderDBManager.shared.getProvider(with: account.accountUID)
self.startFolderChooser(with: provider, account: account)
startFolderChooser(with: provider, account: account.cloudProviderAccount)
} catch {
handleError(error, for: navigationController)
}
}

func selectedAccont(_ account: AccountInfo) throws {
let provider = try CloudProviderDBManager.shared.getProvider(with: account.accountUID)
startFolderChooser(with: provider, account: account.cloudProviderAccount)
if account.cloudProviderType == .sharePoint {
showEnterSharePointURL(for: account)
} else {
let provider = try CloudProviderDBManager.shared.getProvider(with: account.accountUID)
startFolderChooser(with: provider, account: account.cloudProviderAccount)
}
}

private func startFolderChooser(with provider: CloudProvider, account: CloudProviderAccount) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//
//  EnterSharePointURLViewController.swift
//  Cryptomator
//
//  Created by Majid Achhoud on 03.12.24.
//

import Combine
import CryptomatorCommonCore
import UIKit

class EnterSharePointURLViewController: SingleSectionStaticUITableViewController {
weak var coordinator: (SharePointURLSetting & Coordinator)?
private var viewModel: EnterSharePointURLViewModelProtocol
private var lastReturnButtonPressedSubscriber: AnyCancellable?
init(viewModel: EnterSharePointURLViewModelProtocol) {
self.viewModel = viewModel
super.init(viewModel: viewModel)
}

override func viewDidLoad() {
super.viewDidLoad()
let doneButton = UIBarButtonItem(title: LocalizedString.getValue("common.button.next"), style: .done, target: self, action: #selector(nextButtonClicked))
navigationItem.rightBarButtonItem = doneButton
lastReturnButtonPressedSubscriber = viewModel.lastReturnButtonPressed.sink { [weak self] in
self?.lastReturnButtonPressedAction()
}
}

@objc func nextButtonClicked() {
do {
try coordinator?.setSharePointURL(viewModel.getValidatedSharePointURL())
} catch {
coordinator?.handleError(error, for: self)
}
}

func lastReturnButtonPressedAction() {
nextButtonClicked()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
//
// EnterSharePointURLViewModel.swift
// Cryptomator
//
// Created by Majid Achhoud on 03.12.24.
// Copyright © 2024 Skymatic GmbH. All rights reserved.
//

import Combine
import CryptomatorCommonCore
import Foundation

protocol EnterSharePointURLViewModelProtocol: SingleSectionTableViewModel, ReturnButtonSupport {
func getValidatedSharePointURL() throws -> String
}

class EnterSharePointURLViewModel: SingleSectionTableViewModel, EnterSharePointURLViewModelProtocol {
let account: AccountInfo
init(account: AccountInfo) {
self.account = account
}

var lastReturnButtonPressed: AnyPublisher<Void, Never> {
return setupReturnButtonSupport(for: [sharePointURLCellViewModel], subscribers: &subscribers)
}

override var cells: [TableViewCellViewModel] {
return [sharePointURLCellViewModel]
}

override var title: String? {
return LocalizedString.getValue("addVault.enterSharePointURL.title")
}

let sharePointURLCellViewModel = TextFieldCellViewModel(
type: .normal,
placeholder: LocalizedString.getValue("addVault.enterSharePointURL.placeholder"),
isInitialFirstResponder: true
)
var trimmedSharePointURL: String {
return sharePointURLCellViewModel.input.value.trimmingCharacters(in: .whitespacesAndNewlines)
}

private lazy var subscribers = Set<AnyCancellable>()
func getValidatedSharePointURL() throws -> String {
guard !trimmedSharePointURL.isEmpty else {
throw EnterSharePointURLViewModelError.emptyURL
}
try URLValidator.validateSharePointURL(urlString: trimmedSharePointURL)
return trimmedSharePointURL
}

override func getHeaderTitle(for section: Int) -> String? {
guard section == 0 else {
return nil
}
return LocalizedString.getValue("addVault.enterSharePointURL.header.title")
}
}

enum EnterSharePointURLViewModelError: LocalizedError {
case emptyURL
case invalidURL
var errorDescription: String? {
switch self {
case .emptyURL:
return LocalizedString.getValue("addVault.enterSharePointURL.error.emptyURL")
case .invalidURL:
return LocalizedString.getValue("addVault.enterSharePointURL.error.invalidURL")
}
}
}
14 changes: 14 additions & 0 deletions Cryptomator/AddVault/CreateNewVault/SharePointURLSetting.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//
// SharePointURLSetting.swift
// Cryptomator
//
// Created by Majid Achhoud on 03.12.24.
// Copyright © 2024 Skymatic GmbH. All rights reserved.
//

import CryptomatorCommonCore
import UIKit

protocol SharePointURLSetting: AnyObject {
func setSharePointURL(_ url: String)
}
34 changes: 34 additions & 0 deletions Cryptomator/AddVault/CreateNewVault/URLValidator.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//
// URLValidator.swift
// Cryptomator
//
// Created by Majid Achhoud on 03.12.24.
// Copyright © 2024 Skymatic GmbH. All rights reserved.
//

import CryptomatorCommonCore
import Foundation

public enum URLValidatorError: Error, Equatable {
case invalidURLFormat
}

extension URLValidatorError: LocalizedError {
public var errorDescription: String? {
switch self {
case .invalidURLFormat:
return LocalizedString.getValue("addVault.enterSharePointURL.error.invalidURL")
}
}
}

public enum URLValidator {
public static func validateSharePointURL(urlString: String) throws {
let pattern = #"^https:\/\/[a-zA-Z0-9\-]+\.sharepoint\.com\/sites\/[a-zA-Z0-9\-]+$"#
let regex = try NSRegularExpression(pattern: pattern)
let range = NSRange(location: 0, length: urlString.utf16.count)
if regex.firstMatch(in: urlString, options: [], range: range) == nil {
throw URLValidatorError.invalidURLFormat
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class OpenExistingVaultCoordinator: AccountListing, CloudChoosing, DefaultShowEd
}

func start() {
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"))
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"))
let chooseCloudVC = ChooseCloudViewController(viewModel: viewModel)
chooseCloudVC.title = LocalizedString.getValue("addVault.openExistingVault.title")
chooseCloudVC.coordinator = self
Expand Down
10 changes: 5 additions & 5 deletions Cryptomator/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
DropboxSetup.constants = DropboxSetup(appKey: CloudAccessSecrets.dropboxAppKey, sharedContainerIdentifier: nil, keychainService: CryptomatorConstants.mainAppBundleId, forceForegroundSession: true)
GoogleDriveSetup.constants = GoogleDriveSetup(clientId: CloudAccessSecrets.googleDriveClientId, redirectURL: CloudAccessSecrets.googleDriveRedirectURL!, sharedContainerIdentifier: nil)
do {
let oneDriveConfiguration = MSALPublicClientApplicationConfig(clientId: CloudAccessSecrets.oneDriveClientId, redirectUri: CloudAccessSecrets.oneDriveRedirectURI, authority: nil)
oneDriveConfiguration.cacheConfig.keychainSharingGroup = CryptomatorConstants.mainAppBundleId
let oneDriveClientApplication = try MSALPublicClientApplication(configuration: oneDriveConfiguration)
OneDriveSetup.constants = OneDriveSetup(clientApplication: oneDriveClientApplication, sharedContainerIdentifier: nil)
let microsoftGraphConfiguration = MSALPublicClientApplicationConfig(clientId: CloudAccessSecrets.microsoftGraphClientId, redirectUri: CloudAccessSecrets.microsoftGraphRedirectURI, authority: nil)
microsoftGraphConfiguration.cacheConfig.keychainSharingGroup = CryptomatorConstants.mainAppBundleId
let microsoftGraphClientApplication = try MSALPublicClientApplication(configuration: microsoftGraphConfiguration)
MicrosoftGraphSetup.constants = MicrosoftGraphSetup(clientApplication: microsoftGraphClientApplication, sharedContainerIdentifier: nil)
} catch {
DDLogError("Setting up OneDrive failed with error: \(error)")
}
Expand Down Expand Up @@ -87,7 +87,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
}
} else if url.scheme == CloudAccessSecrets.googleDriveRedirectURLScheme {
return GoogleDriveAuthenticator.currentAuthorizationFlow?.resumeExternalUserAgentFlow(with: url) ?? false
} else if url.scheme == CloudAccessSecrets.oneDriveRedirectURIScheme {
} else if url.scheme == CloudAccessSecrets.microsoftGraphRedirectURIScheme {
return MSALPublicClientApplication.handleMSALResponse(url, sourceApplication: options[.sourceApplication] as? String)
}
return false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ class AccountListViewController: ListViewController<AccountCellContent>, ASWebAu

private func supportsEditing(_ cloudProviderType: CloudProviderType) -> Bool {
switch cloudProviderType {
case .box, .dropbox, .googleDrive, .localFileSystem, .oneDrive, .pCloud:
case .box, .dropbox, .googleDrive, .localFileSystem, .oneDrive, .sharePoint, .pCloud:
return false
case .s3, .webDAV:
return true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ class AccountListViewModel: AccountListViewModelProtocol {
case .oneDrive:
let credential = try OneDriveCredential(with: accountInfo.accountUID)
return try createAccountCellContent(for: credential)
case .sharePoint:
let credential = try SharePointCredential(with: accountInfo.accountUID)
return try createAccountCellContent(for: credential)
case .pCloud:
return createAccountCellContentPlaceholder()
case .s3:
Expand Down Expand Up @@ -125,6 +128,11 @@ class AccountListViewModel: AccountListViewModelProtocol {
return AccountCellContent(mainLabelText: username, detailLabelText: nil)
}

func createAccountCellContent(for credential: SharePointCredential) throws -> AccountCellContent {
let username = try credential.getUsername()
return AccountCellContent(mainLabelText: username, detailLabelText: nil)
}

func createAccountCellContent(for credential: PCloudCredential) -> Promise<AccountCellContent> {
return credential.getUsername().then { username in
AccountCellContent(mainLabelText: username, detailLabelText: nil)
Expand Down
Loading

0 comments on commit d76c000

Please sign in to comment.