diff --git a/Vault/Sources/VaultiOS/Views/Backup/AutoBackupSettingsView.swift b/Vault/Sources/VaultiOS/Views/Backup/AutoBackupSettingsView.swift index f13db5abf..7e7da53da 100644 --- a/Vault/Sources/VaultiOS/Views/Backup/AutoBackupSettingsView.swift +++ b/Vault/Sources/VaultiOS/Views/Backup/AutoBackupSettingsView.swift @@ -40,10 +40,14 @@ struct AutoBackupSettingsView: View { } footer: { Text(footerText) } - .sheet(isPresented: $isShowingFolderPicker) { - FolderPickerView { url in - configureSelectedProvider(with: url) - } + .fileImporter( + isPresented: $isShowingFolderPicker, + allowedContentTypes: [.folder], + ) { result in + // A cancelled picker is not an error worth surfacing; the provider + // reports any real configuration failure through its own status. + guard case let .success(url) = result else { return } + configureSelectedProvider(with: url) } .task { // Initialize with current values @@ -251,35 +255,3 @@ struct AutoBackupSettingsView: View { } } } - -// MARK: - Folder Picker - -private struct FolderPickerView: UIViewControllerRepresentable { - let onFolderSelected: (URL) -> Void - - func makeUIViewController(context: Context) -> UIDocumentPickerViewController { - let picker = UIDocumentPickerViewController(forOpeningContentTypes: [.folder]) - picker.delegate = context.coordinator - picker.allowsMultipleSelection = false - return picker - } - - func updateUIViewController(_: UIDocumentPickerViewController, context _: Context) {} - - func makeCoordinator() -> Coordinator { - Coordinator(onFolderSelected: onFolderSelected) - } - - class Coordinator: NSObject, UIDocumentPickerDelegate { - let onFolderSelected: (URL) -> Void - - init(onFolderSelected: @escaping (URL) -> Void) { - self.onFolderSelected = onFolderSelected - } - - func documentPicker(_: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) { - guard let url = urls.first else { return } - onFolderSelected(url) - } - } -} diff --git a/Vault/Sources/VaultiOS/Views/Settings/OpenSourceView.swift b/Vault/Sources/VaultiOS/Views/Settings/OpenSourceView.swift index cdbf2ee57..05535b5d3 100644 --- a/Vault/Sources/VaultiOS/Views/Settings/OpenSourceView.swift +++ b/Vault/Sources/VaultiOS/Views/Settings/OpenSourceView.swift @@ -4,46 +4,39 @@ import VaultSettings struct OpenSourceView: View { var body: some View { - GeometryReader { geometry in - ScrollView { - container - .padding(.vertical, 16) - .padding(24) - .frame(maxWidth: .infinity) - .frame(minHeight: geometry.size.height) - } + Form { + headerSection + aboutSection + linkSection } + .navigationBarTitleDisplayMode(.inline) } - private var container: some View { - VStack(alignment: .center, spacing: 24) { - headerContent - paragraphContent - } - } - - private var headerContent: some View { - VStack(alignment: .center, spacing: 8) { - Image(systemName: "figure.2.arms.open") - .font(.largeTitle.bold()) - Text(OpenSourceStrings.title) - .font(.largeTitle.bold()) - } - .multilineTextAlignment(.center) + private var headerSection: some View { + PlaceholderView( + systemIcon: "figure.2.arms.open", + title: OpenSourceStrings.title, + ) + .padding() + .containerRelativeFrame(.horizontal) } - private var paragraphContent: some View { - VStack(alignment: .leading, spacing: 8) { + private var aboutSection: some View { + Section { Text(OpenSourceStrings.aboutOpenSource) Text(OpenSourceStrings.aboutPrivacy) + } + .foregroundStyle(.secondary) + } + private var linkSection: some View { + Section { Link(destination: OpenSourceStrings.openSourceLink) { - Text(OpenSourceStrings.aboutLink) + FormRow(image: Image(systemName: "chevron.left.forwardslash.chevron.right"), color: .purple) { + Text(OpenSourceStrings.aboutLink) + } } - .foregroundStyle(.tint) } - .foregroundStyle(.secondary) - .multilineTextAlignment(.leading) } } diff --git a/Vault/Sources/VaultiOSAutofill/Views/VaultAutofillConfigurationView.swift b/Vault/Sources/VaultiOSAutofill/Views/VaultAutofillConfigurationView.swift index b30c309f1..aab07db19 100644 --- a/Vault/Sources/VaultiOSAutofill/Views/VaultAutofillConfigurationView.swift +++ b/Vault/Sources/VaultiOSAutofill/Views/VaultAutofillConfigurationView.swift @@ -9,81 +9,66 @@ struct VaultAutofillConfigurationView: View { } var body: some View { - ScrollView { - container - .padding(.vertical, 16) - .padding(24) - .containerRelativeFrame([.horizontal, .vertical]) + List { + headerSection + featuresSection + } + .safeAreaInset(edge: .bottom) { + Button { + viewModel.dismiss() + } label: { + Text("Continue") + .frame(maxWidth: .infinity) + } + .buttonStyle(.borderedProminent) + .controlSize(.large) + .padding() } - .containerRelativeFrame([.horizontal, .vertical]) } - private var container: some View { - VStack(alignment: .center, spacing: 32) { - Spacer() - + private var headerSection: some View { + Section { VStack(alignment: .center, spacing: 12) { Image(systemName: "number.circle.fill") - .font(.system(size: 64, weight: .medium)) - .foregroundStyle(.blue) + .font(.system(size: 64)) + .foregroundStyle(.tint) .symbolRenderingMode(.hierarchical) Text("OTP Autofill") - .font(.system(size: 28, weight: .bold)) - } + .font(.title.bold()) - VStack(alignment: .center, spacing: 24) { Text("Your OTP codes are now available for autofill") - .font(.title3) - .fontWeight(.semibold) - - VStack(alignment: .leading, spacing: 16) { - featureRow( - icon: "network", - text: "OTP codes appear on their configured domain names", - ) - - featureRow( - icon: "arrow.triangle.2.circlepath", - text: "Codes update automatically based on your vault items", - ) - } - .frame(maxWidth: 400) + .font(.headline) + .foregroundStyle(.secondary) } + .multilineTextAlignment(.center) + .frame(maxWidth: .infinity) + .padding(.vertical) + .listRowSeparator(.hidden) + } + } - Spacer() + private var featuresSection: some View { + Section { + featureRow( + icon: "network", + text: "OTP codes appear on their configured domain names", + ) - Button { - viewModel.dismiss() - } label: { - Text("Continue") - .frame(maxWidth: .infinity) - } - .buttonStyle(.borderedProminent) - .controlSize(.large) + featureRow( + icon: "arrow.triangle.2.circlepath", + text: "Codes update automatically based on your vault items", + ) } - .multilineTextAlignment(.center) } private func featureRow(icon: String, text: String) -> some View { - HStack(alignment: .top, spacing: 16) { + Label { + Text(text) + } icon: { Image(systemName: icon) - .font(.system(size: 24)) - .foregroundStyle(.blue) + .foregroundStyle(.tint) .symbolRenderingMode(.hierarchical) - .frame(width: 32, height: 32) - - Text(text) - .font(.body) - .foregroundStyle(.primary) - .multilineTextAlignment(.leading) - .frame(maxWidth: .infinity, alignment: .leading) } - .padding(.horizontal, 16) - .padding(.vertical, 12) - .background( - RoundedRectangle(cornerRadius: 12) - .fill(Color(uiColor: .secondarySystemGroupedBackground)), - ) } } diff --git a/Vault/Tests/VaultiOSAutofillTests/__Snapshots__/VaultAutofillConfigurationViewSnapshotTests/layout.1.png b/Vault/Tests/VaultiOSAutofillTests/__Snapshots__/VaultAutofillConfigurationViewSnapshotTests/layout.1.png index efe7fb56c..e1a1ce78a 100644 Binary files a/Vault/Tests/VaultiOSAutofillTests/__Snapshots__/VaultAutofillConfigurationViewSnapshotTests/layout.1.png and b/Vault/Tests/VaultiOSAutofillTests/__Snapshots__/VaultAutofillConfigurationViewSnapshotTests/layout.1.png differ diff --git a/Vault/Tests/VaultiOSTests/__Snapshots__/OpenSourceViewSnapshotTests/deviceSize.1.png b/Vault/Tests/VaultiOSTests/__Snapshots__/OpenSourceViewSnapshotTests/deviceSize.1.png index 4925b293f..68d5f698b 100644 Binary files a/Vault/Tests/VaultiOSTests/__Snapshots__/OpenSourceViewSnapshotTests/deviceSize.1.png and b/Vault/Tests/VaultiOSTests/__Snapshots__/OpenSourceViewSnapshotTests/deviceSize.1.png differ