From c1ddad4e413c2b19d27ba71871e9f45c0b402877 Mon Sep 17 00:00:00 2001 From: Alexey Martemyanov Date: Tue, 12 Dec 2023 21:11:58 +0600 Subject: [PATCH] design adjustments and fixes --- .../DataImport/View/DataImportErrorView.swift | 2 +- .../View/DataImportNoDataView.swift | 2 +- .../View/DataImportProfilePicker.swift | 29 +- .../View/DataImportSummaryView.swift | 35 +- .../View/DataImportTypePicker.swift | 2 +- .../DataImport/View/DataImportView.swift | 29 +- .../DataImport/View/FileImportView.swift | 317 ++++++++++++++---- .../DataImport/View/ReportFeedbackView.swift | 3 +- DuckDuckGo/Localizable.xcstrings | 149 ++++++-- 9 files changed, 434 insertions(+), 134 deletions(-) diff --git a/DuckDuckGo/DataImport/View/DataImportErrorView.swift b/DuckDuckGo/DataImport/View/DataImportErrorView.swift index 72aafd457c..b730d4483b 100644 --- a/DuckDuckGo/DataImport/View/DataImportErrorView.swift +++ b/DuckDuckGo/DataImport/View/DataImportErrorView.swift @@ -28,7 +28,7 @@ struct DataImportErrorView: View { VStack(alignment: .leading, spacing: 8) { Text("We were unable to import \(dataType.displayName) directly from \(source.importSourceName).", comment: "Message when data import fails from a browser. %1$@ - Bookmarks or Passwords; %2$@ - a browser name") - .font(.headline) + .bold() Text("Let’s try doing it manually. It won’t take long.", comment: "Suggestion to switch to a Manual File Data Import when data import fails.") diff --git a/DuckDuckGo/DataImport/View/DataImportNoDataView.swift b/DuckDuckGo/DataImport/View/DataImportNoDataView.swift index 5e5f2c7324..e694842e22 100644 --- a/DuckDuckGo/DataImport/View/DataImportNoDataView.swift +++ b/DuckDuckGo/DataImport/View/DataImportNoDataView.swift @@ -27,7 +27,7 @@ struct DataImportNoDataView: View { var body: some View { VStack(alignment: .leading, spacing: 8) { Text("We couldn‘t find any \(dataType.displayName)…", comment: "Data import error message: Bookmarks or Passwords (%@) weren‘t found.") - .font(.headline) + .bold() Text("You could try importing \(dataType.displayName) manually.", comment: "Data import error subtitle: suggestion to import Bookmarks or Passwords (%@) manually by selecting a CSV or HTML file.") diff --git a/DuckDuckGo/DataImport/View/DataImportProfilePicker.swift b/DuckDuckGo/DataImport/View/DataImportProfilePicker.swift index 958f2e88f7..58da32140b 100644 --- a/DuckDuckGo/DataImport/View/DataImportProfilePicker.swift +++ b/DuckDuckGo/DataImport/View/DataImportProfilePicker.swift @@ -30,22 +30,20 @@ struct DataImportProfilePicker: View { var body: some View { VStack(alignment: .leading, spacing: 10) { - if profiles.count > 1 { - Text("Select Profile:", comment: "Browser Profile picker title for Data Import") - .font(.headline) + Text("Select Profile:", comment: "Browser Profile picker title for Data Import") + .bold() - Picker(selection: Binding { - selectedProfile.flatMap(profiles.firstIndex(of:)) ?? 0 - } set: { - selectedProfile = profiles[safe: $0] - }) { - ForEach(profiles.indices, id: \.self) { idx in - Text(profiles[idx].profileName) - } - } label: {} - .pickerStyle(.menu) - .controlSize(.large) - } + Picker(selection: Binding { + selectedProfile.flatMap(profiles.firstIndex(of:)) ?? 0 + } set: { + selectedProfile = profiles[safe: $0] + }) { + ForEach(profiles.indices, id: \.self) { idx in + Text(profiles[idx].profileName) + } + } label: {} + .pickerStyle(.menu) + .controlSize(.large) } } @@ -67,4 +65,5 @@ struct DataImportProfilePicker: View { }) .padding() .frame(width: 512) + .font(.custom("SF Pro Text", size: 13)) } diff --git a/DuckDuckGo/DataImport/View/DataImportSummaryView.swift b/DuckDuckGo/DataImport/View/DataImportSummaryView.swift index 580a22eeca..87f51a354c 100644 --- a/DuckDuckGo/DataImport/View/DataImportSummaryView.swift +++ b/DuckDuckGo/DataImport/View/DataImportSummaryView.swift @@ -47,28 +47,34 @@ struct DataImportSummaryView: View { case .fileImportComplete(.passwords): Text("Password import complete. You can now delete the saved passwords file.", comment: "message about Passwords Data Import completion") } - }().padding(.bottom, 16) + }().padding(.bottom, 4) ForEach(model.results, id: \.dataType) { item in switch (item.dataType, item.result) { case (.bookmarks, .success(let summary)): HStack { successImage() - Text("Bookmarks: \(summary.successful)", + Text("Bookmarks:", comment: "Data import summary format of how many bookmarks (%lld) were successfully imported.") + + Text(" " as String) + + Text(String(summary.successful)).bold() } if summary.duplicate > 0 { HStack { failureImage() - Text("Duplicate Bookmarks Skipped: \(summary.duplicate)", + Text("Duplicate Bookmarks Skipped:", comment: "Data import summary format of how many duplicate bookmarks (%lld) were skipped during import.") + + Text(" " as String) + + Text(String(summary.duplicate)).bold() } } if summary.failed > 0 { HStack { failureImage() - Text("Bookmark import failed: \(summary.failed)", + Text("Bookmark import failed:", comment: "Data import summary format of how many bookmarks (%lld) failed to import.") + + Text(" " as String) + + Text(String(summary.failed)).bold() } } @@ -89,14 +95,18 @@ struct DataImportSummaryView: View { case (.passwords, .success(let summary)): HStack { successImage() - Text("Passwords: \(summary.successful)", + Text("Passwords:", comment: "Data import summary format of how many passwords (%lld) were successfully imported.") + + Text(" " as String) + + Text(String(summary.successful)).bold() } if summary.failed > 0 { HStack { failureImage() - Text("Passwords import failed: \(summary.failed)", + Text("Passwords import failed: ", comment: "Data import summary format of how many passwords (%lld) failed to import.") + + Text(" " as String) + + Text(String(summary.failed)).bold() } } } @@ -118,11 +128,14 @@ private func failureImage() -> some View { #Preview { VStack { - DataImportSummaryView(model: .init(source: .chrome, summary: [ - .bookmarks: .success(.init(successful: 123, duplicate: 456, failed: 7890)), - .passwords: .success(.init(successful: 123, duplicate: 456, failed: 7890)) - ])) - .padding(EdgeInsets(top: 20, leading: 20, bottom: 16, trailing: 20)) + HStack { + DataImportSummaryView(model: .init(source: .chrome, summary: [ + .bookmarks: .success(.init(successful: 123, duplicate: 456, failed: 7890)), + .passwords: .success(.init(successful: 123, duplicate: 456, failed: 7890)) + ])) + .padding(EdgeInsets(top: 20, leading: 20, bottom: 16, trailing: 20)) + Spacer() + } } .frame(width: 512) } diff --git a/DuckDuckGo/DataImport/View/DataImportTypePicker.swift b/DuckDuckGo/DataImport/View/DataImportTypePicker.swift index b82b981915..8c872af0f9 100644 --- a/DuckDuckGo/DataImport/View/DataImportTypePicker.swift +++ b/DuckDuckGo/DataImport/View/DataImportTypePicker.swift @@ -30,7 +30,7 @@ struct DataImportTypePicker: View { VStack(alignment: .leading) { Text("Select data to import:", comment: "Data Import section title for checkboxes of data type to import: Passwords or Bookmarks.") - .font(.headline) + .bold() ForEach(DataImport.DataType.allCases, id: \.self) { dataType in // display all types for a browser disabling unavailable options diff --git a/DuckDuckGo/DataImport/View/DataImportView.swift b/DuckDuckGo/DataImport/View/DataImportView.swift index 77ae943f54..270701ef5f 100644 --- a/DuckDuckGo/DataImport/View/DataImportView.swift +++ b/DuckDuckGo/DataImport/View/DataImportView.swift @@ -65,6 +65,11 @@ struct DataImportView: View { .padding(.trailing, 20) .padding(.bottom, 32) + // if import in progress… + if let importProgress = model.importProgress { + progressView(importProgress) + } + Divider() viewFooter() @@ -78,6 +83,7 @@ struct DataImportView: View { } #endif } + .font(.custom("SF Pro Text", size: 13)) .frame(width: 512) .fixedSize() } @@ -85,7 +91,7 @@ struct DataImportView: View { private func viewHeader() -> some View { VStack(alignment: .leading, spacing: 0) { Text(UserText.importDataTitle) - .font(.headline) + .bold() .padding(.bottom, 16) // browser to import data from picker popup @@ -105,10 +111,12 @@ struct DataImportView: View { switch model.screen { case .profileAndDataTypesPicker: // Browser Profile picker - DataImportProfilePicker(profileList: model.browserProfiles, - selectedProfile: $model.selectedProfile) - .disabled(model.isImportSourcePickerDisabled) - .padding(.bottom, 24) + if model.browserProfiles?.validImportableProfiles.count ?? 0 > 1 { + DataImportProfilePicker(profileList: model.browserProfiles, + selectedProfile: $model.selectedProfile) + .disabled(model.isImportSourcePickerDisabled) + .padding(.bottom, 24) + } // Bookmarks/Passwords checkboxes DataImportTypePicker(viewModel: $model) @@ -129,9 +137,10 @@ struct DataImportView: View { if !summaryTypes.isEmpty { DataImportSummaryView(model, dataTypes: summaryTypes) .padding(.bottom, 24) + } // if no data to import - } else if model.summary(for: dataType)?.isEmpty == true + if model.summary(for: dataType)?.isEmpty == true || model.error(for: dataType)?.errorType == .noData { DataImportNoDataView(source: model.importSource, dataType: dataType) @@ -159,11 +168,6 @@ struct DataImportView: View { ReportFeedbackView(model: $model.reportModel) } - - // Import in progress… - if let importProgress = model.importProgress { - progressView(importProgress) - } } } @@ -459,7 +463,7 @@ extension DataImportViewModel.ButtonType { } } - let viewModel = DataImportViewModel(importSource: .chrome) { browser in + let viewModel = DataImportViewModel(importSource: .bookmarksHTML) { browser in guard case .chrome = browser else { print("empty profiles") return .init(browser: browser, profiles: []) @@ -514,6 +518,7 @@ extension DataImportViewModel.ButtonType { }) PreviewPreferencesView() + Spacer() } .frame(minHeight: 666) diff --git a/DuckDuckGo/DataImport/View/FileImportView.swift b/DuckDuckGo/DataImport/View/FileImportView.swift index 8e43de1afd..b3e893c7c3 100644 --- a/DuckDuckGo/DataImport/View/FileImportView.swift +++ b/DuckDuckGo/DataImport/View/FileImportView.swift @@ -38,7 +38,7 @@ struct FileImportView: View { } var body: some View { - VStack(alignment: .leading, spacing: 10) { + VStack(alignment: .leading, spacing: 16) { { switch dataType { case .bookmarks: @@ -46,10 +46,29 @@ struct FileImportView: View { case .passwords: Text("Import Passwords") } - }().font(.headline) + }().bold() - InstructionsView(fontName: "SF Pro Text", fontSize: 13) { + if [.onePassword7, .onePassword8].contains(source) { + HStack { + Image(.info) + Text(""" + You can find your version by selecting **\(source.importSourceName) → About 1Password** from the Menu Bar + """) + Spacer() + } + .padding(EdgeInsets(top: 8, leading: 8, bottom: 8, trailing: 8)) + .background(Color("BlackWhite5")) + .cornerRadius(8) + .overlay( + RoundedRectangle(cornerRadius: 8) + .stroke(Color("SeparatorColor"), + style: StrokeStyle(lineWidth: 1)) + ) + .padding(.top, 8) + .padding(.bottom, 8) + } + InstructionsView { switch (source, dataType) { case (.chrome, .passwords): NSLocalizedString("import.csv.instructions.chrome", value: """ @@ -60,21 +79,39 @@ struct FileImportView: View { %d %@ """, comment: """ Instructions to import Passwords as CSV from Google Chrome browser. - %d is a step number; %s is a Browser name; %@ is for a button image to click + %N$d - step number + %2$s - browser name (Chrome) + %4$@ - hamburger menu icon + %8$@ - “Select Passwords CSV File” button **bold text**; _italic text_ """) source.importSourceName NSImage.menuVertical16 button("Select Passwords CSV File…") - case (.brave, .passwords), - (.chromium, .passwords), - (.coccoc, .passwords), - (.edge, .passwords), - (.vivaldi, .passwords), - (.opera, .passwords), - (.operaGX, .passwords): + case (.brave, .passwords): + NSLocalizedString("import.csv.instructions.brave", value: """ + %d Open **%s** + %d Click %@ to open the application menu then click **Password Manager** + %d Click %@ **at the top left** of the Password Manager and select **Settings** + %d Find “Export Passwords” and click **Download File** + %d Save the passwords file someplace you can find it (e.g. Desktop) + %d %@ + """, comment: """ + Instructions to import Passwords as CSV from Brave browser. + %N$d - step number + %2$s - browser name (Brave) + %4$@, %6$@ - hamburger menu icon + %10$@ - “Select Passwords CSV File” button + **bold text**; _italic text_ + """) + source.importSourceName + NSImage.menuHamburger16 + NSImage.menuHamburger16 + button("Select Passwords CSV File…") + case (.chromium, .passwords), + (.edge, .passwords): NSLocalizedString("import.csv.instructions.chromium", value: """ %d Open **%s** %d In a fresh tab, click %@ then **Password Manager → Settings** @@ -83,7 +120,85 @@ struct FileImportView: View { %d %@ """, comment: """ Instructions to import Passwords as CSV from Chromium-based browsers. - %d is a step number; %s is a Browser name; %@ is for a button image to click + %N$d - step number + %2$s - browser name + %4$@ - hamburger menu icon + %8$@ - “Select Passwords CSV File” button + **bold text**; _italic text_ + """) + source.importSourceName + NSImage.menuVertical16 + button("Select Passwords CSV File…") + + case (.coccoc, .passwords): + NSLocalizedString("import.csv.instructions.coccoc", value: """ + %d Open **%s** + %d Type “_coccoc://settings/passwords_” into the Address field + %d Click %@ (on the right from _Saved Passwords_) and select **Export passwords** + %d Save the passwords file someplace you can find it (e.g. Desktop) + %d %@ + """, comment: """ + Instructions to import Passwords as CSV from Chromium-based browsers. + %N$d - step number + %2$s - browser name (Cốc Cốc) + %5$@ - hamburger menu icon + %8$@ - “Select Passwords CSV File” button + **bold text**; _italic text_ + """) + source.importSourceName + NSImage.menuVertical16 + button("Select Passwords CSV File…") + + case (.opera, .passwords): + NSLocalizedString("import.csv.instructions.opera", value: """ + %d Open **%s** + %d Use the Menu Bar to select **View → Show Password Manager** + %d Select **Settings** + %d Find “Export Passwords” and click **Download File** + %d Save the passwords file someplace you can find it (e.g. Desktop) + %d %@ + """, comment: """ + Instructions to import Passwords as CSV from Chromium-based browsers. + %N$d - step number + %2$s - browser name (Opera) + %8$@ - “Select Passwords CSV File” button + **bold text**; _italic text_ + """) + source.importSourceName + button("Select Passwords CSV File…") + + case (.vivaldi, .passwords): + NSLocalizedString("import.csv.instructions.vivaldi", value: """ + %d Open **%s** + %d Type “_chrome://settings/passwords_” into the Address field + %d Click %@ (on the right from _Saved Passwords_) and select **Export passwords** + %d Save the file someplace you can find it (e.g., Desktop) + %d %@ + """, comment: """ + Instructions to import Passwords exported as CSV from Vivaldi browser. + %N$d - step number + %2$s - browser name (Vivaldi) + %5$@ - menu button icon + %8$@ - “Select Passwords CSV File” button + **bold text**; _italic text_ + """) + source.importSourceName + NSImage.menuVertical16 + button("Select Passwords CSV File…") + + case (.operaGX, .passwords): + NSLocalizedString("import.csv.instructions.operagx", value: """ + %d Open **%s** + %d Use the Menu Bar to select **View → Show Password Manager** + %d Click %@ (on the right from _Saved Passwords_) and select **Export passwords** + %d Save the passwords file someplace you can find it (e.g. Desktop) + %d %@ + """, comment: """ + Instructions to import Passwords as CSV from Chromium-based browsers. + %N$d - step number + %2$s - browser name (Opera GX) + %5$@ - menu button icon + %8$@ - “Select Passwords CSV File” button **bold text**; _italic text_ """) source.importSourceName @@ -92,7 +207,7 @@ struct FileImportView: View { case (.yandex, .passwords): NSLocalizedString("import.csv.instructions.yandex", value: """ - %d Open **Yandex** + %d Open **%s** %d Click %@ to open the application menu then click **Passwords and cards** %d Click %@ then **Export passwords** %d Choose **To a text file (not secure)** and click **Export** @@ -100,7 +215,10 @@ struct FileImportView: View { %d %@ """, comment: """ Instructions to import Passwords as CSV from Yandex Browser. - %d is a step number; %@ is for a button image to click + %N$d - step number + %2$s - browser name (Yandex) + %4$@ - hamburger menu icon + %8$@ - “Select Passwords CSV File” button **bold text**; _italic text_ """) NSImage.menuHamburger16 @@ -108,13 +226,10 @@ struct FileImportView: View { button("Select Passwords CSV File…") case (.brave, .bookmarks), - (.chrome, .bookmarks), - (.chromium, .bookmarks), - (.coccoc, .bookmarks), - (.edge, .bookmarks), - (.vivaldi, .bookmarks), - (.opera, .bookmarks), - (.operaGX, .bookmarks): + (.chrome, .bookmarks), + (.chromium, .bookmarks), + (.coccoc, .bookmarks), + (.edge, .bookmarks): NSLocalizedString("import.html.instructions.chromium", value: """ %d Open **%s** %d Use the Menu Bar to select **Bookmarks → Bookmark Manager** @@ -123,13 +238,67 @@ struct FileImportView: View { %d %@ """, comment: """ Instructions to import Bookmarks exported as HTML from Chromium-based browsers. - %d is a step number; %s is a Browser name; %@ is for a button image to click + %N$d - step number + %2$s - browser name + %5$@ - hamburger menu icon + %8$@ - “Select Bookmarks HTML File” button **bold text**; _italic text_ """) source.importSourceName NSImage.menuVertical16 button("Select Bookmarks HTML File…") + case (.vivaldi, .bookmarks): + NSLocalizedString("import.html.instructions.vivaldi", value: """ + %d Open **%s** + %d Use the Menu Bar to select **File → Export Bookmarks…** + %d Save the file someplace you can find it (e.g., Desktop) + %d %@ + """, comment: """ + Instructions to import Bookmarks exported as HTML from Vivaldi browser. + %N$d - step number + %2$s - browser name (Vivaldi) + %6$@ - “Select Bookmarks HTML File” button + **bold text**; _italic text_ + """) + source.importSourceName + button("Select Bookmarks HTML File…") + + case (.opera, .bookmarks): + NSLocalizedString("import.html.instructions.opera", value: """ + %d Open **%s** + %d Use the Menu Bar to select **Bookmarks → Bookmarks** + %d Click **Open full Bookmarks view…** in the bottom left + %d Click **Import/Export…** in the bottom left and select **Export Bookmarks** + %d Save the file someplace you can find it (e.g., Desktop) + %d %@ + """, comment: """ + Instructions to import Bookmarks exported as HTML from Opera browser. + %N$d - step number + %2$s - browser name (Opera) + %8$@ - “Select Bookmarks HTML File” button + **bold text**; _italic text_ + """) + source.importSourceName + button("Select Bookmarks HTML File…") + + case (.operaGX, .bookmarks): + NSLocalizedString("import.html.instructions.operagx", value: """ + %d Open **%s** + %d Use the Menu Bar to select **Bookmarks → Bookmarks** + %d Click **Import/Export…** in the bottom left and select **Export Bookmarks** + %d Save the file someplace you can find it (e.g., Desktop) + %d %@ + """, comment: """ + Instructions to import Bookmarks exported as HTML from Opera GX browser. + %N$d - step number + %2$s - browser name (Opera GX) + %7$@ - “Select Bookmarks HTML File” button + **bold text**; _italic text_ + """) + source.importSourceName + button("Select Bookmarks HTML File…") + case (.yandex, .bookmarks): NSLocalizedString("import.html.instructions.yandex", value: """ %d Open **%s** @@ -139,7 +308,10 @@ struct FileImportView: View { %d %@ """, comment: """ Instructions to import Bookmarks exported as HTML from Yandex Browser. - %d is a step number; %s is a Browser name; %@ is for a button image to click + %N$d - step number + %2$s - browser name (Yandex) + %5$@ - hamburger menu icon + %8$@ - “Select Bookmarks HTML File” button **bold text**; _italic text_ """) source.importSourceName @@ -154,7 +326,8 @@ struct FileImportView: View { %d %@ """, comment: """ Instructions to import Passwords as CSV from Safari. - %d is a step number; %@ is for a button image to click + %N$d - step number + %5$@ - “Select Passwords CSV File” button **bold text**; _italic text_ """) @@ -168,7 +341,8 @@ struct FileImportView: View { %d %@ """, comment: """ Instructions to import Bookmarks exported as HTML from Safari. - %d is a step number; %@ is for a button image to click + %N$d - step number + %5$@ - “Select Bookmarks HTML File” button **bold text**; _italic text_ """) button("Select Bookmarks HTML File…") @@ -182,12 +356,15 @@ struct FileImportView: View { %d %@ """, comment: """ Instructions to import Passwords as CSV from Firefox. - %d is a step number; %s is a Browser name; %@ is for a button image to click + %N$d - step number + %2$s - browser name (Firefox) + %4$@, %6$@ - hamburger menu icon + %9$@ - “Select Passwords CSV File” button **bold text**; _italic text_ """) source.importSourceName NSImage.menuHamburger16 - NSImage.menuVertical16 + NSImage.menuHorizontal16 button("Select Passwords CSV File…") case (.firefox, .bookmarks), (.tor, .bookmarks): @@ -199,7 +376,10 @@ struct FileImportView: View { %d %@ """, comment: """ Instructions to import Bookmarks exported as HTML from Firefox based browsers. - %d is a step number; %s is a Browser name; %@ is for a button image to click + %N$d - step number + %2$s - browser name (Firefox) + %5$@ - hamburger menu icon + %8$@ - “Select Bookmarks HTML File” button **bold text**; _italic text_ """) source.importSourceName @@ -216,7 +396,8 @@ struct FileImportView: View { %d %@ """, comment: """ Instructions to import Passwords as CSV from 1Password 8. - %d is a step number; %s is 1Password app name; %@ is for a button image to click + %2$s - app name (1Password) + %8$@ - “Select 1Password CSV File” button **bold text**; _italic text_ """) source.importSourceName @@ -233,7 +414,8 @@ struct FileImportView: View { %d %@ """, comment: """ Instructions to import Passwords as CSV from 1Password 7. - %d is a step number; %s is 1Password app name; %@ is for a button image to click + %2$s - app name (1Password) + %9$@ - “Select 1Password CSV File” button **bold text**; _italic text_ """) source.importSourceName @@ -249,7 +431,9 @@ struct FileImportView: View { %d %@ """, comment: """ Instructions to import Passwords as CSV from Bitwarden. - %d is a step number; %s is Bitwarden app name; %@ is for a button image to click + %2$s - app name (Bitwarden) + %7$@ - hamburger menu icon + %9$@ - “Select Bitwarden CSV File” button **bold text**; _italic text_ """) source.importSourceName @@ -266,7 +450,8 @@ struct FileImportView: View { %d %@ """, comment: """ Instructions to import Passwords as CSV from LastPass. - %d is a step number; %s is LastPass app name; %@ is for a button image to click + %2$s - app name (LastPass) + %8$@ - “Select LastPass CSV File” button **bold text**; _italic text_ """) source.importSourceName @@ -281,7 +466,8 @@ struct FileImportView: View { %@ """, comment: """ Instructions to import a generic CSV passwords file. - %d is a step number; %@ is for a button image to click + %N$d - step number + %3$@ - “Select Passwords CSV File” button **bold text**; _italic text_ """) @@ -290,17 +476,16 @@ struct FileImportView: View { case (.bookmarksHTML, .bookmarks): NSLocalizedString("import.html.instructions.generic", value: """ %d Open your old browser - %d Click %@ then select **Bookmarks → Bookmark Manager** - %d Click %@ then **Export bookmarks to HTML…** + %d Open **Bookmark Manager** + %d Export bookmarks to HTML… %d Save the file someplace you can find it (e.g., Desktop) %d %@ """, comment: """ Instructions to import a generic HTML Bookmarks file. - %d is a step number; %@ is for a button image to click + %N$d - step number + %6$@ - “Select Bookmarks HTML File” button **bold text**; _italic text_ """) - NSImage.menuHamburger16 - NSImage.menuVertical16 button("Select Bookmarks HTML File…") case (.bookmarksHTML, .passwords), @@ -386,18 +571,11 @@ struct InstructionsView: View { case view(AnyView) } - // Text font - used to calculate inline image baseline offset and set Text/Font modifier - let fontName: String - let fontSize: CGFloat - // View Model private let instructions: [[InstructionsViewItem]] // swiftlint:disable:next function_body_length cyclomatic_complexity - init(fontName: String, fontSize: CGFloat, @InstructionsBuilder builder: () -> [InstructionsItem]) { - self.fontName = fontName - self.fontSize = fontSize - + init(@InstructionsBuilder builder: () -> [InstructionsItem]) { var args = builder() guard case .string(let format) = args.first else { @@ -548,16 +726,19 @@ struct InstructionsView: View { } var body: some View { - ForEach(instructions.indices, id: \.self) { i in - HStack(spacing: 4) { - ForEach(instructions[i].indices, id: \.self) { j in - switch instructions[i][j] { - case .lineNumber(let number): - CircleNumberView(number: number) - case .textItems(let textParts): - Text(textParts, fontName: fontName, fontSize: fontSize) - case .view(let view): - view + VStack(alignment: .leading, spacing: 8) { + ForEach(instructions.indices, id: \.self) { i in + HStack(spacing: 8) { + ForEach(instructions[i].indices, id: \.self) { j in + switch instructions[i][j] { + case .lineNumber(let number): + CircleNumberView(number: number) + case .textItems(let textParts): + Text(textParts) + .makeSelectable() + case .view(let view): + view + } } } } @@ -568,15 +749,15 @@ struct InstructionsView: View { private extension Text { - init(_ textPart: InstructionsView.TextItem, fontName: String, fontSize: CGFloat) { + init(_ textPart: InstructionsView.TextItem) { switch textPart { case .image(let image): self.init(Image(nsImage: image)) - self = self.baselineOffset(fontSize - image.size.height) + self = self + .baselineOffset(-3) case .text(let text, let isBold, let isItalic): self.init(text) - self = self.font(.custom(fontName, size: fontSize)) if isBold { self = self.bold() } @@ -586,18 +767,18 @@ private extension Text { } } - init(_ textParts: [InstructionsView.TextItem], fontName: String, fontSize: CGFloat) { + init(_ textParts: [InstructionsView.TextItem]) { guard !textParts.isEmpty else { assertionFailure("Empty TextParts") self.init("") return } - self.init(textParts[0], fontName: fontName, fontSize: fontSize) + self.init(textParts[0]) guard textParts.count > 1 else { return } for textPart in textParts[1...] { // swiftlint:disable:next shorthand_operator - self = self + Text(textPart, fontName: fontName, fontSize: fontSize) + self = self + Text(textPart) } } @@ -614,7 +795,7 @@ struct CircleNumberView: View { .overlay( Text("\(number)") .foregroundColor(.onboardingActionButton) - .font(.headline) + .bold() ) } @@ -624,7 +805,11 @@ struct CircleNumberView: View { // MARK: - Preview #Preview { - FileImportView(source: .chrome, dataType: .passwords, isButtonDisabled: false) - .frame(width: 512 - 20) - + HStack { + FileImportView(source: .onePassword7, dataType: .passwords, isButtonDisabled: false) + .padding() + .frame(width: 512 - 20) + } + .font(.custom("SF Pro Text", size: 13)) + .background(Color.white) } diff --git a/DuckDuckGo/DataImport/View/ReportFeedbackView.swift b/DuckDuckGo/DataImport/View/ReportFeedbackView.swift index 4e98d04372..491a8e77aa 100644 --- a/DuckDuckGo/DataImport/View/ReportFeedbackView.swift +++ b/DuckDuckGo/DataImport/View/ReportFeedbackView.swift @@ -34,7 +34,7 @@ struct ReportFeedbackView: View { comment: "Data import failure Report dialog title containing a message that not only automatic data import has failed failed but manual browser data import didn‘t work either.") } }() - .font(.headline) + .bold() .padding(.bottom, 8) VStack(alignment: .leading, spacing: 12) { @@ -69,7 +69,6 @@ struct ReportFeedbackView: View { HStack { Text("Add any details that you think may help us fix the problem", comment: "Data import failure Report dialog suggestion to provide a comments with extra details helping to identify the data import problem.") - .font(.custom("SF Pro Text", size: 13)) .foregroundColor(Color(.placeholderTextColor)) Spacer() }.padding(EdgeInsets(top: 11, leading: 11, bottom: 0, trailing: 11)) diff --git a/DuckDuckGo/Localizable.xcstrings b/DuckDuckGo/Localizable.xcstrings index fb786d43a4..03bdb96bb7 100644 --- a/DuckDuckGo/Localizable.xcstrings +++ b/DuckDuckGo/Localizable.xcstrings @@ -1148,7 +1148,7 @@ } } }, - "Bookmark import failed: %lld" : { + "Bookmark import failed:" : { "comment" : "Data import summary format of how many bookmarks (%lld) failed to import." }, "Bookmark import failed." : { @@ -1205,7 +1205,7 @@ "Bookmarks Import Complete:" : { "comment" : "Bookmarks Data Import result summary headline" }, - "Bookmarks: %lld" : { + "Bookmarks:" : { "comment" : "Data import summary format of how many bookmarks (%lld) were successfully imported." }, "Bookmarks…" : { @@ -2440,7 +2440,7 @@ } } }, - "Duplicate Bookmarks Skipped: %lld" : { + "Duplicate Bookmarks Skipped:" : { "comment" : "Data import summary format of how many duplicate bookmarks (%lld) were skipped during import." }, "duplicate.tab" : { @@ -3429,7 +3429,7 @@ "en" : { "stringUnit" : { "state" : "new", - "value" : "HTML Bookmarks File" + "value" : "HTML Bookmarks File (for other browsers)" } } } @@ -3507,7 +3507,7 @@ } }, "import.csv.instructions.bitwarden" : { - "comment" : "Instructions to import Passwords as CSV from Bitwarden.\n%d is a step number; %s is Bitwarden app name; %@ is for a button image to click\n**bold text**; _italic text_", + "comment" : "Instructions to import Passwords as CSV from Bitwarden.\n%2$s - app name (Bitwarden)\n%7$@ - hamburger menu icon\n%9$@ - “Select Bitwarden CSV File” button\n**bold text**; _italic text_", "extractionState" : "extracted_with_value", "localizations" : { "en" : { @@ -3518,8 +3518,20 @@ } } }, + "import.csv.instructions.brave" : { + "comment" : "Instructions to import Passwords as CSV from Brave browser.\n%N$d - step number\n%2$s - browser name (Brave)\n%4$@, %6$@ - hamburger menu icon\n%10$@ - “Select Passwords CSV File” button\n**bold text**; _italic text_", + "extractionState" : "extracted_with_value", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "%1$d Open **%2$s**\n%3$d Click %4$@ to open the application menu then click **Password Manager**\n%5$d Click %6$@ **at the top left** of the Password Manager and select **Settings**\n%7$d Find “Export Passwords” and click **Download File**\n%8$d Save the passwords file someplace you can find it (e.g. Desktop)\n%9$d %10$@" + } + } + } + }, "import.csv.instructions.chrome" : { - "comment" : "Instructions to import Passwords as CSV from Google Chrome browser.\n%d is a step number; %s is a Browser name; %@ is for a button image to click\n**bold text**; _italic text_", + "comment" : "Instructions to import Passwords as CSV from Google Chrome browser.\n%N$d - step number\n%2$s - browser name (Chrome)\n%4$@ - hamburger menu icon\n%8$@ - “Select Passwords CSV File” button\n**bold text**; _italic text_", "extractionState" : "extracted_with_value", "localizations" : { "en" : { @@ -3531,7 +3543,7 @@ } }, "import.csv.instructions.chromium" : { - "comment" : "Instructions to import Passwords as CSV from Chromium-based browsers.\n%d is a step number; %s is a Browser name; %@ is for a button image to click\n**bold text**; _italic text_", + "comment" : "Instructions to import Passwords as CSV from Chromium-based browsers.\n%N$d - step number\n%2$s - browser name\n%4$@ - hamburger menu icon\n%8$@ - “Select Passwords CSV File” button\n**bold text**; _italic text_", "extractionState" : "extracted_with_value", "localizations" : { "en" : { @@ -3542,8 +3554,20 @@ } } }, + "import.csv.instructions.coccoc" : { + "comment" : "Instructions to import Passwords as CSV from Chromium-based browsers.\n%N$d - step number\n%2$s - browser name (Cốc Cốc)\n%5$@ - hamburger menu icon\n%8$@ - “Select Passwords CSV File” button\n**bold text**; _italic text_", + "extractionState" : "extracted_with_value", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "%1$d Open **%2$s**\n%3$d Type “_coccoc://settings/passwords_” into the Address field\n%4$d Click %5$@ (on the right from _Saved Passwords_) and select **Export passwords**\n%6$d Save the passwords file someplace you can find it (e.g. Desktop)\n%7$d %8$@" + } + } + } + }, "import.csv.instructions.firefox" : { - "comment" : "Instructions to import Passwords as CSV from Firefox.\n%d is a step number; %s is a Browser name; %@ is for a button image to click\n**bold text**; _italic text_", + "comment" : "Instructions to import Passwords as CSV from Firefox.\n%N$d - step number\n%2$s - browser name (Firefox)\n%4$@, %6$@ - hamburger menu icon\n%9$@ - “Select Passwords CSV File” button\n**bold text**; _italic text_", "extractionState" : "extracted_with_value", "localizations" : { "en" : { @@ -3555,7 +3579,7 @@ } }, "import.csv.instructions.generic" : { - "comment" : "Instructions to import a generic CSV passwords file.\n%d is a step number; %@ is for a button image to click\n**bold text**; _italic text_", + "comment" : "Instructions to import a generic CSV passwords file.\n%N$d - step number\n%3$@ - “Select Passwords CSV File” button\n**bold text**; _italic text_", "extractionState" : "extracted_with_value", "localizations" : { "en" : { @@ -3567,7 +3591,7 @@ } }, "import.csv.instructions.lastpass" : { - "comment" : "Instructions to import Passwords as CSV from LastPass.\n%d is a step number; %s is LastPass app name; %@ is for a button image to click\n**bold text**; _italic text_", + "comment" : "Instructions to import Passwords as CSV from LastPass.\n%2$s - app name (LastPass)\n%8$@ - “Select LastPass CSV File” button\n**bold text**; _italic text_", "extractionState" : "extracted_with_value", "localizations" : { "en" : { @@ -3579,7 +3603,7 @@ } }, "import.csv.instructions.onePassword7" : { - "comment" : "Instructions to import Passwords as CSV from 1Password 7.\n%d is a step number; %s is 1Password app name; %@ is for a button image to click\n**bold text**; _italic text_", + "comment" : "Instructions to import Passwords as CSV from 1Password 7.\n%2$s - app name (1Password)\n%9$@ - “Select 1Password CSV File” button\n**bold text**; _italic text_", "extractionState" : "extracted_with_value", "localizations" : { "en" : { @@ -3591,7 +3615,7 @@ } }, "import.csv.instructions.onePassword8" : { - "comment" : "Instructions to import Passwords as CSV from 1Password 8.\n%d is a step number; %s is 1Password app name; %@ is for a button image to click\n**bold text**; _italic text_", + "comment" : "Instructions to import Passwords as CSV from 1Password 8.\n%2$s - app name (1Password)\n%8$@ - “Select 1Password CSV File” button\n**bold text**; _italic text_", "extractionState" : "extracted_with_value", "localizations" : { "en" : { @@ -3602,8 +3626,32 @@ } } }, + "import.csv.instructions.opera" : { + "comment" : "Instructions to import Passwords as CSV from Chromium-based browsers.\n%N$d - step number\n%2$s - browser name (Opera)\n%8$@ - “Select Passwords CSV File” button\n**bold text**; _italic text_", + "extractionState" : "extracted_with_value", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "%1$d Open **%2$s**\n%3$d Use the Menu Bar to select **View → Show Password Manager**\n%4$d Select **Settings**\n%5$d Find “Export Passwords” and click **Download File**\n%6$d Save the passwords file someplace you can find it (e.g. Desktop)\n%7$d %8$@" + } + } + } + }, + "import.csv.instructions.operagx" : { + "comment" : "Instructions to import Passwords as CSV from Chromium-based browsers.\n%N$d - step number\n%2$s - browser name (Opera GX)\n%5$@ - menu button icon\n%8$@ - “Select Passwords CSV File” button\n**bold text**; _italic text_", + "extractionState" : "extracted_with_value", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "%1$d Open **%2$s**\n%3$d Use the Menu Bar to select **View → Show Password Manager**\n%4$d Click %5$@ (on the right from _Saved Passwords_) and select **Export passwords**\n%6$d Save the passwords file someplace you can find it (e.g. Desktop)\n%7$d %8$@" + } + } + } + }, "import.csv.instructions.safari" : { - "comment" : "Instructions to import Passwords as CSV from Safari.\n%d is a step number; %@ is for a button image to click\n**bold text**; _italic text_", + "comment" : "Instructions to import Passwords as CSV from Safari.\n%N$d - step number\n%5$@ - “Select Passwords CSV File” button\n**bold text**; _italic text_", "extractionState" : "extracted_with_value", "localizations" : { "en" : { @@ -3614,14 +3662,26 @@ } } }, + "import.csv.instructions.vivaldi" : { + "comment" : "Instructions to import Passwords exported as CSV from Vivaldi browser.\n%N$d - step number\n%2$s - browser name (Vivaldi)\n%5$@ - menu button icon\n%8$@ - “Select Passwords CSV File” button\n**bold text**; _italic text_", + "extractionState" : "extracted_with_value", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "%1$d Open **%2$s**\n%3$d Type “_chrome://settings/passwords_” into the Address field\n%4$d Click %5$@ (on the right from _Saved Passwords_) and select **Export passwords**\n%6$d Save the file someplace you can find it (e.g., Desktop)\n%7$d %8$@" + } + } + } + }, "import.csv.instructions.yandex" : { - "comment" : "Instructions to import Passwords as CSV from Yandex Browser.\n%d is a step number; %@ is for a button image to click\n**bold text**; _italic text_", + "comment" : "Instructions to import Passwords as CSV from Yandex Browser.\n%N$d - step number\n%2$s - browser name (Yandex)\n%4$@ - hamburger menu icon\n%8$@ - “Select Passwords CSV File” button\n**bold text**; _italic text_", "extractionState" : "extracted_with_value", "localizations" : { "en" : { "stringUnit" : { "state" : "new", - "value" : "%1$d Open **Yandex**\n%2$d Click %3$@ to open the application menu then click **Passwords and cards**\n%4$d Click %5$@ then **Export passwords**\n%6$d Choose **To a text file (not secure)** and click **Export**\n%7$d Save the passwords file someplace you can find it (e.g. Desktop)\n%8$d %9$@" + "value" : "%1$d Open **%2$s**\n%3$d Click %4$@ to open the application menu then click **Passwords and cards**\n%5$d Click %6$@ then **Export passwords**\n%7$d Choose **To a text file (not secure)** and click **Export**\n%8$d Save the passwords file someplace you can find it (e.g. Desktop)\n%9$d %10$@" } } } @@ -3723,7 +3783,7 @@ } }, "import.html.instructions.chromium" : { - "comment" : "Instructions to import Bookmarks exported as HTML from Chromium-based browsers.\n%d is a step number; %s is a Browser name; %@ is for a button image to click\n**bold text**; _italic text_", + "comment" : "Instructions to import Bookmarks exported as HTML from Chromium-based browsers.\n%N$d - step number\n%2$s - browser name\n%5$@ - hamburger menu icon\n%8$@ - “Select Bookmarks HTML File” button\n**bold text**; _italic text_", "extractionState" : "extracted_with_value", "localizations" : { "en" : { @@ -3735,7 +3795,7 @@ } }, "import.html.instructions.firefox" : { - "comment" : "Instructions to import Bookmarks exported as HTML from Firefox based browsers.\n%d is a step number; %s is a Browser name; %@ is for a button image to click\n**bold text**; _italic text_", + "comment" : "Instructions to import Bookmarks exported as HTML from Firefox based browsers.\n%N$d - step number\n%2$s - browser name (Firefox)\n%5$@ - hamburger menu icon\n%8$@ - “Select Bookmarks HTML File” button\n**bold text**; _italic text_", "extractionState" : "extracted_with_value", "localizations" : { "en" : { @@ -3747,19 +3807,43 @@ } }, "import.html.instructions.generic" : { - "comment" : "Instructions to import a generic HTML Bookmarks file.\n%d is a step number; %@ is for a button image to click\n**bold text**; _italic text_", + "comment" : "Instructions to import a generic HTML Bookmarks file.\n%N$d - step number\n%6$@ - “Select Bookmarks HTML File” button\n**bold text**; _italic text_", "extractionState" : "extracted_with_value", "localizations" : { "en" : { "stringUnit" : { "state" : "new", - "value" : "%1$d Open your old browser\n%2$d Click %3$@ then select **Bookmarks → Bookmark Manager**\n%4$d Click %5$@ then **Export bookmarks to HTML…**\n%6$d Save the file someplace you can find it (e.g., Desktop)\n%7$d %8$@" + "value" : "%1$d Open your old browser\n%2$d Open **Bookmark Manager**\n%3$d Export bookmarks to HTML…\n%4$d Save the file someplace you can find it (e.g., Desktop)\n%5$d %6$@" + } + } + } + }, + "import.html.instructions.opera" : { + "comment" : "Instructions to import Bookmarks exported as HTML from Opera browser.\n%N$d - step number\n%2$s - browser name (Opera)\n%8$@ - “Select Bookmarks HTML File” button\n**bold text**; _italic text_", + "extractionState" : "extracted_with_value", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "%1$d Open **%2$s**\n%3$d Use the Menu Bar to select **Bookmarks → Bookmarks**\n%4$d Click **Open full Bookmarks view…** in the bottom left\n%5$d Click **Import/Export…** in the bottom left and select **Export Bookmarks**\n%6$d Save the file someplace you can find it (e.g., Desktop)\n%7$d %8$@" + } + } + } + }, + "import.html.instructions.operagx" : { + "comment" : "Instructions to import Bookmarks exported as HTML from Opera GX browser.\n%N$d - step number\n%2$s - browser name (Opera GX)\n%7$@ - “Select Bookmarks HTML File” button\n**bold text**; _italic text_", + "extractionState" : "extracted_with_value", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "%1$d Open **%2$s**\n%3$d Use the Menu Bar to select **Bookmarks → Bookmarks**\n%4$d Click **Import/Export…** in the bottom left and select **Export Bookmarks**\n%5$d Save the file someplace you can find it (e.g., Desktop)\n%6$d %7$@" } } } }, "import.html.instructions.safari" : { - "comment" : "Instructions to import Bookmarks exported as HTML from Safari.\n%d is a step number; %@ is for a button image to click\n**bold text**; _italic text_", + "comment" : "Instructions to import Bookmarks exported as HTML from Safari.\n%N$d - step number\n%5$@ - “Select Bookmarks HTML File” button\n**bold text**; _italic text_", "extractionState" : "extracted_with_value", "localizations" : { "en" : { @@ -3770,8 +3854,20 @@ } } }, + "import.html.instructions.vivaldi" : { + "comment" : "Instructions to import Bookmarks exported as HTML from Vivaldi browser.\n%N$d - step number\n%2$s - browser name (Vivaldi)\n%6$@ - “Select Bookmarks HTML File” button\n**bold text**; _italic text_", + "extractionState" : "extracted_with_value", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "%1$d Open **%2$s**\n%3$d Use the Menu Bar to select **File → Export Bookmarks…**\n%4$d Save the file someplace you can find it (e.g., Desktop)\n%5$d %6$@" + } + } + } + }, "import.html.instructions.yandex" : { - "comment" : "Instructions to import Bookmarks exported as HTML from Yandex Browser.\n%d is a step number; %s is a Browser name; %@ is for a button image to click\n**bold text**; _italic text_", + "comment" : "Instructions to import Bookmarks exported as HTML from Yandex Browser.\n%N$d - step number\n%2$s - browser name (Yandex)\n%5$@ - hamburger menu icon\n%8$@ - “Select Bookmarks HTML File” button\n**bold text**; _italic text_", "extractionState" : "extracted_with_value", "localizations" : { "en" : { @@ -3789,7 +3885,7 @@ "en" : { "stringUnit" : { "state" : "new", - "value" : "CSV Passwords File" + "value" : "CSV Passwords File (for other browsers)" } } } @@ -5806,13 +5902,13 @@ } } }, - "Passwords import failed: %lld" : { + "Passwords import failed: " : { "comment" : "Data import summary format of how many passwords (%lld) failed to import." }, "Passwords import failed." : { "comment" : "Data import summary message of failed passwords import." }, - "Passwords: %lld" : { + "Passwords:" : { "comment" : "Data import summary format of how many passwords (%lld) were successfully imported." }, "Passwords…" : { @@ -8662,6 +8758,9 @@ }, "Window" : { "comment" : "Main Menu " + }, + "You can find your version by selecting **%@ → About 1Password** from the Menu Bar" : { + }, "You could try importing %@ manually." : { "comment" : "Data import error subtitle: suggestion to import Bookmarks or Passwords (%@) manually by selecting a CSV or HTML file." @@ -8692,4 +8791,4 @@ } }, "version" : "1.0" -} +} \ No newline at end of file