Skip to content

Commit d69ef5f

Browse files
committed
Merge branch 'release/2.1.0'
2 parents d31c2fc + 45e6e25 commit d69ef5f

File tree

142 files changed

+6569
-758
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

142 files changed

+6569
-758
lines changed

.swiftformat

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
--minversion 0.49.0
1+
--minversion 0.49.2
22

33
# format options
44

@@ -15,4 +15,4 @@
1515
# rules
1616

1717
--enable isEmpty
18-
--disable redundantReturn,trailingClosures
18+
--disable redundantReturn,trailingClosures,wrapMultilineStatementBraces

Cryptomator.xcodeproj/project.pbxproj

+177-21
Large diffs are not rendered by default.

Cryptomator.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved

+4-4
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@
2424
"repositoryURL": "https://github.com/cryptomator/cloud-access-swift.git",
2525
"state": {
2626
"branch": null,
27-
"revision": "a033fc732b5c7baa2dbc6134591e6fd5345c620e",
28-
"version": "1.1.2"
27+
"revision": "49555f1a9348003513f933b7a47845b658630b47",
28+
"version": "1.1.3"
2929
}
3030
},
3131
{
@@ -42,8 +42,8 @@
4242
"repositoryURL": "https://github.com/cryptomator/cryptolib-swift.git",
4343
"state": {
4444
"branch": null,
45-
"revision": "9bf1bece50de1a6f45410cf541ef346cc16b9fcc",
46-
"version": "1.0.0"
45+
"revision": "ac2d182a54cc3dc9e3025c906d045c4a8e76ccc4",
46+
"version": "1.0.1"
4747
}
4848
},
4949
{

Cryptomator/AddVault/ChooseCloudViewController.swift

+4
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ class ChooseCloudViewController: BaseUITableViewController {
4444
return cell
4545
}
4646

47+
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
48+
return viewModel.headerTitle
49+
}
50+
4751
// MARK: - UITableViewDelegate
4852

4953
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

Cryptomator/AddVault/ChooseCloudViewModel.swift

-1
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,4 @@ import CryptomatorCommonCore
1111
struct ChooseCloudViewModel {
1212
let clouds: [CloudProviderType]
1313
let headerTitle: String
14-
let headerUppercased = false
1514
}

Cryptomator/AddVault/CreateNewVault/SetVaultNameViewModel.swift

+3-12
Original file line numberDiff line numberDiff line change
@@ -33,21 +33,15 @@ class SetVaultNameViewModel: SingleSectionTableViewModel, SetVaultNameViewModelP
3333
return vaultNameCellViewModel.input.value.trimmingCharacters(in: .whitespacesAndNewlines)
3434
}
3535

36-
// disallowed characters \ / : * ? " < > |
37-
// cannot end with .
38-
// swiftlint:disable:next force_try
39-
private let regex = try! NSRegularExpression(pattern: "[\\\\/:\\*\\?\"<>\\|]|\\.$")
40-
4136
private lazy var subscribers = Set<AnyCancellable>()
4237

38+
// disallowed characters \ / : * ? " < > |
39+
// cannot end with .
4340
func getValidatedVaultName() throws -> String {
4441
guard !trimmedVaultName.isEmpty else {
4542
throw SetVaultNameViewModelError.emptyVaultName
4643
}
47-
let range = NSRange(location: 0, length: trimmedVaultName.utf16.count)
48-
guard regex.firstMatch(in: trimmedVaultName, options: [], range: range) == nil else {
49-
throw SetVaultNameViewModelError.invalidInput
50-
}
44+
try ItemNameValidator.validateName(trimmedVaultName)
5145
return trimmedVaultName
5246
}
5347

@@ -61,14 +55,11 @@ class SetVaultNameViewModel: SingleSectionTableViewModel, SetVaultNameViewModelP
6155

6256
enum SetVaultNameViewModelError: LocalizedError {
6357
case emptyVaultName
64-
case invalidInput
6558

6659
var errorDescription: String? {
6760
switch self {
6861
case .emptyVaultName:
6962
return LocalizedString.getValue("addVault.createNewVault.setVaultName.error.emptyVaultName")
70-
case .invalidInput:
71-
return LocalizedString.getValue("addVault.createNewVault.setVaultName.error.invalidInput")
7263
}
7364
}
7465
}

Cryptomator/AppDelegate.swift

+3-4
Original file line numberDiff line numberDiff line change
@@ -68,17 +68,16 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
6868
SKPaymentQueue.default().add(StoreObserver.shared)
6969

7070
// Create window
71-
let navigationController = BaseNavigationController()
72-
coordinator = MainCoordinator(navigationController: navigationController)
71+
coordinator = MainCoordinator()
7372
#if SNAPSHOTS
74-
coordinator = SnapshotCoordinator(navigationController: navigationController)
73+
coordinator = SnapshotCoordinator()
7574
UIView.setAnimationsEnabled(false)
7675
#endif
7776
coordinator?.start()
7877
StoreObserver.shared.fallbackDelegate = coordinator
7978
window = UIWindow(frame: UIScreen.main.bounds)
8079
window?.tintColor = UIColor(named: "primary")
81-
window?.rootViewController = navigationController
80+
window?.rootViewController = coordinator?.rootViewController
8281
window?.makeKeyAndVisible()
8382
return true
8483
}

Cryptomator/Common/BaseNavigationController.swift

+15
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,21 @@ class BaseNavigationController: UINavigationController {
1313
return .lightContent
1414
}
1515

16+
override var modalPresentationStyle: UIModalPresentationStyle {
17+
get {
18+
if useDefaultModalPresentationStyle {
19+
return .formSheet
20+
}
21+
return super.modalPresentationStyle
22+
}
23+
set {
24+
useDefaultModalPresentationStyle = false
25+
super.modalPresentationStyle = newValue
26+
}
27+
}
28+
29+
private var useDefaultModalPresentationStyle = true
30+
1631
override func viewDidLoad() {
1732
super.viewDidLoad()
1833
let appearance = UINavigationBarAppearance()

Cryptomator/Common/Bindable.swift

+12
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,15 @@ class Bindable<Value> {
1616
self.value = value
1717
}
1818
}
19+
20+
extension Bindable: Equatable where Value: Equatable {
21+
static func == (lhs: Bindable<Value>, rhs: Bindable<Value>) -> Bool {
22+
return lhs.value == rhs.value
23+
}
24+
}
25+
26+
extension Bindable: Hashable where Value: Hashable {
27+
func hash(into hasher: inout Hasher) {
28+
hasher.combine(value)
29+
}
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
//
2+
// CheckMarkCell.swift
3+
// Cryptomator
4+
//
5+
// Created by Philipp Schmid on 14.01.22.
6+
// Copyright © 2022 Skymatic GmbH. All rights reserved.
7+
//
8+
9+
import Combine
10+
import UIKit
11+
12+
class CheckMarkCell: UITableViewCell, ConfigurableTableViewCell {
13+
private var subscriber: AnyCancellable?
14+
15+
func configure(with viewModel: TableViewCellViewModel) {
16+
guard let viewModel = viewModel as? CheckMarkCellViewModelType else {
17+
return
18+
}
19+
textLabel?.text = viewModel.title
20+
subscriber = viewModel.isSelected.$value.receive(on: DispatchQueue.main).sink { [weak self] isSelected in
21+
if isSelected {
22+
self?.accessoryType = .checkmark
23+
} else {
24+
self?.accessoryType = .none
25+
}
26+
}
27+
}
28+
}
29+
30+
protocol CheckMarkCellViewModelType {
31+
var title: String? { get }
32+
var isSelected: Bindable<Bool> { get }
33+
}

Cryptomator/Common/HeaderFooter/AttributedTextHeaderFooterView.swift

+13-7
Original file line numberDiff line numberDiff line change
@@ -65,14 +65,20 @@ class BindableAttributedTextHeaderFooterView: AttributedTextHeaderFooterView {
6565
guard let viewModel = viewModel as? BindableAttributedTextHeaderFooterViewModel else {
6666
return
6767
}
68+
textView.attributedText = viewModel.attributedText.value
6869
viewModel.attributedText.$value.receive(on: DispatchQueue.main).sink(receiveValue: { [weak self] attributedText in
69-
UIView.setAnimationsEnabled(false)
70-
self?.tableView?.performBatchUpdates({
71-
self?.textView.attributedText = attributedText
72-
self?.textView.font = self?.textLabel?.font
73-
self?.textView.textColor = self?.textLabel?.textColor
74-
})
75-
UIView.setAnimationsEnabled(true)
70+
self?.textView.attributedText = attributedText
71+
self?.textView.font = self?.textLabel?.font
72+
self?.textView.textColor = self?.textLabel?.textColor
73+
self?.setNeedsLayout()
74+
guard self?.tableView?.window != nil else {
75+
return
76+
}
77+
UIView.performWithoutAnimation {
78+
self?.tableView?.performBatchUpdates({
79+
// performBatchUpdates call is needed to actually trigger an tableView (layout) update
80+
})
81+
}
7682
}).store(in: &subscriber)
7783
}
7884
}

Cryptomator/Common/HeaderFooter/BaseHeaderFooterView.swift

+12-6
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,18 @@ class BaseHeaderFooterView: UITableViewHeaderFooterView, HeaderFooterViewModelCo
1616

1717
func configure(with viewModel: HeaderFooterViewModel) {
1818
textLabel?.numberOfLines = 0
19-
subscriber = viewModel.title.$value.sink(receiveValue: { [weak self] text in
20-
UIView.setAnimationsEnabled(false)
21-
self?.tableView?.performBatchUpdates({
22-
self?.textLabel?.text = text
23-
})
24-
UIView.setAnimationsEnabled(true)
19+
textLabel?.text = viewModel.title.value
20+
subscriber = viewModel.title.$value.receive(on: DispatchQueue.main).sink(receiveValue: { [weak self] text in
21+
self?.textLabel?.text = text
22+
self?.setNeedsLayout()
23+
guard self?.tableView?.window != nil else {
24+
return
25+
}
26+
UIView.performWithoutAnimation {
27+
self?.tableView?.performBatchUpdates({
28+
// performBatchUpdates call is needed to actually trigger an tableView (layout) update
29+
})
30+
}
2531
})
2632
}
2733
}

Cryptomator/Common/ListViewController.swift

+37-5
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ class ListViewController<T: TableViewCellViewModel>: BaseUITableViewController {
2222
var dataSource: EditableDataSource<Section, T>?
2323
private let viewModel: ListViewModel
2424
private lazy var emptyListMessage = EmptyListMessage(message: viewModel.emptyListMessage)
25+
private var firstTimeLoading = true
26+
private var lastSelectedCellViewModel: T?
2527

2628
init(viewModel: ListViewModel) {
2729
self.viewModel = viewModel
@@ -60,23 +62,22 @@ class ListViewController<T: TableViewCellViewModel>: BaseUITableViewController {
6062
snapshot.appendItems(cells.compactMap({ $0 as? T }))
6163
dataSource?.apply(snapshot, animatingDifferences: false) { [weak self] in
6264
if snapshot.numberOfItems == 0 {
63-
self?.header.isHidden = true
64-
self?.tableView.backgroundView = self?.emptyListMessage
65-
// Prevents `EmptyListMessage` from being placed under the navigation bar
66-
self?.tableView.contentInsetAdjustmentBehavior = .never
67-
self?.tableView.separatorStyle = .none
65+
self?.hideHeaderAndShowEmptyListMessage()
6866
} else {
6967
self?.header.isHidden = false
7068
self?.tableView.backgroundView = nil
7169
self?.tableView.separatorStyle = .singleLine
7270
self?.tableView.contentInsetAdjustmentBehavior = .automatic
71+
self?.restoreSelection()
7372
}
73+
self?.firstTimeLoading = false
7474
}
7575
}
7676

7777
override func setEditing(_ editing: Bool, animated: Bool) {
7878
super.setEditing(editing, animated: animated)
7979
header.isEditing = editing
80+
restoreSelection()
8081
}
8182

8283
@objc func editButtonToggled() {
@@ -126,6 +127,10 @@ class ListViewController<T: TableViewCellViewModel>: BaseUITableViewController {
126127
return configuration
127128
}
128129

130+
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
131+
lastSelectedCellViewModel = dataSource?.itemIdentifier(for: indexPath)
132+
}
133+
129134
// MARK: - Internal
130135

131136
func removeRow(at indexPath: IndexPath) throws {
@@ -166,4 +171,31 @@ class ListViewController<T: TableViewCellViewModel>: BaseUITableViewController {
166171
// no-op
167172
}
168173
}
174+
175+
func hideHeaderAndShowEmptyListMessage() {
176+
hideHeaderAndShowEmptyListMessage(animated: !firstTimeLoading)
177+
}
178+
179+
func hideHeaderAndShowEmptyListMessage(animated: Bool) {
180+
if animated {
181+
hideHeaderAnimated().then(showEmptyListMessageAnimated)
182+
} else {
183+
header.isHidden = true
184+
tableView.backgroundView = emptyListMessage
185+
// Prevents `EmptyListMessage` from being placed under the navigation bar
186+
tableView.contentInsetAdjustmentBehavior = .never
187+
tableView.separatorStyle = .none
188+
}
189+
}
190+
191+
private func restoreSelection() {
192+
guard let splitViewController = splitViewController, !splitViewController.isCollapsed else {
193+
return
194+
}
195+
let snapshot = dataSource?.snapshot()
196+
let maybeLastSelectedCellViewModel = snapshot?.itemIdentifiers.first(where: { $0.hashValue == lastSelectedCellViewModel?.hashValue })
197+
if let lastSelectedCellViewModel = maybeLastSelectedCellViewModel, let lastSelectedIndexPath = dataSource?.indexPath(for: lastSelectedCellViewModel) {
198+
tableView.selectRow(at: lastSelectedIndexPath, animated: true, scrollPosition: .none)
199+
}
200+
}
169201
}

0 commit comments

Comments
 (0)