From b872b700d377b5c8357f488a83f431b66c0f1b8e Mon Sep 17 00:00:00 2001 From: Federico Cappelli Date: Wed, 31 Jul 2024 16:33:07 +0100 Subject: [PATCH 01/20] Images + initial setup --- .../Privacy Pro/Contents.json | 6 + .../SettingsPrivacyPro.imageset/Contents.json | 12 ++ .../SettingsSubscription.pdf | Bin 0 -> 2253 bytes .../Contents.json | 12 ++ .../SettingsSubscriptionITP.pdf | Bin 0 -> 2380 bytes .../Contents.json | 12 ++ .../SettingsSubscriptionPIR.pdf | Bin 0 -> 2374 bytes .../Contents.json | 12 ++ .../SettingsSubscriptionVPN.pdf | Bin 0 -> 2570 bytes DuckDuckGo/SettingsCell.swift | 184 +++++++++--------- DuckDuckGo/SettingsStatus.swift | 13 +- DuckDuckGo/SettingsSubscriptionView.swift | 88 +++++---- DuckDuckGo/UserText.swift | 10 +- DuckDuckGo/en.lproj/Localizable.strings | 11 +- 14 files changed, 211 insertions(+), 149 deletions(-) create mode 100644 DuckDuckGo/Settings.xcassets/Privacy Pro/Contents.json create mode 100644 DuckDuckGo/Settings.xcassets/Privacy Pro/SettingsPrivacyPro.imageset/Contents.json create mode 100644 DuckDuckGo/Settings.xcassets/Privacy Pro/SettingsPrivacyPro.imageset/SettingsSubscription.pdf create mode 100644 DuckDuckGo/Settings.xcassets/Privacy Pro/SettingsPrivacyProITP.imageset/Contents.json create mode 100644 DuckDuckGo/Settings.xcassets/Privacy Pro/SettingsPrivacyProITP.imageset/SettingsSubscriptionITP.pdf create mode 100644 DuckDuckGo/Settings.xcassets/Privacy Pro/SettingsPrivacyProPIR.imageset/Contents.json create mode 100644 DuckDuckGo/Settings.xcassets/Privacy Pro/SettingsPrivacyProPIR.imageset/SettingsSubscriptionPIR.pdf create mode 100644 DuckDuckGo/Settings.xcassets/Privacy Pro/SettingsPrivacyProVPN.imageset/Contents.json create mode 100644 DuckDuckGo/Settings.xcassets/Privacy Pro/SettingsPrivacyProVPN.imageset/SettingsSubscriptionVPN.pdf diff --git a/DuckDuckGo/Settings.xcassets/Privacy Pro/Contents.json b/DuckDuckGo/Settings.xcassets/Privacy Pro/Contents.json new file mode 100644 index 0000000000..73c00596a7 --- /dev/null +++ b/DuckDuckGo/Settings.xcassets/Privacy Pro/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/DuckDuckGo/Settings.xcassets/Privacy Pro/SettingsPrivacyPro.imageset/Contents.json b/DuckDuckGo/Settings.xcassets/Privacy Pro/SettingsPrivacyPro.imageset/Contents.json new file mode 100644 index 0000000000..0bd3ba405b --- /dev/null +++ b/DuckDuckGo/Settings.xcassets/Privacy Pro/SettingsPrivacyPro.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "SettingsSubscription.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/DuckDuckGo/Settings.xcassets/Privacy Pro/SettingsPrivacyPro.imageset/SettingsSubscription.pdf b/DuckDuckGo/Settings.xcassets/Privacy Pro/SettingsPrivacyPro.imageset/SettingsSubscription.pdf new file mode 100644 index 0000000000000000000000000000000000000000..b1878653f8298fc15be71b1c67361154c3c18053 GIT binary patch literal 2253 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 literal 0 HcmV?d00001 diff --git a/DuckDuckGo/SettingsCell.swift b/DuckDuckGo/SettingsCell.swift index 5a90ca6618..e0290c3f20 100644 --- a/DuckDuckGo/SettingsCell.swift +++ b/DuckDuckGo/SettingsCell.swift @@ -35,7 +35,7 @@ struct SettingsCellComponents { /// Encapsulates a View representing a Cell with different configurations struct SettingsCellView: View, Identifiable { - + enum Accessory { case none case rightDetail(String) @@ -43,7 +43,7 @@ struct SettingsCellView: View, Identifiable { case image(Image) case custom(AnyView) } - + var label: String var subtitle: String? var image: Image? @@ -55,7 +55,7 @@ struct SettingsCellView: View, Identifiable { var webLinkIndicator: Bool var id: UUID = UUID() var isButton: Bool - + /// Initializes a `SettingsCellView` with the specified label and accesory. /// /// Use this initializer for standard cell types that require a label. @@ -87,7 +87,7 @@ struct SettingsCellView: View, Identifiable { /// Use this initializer for creating a cell that displays custom content. /// This initializer does not require a label, as the content is entirely custom. /// - Parameters: - /// - action: The closure to execute when the view is tapped. (If not embedded in a NavigationLink) + /// - action: The closure to execute when the view is tapped. (If not embedded in a NavigationLink) /// - customView: A closure that returns the custom view (`AnyView`) to be displayed in the cell. /// - enabled: A Boolean value that determines whether the cell is enabled. init(action: @escaping () -> Void = {}, @ViewBuilder customView: () -> AnyView, enabled: Bool = true) { @@ -99,7 +99,7 @@ struct SettingsCellView: View, Identifiable { self.webLinkIndicator = false self.isButton = false } - + var body: some View { Group { if isButton { @@ -117,9 +117,9 @@ struct SettingsCellView: View, Identifiable { .contentShape(Rectangle()) } }.frame(maxWidth: .infinity) - + } - + private var cellContent: some View { Group { switch accesory { @@ -130,7 +130,7 @@ struct SettingsCellView: View, Identifiable { } } } - + private var defaultView: some View { Group { HStack { @@ -148,11 +148,11 @@ struct SettingsCellView: View, Identifiable { } }.fixedSize(horizontal: false, vertical: true) .layoutPriority(0.7) - + Spacer() - + accesoryView() - + if let statusIndicator { statusIndicator.fixedSize() } @@ -165,7 +165,7 @@ struct SettingsCellView: View, Identifiable { }.padding(EdgeInsets(top: 2, leading: 0, bottom: 2, trailing: 0)) }.contentShape(Rectangle()) } - + @ViewBuilder private func accesoryView() -> some View { switch accesory { @@ -196,7 +196,7 @@ struct SettingsPickerCellView Void, option: String, selected: Bool) -> some View { @@ -305,91 +305,87 @@ struct SettingsCustomCell: View { } } -#if DEBUG -struct SettingsCellView_Previews: PreviewProvider { - enum SampleOption: String, CaseIterable, Hashable, CustomStringConvertible { - case optionOne = "Lorem" - case optionTwo = "Ipsum" - case optionThree = "Dolor" +private enum SampleOption: String, CaseIterable, Hashable, CustomStringConvertible { + case optionOne = "Lorem" + case optionTwo = "Ipsum" + case optionThree = "Dolor" - var description: String { - return self.rawValue - } + var description: String { + return self.rawValue } - - static var previews: some View { - Group { - List { - SettingsCellView(label: "Cell with disclosure", - disclosureIndicator: true) - .previewLayout(.sizeThatFits) - - SettingsCellView(label: "Multi-line Cell with disclosure \nLine 2\nLine 3", - subtitle: "Curabitur erat massa, cursus sed velit", - disclosureIndicator: true) - .previewLayout(.sizeThatFits) - - SettingsCellView(label: "Image cell with disclosure ", - accesory: .image(Image(systemName: "person.circle")), - disclosureIndicator: true) - .previewLayout(.sizeThatFits) - - SettingsCellView(label: "Subtitle image cell with disclosure", - subtitle: "This is the subtitle", - accesory: .image(Image(systemName: "person.circle")), - disclosureIndicator: true) - .previewLayout(.sizeThatFits) - - SettingsCellView(label: "Right Detail cell with disclosure", - accesory: .rightDetail("Detail"), - disclosureIndicator: true) - .previewLayout(.sizeThatFits) - - SettingsCellView(label: "Switch Cell", - accesory: .toggle(isOn: .constant(true))) - .previewLayout(.sizeThatFits) - - SettingsCellView(label: "Switch Cell", - subtitle: "Subtitle goes here", - accesory: .toggle(isOn: .constant(true))) - .previewLayout(.sizeThatFits) - - - @State var selectedOption: SampleOption = .optionOne - SettingsPickerCellView(label: "Proin tempor urna", options: SampleOption.allCases, selectedOption: $selectedOption) - .previewLayout(.sizeThatFits) - - let cellContent: () -> some View = { - HStack(spacing: 15) { - Image(systemName: "bird.circle") - .foregroundColor(.orange) - .imageScale(.large) - Image(systemName: "bird.circle") - .foregroundColor(.orange) - .imageScale(.medium) - - Spacer() - VStack(alignment: .center) { - Text("LOREM IPSUM") - .font(.headline) - } - Spacer() - Image(systemName: "bird.circle") - .foregroundColor(.orange) - .imageScale(.medium) - Image(systemName: "bird.circle") - .foregroundColor(.orange) - .imageScale(.large) +} + +#Preview { + Group { + List { + SettingsCellView(label: "Cell with disclosure", + disclosureIndicator: true) + .previewLayout(.sizeThatFits) + + SettingsCellView(label: "Multi-line Cell with disclosure \nLine 2\nLine 3", + subtitle: "Curabitur erat massa, cursus sed velit", + disclosureIndicator: true) + .previewLayout(.sizeThatFits) + + SettingsCellView(label: "Image cell with disclosure ", + accesory: .image(Image(systemName: "person.circle")), + disclosureIndicator: true) + .previewLayout(.sizeThatFits) + + SettingsCellView(label: "Subtitle image cell with disclosure", + subtitle: "This is the subtitle", + accesory: .image(Image(systemName: "person.circle")), + disclosureIndicator: true) + .previewLayout(.sizeThatFits) + + SettingsCellView(label: "Right Detail cell with disclosure", + accesory: .rightDetail("Detail"), + disclosureIndicator: true) + .previewLayout(.sizeThatFits) + + SettingsCellView(label: "Switch Cell", + accesory: .toggle(isOn: .constant(true))) + .previewLayout(.sizeThatFits) + + SettingsCellView(label: "Switch Cell", + subtitle: "Subtitle goes here", + accesory: .toggle(isOn: .constant(true))) + .previewLayout(.sizeThatFits) + + + @State var selectedOption: SampleOption = .optionOne + SettingsPickerCellView(label: "Proin tempor urna", options: SampleOption.allCases, selectedOption: $selectedOption) + .previewLayout(.sizeThatFits) + + let cellContent: () -> some View = { + HStack(spacing: 15) { + Image(systemName: "bird.circle") + .foregroundColor(.orange) + .imageScale(.large) + Image(systemName: "bird.circle") + .foregroundColor(.orange) + .imageScale(.medium) + + Spacer() + VStack(alignment: .center) { + Text("LOREM IPSUM") + .font(.headline) } + Spacer() + Image(systemName: "bird.circle") + .foregroundColor(.orange) + .imageScale(.medium) + Image(systemName: "bird.circle") + .foregroundColor(.orange) + .imageScale(.large) } - // For some unknown reason, this breaks on CI, but works normally - // Perhaps an XCODE version issue? - SettingsCustomCell(content: cellContent) - .previewLayout(.sizeThatFits) - - } + // For some unknown reason, this breaks on CI, but works normally + // Perhaps an XCODE version issue? + SettingsCustomCell(content: cellContent) + .previewLayout(.sizeThatFits) + + } } } -#endif diff --git a/DuckDuckGo/SettingsStatus.swift b/DuckDuckGo/SettingsStatus.swift index fc43b3eff5..ed75f7fca2 100644 --- a/DuckDuckGo/SettingsStatus.swift +++ b/DuckDuckGo/SettingsStatus.swift @@ -39,7 +39,7 @@ enum StatusIndicator: Equatable { struct StatusIndicatorView: View { var status: StatusIndicator - var isDotHidden = false + var isDotHidden: Bool = false var body: some View { HStack(spacing: 6) { @@ -67,3 +67,14 @@ struct StatusIndicatorView: View { } } } + +#Preview { + VStack { + StatusIndicatorView(status: .on, isDotHidden: false) + StatusIndicatorView(status: .off, isDotHidden: false) + StatusIndicatorView(status: .alwaysOn, isDotHidden: false) + StatusIndicatorView(status: .on, isDotHidden: true) + StatusIndicatorView(status: .off, isDotHidden: true) + StatusIndicatorView(status: .alwaysOn, isDotHidden: true) + } +} diff --git a/DuckDuckGo/SettingsSubscriptionView.swift b/DuckDuckGo/SettingsSubscriptionView.swift index d5b871d8f0..85caf576bd 100644 --- a/DuckDuckGo/SettingsSubscriptionView.swift +++ b/DuckDuckGo/SettingsSubscriptionView.swift @@ -22,6 +22,17 @@ import Subscription import SwiftUI import UIKit +enum SettingsSubscriptionViewConstants { + static let purchaseDescriptionPadding = 5.0 + static let topCellPadding = 3.0 + static let noEntitlementsIconWidth = 20.0 + static let navigationDelay = 0.3 + static let infoIcon = "info-16" + static let alertIcon = "Exclamation-Color-16" + + static let privacyPolicyURL = URL(string: "https://duckduckgo.com/pro/privacy-terms")! +} + struct SettingsSubscriptionView: View { @EnvironmentObject var viewModel: SettingsViewModel @@ -34,27 +45,6 @@ struct SettingsSubscriptionView: View { @State var isShowingStripeView = false @State var isShowingSubscriptionError = false @State var isShowingPrivacyPro = false - - enum Constants { - static let purchaseDescriptionPadding = 5.0 - static let topCellPadding = 3.0 - static let noEntitlementsIconWidth = 20.0 - static let navigationDelay = 0.3 - static let infoIcon = "info-16" - static let alertIcon = "Exclamation-Color-16" - - static let privacyPolicyURL = URL(string: "https://duckduckgo.com/pro/privacy-terms")! - } - - private var subscriptionDescriptionView: some View { - VStack(alignment: .leading) { - Text(UserText.settingsPProSubscribe).daxBodyRegular() - Group { - Text(UserText.settingsPProDescription).daxFootnoteRegular().padding(.bottom, Constants.purchaseDescriptionPadding) - Text(UserText.settingsPProFeatures).daxFootnoteRegular() - }.foregroundColor(Color(designSystemColor: .textSecondary)) - } - } @ViewBuilder private var restorePurchaseView: some View { @@ -87,10 +77,11 @@ struct SettingsSubscriptionView: View { @ViewBuilder private var purchaseSubscriptionView: some View { - Group { - SettingsCustomCell(content: { subscriptionDescriptionView }) - + SettingsCellView(label: UserText.settingsPProSubscribe, + subtitle: UserText.settingsPProDescription, + image: Image("SettingsPrivacyPro")) + let subscribeView = SubscriptionContainerViewFactory.makeSubscribeFlow(origin: nil, navigationCoordinator: subscriptionNavigationCoordinator, subscriptionManager: subscriptionManager, @@ -104,12 +95,14 @@ struct SettingsSubscriptionView: View { } NavigationLink(destination: subscribeView, - isActive: $isShowingSubscribeFlow, - label: { SettingsCellView(label: UserText.settingsPProLearnMore ) }) - + isActive: $isShowingSubscribeFlow) { + SettingsCellView(label: UserText.settingsPProLearnMore) + } + NavigationLink(destination: restoreView, - isActive: $isShowingRestoreFlow, - label: { SettingsCellView(label: UserText.settingsPProIHaveASubscription ) }) + isActive: $isShowingRestoreFlow) { + SettingsCellView(label: UserText.settingsPProIHaveASubscription ) + } } } @@ -118,13 +111,13 @@ struct SettingsSubscriptionView: View { Group { SettingsCustomCell(content: { HStack(alignment: .top) { - Image(Constants.alertIcon) - .frame(width: Constants.noEntitlementsIconWidth) - .padding(.top, Constants.topCellPadding) + Image(SettingsSubscriptionViewConstants.alertIcon) + .frame(width: SettingsSubscriptionViewConstants.noEntitlementsIconWidth) + .padding(.top, SettingsSubscriptionViewConstants.topCellPadding) VStack(alignment: .leading) { Text(UserText.settingsPProSubscriptionExpiredTitle).daxBodyRegular() Text(UserText.settingsPProSubscribeAgain).daxFootnoteRegular() - .padding(.bottom, Constants.purchaseDescriptionPadding) + .padding(.bottom, SettingsSubscriptionViewConstants.purchaseDescriptionPadding) }.foregroundColor(Color(designSystemColor: .textSecondary)) } }) @@ -155,13 +148,13 @@ struct SettingsSubscriptionView: View { Group { SettingsCustomCell(content: { HStack(alignment: .top) { - Image(Constants.infoIcon) - .frame(width: Constants.noEntitlementsIconWidth) - .padding(.top, Constants.topCellPadding) + Image(SettingsSubscriptionViewConstants.infoIcon) + .frame(width: SettingsSubscriptionViewConstants.noEntitlementsIconWidth) + .padding(.top, SettingsSubscriptionViewConstants.topCellPadding) VStack(alignment: .leading) { Text(UserText.settingsPProActivationPendingTitle).daxBodyRegular() Text(UserText.settingsPProActivationPendingDescription).daxFootnoteRegular() - .padding(.bottom, Constants.purchaseDescriptionPadding) + .padding(.bottom, SettingsSubscriptionViewConstants.purchaseDescriptionPadding) }.foregroundColor(Color(designSystemColor: .textSecondary)) } }) @@ -218,7 +211,7 @@ struct SettingsSubscriptionView: View { Group { if isShowingPrivacyPro { let footerLink = Link(UserText.settingsPProSectionFooter, - destination: Constants.privacyPolicyURL + destination: SettingsSubscriptionViewConstants.privacyPolicyURL ).daxFootnoteRegular().accentColor(Color.init(designSystemColor: .accent)) Section(header: Text(UserText.settingsPProSection), footer: footerLink) { @@ -266,3 +259,22 @@ struct SettingsSubscriptionView: View { } } } +// +// let settingsModel = SettingsViewModel(legacyViewProvider: <#T##SettingsLegacyViewProvider#>, +// subscriptionManager: <#T##any SubscriptionManager#>, +// historyManager: <#T##any HistoryManaging#>, +// syncPausedStateManager: <#T##any SyncPausedStateManaging#>, +// privacyProDataReporter: <#T##any PrivacyProDataReporting#>) +// +// #Preview { +// SettingsSubscriptionView(viewModel: settingsModel, +// subscriptionNavigationCoordinator: <#T##SubscriptionNavigationCoordinator#>, +// isShowingDBP: <#T##arg#>, +// isShowingITP: <#T##arg#>, +// isShowingRestoreFlow: <#T##arg#>, +// isShowingSubscribeFlow: <#T##arg#>, +// isShowingGoogleView: <#T##arg#>, +// isShowingStripeView: <#T##arg#>, +// isShowingSubscriptionError: <#T##arg#>, +// isShowingPrivacyPro: <#T##arg#>) +// } diff --git a/DuckDuckGo/UserText.swift b/DuckDuckGo/UserText.swift index 6d3fba3ab3..ba6823590a 100644 --- a/DuckDuckGo/UserText.swift +++ b/DuckDuckGo/UserText.swift @@ -975,14 +975,8 @@ But if you *do* want a peek under the hood, you can find more information about public static let settingsPProSection = NSLocalizedString("settings.ppro", value: "Privacy Pro", comment: "Product name for the subscription bundle") public static let settingsPProSectionFooter = NSLocalizedString("settings.ppro.footer", value: "Privacy Policy and Terms of Service", comment: "Title for Link in the Footer of Privacy Pro section") - public static let settingsPProSubscribe = NSLocalizedString("settings.subscription.subscribe", value: "Subscribe to Privacy Pro", comment: "Call to action title for Privacy Pro") - public static let settingsPProDescription = NSLocalizedString("settings.subscription.description", value:"More seamless privacy with three new protections:", comment: "Privacy pro description subtext") - public static let settingsPProFeatures = NSLocalizedString("settings.subscription.features", value: - """ - • VPN - • Personal Information Removal - • Identity Theft Restoration - """, comment: "Privacy pro features list") + public static let settingsPProSubscribe = NSLocalizedString("settings.subscription.subscribe", value: "Protect your connection and identity with Privacy Pro", comment: "Call to action title for Privacy Pro") + public static let settingsPProDescription = NSLocalizedString("settings.subscription.description", value:"Includes our VPN, Personal Information Removal, and Identity Theft Restoration.", comment: "Privacy pro description subtitle") public static let settingsPProLearnMore = NSLocalizedString("settings.subscription.learn.more", value: "Get Privacy Pro", comment: "Get Privacy Pro button text for privacy pro") public static let settingsPProIHaveASubscription = NSLocalizedString("settings.subscription.existing.subscription", value: "I Have a Subscription", comment: "I have a Subscription button text for privacy pro") diff --git a/DuckDuckGo/en.lproj/Localizable.strings b/DuckDuckGo/en.lproj/Localizable.strings index 88ca572a13..4a6366894f 100644 --- a/DuckDuckGo/en.lproj/Localizable.strings +++ b/DuckDuckGo/en.lproj/Localizable.strings @@ -1979,8 +1979,8 @@ But if you *do* want a peek under the hood, you can find more information about /* Data Broker protection cell title for privacy pro */ "settings.subscription.DBP.title" = "Personal Information Removal"; -/* Privacy pro description subtext */ -"settings.subscription.description" = "More seamless privacy with three new protections:"; +/* Privacy pro description subtitle */ +"settings.subscription.description" = "Includes our VPN, Personal Information Removal, and Identity Theft Restoration."; /* I have a Subscription button text for privacy pro */ "settings.subscription.existing.subscription" = "I Have a Subscription"; @@ -1991,11 +1991,6 @@ But if you *do* want a peek under the hood, you can find more information about /* Subscription expired tittle message */ "settings.subscription.expired.title" = "Your Privacy Pro subscription expired"; -/* Privacy pro features list */ -"settings.subscription.features" = " • VPN - • Personal Information Removal - • Identity Theft Restoration"; - /* Identity theft restoration cell subtitle for privacy pro */ "settings.subscription.ITR.subtitle" = "If your identity is stolen, we'll help restore it"; @@ -2009,7 +2004,7 @@ But if you *do* want a peek under the hood, you can find more information about "settings.subscription.manage" = "Subscription Settings"; /* Call to action title for Privacy Pro */ -"settings.subscription.subscribe" = "Subscribe to Privacy Pro"; +"settings.subscription.subscribe" = "Protect your connection and identity with Privacy Pro"; /* VPN cell title for privacy pro */ "settings.subscription.VPN.title" = "VPN"; From 7c8a71bbd3741fb279302a365b8e6a3ea226c295 Mon Sep 17 00:00:00 2001 From: Federico Cappelli Date: Wed, 31 Jul 2024 16:33:38 +0100 Subject: [PATCH 02/20] typo fixed accesory > accessory --- DuckDuckGo/AboutView.swift | 4 ++-- DuckDuckGo/CookiePopUpProtectionView.swift | 2 +- DuckDuckGo/PrivateSearchView.swift | 4 ++-- DuckDuckGo/SettingsAccessibilityView.swift | 4 ++-- DuckDuckGo/SettingsAppearanceView.swift | 4 ++-- DuckDuckGo/SettingsCell.swift | 20 ++++++++++---------- DuckDuckGo/SettingsDataClearingView.swift | 2 +- DuckDuckGo/SettingsGeneralView.swift | 12 ++++++------ DuckDuckGo/WebTrackingProtectionView.swift | 2 +- 9 files changed, 27 insertions(+), 27 deletions(-) diff --git a/DuckDuckGo/AboutView.swift b/DuckDuckGo/AboutView.swift index a239b8d89a..b423e6b404 100644 --- a/DuckDuckGo/AboutView.swift +++ b/DuckDuckGo/AboutView.swift @@ -76,11 +76,11 @@ struct AboutViewVersion: View { var body: some View { Section(header: Text("DuckDuckGo for iOS"), footer: Text(UserText.settingsSendCrashReportsDescription)) { SettingsCellView(label: UserText.settingsVersion, - accesory: .rightDetail(viewModel.state.version)) + accessory: .rightDetail(viewModel.state.version)) // Send Crash Reports SettingsCellView(label: UserText.settingsSendCrashReports, - accesory: .toggle(isOn: viewModel.crashCollectionOptInStatusBinding)) + accessory: .toggle(isOn: viewModel.crashCollectionOptInStatusBinding)) } } } diff --git a/DuckDuckGo/CookiePopUpProtectionView.swift b/DuckDuckGo/CookiePopUpProtectionView.swift index 3e23c8c9f6..6d0381e5c4 100644 --- a/DuckDuckGo/CookiePopUpProtectionView.swift +++ b/DuckDuckGo/CookiePopUpProtectionView.swift @@ -54,7 +54,7 @@ struct CookiePopUpProtectionViewSettings: View { Section { // Let DuckDuckGo manage cookie consent pop-ups SettingsCellView(label: UserText.letDuckDuckGoManageCookieConsentPopups, - accesory: .toggle(isOn: viewModel.autoconsentBinding)) + accessory: .toggle(isOn: viewModel.autoconsentBinding)) } } } diff --git a/DuckDuckGo/PrivateSearchView.swift b/DuckDuckGo/PrivateSearchView.swift index a16218a4a8..43190bb81e 100644 --- a/DuckDuckGo/PrivateSearchView.swift +++ b/DuckDuckGo/PrivateSearchView.swift @@ -51,13 +51,13 @@ struct PrivateSearchViewSettings: View { Section(footer: Text(UserText.settingsAutocompleteSubtitle)) { // Autocomplete Suggestions SettingsCellView(label: UserText.settingsAutocompleteLabel, - accesory: .toggle(isOn: viewModel.autocompletePrivateSearchBinding)) + accessory: .toggle(isOn: viewModel.autocompletePrivateSearchBinding)) } if viewModel.shouldShowRecentlyVisitedSites { Section(footer: Text(UserText.settingsAutocompleteRecentlyVisitedSubtitle)) { SettingsCellView(label: UserText.settingsAutocompleteRecentlyVisitedLabel, - accesory: .toggle(isOn: viewModel.autocompleteRecentlyVisitedSitesBinding)) + accessory: .toggle(isOn: viewModel.autocompleteRecentlyVisitedSitesBinding)) } } diff --git a/DuckDuckGo/SettingsAccessibilityView.swift b/DuckDuckGo/SettingsAccessibilityView.swift index b013b899ad..ad0141d69f 100644 --- a/DuckDuckGo/SettingsAccessibilityView.swift +++ b/DuckDuckGo/SettingsAccessibilityView.swift @@ -33,7 +33,7 @@ struct SettingsAccessibilityView: View { if viewModel.state.textSize.enabled { SettingsCellView(label: UserText.settingsText, action: { viewModel.presentLegacyView(.textSize) }, - accesory: .rightDetail("\(viewModel.state.textSize.size)%"), + accessory: .rightDetail("\(viewModel.state.textSize.size)%"), disclosureIndicator: true, isButton: true) } @@ -43,7 +43,7 @@ struct SettingsAccessibilityView: View { // Private Voice Search if viewModel.state.speechRecognitionAvailable { SettingsCellView(label: UserText.settingsVoiceSearch, - accesory: .toggle(isOn: viewModel.voiceSearchEnabledBinding)) + accessory: .toggle(isOn: viewModel.voiceSearchEnabledBinding)) } } .alert(isPresented: $shouldShowNoMicrophonePermissionAlert) { diff --git a/DuckDuckGo/SettingsAppearanceView.swift b/DuckDuckGo/SettingsAppearanceView.swift index 3c76b9176c..1163a96416 100644 --- a/DuckDuckGo/SettingsAppearanceView.swift +++ b/DuckDuckGo/SettingsAppearanceView.swift @@ -32,7 +32,7 @@ struct SettingsAppearanceView: View { let image = Image(uiImage: viewModel.state.appIcon.smallImage ?? UIImage()) SettingsCellView(label: UserText.settingsIcon, action: { viewModel.presentLegacyView(.appIcon ) }, - accesory: .image(image), + accessory: .image(image), disclosureIndicator: true, isButton: true) @@ -52,7 +52,7 @@ struct SettingsAppearanceView: View { // Show Full Site Address SettingsCellView(label: UserText.settingsFullURL, - accesory: .toggle(isOn: viewModel.addressBarShowsFullURL)) + accessory: .toggle(isOn: viewModel.addressBarShowsFullURL)) } } .applySettingsListModifiers(title: UserText.settingsAppearanceSection, diff --git a/DuckDuckGo/SettingsCell.swift b/DuckDuckGo/SettingsCell.swift index e0290c3f20..f384a28b4f 100644 --- a/DuckDuckGo/SettingsCell.swift +++ b/DuckDuckGo/SettingsCell.swift @@ -49,14 +49,14 @@ struct SettingsCellView: View, Identifiable { var image: Image? var action: () -> Void = {} var enabled: Bool = true - var accesory: Accessory + var accessory: Accessory var statusIndicator: StatusIndicatorView? var disclosureIndicator: Bool var webLinkIndicator: Bool var id: UUID = UUID() var isButton: Bool - /// Initializes a `SettingsCellView` with the specified label and accesory. + /// Initializes a `SettingsCellView` with the specified label and accessory. /// /// Use this initializer for standard cell types that require a label. /// - Parameters: @@ -64,18 +64,18 @@ struct SettingsCellView: View, Identifiable { /// - subtitle: Displayed below title (if present) /// - image: Image displayed to the left of label /// - action: The closure to execute when the view is tapped. (If not embedded in a NavigationLink) - /// - accesory: The type of cell to display. Excludes the custom cell type. + /// - accessory: The type of cell to display. Excludes the custom cell type. /// - enabled: A Boolean value that determines whether the cell is enabled. /// - disclosureIndicator: Forces Adds a disclosure indicator on the right (chevron) /// - webLinkIndicator: Adds a link indicator on the right /// - isButton: Disables the tap actions on the cell if true - init(label: String, subtitle: String? = nil, image: Image? = nil, action: @escaping () -> Void = {}, accesory: Accessory = .none, enabled: Bool = true, statusIndicator: StatusIndicatorView? = nil, disclosureIndicator: Bool = false, webLinkIndicator: Bool = false, isButton: Bool = false) { + init(label: String, subtitle: String? = nil, image: Image? = nil, action: @escaping () -> Void = {}, accessory: Accessory = .none, enabled: Bool = true, statusIndicator: StatusIndicatorView? = nil, disclosureIndicator: Bool = false, webLinkIndicator: Bool = false, isButton: Bool = false) { self.label = label self.subtitle = subtitle self.image = image self.action = action self.enabled = enabled - self.accesory = accesory + self.accessory = accessory self.statusIndicator = statusIndicator self.disclosureIndicator = disclosureIndicator self.webLinkIndicator = webLinkIndicator @@ -94,7 +94,7 @@ struct SettingsCellView: View, Identifiable { self.label = "" // Not used for custom cell self.action = action self.enabled = enabled - self.accesory = .custom(customView()) + self.accessory = .custom(customView()) self.disclosureIndicator = false self.webLinkIndicator = false self.isButton = false @@ -122,7 +122,7 @@ struct SettingsCellView: View, Identifiable { private var cellContent: some View { Group { - switch accesory { + switch accessory { case .custom(let customView): customView default: @@ -151,7 +151,7 @@ struct SettingsCellView: View, Identifiable { Spacer() - accesoryView() + accessoryView() if let statusIndicator { statusIndicator.fixedSize() @@ -167,8 +167,8 @@ struct SettingsCellView: View, Identifiable { } @ViewBuilder - private func accesoryView() -> some View { - switch accesory { + private func accessoryView() -> some View { + switch accessory { case .none: EmptyView() case .rightDetail(let value): diff --git a/DuckDuckGo/SettingsDataClearingView.swift b/DuckDuckGo/SettingsDataClearingView.swift index d524abcb82..5a73902537 100644 --- a/DuckDuckGo/SettingsDataClearingView.swift +++ b/DuckDuckGo/SettingsDataClearingView.swift @@ -44,7 +44,7 @@ struct SettingsDataClearingView: View { // Automatically Clear Data SettingsCellView(label: UserText.settingsClearData, action: { viewModel.presentLegacyView(.autoclearData) }, - accesory: .rightDetail(viewModel.state.autoclearDataEnabled + accessory: .rightDetail(viewModel.state.autoclearDataEnabled ? UserText.autoClearAccessoryOn : UserText.autoClearAccessoryOff), disclosureIndicator: true, diff --git a/DuckDuckGo/SettingsGeneralView.swift b/DuckDuckGo/SettingsGeneralView.swift index 5f51165375..2307c092f7 100644 --- a/DuckDuckGo/SettingsGeneralView.swift +++ b/DuckDuckGo/SettingsGeneralView.swift @@ -31,7 +31,7 @@ struct SettingsGeneralView: View { // Application Lock Section(footer: Text(UserText.settingsAutoLockDescription)) { SettingsCellView(label: UserText.settingsAutolock, - accesory: .toggle(isOn: viewModel.applicationLockBinding)) + accessory: .toggle(isOn: viewModel.applicationLockBinding)) } @@ -39,13 +39,13 @@ struct SettingsGeneralView: View { footer: Text(UserText.settingsAutocompleteSubtitle)) { // Autocomplete Suggestions SettingsCellView(label: UserText.settingsAutocompleteLabel, - accesory: .toggle(isOn: viewModel.autocompleteGeneralBinding)) + accessory: .toggle(isOn: viewModel.autocompleteGeneralBinding)) } if viewModel.shouldShowRecentlyVisitedSites { Section(footer: Text(UserText.settingsAutocompleteRecentlyVisitedSubtitle)) { SettingsCellView(label: UserText.settingsAutocompleteRecentlyVisitedLabel, - accesory: .toggle(isOn: viewModel.autocompleteRecentlyVisitedSitesBinding)) + accessory: .toggle(isOn: viewModel.autocompleteRecentlyVisitedSitesBinding)) } } @@ -53,7 +53,7 @@ struct SettingsGeneralView: View { // Private Voice Search if viewModel.state.speechRecognitionAvailable { SettingsCellView(label: UserText.settingsVoiceSearch, - accesory: .toggle(isOn: viewModel.voiceSearchEnabledBinding)) + accessory: .toggle(isOn: viewModel.voiceSearchEnabledBinding)) } } .alert(isPresented: $shouldShowNoMicrophonePermissionAlert) { @@ -79,11 +79,11 @@ struct SettingsGeneralView: View { // Long-Press Previews SettingsCellView(label: UserText.settingsPreviews, - accesory: .toggle(isOn: viewModel.longPressBinding)) + accessory: .toggle(isOn: viewModel.longPressBinding)) // Open Links in Associated Apps SettingsCellView(label: UserText.settingsAssociatedApps, - accesory: .toggle(isOn: viewModel.universalLinksBinding)) + accessory: .toggle(isOn: viewModel.universalLinksBinding)) } } .applySettingsListModifiers(title: UserText.general, diff --git a/DuckDuckGo/WebTrackingProtectionView.swift b/DuckDuckGo/WebTrackingProtectionView.swift index 18a7467eab..f31df3a876 100644 --- a/DuckDuckGo/WebTrackingProtectionView.swift +++ b/DuckDuckGo/WebTrackingProtectionView.swift @@ -54,7 +54,7 @@ struct WebTrackingProtectionViewSettings: View { Section { // Global Privacy Control SettingsCellView(label: UserText.settingsGPC, - accesory: .toggle(isOn: viewModel.gpcBinding)) + accessory: .toggle(isOn: viewModel.gpcBinding)) // Unprotected Sites SettingsCellView(label: UserText.settingsUnprotectedSites, From 6135b3b18dec7805ba84754e88cc8d9aefb5332c Mon Sep 17 00:00:00 2001 From: Federico Cappelli Date: Fri, 2 Aug 2024 09:10:01 +0100 Subject: [PATCH 03/20] stage 2 of new subscription settings --- DuckDuckGo.xcodeproj/project.pbxproj | 12 ++- DuckDuckGo/SettingsCell.swift | 74 +++++++++++++------ DuckDuckGo/SettingsSubscriptionView.swift | 45 ++++++----- DuckDuckGo/StatusIndicator.swift | 38 ++++++++++ ...Status.swift => StatusIndicatorView.swift} | 20 +---- 5 files changed, 120 insertions(+), 69 deletions(-) create mode 100644 DuckDuckGo/StatusIndicator.swift rename DuckDuckGo/{SettingsStatus.swift => StatusIndicatorView.swift} (84%) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 6e74ea87f0..cd457efa86 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -32,7 +32,7 @@ 1DEAADEC2BA45B4500E25A97 /* SettingsAccessibilityView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DEAADEB2BA45B4400E25A97 /* SettingsAccessibilityView.swift */; }; 1DEAADEE2BA45DFE00E25A97 /* SettingsDataClearingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DEAADED2BA45DFE00E25A97 /* SettingsDataClearingView.swift */; }; 1DEAADF02BA46E0700E25A97 /* PrivateSearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DEAADEF2BA46E0600E25A97 /* PrivateSearchView.swift */; }; - 1DEAADF22BA4716C00E25A97 /* SettingsStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DEAADF12BA4716C00E25A97 /* SettingsStatus.swift */; }; + 1DEAADF22BA4716C00E25A97 /* StatusIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DEAADF12BA4716C00E25A97 /* StatusIndicator.swift */; }; 1DEAADF42BA47B5300E25A97 /* WebTrackingProtectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DEAADF32BA47B5300E25A97 /* WebTrackingProtectionView.swift */; }; 1DEAADF62BA4809400E25A97 /* CookiePopUpProtectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DEAADF52BA4809400E25A97 /* CookiePopUpProtectionView.swift */; }; 1DEAADFB2BA71E9A00E25A97 /* SettingsPrivacyProtectionDescriptionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DEAADFA2BA71E9A00E25A97 /* SettingsPrivacyProtectionDescriptionView.swift */; }; @@ -1011,6 +1011,7 @@ F1E4A4451EE89460006F2EAE /* Bookmarks.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F1E4A4431EE89460006F2EAE /* Bookmarks.storyboard */; }; F1E90C201E678E7C005E7E21 /* HomeControllerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1E90C1F1E678E7C005E7E21 /* HomeControllerDelegate.swift */; }; F1ED309D1EDC2EA400651986 /* TabSwitcher.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F1ED309B1EDC2EA400651986 /* TabSwitcher.storyboard */; }; + F1EFB0062C5B8B8E009AB44B /* StatusIndicatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1EFB0052C5B8B8E009AB44B /* StatusIndicatorView.swift */; }; F1F5337C1F26A9EF00D80D4F /* UserText.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1F5337B1F26A9EF00D80D4F /* UserText.swift */; }; F1F533841F26ABAC00D80D4F /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = F1F533861F26ABAC00D80D4F /* Localizable.strings */; }; F1FDC9302BF4E0B3006B1435 /* SubscriptionEnvironment+Default.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1FDC92F2BF4E0B3006B1435 /* SubscriptionEnvironment+Default.swift */; }; @@ -1216,7 +1217,7 @@ 1DEAADEB2BA45B4400E25A97 /* SettingsAccessibilityView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsAccessibilityView.swift; sourceTree = ""; }; 1DEAADED2BA45DFE00E25A97 /* SettingsDataClearingView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsDataClearingView.swift; sourceTree = ""; }; 1DEAADEF2BA46E0600E25A97 /* PrivateSearchView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PrivateSearchView.swift; sourceTree = ""; }; - 1DEAADF12BA4716C00E25A97 /* SettingsStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsStatus.swift; sourceTree = ""; }; + 1DEAADF12BA4716C00E25A97 /* StatusIndicator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusIndicator.swift; sourceTree = ""; }; 1DEAADF32BA47B5300E25A97 /* WebTrackingProtectionView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WebTrackingProtectionView.swift; sourceTree = ""; }; 1DEAADF52BA4809400E25A97 /* CookiePopUpProtectionView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CookiePopUpProtectionView.swift; sourceTree = ""; }; 1DEAADFA2BA71E9A00E25A97 /* SettingsPrivacyProtectionDescriptionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsPrivacyProtectionDescriptionView.swift; sourceTree = ""; }; @@ -2747,6 +2748,7 @@ F1E4A4441EE89460006F2EAE /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Bookmarks.storyboard; sourceTree = ""; }; F1E90C1F1E678E7C005E7E21 /* HomeControllerDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeControllerDelegate.swift; sourceTree = ""; }; F1ED309C1EDC2EA400651986 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/TabSwitcher.storyboard; sourceTree = ""; }; + F1EFB0052C5B8B8E009AB44B /* StatusIndicatorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusIndicatorView.swift; sourceTree = ""; }; F1F5337B1F26A9EF00D80D4F /* UserText.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserText.swift; sourceTree = ""; }; F1FDC92F2BF4E0B3006B1435 /* SubscriptionEnvironment+Default.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SubscriptionEnvironment+Default.swift"; sourceTree = ""; }; F1FDC9342BF51E41006B1435 /* VPNSettings+Environment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "VPNSettings+Environment.swift"; sourceTree = ""; }; @@ -4924,7 +4926,8 @@ D6E83C472B20C812006C8AFB /* SettingsHostingController.swift */, 1DDF401F2BA049FA006850D9 /* SettingsRootView.swift */, D6E83C302B1EA309006C8AFB /* SettingsCell.swift */, - 1DEAADF12BA4716C00E25A97 /* SettingsStatus.swift */, + 1DEAADF12BA4716C00E25A97 /* StatusIndicator.swift */, + F1EFB0052C5B8B8E009AB44B /* StatusIndicatorView.swift */, 1DDF40272BA04CF3006850D9 /* Sections */, 85449EF623FDA03100512AAF /* UIkit */, ); @@ -6983,7 +6986,7 @@ BD2F39EB2C19F955005B19E7 /* NetworkProtectionDNSSettingsView.swift in Sources */, CB2A7EEF283D185100885F67 /* RulesCompilationMonitor.swift in Sources */, D6037E692C32F2E7009AAEC0 /* DuckPlayerSettings.swift in Sources */, - 1DEAADF22BA4716C00E25A97 /* SettingsStatus.swift in Sources */, + 1DEAADF22BA4716C00E25A97 /* StatusIndicator.swift in Sources */, C18ED43C2AB8364400BF3805 /* FileTextPreviewDebugViewController.swift in Sources */, 1EEF12502851016B003DDE57 /* PrivacyIconAndTrackersAnimator.swift in Sources */, 31CB4251273AF50700FA0F3F /* SpeechRecognizerProtocol.swift in Sources */, @@ -7003,6 +7006,7 @@ D66F683D2BB333C100AE93E2 /* SubscriptionContainerView.swift in Sources */, 851B128822200575004781BC /* Onboarding.swift in Sources */, 9FB027192C26BC29009EA190 /* BrowsersComparisonModel.swift in Sources */, + F1EFB0062C5B8B8E009AB44B /* StatusIndicatorView.swift in Sources */, 3151F0EE2735800800226F58 /* VoiceSearchFeedbackView.swift in Sources */, 37CF91642BB4A82A00BADCAE /* CrashCollectionOnboardingViewModel.swift in Sources */, 6F64AA5D2C4920D200CF4489 /* ShortcutAccessoryView.swift in Sources */, diff --git a/DuckDuckGo/SettingsCell.swift b/DuckDuckGo/SettingsCell.swift index f384a28b4f..5814e3cc58 100644 --- a/DuckDuckGo/SettingsCell.swift +++ b/DuckDuckGo/SettingsCell.swift @@ -133,23 +133,29 @@ struct SettingsCellView: View, Identifiable { private var defaultView: some View { Group { - HStack { - if let image { - image - } - VStack(alignment: .leading) { - Text(label) - .daxBodyRegular() - .foregroundColor(Color(designSystemColor: .textPrimary)) - if let subtitleText = subtitle { - Text(subtitleText) - .daxFootnoteRegular() - .foregroundColor(Color(designSystemColor: .textSecondary)) + HStack(alignment: .center) { + HStack(alignment: .top) { + // Image + if let image { + image + .padding(.top, -2) } - }.fixedSize(horizontal: false, vertical: true) - .layoutPriority(0.7) - - Spacer() + VStack(alignment: .leading) { + // Title + Text(label) + .daxBodyRegular() + .foregroundColor(Color(designSystemColor: .textPrimary)) + // Subtitle + if let subtitleText = subtitle { + Text(subtitleText) + .daxFootnoteRegular() + .foregroundColor(Color(designSystemColor: .textSecondary)) + } + }.fixedSize(horizontal: false, vertical: true) + .layoutPriority(0.7) + }.scaledToFit() + + Spacer(minLength: 8) accessoryView() @@ -179,6 +185,7 @@ struct SettingsCellView: View, Identifiable { case .toggle(let isOn): Toggle("", isOn: isOn) .toggleStyle(SwitchToggleStyle(tint: Color(designSystemColor: .accent))) + .fixedSize() case .image(let image): image .resizable() @@ -305,7 +312,7 @@ struct SettingsCustomCell: View { } } -private enum SampleOption: String, CaseIterable, Hashable, CustomStringConvertible { +enum SampleOption: String, CaseIterable, Hashable, CustomStringConvertible { case optionOne = "Lorem" case optionTwo = "Ipsum" case optionThree = "Dolor" @@ -324,32 +331,34 @@ private enum SampleOption: String, CaseIterable, Hashable, CustomStringConvertib SettingsCellView(label: "Multi-line Cell with disclosure \nLine 2\nLine 3", subtitle: "Curabitur erat massa, cursus sed velit", + image: Image("SettingsAppearance"), disclosureIndicator: true) .previewLayout(.sizeThatFits) SettingsCellView(label: "Image cell with disclosure ", - accesory: .image(Image(systemName: "person.circle")), + accessory: .image(Image(systemName: "person.circle")), disclosureIndicator: true) .previewLayout(.sizeThatFits) SettingsCellView(label: "Subtitle image cell with disclosure", subtitle: "This is the subtitle", - accesory: .image(Image(systemName: "person.circle")), + accessory: .image(Image("SettingsAppearance")), disclosureIndicator: true) .previewLayout(.sizeThatFits) SettingsCellView(label: "Right Detail cell with disclosure", - accesory: .rightDetail("Detail"), + accessory: .rightDetail("Detail"), disclosureIndicator: true) .previewLayout(.sizeThatFits) SettingsCellView(label: "Switch Cell", - accesory: .toggle(isOn: .constant(true))) + image: Image("SettingsAppearance"), + accessory: .toggle(isOn: .constant(true))) .previewLayout(.sizeThatFits) SettingsCellView(label: "Switch Cell", subtitle: "Subtitle goes here", - accesory: .toggle(isOn: .constant(true))) + accessory: .toggle(isOn: .constant(true))) .previewLayout(.sizeThatFits) @@ -385,7 +394,26 @@ private enum SampleOption: String, CaseIterable, Hashable, CustomStringConvertib SettingsCustomCell(content: cellContent) .previewLayout(.sizeThatFits) - + SettingsCellView(label: "Cell with image", + image: Image("SettingsAppearance"), + statusIndicator: StatusIndicatorView(status: .off), + disclosureIndicator: true + ) + + + SettingsCellView(label: "Cell a long long long long long long long title", + image: Image("SettingsAppearance"), + statusIndicator: StatusIndicatorView(status: .alwaysOn), + disclosureIndicator: true + ) + + SettingsCellView(label: "Cell with everything Lorem ipsum dolor sit amet, consectetur", + subtitle: "Long subtitle Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation", + image: Image("SettingsAppearance"), + accessory: .toggle(isOn: .constant(true)), + statusIndicator: StatusIndicatorView(status: .on), + disclosureIndicator: true + ) } } } diff --git a/DuckDuckGo/SettingsSubscriptionView.swift b/DuckDuckGo/SettingsSubscriptionView.swift index 85caf576bd..293497cb42 100644 --- a/DuckDuckGo/SettingsSubscriptionView.swift +++ b/DuckDuckGo/SettingsSubscriptionView.swift @@ -67,8 +67,10 @@ struct SettingsSubscriptionView: View { } private var manageSubscriptionView: some View { - Text(UserText.settingsPProManageSubscription) - .daxBodyRegular() + SettingsCellView( + label: UserText.settingsPProManageSubscription, + image: Image("SettingsPrivacyPro") + ) } private var subscriptionManager: SubscriptionManager { @@ -166,37 +168,34 @@ struct SettingsSubscriptionView: View { private var subscriptionDetailsView: some View { if viewModel.state.subscription.entitlements.contains(.networkProtection) { - SettingsCellView( - label: UserText.settingsPProVPNTitle, - subtitle: viewModel.state.networkProtection.status != "" - ? viewModel.state.networkProtection.status : nil, - action: { viewModel.presentLegacyView(.netP) }, - disclosureIndicator: true, - isButton: true) + SettingsCellView(label: UserText.settingsPProVPNTitle, + image: Image("SettingsPrivacyProVPN"), + action: { viewModel.presentLegacyView(.netP) }, + statusIndicator: StatusIndicatorView(status: viewModel.state.networkProtection.enabled ? .on : .off), + disclosureIndicator: true, + isButton: true) } if viewModel.state.subscription.entitlements.contains(.dataBrokerProtection) { - NavigationLink( - destination: SubscriptionPIRView(), - isActive: $isShowingDBP, - label: { - SettingsCellView( - label: UserText.settingsPProDBPTitle, - subtitle: UserText.settingsPProDBPSubTitle) - }) - + NavigationLink(destination: SubscriptionPIRView(), isActive: $isShowingDBP) { + SettingsCellView( + label: UserText.settingsPProDBPTitle, + image: Image("SettingsPrivacyProPIR"), + statusIndicator: StatusIndicatorView(status: .on) + ) + } } if viewModel.state.subscription.entitlements.contains(.identityTheftRestoration) { NavigationLink( destination: SubscriptionITPView(), - isActive: $isShowingITP, - label: { + isActive: $isShowingITP) { SettingsCellView( label: UserText.settingsPProITRTitle, - subtitle: UserText.settingsPProITRSubTitle) - }) - + image: Image("SettingsPrivacyProITP"), + statusIndicator: StatusIndicatorView(status: .on) + ) + } } NavigationLink( diff --git a/DuckDuckGo/StatusIndicator.swift b/DuckDuckGo/StatusIndicator.swift new file mode 100644 index 0000000000..f65a4289a2 --- /dev/null +++ b/DuckDuckGo/StatusIndicator.swift @@ -0,0 +1,38 @@ +// +// SettingsStatus.swift +// DuckDuckGo +// +// Copyright © 2024 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import SwiftUI +import Combine + +enum StatusIndicator: Equatable { + case alwaysOn + case on + case off + + var text: String { + switch self { + case .alwaysOn: + return UserText.settingsAlwaysOn + case .on: + return UserText.settingsOn + case .off: + return UserText.settingsOff + } + } +} diff --git a/DuckDuckGo/SettingsStatus.swift b/DuckDuckGo/StatusIndicatorView.swift similarity index 84% rename from DuckDuckGo/SettingsStatus.swift rename to DuckDuckGo/StatusIndicatorView.swift index ed75f7fca2..fe3fd4a382 100644 --- a/DuckDuckGo/SettingsStatus.swift +++ b/DuckDuckGo/StatusIndicatorView.swift @@ -1,5 +1,5 @@ // -// SettingsStatus.swift +// StatusIndicatorView.swift // DuckDuckGo // // Copyright © 2024 DuckDuckGo. All rights reserved. @@ -18,24 +18,6 @@ // import SwiftUI -import Combine - -enum StatusIndicator: Equatable { - case alwaysOn - case on - case off - - var text: String { - switch self { - case .alwaysOn: - return UserText.settingsAlwaysOn - case .on: - return UserText.settingsOn - case .off: - return UserText.settingsOff - } - } -} struct StatusIndicatorView: View { var status: StatusIndicator From 281e91a409b7fd8ef6ce4f5fa333e19eb6ae0985 Mon Sep 17 00:00:00 2001 From: Federico Cappelli Date: Mon, 5 Aug 2024 12:44:37 +0100 Subject: [PATCH 04/20] Root Settings changes completed --- .../SettingsPrivacyPro.imageset/Contents.json | 2 +- .../Privacy-Pro-24-Multicolor.pdf | Bin .../SettingsSubscription.pdf | Bin 2253 -> 0 bytes .../Contents.json | 2 +- .../SettingsPrivacyProWarning.pdf | Bin 0 -> 1041 bytes DuckDuckGo/SettingsCell.swift | 42 +++- DuckDuckGo/SettingsSubscriptionView.swift | 218 ++++++++++-------- 7 files changed, 156 insertions(+), 108 deletions(-) rename DuckDuckGo/Settings.xcassets/{Images => Privacy Pro}/SettingsPrivacyPro.imageset/Privacy-Pro-24-Multicolor.pdf (100%) delete mode 100644 DuckDuckGo/Settings.xcassets/Privacy Pro/SettingsPrivacyPro.imageset/SettingsSubscription.pdf rename DuckDuckGo/Settings.xcassets/{Images/SettingsPrivacyPro.imageset => Privacy Pro/SettingsPrivacyProWarning.imageset}/Contents.json (70%) create mode 100644 DuckDuckGo/Settings.xcassets/Privacy Pro/SettingsPrivacyProWarning.imageset/SettingsPrivacyProWarning.pdf diff --git a/DuckDuckGo/Settings.xcassets/Privacy Pro/SettingsPrivacyPro.imageset/Contents.json b/DuckDuckGo/Settings.xcassets/Privacy Pro/SettingsPrivacyPro.imageset/Contents.json index 0bd3ba405b..225cc1e823 100644 --- a/DuckDuckGo/Settings.xcassets/Privacy Pro/SettingsPrivacyPro.imageset/Contents.json +++ b/DuckDuckGo/Settings.xcassets/Privacy Pro/SettingsPrivacyPro.imageset/Contents.json @@ -1,7 +1,7 @@ { "images" : [ { - "filename" : "SettingsSubscription.pdf", + "filename" : "Privacy-Pro-24-Multicolor.pdf", "idiom" : "universal" } ], diff --git a/DuckDuckGo/Settings.xcassets/Images/SettingsPrivacyPro.imageset/Privacy-Pro-24-Multicolor.pdf b/DuckDuckGo/Settings.xcassets/Privacy Pro/SettingsPrivacyPro.imageset/Privacy-Pro-24-Multicolor.pdf similarity index 100% rename from DuckDuckGo/Settings.xcassets/Images/SettingsPrivacyPro.imageset/Privacy-Pro-24-Multicolor.pdf rename to DuckDuckGo/Settings.xcassets/Privacy Pro/SettingsPrivacyPro.imageset/Privacy-Pro-24-Multicolor.pdf diff --git a/DuckDuckGo/Settings.xcassets/Privacy Pro/SettingsPrivacyPro.imageset/SettingsSubscription.pdf b/DuckDuckGo/Settings.xcassets/Privacy Pro/SettingsPrivacyPro.imageset/SettingsSubscription.pdf deleted file mode 100644 index b1878653f8298fc15be71b1c67361154c3c18053..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2253 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 literal 0 HcmV?d00001 diff --git a/DuckDuckGo/SettingsCell.swift b/DuckDuckGo/SettingsCell.swift index 5814e3cc58..a38ff6baee 100644 --- a/DuckDuckGo/SettingsCell.swift +++ b/DuckDuckGo/SettingsCell.swift @@ -55,6 +55,7 @@ struct SettingsCellView: View, Identifiable { var webLinkIndicator: Bool var id: UUID = UUID() var isButton: Bool + var isGreyedOut: Bool /// Initializes a `SettingsCellView` with the specified label and accessory. /// @@ -69,7 +70,7 @@ struct SettingsCellView: View, Identifiable { /// - disclosureIndicator: Forces Adds a disclosure indicator on the right (chevron) /// - webLinkIndicator: Adds a link indicator on the right /// - isButton: Disables the tap actions on the cell if true - init(label: String, subtitle: String? = nil, image: Image? = nil, action: @escaping () -> Void = {}, accessory: Accessory = .none, enabled: Bool = true, statusIndicator: StatusIndicatorView? = nil, disclosureIndicator: Bool = false, webLinkIndicator: Bool = false, isButton: Bool = false) { + init(label: String, subtitle: String? = nil, image: Image? = nil, action: @escaping () -> Void = {}, accessory: Accessory = .none, enabled: Bool = true, statusIndicator: StatusIndicatorView? = nil, disclosureIndicator: Bool = false, webLinkIndicator: Bool = false, isButton: Bool = false, isGreyedOut: Bool = false) { self.label = label self.subtitle = subtitle self.image = image @@ -80,6 +81,7 @@ struct SettingsCellView: View, Identifiable { self.disclosureIndicator = disclosureIndicator self.webLinkIndicator = webLinkIndicator self.isButton = isButton + self.isGreyedOut = isGreyedOut } /// Initializes a `SettingsCellView` for custom content. @@ -98,6 +100,7 @@ struct SettingsCellView: View, Identifiable { self.disclosureIndicator = false self.webLinkIndicator = false self.isButton = false + self.isGreyedOut = false } var body: some View { @@ -137,14 +140,21 @@ struct SettingsCellView: View, Identifiable { HStack(alignment: .top) { // Image if let image { - image - .padding(.top, -2) + if isGreyedOut { + image + .padding(.top, -2) + .saturation(0) + .opacity(0.7) + } else { + image + .padding(.top, -2) + } } VStack(alignment: .leading) { // Title Text(label) .daxBodyRegular() - .foregroundColor(Color(designSystemColor: .textPrimary)) + .foregroundColor(Color(designSystemColor: isGreyedOut == true ? .textSecondary : .textPrimary)) // Subtitle if let subtitleText = subtitle { Text(subtitleText) @@ -187,11 +197,13 @@ struct SettingsCellView: View, Identifiable { .toggleStyle(SwitchToggleStyle(tint: Color(designSystemColor: .accent))) .fixedSize() case .image(let image): - image - .resizable() - .scaledToFit() - .frame(width: 25, height: 25) - .cornerRadius(4) + if isGreyedOut { + image + .saturation(0) + .opacity(0.7) + } else { + image + } case .custom(let customView): customView } @@ -331,7 +343,7 @@ enum SampleOption: String, CaseIterable, Hashable, CustomStringConvertible { SettingsCellView(label: "Multi-line Cell with disclosure \nLine 2\nLine 3", subtitle: "Curabitur erat massa, cursus sed velit", - image: Image("SettingsAppearance"), + image: Image("SettingsPrivacyProITP"), disclosureIndicator: true) .previewLayout(.sizeThatFits) @@ -342,10 +354,18 @@ enum SampleOption: String, CaseIterable, Hashable, CustomStringConvertible { SettingsCellView(label: "Subtitle image cell with disclosure", subtitle: "This is the subtitle", - accessory: .image(Image("SettingsAppearance")), + accessory: .image(Image("SettingsPrivacyProWarning")), disclosureIndicator: true) .previewLayout(.sizeThatFits) + SettingsCellView(label: "Greyed out cell", + subtitle: "This is the subtitle", + image: Image("SettingsPrivacyProITP"), + accessory: .image(Image("SettingsPrivacyProWarning")), + disclosureIndicator: true, + isGreyedOut: true) + .previewLayout(.sizeThatFits) + SettingsCellView(label: "Right Detail cell with disclosure", accessory: .rightDetail("Detail"), disclosureIndicator: true) diff --git a/DuckDuckGo/SettingsSubscriptionView.swift b/DuckDuckGo/SettingsSubscriptionView.swift index 293497cb42..569d41c128 100644 --- a/DuckDuckGo/SettingsSubscriptionView.swift +++ b/DuckDuckGo/SettingsSubscriptionView.swift @@ -95,75 +95,118 @@ struct SettingsSubscriptionView: View { .onFirstAppear { Pixel.fire(pixel: .privacyProRestorePurchaseClick) } - NavigationLink(destination: subscribeView, isActive: $isShowingSubscribeFlow) { - SettingsCellView(label: UserText.settingsPProLearnMore) + SettingsCellView(label: UserText.settingsPProLearnMore).padding(.leading, 32.0) } NavigationLink(destination: restoreView, isActive: $isShowingRestoreFlow) { - SettingsCellView(label: UserText.settingsPProIHaveASubscription ) + SettingsCellView(label: UserText.settingsPProIHaveASubscription).padding(.leading, 32.0) } } } @ViewBuilder private var subscriptionExpiredView: some View { - Group { - SettingsCustomCell(content: { - HStack(alignment: .top) { - Image(SettingsSubscriptionViewConstants.alertIcon) - .frame(width: SettingsSubscriptionViewConstants.noEntitlementsIconWidth) - .padding(.top, SettingsSubscriptionViewConstants.topCellPadding) - VStack(alignment: .leading) { - Text(UserText.settingsPProSubscriptionExpiredTitle).daxBodyRegular() - Text(UserText.settingsPProSubscribeAgain).daxFootnoteRegular() - .padding(.bottom, SettingsSubscriptionViewConstants.purchaseDescriptionPadding) - }.foregroundColor(Color(designSystemColor: .textSecondary)) - } - }) - - let subscribeView = SubscriptionContainerViewFactory.makeSubscribeFlow(origin: nil, - navigationCoordinator: subscriptionNavigationCoordinator, - subscriptionManager: subscriptionManager, - privacyProDataReporter: viewModel.privacyProDataReporter - ).navigationViewStyle(.stack) - NavigationLink( - destination: subscribeView, - isActive: $isShowingSubscribeFlow, - label: { SettingsCellView(label: UserText.subscriptionRestoreNotFoundPlans) }) - - // Renew Subscription (Expired) - let settingsView = SubscriptionSettingsView(viewPlans: { - isShowingSubscribeFlow = true - }) - .environmentObject(subscriptionNavigationCoordinator) - NavigationLink(destination: settingsView) { - SettingsCustomCell(content: { manageSubscriptionView }) - } + if viewModel.state.subscription.entitlements.contains(.networkProtection) { + SettingsCellView(label: UserText.settingsPProVPNTitle, + image: Image("SettingsPrivacyProVPN"), + statusIndicator: StatusIndicatorView(status: .off), + isGreyedOut: true + ) + } + + if viewModel.state.subscription.entitlements.contains(.dataBrokerProtection) { + SettingsCellView( + label: UserText.settingsPProDBPTitle, + image: Image("SettingsPrivacyProPIR"), + statusIndicator: StatusIndicatorView(status: .off), + isGreyedOut: true + ) + } + + if viewModel.state.subscription.entitlements.contains(.identityTheftRestoration) { + SettingsCellView( + label: UserText.settingsPProITRTitle, + image: Image("SettingsPrivacyProITP"), + statusIndicator: StatusIndicatorView(status: .off), + isGreyedOut: true + ) + } + + // Renew Subscription (Expired) + let settingsView = SubscriptionSettingsView(viewPlans: { isShowingSubscribeFlow = true }) + .environmentObject(subscriptionNavigationCoordinator) + NavigationLink(destination: settingsView) { + SettingsCellView( + label: UserText.settingsPProManageSubscription, + subtitle: "Your Privacy Pro subscription expired", + image: Image("SettingsPrivacyPro"), + accessory: .image(Image("SettingsPrivacyProWarning")) + ) } } - + +// @ViewBuilder +// private var noEntitlementsAvailableView: some View { +// Group { +// SettingsCustomCell(content: { +// HStack(alignment: .top) { +// Image(SettingsSubscriptionViewConstants.infoIcon) +// .frame(width: SettingsSubscriptionViewConstants.noEntitlementsIconWidth) +// .padding(.top, SettingsSubscriptionViewConstants.topCellPadding) +// VStack(alignment: .leading) { +// Text(UserText.settingsPProActivationPendingTitle).daxBodyRegular() +// Text(UserText.settingsPProActivationPendingDescription).daxFootnoteRegular() +// .padding(.bottom, SettingsSubscriptionViewConstants.purchaseDescriptionPadding) +// }.foregroundColor(Color(designSystemColor: .textSecondary)) +// } +// }) +// restorePurchaseView +// } +// } + @ViewBuilder private var noEntitlementsAvailableView: some View { - Group { - SettingsCustomCell(content: { - HStack(alignment: .top) { - Image(SettingsSubscriptionViewConstants.infoIcon) - .frame(width: SettingsSubscriptionViewConstants.noEntitlementsIconWidth) - .padding(.top, SettingsSubscriptionViewConstants.topCellPadding) - VStack(alignment: .leading) { - Text(UserText.settingsPProActivationPendingTitle).daxBodyRegular() - Text(UserText.settingsPProActivationPendingDescription).daxFootnoteRegular() - .padding(.bottom, SettingsSubscriptionViewConstants.purchaseDescriptionPadding) - }.foregroundColor(Color(designSystemColor: .textSecondary)) - } - }) - restorePurchaseView + if viewModel.state.subscription.entitlements.contains(.networkProtection) { + SettingsCellView(label: UserText.settingsPProVPNTitle, + image: Image("SettingsPrivacyProVPN"), + statusIndicator: StatusIndicatorView(status: .off), + isGreyedOut: true + ) + } + + if viewModel.state.subscription.entitlements.contains(.dataBrokerProtection) { + SettingsCellView( + label: UserText.settingsPProDBPTitle, + image: Image("SettingsPrivacyProPIR"), + statusIndicator: StatusIndicatorView(status: .off), + isGreyedOut: true + ) + } + + if viewModel.state.subscription.entitlements.contains(.identityTheftRestoration) { + SettingsCellView( + label: UserText.settingsPProITRTitle, + image: Image("SettingsPrivacyProITP"), + statusIndicator: StatusIndicatorView(status: .off), + isGreyedOut: true + ) + } + + // Renew Subscription (Expired) + let settingsView = SubscriptionSettingsView(viewPlans: { isShowingSubscribeFlow = true }) + .environmentObject(subscriptionNavigationCoordinator) + NavigationLink(destination: settingsView) { + SettingsCellView( + label: UserText.settingsPProManageSubscription, + subtitle: "Activating", + image: Image("SettingsPrivacyPro") + ) } } - + @ViewBuilder private var subscriptionDetailsView: some View { @@ -209,34 +252,38 @@ struct SettingsSubscriptionView: View { var body: some View { Group { if isShowingPrivacyPro { + + let isSignedIn = viewModel.state.subscription.isSignedIn + let hasActiveSubscription = viewModel.state.subscription.hasActiveSubscription + let hasNoEntitlements = viewModel.state.subscription.entitlements.isEmpty + let footerLink = Link(UserText.settingsPProSectionFooter, - destination: SettingsSubscriptionViewConstants.privacyPolicyURL - ).daxFootnoteRegular().accentColor(Color.init(designSystemColor: .accent)) + destination: SettingsSubscriptionViewConstants.privacyPolicyURL) + .daxFootnoteRegular().accentColor(Color.init(designSystemColor: .accent)) - Section(header: Text(UserText.settingsPProSection), footer: footerLink) { + Section(header: Text(UserText.settingsPProSection), + footer: !isSignedIn ? footerLink : nil + ) { + purchaseSubscriptionView - switch ( - viewModel.state.subscription.isSignedIn, - viewModel.state.subscription.hasActiveSubscription, - viewModel.state.subscription.entitlements.isEmpty - ) { - - // Signed In, Subscription Expired - case (true, false, _): - subscriptionExpiredView - - // Signed in, Subscription Active, Valid entitlements - case (true, true, false): - subscriptionDetailsView // View for valid subscription details - - // Signed in, Subscription Active, Empty Entitlements - case (true, true, true): - noEntitlementsAvailableView // View for no entitlements - - // Signed out - case (false, _, _): - purchaseSubscriptionView // View for signing up or purchasing a subscription - } +// switch (isSignedIn, hasActiveSubscription, hasNoEntitlements) { +// +// // Signed In, Subscription Expired +// case (true, false, _): +// subscriptionExpiredView +// +// // Signed in, Subscription Active, Valid entitlements +// case (true, true, false): +// subscriptionDetailsView // View for valid subscription details +// +// // Signed in, Subscription Active, Empty Entitlements +// case (true, true, true): +// noEntitlementsAvailableView // View for no entitlements +// +// // Signed out +// case (false, _, _): +// purchaseSubscriptionView // View for signing up or purchasing a subscription +// } } .onChange(of: viewModel.state.subscription.shouldDisplayRestoreSubscriptionError) { value in @@ -258,22 +305,3 @@ struct SettingsSubscriptionView: View { } } } -// -// let settingsModel = SettingsViewModel(legacyViewProvider: <#T##SettingsLegacyViewProvider#>, -// subscriptionManager: <#T##any SubscriptionManager#>, -// historyManager: <#T##any HistoryManaging#>, -// syncPausedStateManager: <#T##any SyncPausedStateManaging#>, -// privacyProDataReporter: <#T##any PrivacyProDataReporting#>) -// -// #Preview { -// SettingsSubscriptionView(viewModel: settingsModel, -// subscriptionNavigationCoordinator: <#T##SubscriptionNavigationCoordinator#>, -// isShowingDBP: <#T##arg#>, -// isShowingITP: <#T##arg#>, -// isShowingRestoreFlow: <#T##arg#>, -// isShowingSubscribeFlow: <#T##arg#>, -// isShowingGoogleView: <#T##arg#>, -// isShowingStripeView: <#T##arg#>, -// isShowingSubscriptionError: <#T##arg#>, -// isShowingPrivacyPro: <#T##arg#>) -// } From ce7de9ee46b2b534fd6add586657ed5adbb1cb44 Mon Sep 17 00:00:00 2001 From: Federico Cappelli Date: Tue, 6 Aug 2024 16:43:51 +0100 Subject: [PATCH 05/20] DesignResourceKit 3.1.0, all subscription settings completed --- DuckDuckGo.xcodeproj/project.pbxproj | 8 +- .../xcshareddata/swiftpm/Package.resolved | 9 - .../Colors/AlertGreen.colorset/Contents.json | 20 -- .../Settings.xcassets/Colors/Contents.json | 6 - .../Contents.json | 12 - .../SettingsPrivacyProWarning.pdf | Bin 1041 -> 0 bytes DuckDuckGo/SettingsCell.swift | 8 +- DuckDuckGo/SettingsRootView.swift | 3 +- DuckDuckGo/SettingsSubscriptionView.swift | 246 ++++++++---------- DuckDuckGo/StatusIndicatorView.swift | 4 +- .../Contents.json | 12 - .../Exclamation-Color-16.pdf | Bin 1697 -> 0 bytes .../Privacy-Pro-96x96.svg | 13 - .../Contents.json | 2 +- .../PrivacyProHeader.pdf | Bin 0 -> 5593 bytes .../SubscriptionSettingsViewModel.swift | 7 +- .../SubscriptionContainerViewFactory.swift | 8 +- .../Views/SubscriptionFlowView.swift | 6 +- .../SubscriptionSettingsHeaderView.swift | 82 ++++++ .../Views/SubscriptionSettingsView.swift | 198 ++++++++------ DuckDuckGo/UserText.swift | 12 +- DuckDuckGo/en.lproj/Localizable.strings | 15 +- LocalPackages/SyncUI/Package.swift | 2 +- LocalPackages/Waitlist/Package.swift | 2 +- 24 files changed, 353 insertions(+), 322 deletions(-) delete mode 100644 DuckDuckGo/Settings.xcassets/Colors/AlertGreen.colorset/Contents.json delete mode 100644 DuckDuckGo/Settings.xcassets/Colors/Contents.json delete mode 100644 DuckDuckGo/Settings.xcassets/Privacy Pro/SettingsPrivacyProWarning.imageset/Contents.json delete mode 100644 DuckDuckGo/Settings.xcassets/Privacy Pro/SettingsPrivacyProWarning.imageset/SettingsPrivacyProWarning.pdf delete mode 100644 DuckDuckGo/Subscription/Subscription.xcassets/Exclamation-Color-16.imageset/Contents.json delete mode 100644 DuckDuckGo/Subscription/Subscription.xcassets/Exclamation-Color-16.imageset/Exclamation-Color-16.pdf delete mode 100644 DuckDuckGo/Subscription/Subscription.xcassets/Privacy-Pro-96x96.imageset/Privacy-Pro-96x96.svg rename DuckDuckGo/Subscription/Subscription.xcassets/{Privacy-Pro-96x96.imageset => PrivacyProHeader.imageset}/Contents.json (73%) create mode 100644 DuckDuckGo/Subscription/Subscription.xcassets/PrivacyProHeader.imageset/PrivacyProHeader.pdf create mode 100644 DuckDuckGo/Subscription/Views/SubscriptionSettingsHeaderView.swift diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index cd457efa86..888f370a92 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -945,6 +945,7 @@ F114C55B1E66EB020018F95F /* NibLoading.swift in Sources */ = {isa = PBXBuildFile; fileRef = F114C55A1E66EB020018F95F /* NibLoading.swift */; }; F115ED9C2B4EFC8E001A0453 /* TestUtils in Frameworks */ = {isa = PBXBuildFile; productRef = F115ED9B2B4EFC8E001A0453 /* TestUtils */; }; F130D73A1E5776C500C45811 /* OmniBarDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F130D7391E5776C500C45811 /* OmniBarDelegate.swift */; }; + F132D6A52C62239B00D85426 /* SubscriptionSettingsHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F132D6A42C62239B00D85426 /* SubscriptionSettingsHeaderView.swift */; }; F1386BA41E6846C40062FC3C /* TabDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1386BA31E6846C40062FC3C /* TabDelegate.swift */; }; F13B4BC01F180D8A00814661 /* TabsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F13B4BBF1F180D8A00814661 /* TabsModel.swift */; }; F13B4BD31F1822C700814661 /* Tab.swift in Sources */ = {isa = PBXBuildFile; fileRef = F13B4BD21F1822C700814661 /* Tab.swift */; }; @@ -2676,6 +2677,8 @@ F1134ED41F40F15800B73467 /* StatisticsUserDefaultsTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StatisticsUserDefaultsTests.swift; sourceTree = ""; }; F114C55A1E66EB020018F95F /* NibLoading.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NibLoading.swift; sourceTree = ""; }; F130D7391E5776C500C45811 /* OmniBarDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OmniBarDelegate.swift; sourceTree = ""; }; + F132D6A42C62239B00D85426 /* SubscriptionSettingsHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionSettingsHeaderView.swift; sourceTree = ""; }; + F132D6A62C62294000D85426 /* DesignResourcesKit */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = DesignResourcesKit; path = ../DesignResourcesKit; sourceTree = ""; }; F1386BA31E6846C40062FC3C /* TabDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TabDelegate.swift; sourceTree = ""; }; F13B4BBF1F180D8A00814661 /* TabsModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TabsModel.swift; sourceTree = ""; }; F13B4BD21F1822C700814661 /* Tab.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Tab.swift; sourceTree = ""; }; @@ -3818,6 +3821,7 @@ 84E341891E2F7EFB00BDBA6F = { isa = PBXGroup; children = ( + F132D6A62C62294000D85426 /* DesignResourcesKit */, EE3B98EB2A963515002F63A0 /* WidgetsExtensionAlpha.entitlements */, 6FB030C7234331B400A10DB9 /* Configuration.xcconfig */, EEB8FDB92A990AEE00EBEDCF /* Configuration-Alpha.xcconfig */, @@ -4880,6 +4884,7 @@ D68A21432B7EC08500BB372E /* SubscriptionExternalLinkView.swift */, D6BFCB5E2B7524AA0051FF81 /* SubscriptionPIRView.swift */, D6F93E3D2B50A8A0004C268D /* SubscriptionSettingsView.swift */, + F132D6A42C62239B00D85426 /* SubscriptionSettingsHeaderView.swift */, D60B1F262B9DDE5A00AE4760 /* SubscriptionGoogleView.swift */, D670E5BA2BB6A75200941A42 /* SubscriptionNavigationCoordinator.swift */, D63677F42BBDB1C300605BA5 /* DaxLogoNavbarTitle.swift */, @@ -6821,6 +6826,7 @@ D6FEB8B32B74990D00C3615F /* HeadlessWebViewNavCoordinator.swift in Sources */, 85BDC310243359040053DB07 /* FindInPageUserScript.swift in Sources */, F1DE78581E5CAE350058895A /* TabViewGridCell.swift in Sources */, + F132D6A52C62239B00D85426 /* SubscriptionSettingsHeaderView.swift in Sources */, 984D035824ACCC6F0066CFB8 /* TabViewListCell.swift in Sources */, B6BA95C328891E33004ABA20 /* BrowsingMenuAnimator.swift in Sources */, D68DF81C2B58302E0023DBEA /* SubscriptionRestoreView.swift in Sources */, @@ -10330,7 +10336,7 @@ repositoryURL = "https://github.com/duckduckgo/DesignResourcesKit"; requirement = { kind = exactVersion; - version = 3.0.0; + version = 3.1.0; }; }; F486D2EF25069482002D07D7 /* XCRemoteSwiftPackageReference "Kingfisher" */ = { diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index a2a8000cb8..c2445f4ac1 100644 --- a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -45,15 +45,6 @@ "version" : "6.4.0" } }, - { - "identity" : "designresourceskit", - "kind" : "remoteSourceControl", - "location" : "https://github.com/duckduckgo/DesignResourcesKit", - "state" : { - "revision" : "ae83941bb277a2750abc2d6697fa278f8c8c5f5e", - "version" : "3.0.0" - } - }, { "identity" : "duckduckgo-autofill", "kind" : "remoteSourceControl", diff --git a/DuckDuckGo/Settings.xcassets/Colors/AlertGreen.colorset/Contents.json b/DuckDuckGo/Settings.xcassets/Colors/AlertGreen.colorset/Contents.json deleted file mode 100644 index ba7383a80e..0000000000 --- a/DuckDuckGo/Settings.xcassets/Colors/AlertGreen.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0x00", - "green" : "0xC0", - "red" : "0x21" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/DuckDuckGo/Settings.xcassets/Colors/Contents.json b/DuckDuckGo/Settings.xcassets/Colors/Contents.json deleted file mode 100644 index 73c00596a7..0000000000 --- a/DuckDuckGo/Settings.xcassets/Colors/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/DuckDuckGo/Settings.xcassets/Privacy Pro/SettingsPrivacyProWarning.imageset/Contents.json b/DuckDuckGo/Settings.xcassets/Privacy Pro/SettingsPrivacyProWarning.imageset/Contents.json deleted file mode 100644 index 79cc3b357b..0000000000 --- a/DuckDuckGo/Settings.xcassets/Privacy Pro/SettingsPrivacyProWarning.imageset/Contents.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "images" : [ - { - "filename" : "SettingsPrivacyProWarning.pdf", - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/DuckDuckGo/Settings.xcassets/Privacy Pro/SettingsPrivacyProWarning.imageset/SettingsPrivacyProWarning.pdf b/DuckDuckGo/Settings.xcassets/Privacy Pro/SettingsPrivacyProWarning.imageset/SettingsPrivacyProWarning.pdf deleted file mode 100644 index 506a8447120d791eedda7f0e500881753b824ee2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1041 zcmY!laBvK;I`dFTEr~!5FAK2q*+Jp}3?dH8Gc~g0Xkfc`p`2o@4K8yDYh{{mH!i zY*kbGS3x${#BxWmn!3q}>bmRLt7o3k&;9f?`@P)#efHP4PhbA~=|j`Iz5Bn;*(a2C z;~G~xCNdaIqnDY?RRveFk@ z79E?#CgZwHV^d+u0@J^NRU92kjf;+DIp4Z|EkN9HNmBI$)7mB9XP0=L|6dwWlz)}^ zUZ@q&Gwd+dNFLt z5EI!F%bvcM(JXncOV^K+T0N-;XB?2~Dw)c8Gc{FEcY&~t#2eqMBD@}wT|w6`9`sDq zaMQEeGplLAE_3m{aXz(vre!m^iZ@%HHnDtm$|H*Xqh)<{*@oSRdbaG8KXvC%SW9No zly8Q+u{MQfg{OzVP3s9hEp z8$rnoCK!-d1Qb&+g$f}hMvxFZk?MPArU0F)0P;bQ0?Z!g{8FG^Lk#y9LlPw{Fw9U) z0(r0)ZXw8F5RW@2mL%rnr=x1EC`wJ^GEgw*f_NW9Dwvs?8k;Hrg`vREKmp8B$b$qOh9OjANl{{EPHGVsDE2&EfWe_roS&Pjsi2XW rq6rBU{h<8(5(SWl!2zrvoLQ9$bUiqSCKi=|y<=fyz@@6{>hA^s5BpE$ diff --git a/DuckDuckGo/SettingsCell.swift b/DuckDuckGo/SettingsCell.swift index a38ff6baee..e6107398f4 100644 --- a/DuckDuckGo/SettingsCell.swift +++ b/DuckDuckGo/SettingsCell.swift @@ -144,7 +144,7 @@ struct SettingsCellView: View, Identifiable { image .padding(.top, -2) .saturation(0) - .opacity(0.7) + .opacity(0.5) } else { image .padding(.top, -2) @@ -200,7 +200,7 @@ struct SettingsCellView: View, Identifiable { if isGreyedOut { image .saturation(0) - .opacity(0.7) + .opacity(0.5) } else { image } @@ -354,14 +354,14 @@ enum SampleOption: String, CaseIterable, Hashable, CustomStringConvertible { SettingsCellView(label: "Subtitle image cell with disclosure", subtitle: "This is the subtitle", - accessory: .image(Image("SettingsPrivacyProWarning")), + accessory: .image(Image("Exclamation-Color-16")), disclosureIndicator: true) .previewLayout(.sizeThatFits) SettingsCellView(label: "Greyed out cell", subtitle: "This is the subtitle", image: Image("SettingsPrivacyProITP"), - accessory: .image(Image("SettingsPrivacyProWarning")), + accessory: .image(Image("Exclamation-Color-16")), disclosureIndicator: true, isGreyedOut: true) .previewLayout(.sizeThatFits) diff --git a/DuckDuckGo/SettingsRootView.swift b/DuckDuckGo/SettingsRootView.swift index 765d1a66f3..b91ea60e2b 100644 --- a/DuckDuckGo/SettingsRootView.swift +++ b/DuckDuckGo/SettingsRootView.swift @@ -112,9 +112,10 @@ struct SettingsRootView: View { SubscriptionITPView() case let .subscriptionFlow(origin): SubscriptionContainerViewFactory.makeSubscribeFlow(origin: origin, - navigationCoordinator: subscriptionNavigationCoordinator, +// navigationCoordinator: subscriptionNavigationCoordinator, subscriptionManager: AppDependencyProvider.shared.subscriptionManager, privacyProDataReporter: viewModel.privacyProDataReporter) + .environmentObject(subscriptionNavigationCoordinator) case .restoreFlow: SubscriptionContainerViewFactory.makeEmailFlow(navigationCoordinator: subscriptionNavigationCoordinator, subscriptionManager: AppDependencyProvider.shared.subscriptionManager, diff --git a/DuckDuckGo/SettingsSubscriptionView.swift b/DuckDuckGo/SettingsSubscriptionView.swift index 569d41c128..a6357c5488 100644 --- a/DuckDuckGo/SettingsSubscriptionView.swift +++ b/DuckDuckGo/SettingsSubscriptionView.swift @@ -29,7 +29,6 @@ enum SettingsSubscriptionViewConstants { static let navigationDelay = 0.3 static let infoIcon = "info-16" static let alertIcon = "Exclamation-Color-16" - static let privacyPolicyURL = URL(string: "https://duckduckgo.com/pro/privacy-terms")! } @@ -43,28 +42,33 @@ struct SettingsSubscriptionView: View { @State var isShowingSubscribeFlow = false @State var isShowingGoogleView = false @State var isShowingStripeView = false - @State var isShowingSubscriptionError = false +// @State var isShowingSubscriptionError = false @State var isShowingPrivacyPro = false - - @ViewBuilder - private var restorePurchaseView: some View { - let text = !viewModel.state.subscription.isRestoring ? UserText.subscriptionActivateAppleIDButton : UserText.subscriptionRestoringTitle - SettingsCustomCell(content: { - Text(text) - .daxBodyRegular() - .foregroundColor(Color.init(designSystemColor: .accent)) }, - action: { - Task { await viewModel.restoreAccountPurchase() } - }, - isButton: !viewModel.state.subscription.isRestoring ) - .alert(isPresented: $isShowingSubscriptionError) { - Alert( - title: Text(UserText.subscriptionAppStoreErrorTitle), - message: Text(UserText.subscriptionAppStoreErrorMessage), - dismissButton: .default(Text(UserText.actionOK)) {} - ) - } + + var subscriptionRestoreView: some View { + SubscriptionContainerViewFactory.makeRestoreFlow(navigationCoordinator: subscriptionNavigationCoordinator, + subscriptionManager: subscriptionManager) } + +// @ViewBuilder +// private var restorePurchaseView: some View { +// let text = !viewModel.state.subscription.isRestoring ? UserText.subscriptionActivateAppleIDButton : UserText.subscriptionRestoringTitle +// SettingsCustomCell(content: { +// Text(text) +// .daxBodyRegular() +// .foregroundColor(Color.init(designSystemColor: .accent)) }, +// action: { +// Task { await viewModel.restoreAccountPurchase() } +// }, +// isButton: !viewModel.state.subscription.isRestoring ) +// .alert(isPresented: $isShowingSubscriptionError) { +// Alert( +// title: Text(UserText.subscriptionAppStoreErrorTitle), +// message: Text(UserText.subscriptionAppStoreErrorMessage), +// dismissButton: .default(Text(UserText.actionOK)) {} +// ) +// } +// } private var manageSubscriptionView: some View { SettingsCellView( @@ -84,22 +88,22 @@ struct SettingsSubscriptionView: View { subtitle: UserText.settingsPProDescription, image: Image("SettingsPrivacyPro")) - let subscribeView = SubscriptionContainerViewFactory.makeSubscribeFlow(origin: nil, - navigationCoordinator: subscriptionNavigationCoordinator, - subscriptionManager: subscriptionManager, - privacyProDataReporter: viewModel.privacyProDataReporter - ).navigationViewStyle(.stack) - let restoreView = SubscriptionContainerViewFactory.makeRestoreFlow(navigationCoordinator: subscriptionNavigationCoordinator, - subscriptionManager: subscriptionManager) + // Get privacy pro + SettingsCustomCell(content: { + Text(UserText.settingsPProLearnMore) + .daxBodyRegular() + .foregroundColor(Color.init(designSystemColor: .accent)) + .padding(.leading, 32.0) + }, action: { + isShowingSubscribeFlow = true + }, isButton: true) + + // Restore subscription + let restoreView = subscriptionRestoreView .navigationViewStyle(.stack) .onFirstAppear { Pixel.fire(pixel: .privacyProRestorePurchaseClick) } - NavigationLink(destination: subscribeView, - isActive: $isShowingSubscribeFlow) { - SettingsCellView(label: UserText.settingsPProLearnMore).padding(.leading, 32.0) - } - NavigationLink(destination: restoreView, isActive: $isShowingRestoreFlow) { SettingsCellView(label: UserText.settingsPProIHaveASubscription).padding(.leading, 32.0) @@ -108,100 +112,66 @@ struct SettingsSubscriptionView: View { } @ViewBuilder - private var subscriptionExpiredView: some View { - if viewModel.state.subscription.entitlements.contains(.networkProtection) { - SettingsCellView(label: UserText.settingsPProVPNTitle, - image: Image("SettingsPrivacyProVPN"), - statusIndicator: StatusIndicatorView(status: .off), - isGreyedOut: true - ) - } + private var disabledFeaturesView: some View { + // if viewModel.state.subscription.entitlements.contains(.networkProtection) { + SettingsCellView(label: UserText.settingsPProVPNTitle, + image: Image("SettingsPrivacyProVPN"), + statusIndicator: StatusIndicatorView(status: .off), + isGreyedOut: true + ) + // } - if viewModel.state.subscription.entitlements.contains(.dataBrokerProtection) { - SettingsCellView( - label: UserText.settingsPProDBPTitle, - image: Image("SettingsPrivacyProPIR"), - statusIndicator: StatusIndicatorView(status: .off), - isGreyedOut: true - ) - } + // if viewModel.state.subscription.entitlements.contains(.dataBrokerProtection) { + SettingsCellView( + label: UserText.settingsPProDBPTitle, + image: Image("SettingsPrivacyProPIR"), + statusIndicator: StatusIndicatorView(status: .off), + isGreyedOut: true + ) + // } - if viewModel.state.subscription.entitlements.contains(.identityTheftRestoration) { - SettingsCellView( - label: UserText.settingsPProITRTitle, - image: Image("SettingsPrivacyProITP"), - statusIndicator: StatusIndicatorView(status: .off), - isGreyedOut: true - ) - } + // if viewModel.state.subscription.entitlements.contains(.identityTheftRestoration) { + SettingsCellView( + label: UserText.settingsPProITRTitle, + image: Image("SettingsPrivacyProITP"), + statusIndicator: StatusIndicatorView(status: .off), + isGreyedOut: true + ) + // } + } + + @ViewBuilder + private var subscriptionExpiredView: some View { + disabledFeaturesView // Renew Subscription (Expired) - let settingsView = SubscriptionSettingsView(viewPlans: { isShowingSubscribeFlow = true }) + let settingsView = SubscriptionSettingsView(configuration: .expired, + settingsViewModel: viewModel, + viewPlans: { isShowingSubscribeFlow = true }) .environmentObject(subscriptionNavigationCoordinator) NavigationLink(destination: settingsView) { SettingsCellView( label: UserText.settingsPProManageSubscription, - subtitle: "Your Privacy Pro subscription expired", + subtitle: UserText.settingsPProSubscriptionExpiredTitle, image: Image("SettingsPrivacyPro"), - accessory: .image(Image("SettingsPrivacyProWarning")) + accessory: .image(Image("Exclamation-Color-16")) ) } } -// @ViewBuilder -// private var noEntitlementsAvailableView: some View { -// Group { -// SettingsCustomCell(content: { -// HStack(alignment: .top) { -// Image(SettingsSubscriptionViewConstants.infoIcon) -// .frame(width: SettingsSubscriptionViewConstants.noEntitlementsIconWidth) -// .padding(.top, SettingsSubscriptionViewConstants.topCellPadding) -// VStack(alignment: .leading) { -// Text(UserText.settingsPProActivationPendingTitle).daxBodyRegular() -// Text(UserText.settingsPProActivationPendingDescription).daxFootnoteRegular() -// .padding(.bottom, SettingsSubscriptionViewConstants.purchaseDescriptionPadding) -// }.foregroundColor(Color(designSystemColor: .textSecondary)) -// } -// }) -// restorePurchaseView -// } -// } - @ViewBuilder private var noEntitlementsAvailableView: some View { - if viewModel.state.subscription.entitlements.contains(.networkProtection) { - SettingsCellView(label: UserText.settingsPProVPNTitle, - image: Image("SettingsPrivacyProVPN"), - statusIndicator: StatusIndicatorView(status: .off), - isGreyedOut: true - ) - } - - if viewModel.state.subscription.entitlements.contains(.dataBrokerProtection) { - SettingsCellView( - label: UserText.settingsPProDBPTitle, - image: Image("SettingsPrivacyProPIR"), - statusIndicator: StatusIndicatorView(status: .off), - isGreyedOut: true - ) - } - - if viewModel.state.subscription.entitlements.contains(.identityTheftRestoration) { - SettingsCellView( - label: UserText.settingsPProITRTitle, - image: Image("SettingsPrivacyProITP"), - statusIndicator: StatusIndicatorView(status: .off), - isGreyedOut: true - ) - } - + disabledFeaturesView + // Renew Subscription (Expired) - let settingsView = SubscriptionSettingsView(viewPlans: { isShowingSubscribeFlow = true }) + let settingsView = SubscriptionSettingsView(configuration: .activating, + settingsViewModel: viewModel, + viewPlans: { isShowingSubscribeFlow = true }) .environmentObject(subscriptionNavigationCoordinator) NavigationLink(destination: settingsView) { SettingsCellView( label: UserText.settingsPProManageSubscription, - subtitle: "Activating", + subtitle: UserText.settingsPProActivating, image: Image("SettingsPrivacyPro") ) } @@ -242,17 +212,17 @@ struct SettingsSubscriptionView: View { } NavigationLink( - destination: SubscriptionSettingsView().environmentObject(subscriptionNavigationCoordinator) + destination: SubscriptionSettingsView(configuration: .subscribed, + settingsViewModel: viewModel) + .environmentObject(subscriptionNavigationCoordinator) ) { SettingsCustomCell(content: { manageSubscriptionView }) } - } var body: some View { Group { if isShowingPrivacyPro { - let isSignedIn = viewModel.state.subscription.isSignedIn let hasActiveSubscription = viewModel.state.subscription.hasActiveSubscription let hasNoEntitlements = viewModel.state.subscription.entitlements.isEmpty @@ -264,43 +234,45 @@ struct SettingsSubscriptionView: View { Section(header: Text(UserText.settingsPProSection), footer: !isSignedIn ? footerLink : nil ) { - purchaseSubscriptionView + switch (isSignedIn, hasActiveSubscription, hasNoEntitlements) { -// switch (isSignedIn, hasActiveSubscription, hasNoEntitlements) { -// -// // Signed In, Subscription Expired -// case (true, false, _): -// subscriptionExpiredView -// -// // Signed in, Subscription Active, Valid entitlements -// case (true, true, false): -// subscriptionDetailsView // View for valid subscription details -// -// // Signed in, Subscription Active, Empty Entitlements -// case (true, true, true): -// noEntitlementsAvailableView // View for no entitlements -// -// // Signed out -// case (false, _, _): -// purchaseSubscriptionView // View for signing up or purchasing a subscription -// } - } - - .onChange(of: viewModel.state.subscription.shouldDisplayRestoreSubscriptionError) { value in - if value { - isShowingSubscriptionError = true + // Signed In, Subscription Expired + case (true, false, _): + subscriptionExpiredView + + // Signed in, Subscription Active, Valid entitlements + case (true, true, false): + subscriptionDetailsView // View for valid subscription details + + // Signed in, Subscription Active, Empty Entitlements + case (true, true, true): + noEntitlementsAvailableView // View for no entitlements + + // Signed out + case (false, _, _): + purchaseSubscriptionView // View for signing up or purchasing a subscription } } - +// .onChange(of: viewModel.state.subscription.shouldDisplayRestoreSubscriptionError) { value in +// if value { +// isShowingSubscriptionError = true +// } +// } .onReceive(subscriptionNavigationCoordinator.$shouldPopToAppSettings) { shouldDismiss in if shouldDismiss { isShowingRestoreFlow = false isShowingSubscribeFlow = false } } - + .sheet(isPresented: $isShowingSubscribeFlow, content: { + SubscriptionContainerViewFactory.makeSubscribeFlow(origin: nil, + navigationCoordinator: subscriptionNavigationCoordinator, + subscriptionManager: AppDependencyProvider.shared.subscriptionManager, + privacyProDataReporter: viewModel.privacyProDataReporter) + }) } - }.onReceive(viewModel.$state) { state in + } + .onReceive(viewModel.$state) { state in isShowingPrivacyPro = state.subscription.enabled && (state.subscription.isSignedIn || state.subscription.canPurchase) } } diff --git a/DuckDuckGo/StatusIndicatorView.swift b/DuckDuckGo/StatusIndicatorView.swift index fe3fd4a382..615bfb3bf7 100644 --- a/DuckDuckGo/StatusIndicatorView.swift +++ b/DuckDuckGo/StatusIndicatorView.swift @@ -21,7 +21,7 @@ import SwiftUI struct StatusIndicatorView: View { var status: StatusIndicator - var isDotHidden: Bool = false + var isDotHidden = false var body: some View { HStack(spacing: 6) { @@ -43,7 +43,7 @@ struct StatusIndicatorView: View { private func colorForStatus(_ status: StatusIndicator) -> Color { switch status { case .on, .alwaysOn: - return Color("AlertGreen") + return Color(designSystemColor: .alertGreen) case .off: return Color(designSystemColor: .textSecondary).opacity(0.33) } diff --git a/DuckDuckGo/Subscription/Subscription.xcassets/Exclamation-Color-16.imageset/Contents.json b/DuckDuckGo/Subscription/Subscription.xcassets/Exclamation-Color-16.imageset/Contents.json deleted file mode 100644 index 46fb7184c1..0000000000 --- a/DuckDuckGo/Subscription/Subscription.xcassets/Exclamation-Color-16.imageset/Contents.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "images" : [ - { - "filename" : "Exclamation-Color-16.pdf", - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/DuckDuckGo/Subscription/Subscription.xcassets/Exclamation-Color-16.imageset/Exclamation-Color-16.pdf b/DuckDuckGo/Subscription/Subscription.xcassets/Exclamation-Color-16.imageset/Exclamation-Color-16.pdf deleted file mode 100644 index b1e1522dfbf775dacdcfc3a544c222efd8e8f407..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1697 zcma)-O>fjN5Qgvm6~0tzk(&5Bwp3N3yA&Znlr6W4L&&<@MY{g^R@+|IwMj{RgZ6T{$Qnw-<@Nd^`Bhcrc%L&YQhnCky>f)y5-vxQL}Y!uoGj)!8b4ys5@WD2C61!APq z0*^^~WsL&jBq$h;c}i_`Fg#3bhfD<-6fm!xkpeA)w;*+06^9Z)`Mf@F*nUZ^^gb8-!FeSby z`*PQ8o|`_k@tz4@utOIa9;vfdTMl>}_^yTpc+um~5a%-_rGY4pkPF91!=pahWn?n! z+j6^$EyL4$w}f(>cFjJX@zd@4>`;_^)igcFgogs>x7$Avq91-HW!n$wNVxp$%U7oVr~Ec diff --git a/DuckDuckGo/Subscription/Subscription.xcassets/Privacy-Pro-96x96.imageset/Privacy-Pro-96x96.svg b/DuckDuckGo/Subscription/Subscription.xcassets/Privacy-Pro-96x96.imageset/Privacy-Pro-96x96.svg deleted file mode 100644 index 58a03b528e..0000000000 --- a/DuckDuckGo/Subscription/Subscription.xcassets/Privacy-Pro-96x96.imageset/Privacy-Pro-96x96.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - diff --git a/DuckDuckGo/Subscription/Subscription.xcassets/Privacy-Pro-96x96.imageset/Contents.json b/DuckDuckGo/Subscription/Subscription.xcassets/PrivacyProHeader.imageset/Contents.json similarity index 73% rename from DuckDuckGo/Subscription/Subscription.xcassets/Privacy-Pro-96x96.imageset/Contents.json rename to DuckDuckGo/Subscription/Subscription.xcassets/PrivacyProHeader.imageset/Contents.json index d407c89ceb..20b52f24b7 100644 --- a/DuckDuckGo/Subscription/Subscription.xcassets/Privacy-Pro-96x96.imageset/Contents.json +++ b/DuckDuckGo/Subscription/Subscription.xcassets/PrivacyProHeader.imageset/Contents.json @@ -1,7 +1,7 @@ { "images" : [ { - "filename" : "Privacy-Pro-96x96.svg", + "filename" : "PrivacyProHeader.pdf", "idiom" : "universal" } ], diff --git a/DuckDuckGo/Subscription/Subscription.xcassets/PrivacyProHeader.imageset/PrivacyProHeader.pdf b/DuckDuckGo/Subscription/Subscription.xcassets/PrivacyProHeader.imageset/PrivacyProHeader.pdf new file mode 100644 index 0000000000000000000000000000000000000000..4742dd8cad97911247535d63464275e6de23be07 GIT binary patch literal 5593 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 LY9J8uK;?e`ju;&B literal 0 HcmV?d00001 diff --git a/DuckDuckGo/Subscription/ViewModel/SubscriptionSettingsViewModel.swift b/DuckDuckGo/Subscription/ViewModel/SubscriptionSettingsViewModel.swift index 8019e9cd29..a9f7c1d02b 100644 --- a/DuckDuckGo/Subscription/ViewModel/SubscriptionSettingsViewModel.swift +++ b/DuckDuckGo/Subscription/ViewModel/SubscriptionSettingsViewModel.swift @@ -256,7 +256,12 @@ final class SubscriptionSettingsViewModel: ObservableObject { } } } - + + @MainActor + func showTermsOfService() { + self.openURL(SettingsSubscriptionViewConstants.privacyPolicyURL) + } + // MARK: - @MainActor private func manageAppleSubscription() async { diff --git a/DuckDuckGo/Subscription/Views/SubscriptionContainerViewFactory.swift b/DuckDuckGo/Subscription/Views/SubscriptionContainerViewFactory.swift index 3ee28dfa22..547523b58f 100644 --- a/DuckDuckGo/Subscription/Views/SubscriptionContainerViewFactory.swift +++ b/DuckDuckGo/Subscription/Views/SubscriptionContainerViewFactory.swift @@ -22,7 +22,10 @@ import Subscription enum SubscriptionContainerViewFactory { - static func makeSubscribeFlow(origin: String?, navigationCoordinator: SubscriptionNavigationCoordinator, subscriptionManager: SubscriptionManager, privacyProDataReporter: PrivacyProDataReporting?) -> some View { + static func makeSubscribeFlow(origin: String?, + navigationCoordinator: SubscriptionNavigationCoordinator, + subscriptionManager: SubscriptionManager, + privacyProDataReporter: PrivacyProDataReporting?) -> some View { let appStoreRestoreFlow = DefaultAppStoreRestoreFlow(accountManager: subscriptionManager.accountManager, storePurchaseManager: subscriptionManager.storePurchaseManager(), subscriptionEndpointService: subscriptionManager.subscriptionEndpointService, @@ -51,7 +54,8 @@ enum SubscriptionContainerViewFactory { .environmentObject(navigationCoordinator) } - static func makeRestoreFlow(navigationCoordinator: SubscriptionNavigationCoordinator, subscriptionManager: SubscriptionManager) -> some View { + static func makeRestoreFlow(navigationCoordinator: SubscriptionNavigationCoordinator, + subscriptionManager: SubscriptionManager) -> some View { let appStoreRestoreFlow = DefaultAppStoreRestoreFlow(accountManager: subscriptionManager.accountManager, storePurchaseManager: subscriptionManager.storePurchaseManager(), subscriptionEndpointService: subscriptionManager.subscriptionEndpointService, diff --git a/DuckDuckGo/Subscription/Views/SubscriptionFlowView.swift b/DuckDuckGo/Subscription/Views/SubscriptionFlowView.swift index 8cbedebb2a..6830b7e7e3 100644 --- a/DuckDuckGo/Subscription/Views/SubscriptionFlowView.swift +++ b/DuckDuckGo/Subscription/Views/SubscriptionFlowView.swift @@ -25,7 +25,7 @@ import Core struct SubscriptionFlowView: View { @Environment(\.dismiss) var dismiss - @EnvironmentObject var subscriptionNavigationCoordinator: SubscriptionNavigationCoordinator +// @EnvironmentObject var subscriptionNavigationCoordinator: SubscriptionNavigationCoordinator @StateObject var viewModel: SubscriptionFlowViewModel @State private var isPurchaseInProgress = false @@ -180,9 +180,7 @@ struct SubscriptionFlowView: View { .alert(isPresented: $isPresentingError) { getAlert(error: self.errorMessage) - } - } private func getAlert(error: SubscriptionErrorMessage) -> Alert { @@ -223,9 +221,7 @@ struct SubscriptionFlowView: View { @ViewBuilder private var webView: some View { - ZStack(alignment: .top) { - AsyncHeadlessWebView(viewModel: viewModel.webViewModel) .background() diff --git a/DuckDuckGo/Subscription/Views/SubscriptionSettingsHeaderView.swift b/DuckDuckGo/Subscription/Views/SubscriptionSettingsHeaderView.swift new file mode 100644 index 0000000000..f56bf8a620 --- /dev/null +++ b/DuckDuckGo/Subscription/Views/SubscriptionSettingsHeaderView.swift @@ -0,0 +1,82 @@ +// +// SubscriptionSettingsHeaderView.swift +// DuckDuckGo +// +// Copyright © 2024 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import SwiftUI + +struct SubscriptionSettingsHeaderView: View { + + enum HeaderState { + case subscribed + case expired(_ details: String) + case activating + } + + let state: HeaderState + + var body: some View { + VStack(alignment: .center, spacing: 8) { + Image("PrivacyProHeader") + Text(UserText.subscriptionTitle) + .daxTitle2() + .foregroundColor(Color(designSystemColor: .textPrimary)) + + switch state { + case .subscribed: + HStack(spacing: 4) { + Circle() + .fill(Color(designSystemColor: .alertGreen)) + .frame(width: 8, height: 8) + Text(UserText.subscriptionSubscribed) + .daxBodyRegular() + .foregroundColor(Color(designSystemColor: .textSecondary)) + } + case .expired(let details): + HStack(spacing: 4) { + Image("Exclamation-Color-16") + .frame(width: 16, height: 16) + Text(details) + .daxBodyRegular() + .foregroundColor(Color(designSystemColor: .textSecondary)) + } + case .activating: + HStack(spacing: 4) { + Circle() + .fill(Color(designSystemColor: .alertYellow)) + .frame(width: 8, height: 8) + Text(UserText.settingsPProActivating) + .daxBodyRegular() + .foregroundColor(Color(designSystemColor: .textSecondary)) + } + Text(UserText.settingsPProActivationPendingDescription) + .daxBodyRegular() + .foregroundColor(Color(designSystemColor: .textSecondary)) + .padding(.top, 9) + .multilineTextAlignment(.center) + } + } + } +} + +#Preview { + VStack { + SubscriptionSettingsHeaderView(state: .subscribed) + SubscriptionSettingsHeaderView(state: .expired("Your subscription expired on April 20, 2027")) + SubscriptionSettingsHeaderView(state: .activating) + } +} diff --git a/DuckDuckGo/Subscription/Views/SubscriptionSettingsView.swift b/DuckDuckGo/Subscription/Views/SubscriptionSettingsView.swift index 6462f25c3f..186bbefd51 100644 --- a/DuckDuckGo/Subscription/Views/SubscriptionSettingsView.swift +++ b/DuckDuckGo/Subscription/Views/SubscriptionSettingsView.swift @@ -23,9 +23,16 @@ import DesignResourcesKit import Core struct SubscriptionSettingsView: View { - + + enum Configuration { + case subscribed + case expired + case activating + } + @State var configuration: Configuration @Environment(\.dismiss) var dismiss @StateObject var viewModel = SubscriptionSettingsViewModel() + @StateObject var settingsViewModel: SettingsViewModel @EnvironmentObject var subscriptionNavigationCoordinator: SubscriptionNavigationCoordinator var viewPlans: (() -> Void)? @@ -36,36 +43,34 @@ struct SubscriptionSettingsView: View { @State var isShowingLearnMoreView = false @State var isShowingEmailView = false @State var isShowingConnectionError = false + @State var isShowingSubscriptionError = false - enum Constants { - static let alertIcon = "Exclamation-Color-16" - } - var body: some View { optionsView .onFirstAppear { Pixel.fire(pixel: .privacyProSubscriptionSettings, debounce: 1) } .navigationBarTitleDisplayMode(.inline) + .onChange(of: settingsViewModel.state.subscription.shouldDisplayRestoreSubscriptionError) { value in + if value { + isShowingSubscriptionError = true + } + } } // MARK: - - + @ViewBuilder private var headerSection: some View { +// let isExpired = !(viewModel.state.subscriptionInfo?.isActive ?? false) +// let isSubscribed = viewModel.state.subscriptionInfo?.isActive ?? false Section { - let isExpired = !(viewModel.state.subscriptionInfo?.isActive ?? false) - VStack(alignment: .center, spacing: 7) { - Image("Privacy-Pro-96x96") - Text(UserText.subscriptionTitle).daxTitle2() - - if isExpired { - HStack { - Image(Constants.alertIcon) - Text(viewModel.state.subscriptionDetails) - .daxSubheadRegular() - .foregroundColor(Color(designSystemColor: .textSecondary)) - } - } + switch configuration { + case .subscribed: + SubscriptionSettingsHeaderView(state: .subscribed) + case .expired: + SubscriptionSettingsHeaderView(state: .expired(viewModel.state.subscriptionDetails)) + case .activating: + SubscriptionSettingsHeaderView(state: .activating) } } .listRowBackground(Color.clear) @@ -110,51 +115,76 @@ struct SubscriptionSettingsView: View { private var manageSection: some View { Section(header: Text(UserText.subscriptionManageTitle), footer: manageSectionFooter) { - let active = viewModel.state.subscriptionInfo?.isActive ?? false - SettingsCustomCell(content: { - if !viewModel.state.isLoadingSubscriptionInfo { - if active { - Text(UserText.subscriptionChangePlan) - .daxBodyRegular() - .foregroundColor(Color.init(designSystemColor: .accent)) - } else { - Text(UserText.subscriptionRestoreNotFoundPlans) - .daxBodyRegular() - .foregroundColor(Color.init(designSystemColor: .accent)) - } - } else { - SwiftUI.ProgressView() - } - }, - action: { - if !viewModel.state.isLoadingSubscriptionInfo { - Task { + switch configuration { + case .subscribed, .expired: + let active = viewModel.state.subscriptionInfo?.isActive ?? false + SettingsCustomCell(content: { + if !viewModel.state.isLoadingSubscriptionInfo { if active { - viewModel.manageSubscription() - Pixel.fire(pixel: .privacyProSubscriptionManagementPlanBilling, debounce: 1) + Text(UserText.subscriptionChangePlan) + .daxBodyRegular() + .foregroundColor(Color.init(designSystemColor: .accent)) } else { - viewPlans?() + Text(UserText.subscriptionRestoreNotFoundPlans) + .daxBodyRegular() + .foregroundColor(Color.init(designSystemColor: .accent)) } + } else { + SwiftUI.ProgressView() + } + }, + action: { + if !viewModel.state.isLoadingSubscriptionInfo { + Task { + if active { + viewModel.manageSubscription() + Pixel.fire(pixel: .privacyProSubscriptionManagementPlanBilling, debounce: 1) + } else { + viewPlans?() + } + } + } + }, + isButton: true) + .sheet(isPresented: $isShowingStripeView) { + if let stripeViewModel = viewModel.state.stripeViewModel { + SubscriptionExternalLinkView(viewModel: stripeViewModel, title: UserText.subscriptionManagePlan) } } - }, - isButton: true) - .sheet(isPresented: $isShowingStripeView) { - if let stripeViewModel = viewModel.state.stripeViewModel { - SubscriptionExternalLinkView(viewModel: stripeViewModel, title: UserText.subscriptionManagePlan) - } - } - SettingsCustomCell(content: { - Text(UserText.subscriptionRemoveFromDevice) + SettingsCustomCell(content: { + Text(UserText.subscriptionRemoveFromDevice) .daxBodyRegular() - .foregroundColor(Color.init(designSystemColor: .accent))}, - action: { viewModel.displayRemovalNotice(true) }, - isButton: true) + .foregroundColor(Color.init(designSystemColor: .accent))}, + action: { viewModel.displayRemovalNotice(true) }, + isButton: true) + case .activating: + restorePurchaseView + } } } + @ViewBuilder + var restorePurchaseView: some View { + let text = !settingsViewModel.state.subscription.isRestoring ? UserText.subscriptionActivateAppleIDButton : UserText.subscriptionRestoringTitle + SettingsCustomCell(content: { + Text(text) + .daxBodyRegular() + .foregroundColor(Color.init(designSystemColor: .accent)) }, + action: { + Task { await settingsViewModel.restoreAccountPurchase() } + }, + isButton: !settingsViewModel.state.subscription.isRestoring ) + .alert(isPresented: $isShowingSubscriptionError) { + Alert( + title: Text(UserText.subscriptionAppStoreErrorTitle), + message: Text(UserText.subscriptionAppStoreErrorMessage), + dismissButton: .default(Text(UserText.actionOK)) {} + ) + } + } + private var manageSectionFooter: some View { let isExpired = !(viewModel.state.subscriptionInfo?.isActive ?? false) return Group { @@ -169,8 +199,7 @@ struct SubscriptionSettingsView: View { @ViewBuilder var helpSection: some View { Section(header: Text(UserText.subscriptionHelpAndSupport), footer: Text(UserText.subscriptionFAQFooter)) { - - + SettingsCustomCell(content: { Text(UserText.subscriptionFAQ) .daxBodyRegular() @@ -182,7 +211,20 @@ struct SubscriptionSettingsView: View { } } - + + @ViewBuilder var privacyPolicySection: some View { + Section { + SettingsCustomCell(content: { + Text(UserText.settingsPProSectionFooter) + .daxBodyRegular() + .foregroundColor(Color(designSystemColor: .accent)) + }, + action: { viewModel.showTermsOfService() }, + disclosureIndicator: false, + isButton: true) + } + } + @ViewBuilder private var optionsView: some View { NavigationLink(destination: SubscriptionGoogleView(), @@ -192,27 +234,28 @@ struct SubscriptionSettingsView: View { List { headerSection - devicesSection - .alert(isPresented: $isShowingRemovalNotice) { - Alert( - title: Text(UserText.subscriptionRemoveFromDeviceConfirmTitle), - message: Text(UserText.subscriptionRemoveFromDeviceConfirmText), - primaryButton: .cancel(Text(UserText.subscriptionRemoveCancel)) { - }, - secondaryButton: .destructive(Text(UserText.subscriptionRemove)) { - Pixel.fire(pixel: .privacyProSubscriptionManagementRemoval) - viewModel.removeSubscription() - dismiss() - } - ) - } + if configuration == .subscribed || configuration == .expired { + devicesSection + } manageSection helpSection - + privacyPolicySection } .navigationTitle(UserText.settingsPProManageSubscription) .applyInsetGroupedListStyle() - + .alert(isPresented: $isShowingRemovalNotice) { + Alert( + title: Text(UserText.subscriptionRemoveFromDeviceConfirmTitle), + message: Text(UserText.subscriptionRemoveFromDeviceConfirmText), + primaryButton: .cancel(Text(UserText.subscriptionRemoveCancel)) { + }, + secondaryButton: .destructive(Text(UserText.subscriptionRemove)) { + Pixel.fire(pixel: .privacyProSubscriptionManagementRemoval) + viewModel.removeSubscription() + dismiss() + } + ) + } .onChange(of: viewModel.state.shouldDismissView) { value in if value { dismiss() @@ -306,20 +349,11 @@ struct SubscriptionSettingsView: View { } } - + @ViewBuilder private var stripeView: some View { if let stripeViewModel = viewModel.state.stripeViewModel { SubscriptionExternalLinkView(viewModel: stripeViewModel) } } - - } - -// Commented out because CI fails if a SwiftUI preview is enabled https://app.asana.com/0/414709148257752/1206774081310425/f -// struct SubscriptionSettingsView_Previews: PreviewProvider { -// static var previews: some View { -// SubscriptionSettingsView() -// } -// } diff --git a/DuckDuckGo/UserText.swift b/DuckDuckGo/UserText.swift index ba6823590a..cf10c79f6f 100644 --- a/DuckDuckGo/UserText.swift +++ b/DuckDuckGo/UserText.swift @@ -974,9 +974,9 @@ But if you *do* want a peek under the hood, you can find more information about // Subscription Section public static let settingsPProSection = NSLocalizedString("settings.ppro", value: "Privacy Pro", comment: "Product name for the subscription bundle") public static let settingsPProSectionFooter = NSLocalizedString("settings.ppro.footer", value: "Privacy Policy and Terms of Service", comment: "Title for Link in the Footer of Privacy Pro section") - - public static let settingsPProSubscribe = NSLocalizedString("settings.subscription.subscribe", value: "Protect your connection and identity with Privacy Pro", comment: "Call to action title for Privacy Pro") - public static let settingsPProDescription = NSLocalizedString("settings.subscription.description", value:"Includes our VPN, Personal Information Removal, and Identity Theft Restoration.", comment: "Privacy pro description subtitle") + public static let settingsPProSubscribe = NSLocalizedString("settings.subscription.subscribe", value: "Protect your connection and identity with Privacy Pro", comment: "Call to action title for Privacy Pro settings") + public static let settingsPProDescription = NSLocalizedString("settings.subscription.description", value:"Includes our VPN, Personal Information Removal, and Identity Theft Restoration.", comment: "Privacy pro description subtitle in settings") + public static let settingsPProActivating = NSLocalizedString("settings.subscription.activating", value:"Activating", comment: "Privacy pro description subtitle in settings when the is activating") public static let settingsPProLearnMore = NSLocalizedString("settings.subscription.learn.more", value: "Get Privacy Pro", comment: "Get Privacy Pro button text for privacy pro") public static let settingsPProIHaveASubscription = NSLocalizedString("settings.subscription.existing.subscription", value: "I Have a Subscription", comment: "I have a Subscription button text for privacy pro") @@ -989,9 +989,8 @@ But if you *do* want a peek under the hood, you can find more information about public static let settingsPProITRTitle = NSLocalizedString("settings.subscription.ITR.title", value: "Identity Theft Restoration", comment: "Identity theft restoration cell title for privacy pro") public static let settingsPProITRSubTitle = NSLocalizedString("settings.subscription.ITR.subtitle", value: "If your identity is stolen, we'll help restore it", comment: "Identity theft restoration cell subtitle for privacy pro") - public static let settingsPProActivationPendingTitle = NSLocalizedString("settings.subscription.activation.pending.title", value: "Your Subscription is being activated", comment: "Subscription activation pending title") public static let settingsPProActivationPendingDescription = NSLocalizedString("settings.subscription.activation.pending.description", value: "This is taking longer than usual, please check back later.", comment: "Subscription activation pending description") - + // Expired Subscription public static let settingsPProSubscriptionExpiredTitle = NSLocalizedString("settings.subscription.expired.title", value: "Your Privacy Pro subscription expired", comment: "Subscription expired tittle message") public static let settingsPProSubscribeAgain = NSLocalizedString("settings.subscription.expired.comment", value: "Subscribe again to continue using Privacy Pro", comment: "Subscription expired description") @@ -1039,6 +1038,7 @@ But if you *do* want a peek under the hood, you can find more information about // Subscription Settings public static let subscriptionTitle = NSLocalizedString("subscription.title", value: "Privacy Pro", comment: "Navigation bar Title for subscriptions") + public static let subscriptionSubscribed = NSLocalizedString("subscription.subscribed", value: "Subscribed", comment: "Subtitle in header when subscribed") public static let subscriptionCloseButton = NSLocalizedString("subscription.close", value: "Close", comment: "Navigation Button for closing subscription view") static func renewingSubscriptionInfo(billingPeriod: String, renewalDate: String) -> String { @@ -1066,7 +1066,7 @@ But if you *do* want a peek under the hood, you can find more information about public static let subscriptionAnnualBillingPeriod = NSLocalizedString("subscription.billing.period.annual", value: "annual", comment: "Subscription annual billing period type") public static let subscriptionDevicesSectionHeader = NSLocalizedString("subscription.devices.header", value: "Activate on Other Devices", comment: "Header for section for activating subscription on other devices") - public static let subscriptionDevicesSectionNoEmailFooter = NSLocalizedString("subscription.devices.no.email.footer", value: "Add an optional email to your subscription or use your Apple ID to access Privacy Pro on other devices. **[Learn more](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**", comment: "Footer for section for activating subscription on other devices when email was not yet added") + public static let subscriptionDevicesSectionNoEmailFooter = NSLocalizedString("subscription.devices.no.email.footer", value: "Add an optional email to your subscription to access Privacy Pro on other devices. **[Learn more](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**", comment: "Footer for section for activating subscription on other devices when email was not yet added") public static let subscriptionDevicesSectionWithEmailFooter = NSLocalizedString("subscription.devices.with.email.footer", value: "Use this email to activate your subscription in Settings > Privacy Pro in the DuckDuckGo app on your other devices. **[Learn more](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**", comment: "Footer for section for activating subscription on other devices when email is added") public static let subscriptionRemoveFromDevice = NSLocalizedString("subscription.remove.from.device.button", value: "Remove From This Device", comment: "Remove from this device button") public static let subscriptionManageTitle = NSLocalizedString("subscription.manage.title", value: "Subscription", comment: "Header for the subscription section") diff --git a/DuckDuckGo/en.lproj/Localizable.strings b/DuckDuckGo/en.lproj/Localizable.strings index 4a6366894f..d2d165692c 100644 --- a/DuckDuckGo/en.lproj/Localizable.strings +++ b/DuckDuckGo/en.lproj/Localizable.strings @@ -1967,19 +1967,19 @@ But if you *do* want a peek under the hood, you can find more information about /* Settings screen cell text for setting address bar position */ "settings.set.your.address.bar.position" = "Set Your Address Bar Position"; +/* Privacy pro description subtitle in settings when the is activating */ +"settings.subscription.activating" = "Activating"; + /* Subscription activation pending description */ "settings.subscription.activation.pending.description" = "This is taking longer than usual, please check back later."; -/* Subscription activation pending title */ -"settings.subscription.activation.pending.title" = "Your Subscription is being activated"; - /* Data Broker protection cell subtitle for privacy pro */ "settings.subscription.DBP.subtitle" = "Remove your info from sites that sell it"; /* Data Broker protection cell title for privacy pro */ "settings.subscription.DBP.title" = "Personal Information Removal"; -/* Privacy pro description subtitle */ +/* Privacy pro description subtitle in settings */ "settings.subscription.description" = "Includes our VPN, Personal Information Removal, and Identity Theft Restoration."; /* I have a Subscription button text for privacy pro */ @@ -2003,7 +2003,7 @@ But if you *do* want a peek under the hood, you can find more information about /* Subscription Settings button text for privacy pro */ "settings.subscription.manage" = "Subscription Settings"; -/* Call to action title for Privacy Pro */ +/* Call to action title for Privacy Pro settings */ "settings.subscription.subscribe" = "Protect your connection and identity with Privacy Pro"; /* VPN cell title for privacy pro */ @@ -2148,7 +2148,7 @@ But if you *do* want a peek under the hood, you can find more information about "subscription.devices.header" = "Activate on Other Devices"; /* Footer for section for activating subscription on other devices when email was not yet added */ -"subscription.devices.no.email.footer" = "Add an optional email to your subscription or use your Apple ID to access Privacy Pro on other devices. **[Learn more](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; +"subscription.devices.no.email.footer" = "Add an optional email to your subscription to access Privacy Pro on other devices. **[Learn more](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; /* Footer for section for activating subscription on other devices when email is added */ "subscription.devices.with.email.footer" = "Use this email to activate your subscription in Settings > Privacy Pro in the DuckDuckGo app on your other devices. **[Learn more](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; @@ -2264,6 +2264,9 @@ But if you *do* want a peek under the hood, you can find more information about /* Alert title for restored purchase */ "subscription.restore.success.alert.title" = "You’re all set."; +/* Subtitle in header when subscribed */ +"subscription.subscribed" = "Subscribed"; + /* Subscription Expired Data. This reads as 'Your subscription expired on (date)' */ "subscription.subscription.expired.caption" = "Your subscription expired on %@"; diff --git a/LocalPackages/SyncUI/Package.swift b/LocalPackages/SyncUI/Package.swift index 9b9ed8ab5f..0fab03538e 100644 --- a/LocalPackages/SyncUI/Package.swift +++ b/LocalPackages/SyncUI/Package.swift @@ -33,7 +33,7 @@ let package = Package( ], dependencies: [ .package(path: "../DuckUI"), - .package(url: "https://github.com/duckduckgo/DesignResourcesKit", exact: "3.0.0"), + .package(url: "https://github.com/duckduckgo/DesignResourcesKit", exact: "3.1.0"), .package(url: "https://github.com/duckduckgo/apple-toolbox.git", exact: "3.1.2"), ], targets: [ diff --git a/LocalPackages/Waitlist/Package.swift b/LocalPackages/Waitlist/Package.swift index 75a0994bde..977911513d 100644 --- a/LocalPackages/Waitlist/Package.swift +++ b/LocalPackages/Waitlist/Package.swift @@ -15,7 +15,7 @@ let package = Package( targets: ["Waitlist", "WaitlistMocks"]) ], dependencies: [ - .package(url: "https://github.com/duckduckgo/DesignResourcesKit", exact: "3.0.0"), + .package(url: "https://github.com/duckduckgo/DesignResourcesKit", exact: "3.1.0"), .package(url: "https://github.com/duckduckgo/apple-toolbox.git", exact: "3.1.2"), ], targets: [ From cb975929fa62023e8216865ffd6553e9697fb198 Mon Sep 17 00:00:00 2001 From: Federico Cappelli Date: Thu, 8 Aug 2024 09:17:13 +0100 Subject: [PATCH 06/20] nav controller restored --- DuckDuckGo/SettingsRootView.swift | 2 +- .../Subscription/Views/SubscriptionContainerView.swift | 6 ++++-- .../Subscription/Views/SubscriptionSettingsView.swift | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/DuckDuckGo/SettingsRootView.swift b/DuckDuckGo/SettingsRootView.swift index b91ea60e2b..dd2cf2eb65 100644 --- a/DuckDuckGo/SettingsRootView.swift +++ b/DuckDuckGo/SettingsRootView.swift @@ -112,7 +112,7 @@ struct SettingsRootView: View { SubscriptionITPView() case let .subscriptionFlow(origin): SubscriptionContainerViewFactory.makeSubscribeFlow(origin: origin, -// navigationCoordinator: subscriptionNavigationCoordinator, + navigationCoordinator: subscriptionNavigationCoordinator, subscriptionManager: AppDependencyProvider.shared.subscriptionManager, privacyProDataReporter: viewModel.privacyProDataReporter) .environmentObject(subscriptionNavigationCoordinator) diff --git a/DuckDuckGo/Subscription/Views/SubscriptionContainerView.swift b/DuckDuckGo/Subscription/Views/SubscriptionContainerView.swift index 1723e38a89..369820fd67 100644 --- a/DuckDuckGo/Subscription/Views/SubscriptionContainerView.swift +++ b/DuckDuckGo/Subscription/Views/SubscriptionContainerView.swift @@ -48,11 +48,13 @@ struct SubscriptionContainerView: View { switch currentViewState { case .subscribe: SubscriptionFlowView(viewModel: flowViewModel, - currentView: $currentViewState).environmentObject(subscriptionNavigationCoordinator) + currentView: $currentViewState) + .environmentObject(subscriptionNavigationCoordinator) case .restore: SubscriptionRestoreView(viewModel: restoreViewModel, emailViewModel: emailViewModel, - currentView: $currentViewState).environmentObject(subscriptionNavigationCoordinator) + currentView: $currentViewState) + .environmentObject(subscriptionNavigationCoordinator) case .email: SubscriptionEmailView(viewModel: emailViewModel) } diff --git a/DuckDuckGo/Subscription/Views/SubscriptionSettingsView.swift b/DuckDuckGo/Subscription/Views/SubscriptionSettingsView.swift index 186bbefd51..28ae1904c4 100644 --- a/DuckDuckGo/Subscription/Views/SubscriptionSettingsView.swift +++ b/DuckDuckGo/Subscription/Views/SubscriptionSettingsView.swift @@ -165,7 +165,7 @@ struct SubscriptionSettingsView: View { } } - @ViewBuilder + @ViewBuilder var restorePurchaseView: some View { let text = !settingsViewModel.state.subscription.isRestoring ? UserText.subscriptionActivateAppleIDButton : UserText.subscriptionRestoringTitle SettingsCustomCell(content: { From caf2432700be404d9b9d45f69e3d964d9f02f64d Mon Sep 17 00:00:00 2001 From: Federico Cappelli Date: Thu, 8 Aug 2024 10:45:49 +0100 Subject: [PATCH 07/20] cleanup --- DuckDuckGo/SettingsSubscriptionView.swift | 56 ++++++------------- .../SubscriptionSettingsViewModel.swift | 2 +- .../Views/SubscriptionContainerView.swift | 16 +++--- .../Views/SubscriptionFlowView.swift | 7 +-- .../Views/SubscriptionRestoreView.swift | 2 +- 5 files changed, 30 insertions(+), 53 deletions(-) diff --git a/DuckDuckGo/SettingsSubscriptionView.swift b/DuckDuckGo/SettingsSubscriptionView.swift index a6357c5488..35a016dc3f 100644 --- a/DuckDuckGo/SettingsSubscriptionView.swift +++ b/DuckDuckGo/SettingsSubscriptionView.swift @@ -22,18 +22,18 @@ import Subscription import SwiftUI import UIKit -enum SettingsSubscriptionViewConstants { - static let purchaseDescriptionPadding = 5.0 - static let topCellPadding = 3.0 - static let noEntitlementsIconWidth = 20.0 - static let navigationDelay = 0.3 - static let infoIcon = "info-16" - static let alertIcon = "Exclamation-Color-16" - static let privacyPolicyURL = URL(string: "https://duckduckgo.com/pro/privacy-terms")! -} - struct SettingsSubscriptionView: View { - + + enum ViewConstants { + static let purchaseDescriptionPadding = 5.0 + static let topCellPadding = 3.0 + static let noEntitlementsIconWidth = 20.0 + static let navigationDelay = 0.3 + static let infoIcon = "info-16" + static let alertIcon = "Exclamation-Color-16" + static let privacyPolicyURL = URL(string: "https://duckduckgo.com/pro/privacy-terms")! + } + @EnvironmentObject var viewModel: SettingsViewModel @EnvironmentObject var subscriptionNavigationCoordinator: SubscriptionNavigationCoordinator @State var isShowingDBP = false @@ -42,33 +42,12 @@ struct SettingsSubscriptionView: View { @State var isShowingSubscribeFlow = false @State var isShowingGoogleView = false @State var isShowingStripeView = false -// @State var isShowingSubscriptionError = false @State var isShowingPrivacyPro = false var subscriptionRestoreView: some View { SubscriptionContainerViewFactory.makeRestoreFlow(navigationCoordinator: subscriptionNavigationCoordinator, subscriptionManager: subscriptionManager) } - -// @ViewBuilder -// private var restorePurchaseView: some View { -// let text = !viewModel.state.subscription.isRestoring ? UserText.subscriptionActivateAppleIDButton : UserText.subscriptionRestoringTitle -// SettingsCustomCell(content: { -// Text(text) -// .daxBodyRegular() -// .foregroundColor(Color.init(designSystemColor: .accent)) }, -// action: { -// Task { await viewModel.restoreAccountPurchase() } -// }, -// isButton: !viewModel.state.subscription.isRestoring ) -// .alert(isPresented: $isShowingSubscriptionError) { -// Alert( -// title: Text(UserText.subscriptionAppStoreErrorTitle), -// message: Text(UserText.subscriptionAppStoreErrorMessage), -// dismissButton: .default(Text(UserText.actionOK)) {} -// ) -// } -// } private var manageSubscriptionView: some View { SettingsCellView( @@ -228,7 +207,7 @@ struct SettingsSubscriptionView: View { let hasNoEntitlements = viewModel.state.subscription.entitlements.isEmpty let footerLink = Link(UserText.settingsPProSectionFooter, - destination: SettingsSubscriptionViewConstants.privacyPolicyURL) + destination: ViewConstants.privacyPolicyURL) .daxFootnoteRegular().accentColor(Color.init(designSystemColor: .accent)) Section(header: Text(UserText.settingsPProSection), @@ -253,18 +232,17 @@ struct SettingsSubscriptionView: View { purchaseSubscriptionView // View for signing up or purchasing a subscription } } -// .onChange(of: viewModel.state.subscription.shouldDisplayRestoreSubscriptionError) { value in -// if value { -// isShowingSubscriptionError = true -// } -// } .onReceive(subscriptionNavigationCoordinator.$shouldPopToAppSettings) { shouldDismiss in if shouldDismiss { isShowingRestoreFlow = false isShowingSubscribeFlow = false } } - .sheet(isPresented: $isShowingSubscribeFlow, content: { + .sheet(isPresented: $isShowingSubscribeFlow, + onDismiss: { +print("") + }, + content: { SubscriptionContainerViewFactory.makeSubscribeFlow(origin: nil, navigationCoordinator: subscriptionNavigationCoordinator, subscriptionManager: AppDependencyProvider.shared.subscriptionManager, diff --git a/DuckDuckGo/Subscription/ViewModel/SubscriptionSettingsViewModel.swift b/DuckDuckGo/Subscription/ViewModel/SubscriptionSettingsViewModel.swift index a9f7c1d02b..e1559d3022 100644 --- a/DuckDuckGo/Subscription/ViewModel/SubscriptionSettingsViewModel.swift +++ b/DuckDuckGo/Subscription/ViewModel/SubscriptionSettingsViewModel.swift @@ -259,7 +259,7 @@ final class SubscriptionSettingsViewModel: ObservableObject { @MainActor func showTermsOfService() { - self.openURL(SettingsSubscriptionViewConstants.privacyPolicyURL) + self.openURL(SettingsSubscriptionView.ViewConstants.privacyPolicyURL) } // MARK: - diff --git a/DuckDuckGo/Subscription/Views/SubscriptionContainerView.swift b/DuckDuckGo/Subscription/Views/SubscriptionContainerView.swift index 369820fd67..56ce23e781 100644 --- a/DuckDuckGo/Subscription/Views/SubscriptionContainerView.swift +++ b/DuckDuckGo/Subscription/Views/SubscriptionContainerView.swift @@ -22,38 +22,38 @@ import SwiftUI struct SubscriptionContainerView: View { - enum CurrentView { + enum CurrentViewType { case subscribe, restore, email } @Environment(\.dismiss) var dismiss @EnvironmentObject var subscriptionNavigationCoordinator: SubscriptionNavigationCoordinator - @State private var currentViewState: CurrentView + @State private var currentViewType: CurrentViewType private let viewModel: SubscriptionContainerViewModel private let flowViewModel: SubscriptionFlowViewModel private let restoreViewModel: SubscriptionRestoreViewModel private let emailViewModel: SubscriptionEmailViewModel - init(currentView: CurrentView, + init(currentView: CurrentViewType, viewModel: SubscriptionContainerViewModel) { - _currentViewState = State(initialValue: currentView) + _currentViewType = State(initialValue: currentView) self.viewModel = viewModel flowViewModel = viewModel.flow restoreViewModel = viewModel.restore emailViewModel = viewModel.email } - + var body: some View { VStack { - switch currentViewState { + switch currentViewType { case .subscribe: SubscriptionFlowView(viewModel: flowViewModel, - currentView: $currentViewState) + currentView: $currentViewType) .environmentObject(subscriptionNavigationCoordinator) case .restore: SubscriptionRestoreView(viewModel: restoreViewModel, emailViewModel: emailViewModel, - currentView: $currentViewState) + currentView: $currentViewType) .environmentObject(subscriptionNavigationCoordinator) case .email: SubscriptionEmailView(viewModel: emailViewModel) diff --git a/DuckDuckGo/Subscription/Views/SubscriptionFlowView.swift b/DuckDuckGo/Subscription/Views/SubscriptionFlowView.swift index 6830b7e7e3..fffcf42c7a 100644 --- a/DuckDuckGo/Subscription/Views/SubscriptionFlowView.swift +++ b/DuckDuckGo/Subscription/Views/SubscriptionFlowView.swift @@ -32,7 +32,7 @@ struct SubscriptionFlowView: View { @State private var isShowingITR = false @State private var isShowingDBP = false @State private var isShowingNetP = false - @Binding var currentView: SubscriptionContainerView.CurrentView + @Binding var currentView: SubscriptionContainerView.CurrentViewType // Local View State @State private var errorMessage: SubscriptionErrorMessage = .general @@ -111,8 +111,7 @@ struct SubscriptionFlowView: View { return "" } } - - + @ViewBuilder private var baseView: some View { ZStack(alignment: .top) { @@ -192,7 +191,7 @@ struct SubscriptionFlowView: View { message: Text(UserText.subscriptionFoundText), primaryButton: .cancel(Text(UserText.subscriptionFoundCancel)) { viewModel.clearTransactionError() - dismiss() + dismiss() }, secondaryButton: .default(Text(UserText.subscriptionFoundRestore)) { viewModel.restoreAppstoreTransaction() diff --git a/DuckDuckGo/Subscription/Views/SubscriptionRestoreView.swift b/DuckDuckGo/Subscription/Views/SubscriptionRestoreView.swift index 1063398adc..9b8bc29348 100644 --- a/DuckDuckGo/Subscription/Views/SubscriptionRestoreView.swift +++ b/DuckDuckGo/Subscription/Views/SubscriptionRestoreView.swift @@ -33,7 +33,7 @@ struct SubscriptionRestoreView: View { @State private var isAlertVisible = false @State private var isShowingWelcomePage = false @State private var isShowingActivationFlow = false - @Binding var currentView: SubscriptionContainerView.CurrentView + @Binding var currentView: SubscriptionContainerView.CurrentViewType private enum Constants { static let heroImage = "ManageSubscriptionHero" From 6518efa59d2b322f42e69aa3e8daf6fefbc292b2 Mon Sep 17 00:00:00 2001 From: Federico Cappelli Date: Thu, 8 Aug 2024 13:02:41 +0100 Subject: [PATCH 08/20] original navigation stack restored --- DuckDuckGo/SettingsRootView.swift | 10 ++++- DuckDuckGo/SettingsSubscriptionView.swift | 38 +++++++++++-------- .../SubscriptionNavigationCoordinator.swift | 1 + 3 files changed, 33 insertions(+), 16 deletions(-) diff --git a/DuckDuckGo/SettingsRootView.swift b/DuckDuckGo/SettingsRootView.swift index dd2cf2eb65..f63393a8a2 100644 --- a/DuckDuckGo/SettingsRootView.swift +++ b/DuckDuckGo/SettingsRootView.swift @@ -30,10 +30,11 @@ struct SettingsRootView: View { @State private var shouldDisplayDeepLinkSheet: Bool = false @State private var shouldDisplayDeepLinkPush: Bool = false @State var deepLinkTarget: SettingsViewModel.SettingsDeepLinkSection? + @State var isShowingSubscribeFlow = false var body: some View { - // Hidden navigationLink for programatic navigation + // Hidden navigationLinks for programatic navigation if let target = deepLinkTarget { NavigationLink(destination: deepLinkDestinationView(for: target), isActive: $shouldDisplayDeepLinkPush) { @@ -41,6 +42,9 @@ struct SettingsRootView: View { } } + NavigationLink(destination: deepLinkDestinationView(for: .subscriptionFlow(origin: nil)), + isActive: $isShowingSubscribeFlow) { EmptyView() } + List { SettingsPrivacyProtectionsView() SettingsSubscriptionView().environmentObject(subscriptionNavigationCoordinator) @@ -100,6 +104,9 @@ struct SettingsRootView: View { shouldDisplayDeepLinkPush = false } } + .onReceive(subscriptionNavigationCoordinator.$shouldPushSubscriptionWebView) { shouldPush in + isShowingSubscribeFlow = shouldPush + } } // MARK: DeepLink Views @@ -116,6 +123,7 @@ struct SettingsRootView: View { subscriptionManager: AppDependencyProvider.shared.subscriptionManager, privacyProDataReporter: viewModel.privacyProDataReporter) .environmentObject(subscriptionNavigationCoordinator) + case .restoreFlow: SubscriptionContainerViewFactory.makeEmailFlow(navigationCoordinator: subscriptionNavigationCoordinator, subscriptionManager: AppDependencyProvider.shared.subscriptionManager, diff --git a/DuckDuckGo/SettingsSubscriptionView.swift b/DuckDuckGo/SettingsSubscriptionView.swift index 35a016dc3f..7e4ca4a858 100644 --- a/DuckDuckGo/SettingsSubscriptionView.swift +++ b/DuckDuckGo/SettingsSubscriptionView.swift @@ -39,7 +39,7 @@ struct SettingsSubscriptionView: View { @State var isShowingDBP = false @State var isShowingITP = false @State var isShowingRestoreFlow = false - @State var isShowingSubscribeFlow = false +// @State var isShowingSubscribeFlow = false @State var isShowingGoogleView = false @State var isShowingStripeView = false @State var isShowingPrivacyPro = false @@ -74,7 +74,8 @@ struct SettingsSubscriptionView: View { .foregroundColor(Color.init(designSystemColor: .accent)) .padding(.leading, 32.0) }, action: { - isShowingSubscribeFlow = true +// isShowingSubscribeFlow = true + subscriptionNavigationCoordinator.shouldPushSubscriptionWebView = true }, isButton: true) // Restore subscription @@ -126,7 +127,10 @@ struct SettingsSubscriptionView: View { // Renew Subscription (Expired) let settingsView = SubscriptionSettingsView(configuration: .expired, settingsViewModel: viewModel, - viewPlans: { isShowingSubscribeFlow = true }) + viewPlans: { +// isShowingSubscribeFlow = true + subscriptionNavigationCoordinator.shouldPushSubscriptionWebView = true + }) .environmentObject(subscriptionNavigationCoordinator) NavigationLink(destination: settingsView) { SettingsCellView( @@ -145,7 +149,10 @@ struct SettingsSubscriptionView: View { // Renew Subscription (Expired) let settingsView = SubscriptionSettingsView(configuration: .activating, settingsViewModel: viewModel, - viewPlans: { isShowingSubscribeFlow = true }) + viewPlans: { +// isShowingSubscribeFlow = true + subscriptionNavigationCoordinator.shouldPushSubscriptionWebView = true + }) .environmentObject(subscriptionNavigationCoordinator) NavigationLink(destination: settingsView) { SettingsCellView( @@ -235,19 +242,20 @@ struct SettingsSubscriptionView: View { .onReceive(subscriptionNavigationCoordinator.$shouldPopToAppSettings) { shouldDismiss in if shouldDismiss { isShowingRestoreFlow = false - isShowingSubscribeFlow = false +// isShowingSubscribeFlow = false + subscriptionNavigationCoordinator.shouldPushSubscriptionWebView = false } } - .sheet(isPresented: $isShowingSubscribeFlow, - onDismiss: { -print("") - }, - content: { - SubscriptionContainerViewFactory.makeSubscribeFlow(origin: nil, - navigationCoordinator: subscriptionNavigationCoordinator, - subscriptionManager: AppDependencyProvider.shared.subscriptionManager, - privacyProDataReporter: viewModel.privacyProDataReporter) - }) +// .sheet(isPresented: $isShowingSubscribeFlow, +// onDismiss: { +// print("dismiss") +// }, +// content: { +// SubscriptionContainerViewFactory.makeSubscribeFlow(origin: nil, +// navigationCoordinator: subscriptionNavigationCoordinator, +// subscriptionManager: AppDependencyProvider.shared.subscriptionManager, +// privacyProDataReporter: viewModel.privacyProDataReporter) +// }) } } .onReceive(viewModel.$state) { state in diff --git a/DuckDuckGo/Subscription/Views/SubscriptionNavigationCoordinator.swift b/DuckDuckGo/Subscription/Views/SubscriptionNavigationCoordinator.swift index b8fe1f7867..92042310f0 100644 --- a/DuckDuckGo/Subscription/Views/SubscriptionNavigationCoordinator.swift +++ b/DuckDuckGo/Subscription/Views/SubscriptionNavigationCoordinator.swift @@ -22,4 +22,5 @@ import Foundation final class SubscriptionNavigationCoordinator: ObservableObject { @Published var shouldPopToSubscriptionSettings: Bool = false @Published var shouldPopToAppSettings: Bool = false + @Published var shouldPushSubscriptionWebView: Bool = false } From d6fc1a63a79e77b6b90da8507d2c6c5ee76c67a7 Mon Sep 17 00:00:00 2001 From: Federico Cappelli Date: Fri, 9 Aug 2024 10:19:44 +0100 Subject: [PATCH 09/20] os_log migrated to Logger in Subscription, stack navigation restored --- .swiftlint.yml | 10 --- DuckDuckGo.xcodeproj/project.pbxproj | 4 ++ DuckDuckGo/SettingsRootView.swift | 11 ++- DuckDuckGo/SettingsSubscriptionView.swift | 9 +-- .../Extensions/Logger+Subscription.swift | 28 ++++++++ ...ntityTheftRestorationPagesUserScript.swift | 3 + ...scriptionPagesUseSubscriptionFeature.swift | 67 ++++++++++++------- .../SubscriptionPagesUserScript.swift | 7 +- .../SubscriptionSettingsViewModel.swift | 15 ++++- .../Views/SubscriptionSettingsView.swift | 35 +++++----- 10 files changed, 116 insertions(+), 73 deletions(-) create mode 100644 DuckDuckGo/Subscription/Extensions/Logger+Subscription.swift diff --git a/.swiftlint.yml b/.swiftlint.yml index 4abcdbfd77..91c91139bc 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -21,16 +21,6 @@ opt_in_rules: - fatal_error_message - file_header -custom_rules: - enforce_os_log_wrapper: - included: ".*\\.swift" - excluded: ".*Tests.*" - name: "Use `import Common` for os_log instead of `import os.log`" - regex: "^(import (?:os\\.log|os|OSLog))$" - capture_group: 0 - message: "os_log wrapper ensures log args are @autoclosures (computed when needed) and to be able to use String Interpolation." - severity: error - analyzer_rules: - unused_import diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 738527536a..325b51e1cc 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -983,6 +983,7 @@ F115ED9C2B4EFC8E001A0453 /* TestUtils in Frameworks */ = {isa = PBXBuildFile; productRef = F115ED9B2B4EFC8E001A0453 /* TestUtils */; }; F130D73A1E5776C500C45811 /* OmniBarDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F130D7391E5776C500C45811 /* OmniBarDelegate.swift */; }; F132D6A52C62239B00D85426 /* SubscriptionSettingsHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F132D6A42C62239B00D85426 /* SubscriptionSettingsHeaderView.swift */; }; + F132D6A82C6524B600D85426 /* Logger+Subscription.swift in Sources */ = {isa = PBXBuildFile; fileRef = F132D6A72C6524B600D85426 /* Logger+Subscription.swift */; }; F1386BA41E6846C40062FC3C /* TabDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1386BA31E6846C40062FC3C /* TabDelegate.swift */; }; F13B4BC01F180D8A00814661 /* TabsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F13B4BBF1F180D8A00814661 /* TabsModel.swift */; }; F13B4BD31F1822C700814661 /* Tab.swift in Sources */ = {isa = PBXBuildFile; fileRef = F13B4BD21F1822C700814661 /* Tab.swift */; }; @@ -2752,6 +2753,7 @@ F130D7391E5776C500C45811 /* OmniBarDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OmniBarDelegate.swift; sourceTree = ""; }; F132D6A42C62239B00D85426 /* SubscriptionSettingsHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionSettingsHeaderView.swift; sourceTree = ""; }; F132D6A62C62294000D85426 /* DesignResourcesKit */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = DesignResourcesKit; path = ../DesignResourcesKit; sourceTree = ""; }; + F132D6A72C6524B600D85426 /* Logger+Subscription.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Logger+Subscription.swift"; sourceTree = ""; }; F1386BA31E6846C40062FC3C /* TabDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TabDelegate.swift; sourceTree = ""; }; F13B4BBF1F180D8A00814661 /* TabsModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TabsModel.swift; sourceTree = ""; }; F13B4BD21F1822C700814661 /* Tab.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Tab.swift; sourceTree = ""; }; @@ -5006,6 +5008,7 @@ isa = PBXGroup; children = ( F1FDC9342BF51E41006B1435 /* VPNSettings+Environment.swift */, + F132D6A72C6524B600D85426 /* Logger+Subscription.swift */, D664C7982B289AA000CBFA76 /* WKUserContentController+Handler.swift */, ); path = Extensions; @@ -7276,6 +7279,7 @@ 85C861E628FF1B5F00189466 /* HomeViewSectionRenderersExtension.swift in Sources */, CB825C922C071B1400BCC586 /* AlertView.swift in Sources */, 1DDF40292BA04FCD006850D9 /* SettingsPrivacyProtectionsView.swift in Sources */, + F132D6A82C6524B600D85426 /* Logger+Subscription.swift in Sources */, 6F64AA5F2C49463C00CF4489 /* ShortcutsModel.swift in Sources */, F1D477C61F2126CC0031ED49 /* OmniBarState.swift in Sources */, 85F2FFCD2211F615006BB258 /* MainViewController+KeyCommands.swift in Sources */, diff --git a/DuckDuckGo/SettingsRootView.swift b/DuckDuckGo/SettingsRootView.swift index f63393a8a2..83c9f1acf9 100644 --- a/DuckDuckGo/SettingsRootView.swift +++ b/DuckDuckGo/SettingsRootView.swift @@ -36,13 +36,13 @@ struct SettingsRootView: View { // Hidden navigationLinks for programatic navigation if let target = deepLinkTarget { - NavigationLink(destination: deepLinkDestinationView(for: target), + NavigationLink(destination: navigationDestinationView(for: target), isActive: $shouldDisplayDeepLinkPush) { EmptyView() } } - NavigationLink(destination: deepLinkDestinationView(for: .subscriptionFlow(origin: nil)), + NavigationLink(destination: navigationDestinationView(for: .subscriptionFlow(origin: nil)), isActive: $isShowingSubscribeFlow) { EmptyView() } List { @@ -71,7 +71,7 @@ struct SettingsRootView: View { shouldDisplayDeepLinkSheet = false }, content: { if let target = deepLinkTarget { - deepLinkDestinationView(for: target) + navigationDestinationView(for: target) } }) @@ -109,9 +109,8 @@ struct SettingsRootView: View { } } - // MARK: DeepLink Views - @ViewBuilder - func deepLinkDestinationView(for target: SettingsViewModel.SettingsDeepLinkSection) -> some View { + /// Navigation Views for DeepLink and programmatic navigation + @ViewBuilder func navigationDestinationView(for target: SettingsViewModel.SettingsDeepLinkSection) -> some View { switch target { case .dbp: SubscriptionPIRView() diff --git a/DuckDuckGo/SettingsSubscriptionView.swift b/DuckDuckGo/SettingsSubscriptionView.swift index 7e4ca4a858..f3ed16e4cf 100644 --- a/DuckDuckGo/SettingsSubscriptionView.swift +++ b/DuckDuckGo/SettingsSubscriptionView.swift @@ -39,7 +39,6 @@ struct SettingsSubscriptionView: View { @State var isShowingDBP = false @State var isShowingITP = false @State var isShowingRestoreFlow = false -// @State var isShowingSubscribeFlow = false @State var isShowingGoogleView = false @State var isShowingStripeView = false @State var isShowingPrivacyPro = false @@ -74,7 +73,6 @@ struct SettingsSubscriptionView: View { .foregroundColor(Color.init(designSystemColor: .accent)) .padding(.leading, 32.0) }, action: { -// isShowingSubscribeFlow = true subscriptionNavigationCoordinator.shouldPushSubscriptionWebView = true }, isButton: true) @@ -128,7 +126,6 @@ struct SettingsSubscriptionView: View { let settingsView = SubscriptionSettingsView(configuration: .expired, settingsViewModel: viewModel, viewPlans: { -// isShowingSubscribeFlow = true subscriptionNavigationCoordinator.shouldPushSubscriptionWebView = true }) .environmentObject(subscriptionNavigationCoordinator) @@ -150,7 +147,6 @@ struct SettingsSubscriptionView: View { let settingsView = SubscriptionSettingsView(configuration: .activating, settingsViewModel: viewModel, viewPlans: { -// isShowingSubscribeFlow = true subscriptionNavigationCoordinator.shouldPushSubscriptionWebView = true }) .environmentObject(subscriptionNavigationCoordinator) @@ -209,6 +205,7 @@ struct SettingsSubscriptionView: View { var body: some View { Group { if isShowingPrivacyPro { + let isSignedIn = viewModel.state.subscription.isSignedIn let hasActiveSubscription = viewModel.state.subscription.hasActiveSubscription let hasNoEntitlements = viewModel.state.subscription.entitlements.isEmpty @@ -242,14 +239,10 @@ struct SettingsSubscriptionView: View { .onReceive(subscriptionNavigationCoordinator.$shouldPopToAppSettings) { shouldDismiss in if shouldDismiss { isShowingRestoreFlow = false -// isShowingSubscribeFlow = false subscriptionNavigationCoordinator.shouldPushSubscriptionWebView = false } } // .sheet(isPresented: $isShowingSubscribeFlow, -// onDismiss: { -// print("dismiss") -// }, // content: { // SubscriptionContainerViewFactory.makeSubscribeFlow(origin: nil, // navigationCoordinator: subscriptionNavigationCoordinator, diff --git a/DuckDuckGo/Subscription/Extensions/Logger+Subscription.swift b/DuckDuckGo/Subscription/Extensions/Logger+Subscription.swift new file mode 100644 index 0000000000..11ec2a3b73 --- /dev/null +++ b/DuckDuckGo/Subscription/Extensions/Logger+Subscription.swift @@ -0,0 +1,28 @@ +// +// Logger+Subscription.swift +// DuckDuckGo +// +// Copyright © 2024 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation +import os.log + +extension Logger { + + static var subscription: Logger = { + Logger(subsystem: Bundle.main.bundleIdentifier ?? "DuckDuckGo", category: "SubscriptionPro") + }() +} diff --git a/DuckDuckGo/Subscription/UserScripts/IdentityTheftRestorationPagesUserScript.swift b/DuckDuckGo/Subscription/UserScripts/IdentityTheftRestorationPagesUserScript.swift index 0eb08ca0a4..d68b4fe067 100644 --- a/DuckDuckGo/Subscription/UserScripts/IdentityTheftRestorationPagesUserScript.swift +++ b/DuckDuckGo/Subscription/UserScripts/IdentityTheftRestorationPagesUserScript.swift @@ -23,6 +23,7 @@ import Combine import Foundation import WebKit import UserScript +import os.log /// /// The user script that will be the broker for all identity theft protection features @@ -54,6 +55,7 @@ extension IdentityTheftRestorationPagesUserScript: WKScriptMessageHandlerWithRep return (json, nil) } catch { // forward uncaught errors to the client + Logger.subscription.error("IdentityTheftRestorationPagesUserScript error: \(error.localizedDescription)") return (nil, error.localizedDescription) } } @@ -62,5 +64,6 @@ extension IdentityTheftRestorationPagesUserScript: WKScriptMessageHandlerWithRep extension IdentityTheftRestorationPagesUserScript: WKScriptMessageHandler { public func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { // unsupported + Logger.subscription.debug("Unsupported function: \(#function)") } } diff --git a/DuckDuckGo/Subscription/UserScripts/SubscriptionPagesUseSubscriptionFeature.swift b/DuckDuckGo/Subscription/UserScripts/SubscriptionPagesUseSubscriptionFeature.swift index 6333186203..3cc20634b2 100644 --- a/DuckDuckGo/Subscription/UserScripts/SubscriptionPagesUseSubscriptionFeature.swift +++ b/DuckDuckGo/Subscription/UserScripts/SubscriptionPagesUseSubscriptionFeature.swift @@ -25,8 +25,9 @@ import UserScript import Combine import Subscription import Core +import os.log -enum SubscriptionTransactionStatus { +enum SubscriptionTransactionStatus: String { case idle, purchasing, restoring, polling } @@ -140,7 +141,7 @@ final class SubscriptionPagesUseSubscriptionFeature: Subfeature, ObservableObjec func handler(forMethodNamed methodName: String) -> Subfeature.Handler? { - os_log("WebView handler: %s", log: .subscription, type: .debug, methodName) + Logger.subscription.debug("WebView handler: \(methodName)") switch methodName { case Handlers.getSubscription: return getSubscription case Handlers.setSubscription: return setSubscription @@ -157,7 +158,7 @@ final class SubscriptionPagesUseSubscriptionFeature: Subfeature, ObservableObjec case Handlers.subscriptionsWelcomeFaqClicked: return subscriptionsWelcomeFaqClicked case Handlers.getAccessToken: return getAccessToken default: - os_log("Unhandled web message: %s", log: .subscription, type: .error, methodName) + Logger.subscription.error("Unhandled web message: \(methodName)") return nil } } @@ -171,8 +172,7 @@ final class SubscriptionPagesUseSubscriptionFeature: Subfeature, ObservableObjec let token: String } // swiftlint:enable nesting - - + private func resetSubscriptionFlow() { setTransactionError(nil) } @@ -182,11 +182,14 @@ final class SubscriptionPagesUseSubscriptionFeature: Subfeature, ObservableObjec } private func setTransactionStatus(_ status: SubscriptionTransactionStatus) { - transactionStatus = status + if status != transactionStatus { + Logger.subscription.debug("Transaction state updated: \(status.rawValue)") + transactionStatus = status + } } - // MARK: Broker Methods (Called from WebView via UserScripts) + func getSubscription(params: Any, original: WKScriptMessage) async -> Encodable? { await appStoreAccountManagementFlow.refreshAuthTokenIfNeeded() let authToken = accountManager.authToken ?? Constants.empty @@ -203,7 +206,7 @@ final class SubscriptionPagesUseSubscriptionFeature: Subfeature, ObservableObjec return SubscriptionOptions.empty } } else { - os_log("Failed to obtain subscription options", log: .subscription, type: .error) + Logger.subscription.error("Failed to obtain subscription options") setTransactionError(.failedToGetSubscriptionOptions) return SubscriptionOptions.empty } @@ -223,12 +226,14 @@ final class SubscriptionPagesUseSubscriptionFeature: Subfeature, ObservableObjec let message = original guard let subscriptionSelection: SubscriptionSelection = DecodableHelper.decode(from: params) else { assertionFailure("SubscriptionPagesUserScript: expected JSON representation of SubscriptionSelection") + Logger.subscription.error("SubscriptionPagesUserScript: expected JSON representation of SubscriptionSelection") setTransactionStatus(.idle) return nil } // Check for active subscriptions if await subscriptionManager.storePurchaseManager().hasActiveSubscription() { + Logger.subscription.debug("Subscription already active") setTransactionError(.hasActiveSubscription) Pixel.fire(pixel: .privacyProRestoreAfterPurchaseAttempt) setTransactionStatus(.idle) @@ -241,9 +246,11 @@ final class SubscriptionPagesUseSubscriptionFeature: Subfeature, ObservableObjec switch await appStorePurchaseFlow.purchaseSubscription(with: subscriptionSelection.id, emailAccessToken: emailAccessToken) { case .success(let transactionJWS): + Logger.subscription.debug("Subscription purchased successfully") purchaseTransactionJWS = transactionJWS case .failure(let error): + Logger.subscription.error("App store purchase error: \(error.localizedDescription)") setTransactionStatus(.idle) switch error { case .cancelledByUser: @@ -264,23 +271,25 @@ final class SubscriptionPagesUseSubscriptionFeature: Subfeature, ObservableObjec setTransactionStatus(.polling) switch await appStorePurchaseFlow.completeSubscriptionPurchase(with: purchaseTransactionJWS) { case .success(let purchaseUpdate): + Logger.subscription.debug("Subscription purchase completed successfully") DailyPixel.fireDailyAndCount(pixel: .privacyProPurchaseSuccess) UniquePixel.fire(pixel: .privacyProSubscriptionActivated) Pixel.fireAttribution(pixel: .privacyProSuccessfulSubscriptionAttribution, origin: subscriptionAttributionOrigin, privacyProDataReporter: privacyProDataReporter) setTransactionStatus(.idle) await pushPurchaseUpdate(originalMessage: message, purchaseUpdate: purchaseUpdate) - case .failure: + case .failure(let error): + Logger.subscription.error("App store complete subscription purchase error: \(error.localizedDescription)") setTransactionStatus(.idle) setTransactionError(.missingEntitlements) await pushPurchaseUpdate(originalMessage: message, purchaseUpdate: PurchaseUpdate(type: "completed")) } return nil - } func setSubscription(params: Any, original: WKScriptMessage) async -> Encodable? { guard let subscriptionValues: SubscriptionValues = DecodableHelper.decode(from: params) else { assertionFailure("SubscriptionPagesUserScript: expected JSON representation of SubscriptionValues") + Logger.subscription.error("SubscriptionPagesUserScript: expected JSON representation of SubscriptionValues") setTransactionError(.generalError) return nil } @@ -296,7 +305,7 @@ final class SubscriptionPagesUseSubscriptionFeature: Subfeature, ObservableObjec onSetSubscription?() } else { - os_log("Failed to obtain subscription options", log: .subscription, type: .error) + Logger.subscription.error("Failed to obtain subscription options") setTransactionError(.failedToSetSubscription) } @@ -312,11 +321,13 @@ final class SubscriptionPagesUseSubscriptionFeature: Subfeature, ObservableObjec func featureSelected(params: Any, original: WKScriptMessage) async -> Encodable? { guard let featureSelection: FeatureSelection = DecodableHelper.decode(from: params) else { assertionFailure("SubscriptionPagesUserScript: expected JSON representation of FeatureSelection") + Logger.subscription.error("SubscriptionPagesUserScript: expected JSON representation of FeatureSelection") return nil } guard let featureSelection = SubscriptionFeatureSelection(featureName: featureSelection.feature) else { assertionFailure("SubscriptionPagesUserScript: unexpected feature name value") + Logger.subscription.error("SubscriptionPagesUserScript: unexpected feature name value") setTransactionError(.generalError) return nil } @@ -327,26 +338,29 @@ final class SubscriptionPagesUseSubscriptionFeature: Subfeature, ObservableObjec } func backToSettings(params: Any, original: WKScriptMessage) async -> Encodable? { - if let accessToken = accountManager.accessToken, - case let .success(accountDetails) = await accountManager.fetchAccountDetails(with: accessToken) { - switch await subscriptionManager.subscriptionEndpointService.getSubscription(accessToken: accessToken) { + guard let accessToken = accountManager.accessToken else { + Logger.subscription.error("Missing access token") + return nil + } + switch await accountManager.fetchAccountDetails(with: accessToken) { + case .success(let accountDetails): + switch await subscriptionManager.subscriptionEndpointService.getSubscription(accessToken: accessToken) { case .success: accountManager.storeAccount(token: accessToken, email: accountDetails.email, externalID: accountDetails.externalID) onBackToSettings?() - default: - break + case .failure(let error): + Logger.subscription.error("Error retrieving subscription details: \(error.localizedDescription)") } - - } else { - os_log("General error. Could not get account Details", log: .subscription, type: .error) + case .failure(let error): + Logger.subscription.error("Could not get account Details: \(error.localizedDescription)") setTransactionError(.generalError) } return nil } - + func getAccessToken(params: Any, original: WKScriptMessage) async throws -> Encodable? { if let accessToken = subscriptionManager.accountManager.accessToken { return [Constants.token: accessToken] @@ -358,31 +372,37 @@ final class SubscriptionPagesUseSubscriptionFeature: Subfeature, ObservableObjec // MARK: Pixel related actions func subscriptionsMonthlyPriceClicked(params: Any, original: WKScriptMessage) async -> Encodable? { + Logger.subscription.debug("Web function called: \(#function)") Pixel.fire(pixel: .privacyProOfferMonthlyPriceClick) return nil } func subscriptionsYearlyPriceClicked(params: Any, original: WKScriptMessage) async -> Encodable? { + Logger.subscription.debug("Web function called: \(#function)") Pixel.fire(pixel: .privacyProOfferYearlyPriceClick) return nil } func subscriptionsUnknownPriceClicked(params: Any, original: WKScriptMessage) async -> Encodable? { // Not used + Logger.subscription.debug("Web function called: \(#function)") return nil } func subscriptionsAddEmailSuccess(params: Any, original: WKScriptMessage) async -> Encodable? { + Logger.subscription.debug("Web function called: \(#function)") UniquePixel.fire(pixel: .privacyProAddEmailSuccess) return nil } func subscriptionsWelcomeFaqClicked(params: Any, original: WKScriptMessage) async -> Encodable? { + Logger.subscription.debug("Web function called: \(#function)") UniquePixel.fire(pixel: .privacyProWelcomeFAQClick) return nil } // MARK: Push actions (Push Data back to WebViews) + enum SubscribeActionName: String { case onPurchaseUpdate } @@ -396,9 +416,9 @@ final class SubscriptionPagesUseSubscriptionFeature: Subfeature, ObservableObjec let broker = UserScriptMessageBroker(context: SubscriptionPagesUserScript.context, requiresRunInPageContentWorld: true ) broker.push(method: method.rawValue, params: params, for: self, into: webView) } - - + // MARK: Native methods - Called from ViewModels + func restoreAccountFromAppStorePurchase() async throws { setTransactionStatus(.restoring) let result = await appStoreRestoreFlow.restoreAccountFromPastPurchase() @@ -413,7 +433,9 @@ final class SubscriptionPagesUseSubscriptionFeature: Subfeature, ObservableObjec } // MARK: Utility Methods + func mapAppStoreRestoreErrorToTransactionError(_ error: AppStoreRestoreFlowError) -> UseSubscriptionError { + Logger.subscription.error("\(#function): \(error.localizedDescription)") switch error { case .subscriptionExpired: return .subscriptionExpired @@ -433,7 +455,6 @@ final class SubscriptionPagesUseSubscriptionFeature: Subfeature, ObservableObjec onActivateSubscription = nil onBackToSettings = nil } - } private extension Pixel { diff --git a/DuckDuckGo/Subscription/UserScripts/SubscriptionPagesUserScript.swift b/DuckDuckGo/Subscription/UserScripts/SubscriptionPagesUserScript.swift index 5a841f17fc..406f02b2ba 100644 --- a/DuckDuckGo/Subscription/UserScripts/SubscriptionPagesUserScript.swift +++ b/DuckDuckGo/Subscription/UserScripts/SubscriptionPagesUserScript.swift @@ -24,13 +24,11 @@ import Foundation import WebKit import UserScript import Core +import os.log -/// /// The user script that will be the broker for all subscription features -/// public final class SubscriptionPagesUserScript: NSObject, UserScript, UserScriptMessaging { public var source: String = "" - public static let context = "subscriptionPages" // special pages messaging cannot be isolated as we'll want regular page-scripts to be able to communicate @@ -63,7 +61,6 @@ extension SubscriptionPagesUserScript: WKScriptMessageHandlerWithReply { extension SubscriptionPagesUserScript: WKScriptMessageHandler { public func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { - // unsupported - os_log("SubscriptionPagesUserScript sent an unsupported message: %s", log: .generalLog, type: .fault, message.messageName) + Logger.subscription.fault("SubscriptionPagesUserScript sent an unsupported message: \(message.messageName)") } } diff --git a/DuckDuckGo/Subscription/ViewModel/SubscriptionSettingsViewModel.swift b/DuckDuckGo/Subscription/ViewModel/SubscriptionSettingsViewModel.swift index e1559d3022..fd1669a311 100644 --- a/DuckDuckGo/Subscription/ViewModel/SubscriptionSettingsViewModel.swift +++ b/DuckDuckGo/Subscription/ViewModel/SubscriptionSettingsViewModel.swift @@ -22,6 +22,7 @@ import SwiftUI import StoreKit import Subscription import Core +import os.log final class SubscriptionSettingsViewModel: ObservableObject { @@ -105,6 +106,7 @@ final class SubscriptionSettingsViewModel: ObservableObject { } private func fetchAndUpdateSubscriptionDetails(cachePolicy: APICachePolicy, loadingIndicator: Bool) async -> Bool { + Logger.subscription.debug("\(#function)") guard let token = self.subscriptionManager.accountManager.accessToken else { return false } if loadingIndicator { displaySubscriptionLoader(true) } @@ -121,7 +123,8 @@ final class SubscriptionSettingsViewModel: ObservableObject { product: subscription.productId, billingPeriod: subscription.billingPeriod) return true - default: + case .failure(let error): + Logger.subscription.error("\(#function) error: \(error.localizedDescription)") DispatchQueue.main.async { if loadingIndicator { self.displaySubscriptionLoader(true) } } @@ -130,11 +133,14 @@ final class SubscriptionSettingsViewModel: ObservableObject { } func fetchAndUpdateAccountEmail(cachePolicy: APICachePolicy = .returnCacheDataElseLoad, loadingIndicator: Bool) async -> Bool { + Logger.subscription.debug("\(#function)") guard let token = self.subscriptionManager.accountManager.accessToken else { return false } switch cachePolicy { case .returnCacheDataDontLoad, .returnCacheDataElseLoad: - self.state.subscriptionEmail = self.subscriptionManager.accountManager.email + DispatchQueue.main.async { + self.state.subscriptionEmail = self.subscriptionManager.accountManager.email + } return true case .reloadIgnoringLocalCacheData: break @@ -143,6 +149,7 @@ final class SubscriptionSettingsViewModel: ObservableObject { if loadingIndicator { displayEmailLoader(true) } switch await self.subscriptionManager.accountManager.fetchAccountDetails(with: token) { case .success(let details): + Logger.subscription.debug("Account details fetched successfully") DispatchQueue.main.async { self.state.subscriptionEmail = details.email if loadingIndicator { self.displayEmailLoader(false) } @@ -154,7 +161,8 @@ final class SubscriptionSettingsViewModel: ObservableObject { subscriptionManager.accountManager.storeAccount(token: token, email: details.email, externalID: externalID) } return true - default: + case .failure(let error): + Logger.subscription.error("\(#function) error: \(error.localizedDescription)") DispatchQueue.main.async { if loadingIndicator { self.displayEmailLoader(true) } } @@ -175,6 +183,7 @@ final class SubscriptionSettingsViewModel: ObservableObject { } func manageSubscription() { + Logger.subscription.debug("User action: \(#function)") switch state.subscriptionInfo?.platform { case .apple: Task { await manageAppleSubscription() } diff --git a/DuckDuckGo/Subscription/Views/SubscriptionSettingsView.swift b/DuckDuckGo/Subscription/Views/SubscriptionSettingsView.swift index 28ae1904c4..5f2be76b58 100644 --- a/DuckDuckGo/Subscription/Views/SubscriptionSettingsView.swift +++ b/DuckDuckGo/Subscription/Views/SubscriptionSettingsView.swift @@ -50,12 +50,12 @@ struct SubscriptionSettingsView: View { .onFirstAppear { Pixel.fire(pixel: .privacyProSubscriptionSettings, debounce: 1) } - .navigationBarTitleDisplayMode(.inline) - .onChange(of: settingsViewModel.state.subscription.shouldDisplayRestoreSubscriptionError) { value in - if value { - isShowingSubscriptionError = true + .navigationBarTitleDisplayMode(.inline) + .onChange(of: settingsViewModel.state.subscription.shouldDisplayRestoreSubscriptionError) { value in + if value { + isShowingSubscriptionError = true + } } - } } // MARK: - @@ -236,6 +236,18 @@ struct SubscriptionSettingsView: View { headerSection if configuration == .subscribed || configuration == .expired { devicesSection + .alert(isPresented: $isShowingRemovalNotice) { + Alert( + title: Text(UserText.subscriptionRemoveFromDeviceConfirmTitle), + message: Text(UserText.subscriptionRemoveFromDeviceConfirmText), + primaryButton: .cancel(Text(UserText.subscriptionRemoveCancel)) {}, + secondaryButton: .destructive(Text(UserText.subscriptionRemove)) { + Pixel.fire(pixel: .privacyProSubscriptionManagementRemoval) + viewModel.removeSubscription() + dismiss() + } + ) + } } manageSection helpSection @@ -243,19 +255,6 @@ struct SubscriptionSettingsView: View { } .navigationTitle(UserText.settingsPProManageSubscription) .applyInsetGroupedListStyle() - .alert(isPresented: $isShowingRemovalNotice) { - Alert( - title: Text(UserText.subscriptionRemoveFromDeviceConfirmTitle), - message: Text(UserText.subscriptionRemoveFromDeviceConfirmText), - primaryButton: .cancel(Text(UserText.subscriptionRemoveCancel)) { - }, - secondaryButton: .destructive(Text(UserText.subscriptionRemove)) { - Pixel.fire(pixel: .privacyProSubscriptionManagementRemoval) - viewModel.removeSubscription() - dismiss() - } - ) - } .onChange(of: viewModel.state.shouldDismissView) { value in if value { dismiss() From 5833fe828652bc4f4f054904c861e1a9715f36c4 Mon Sep 17 00:00:00 2001 From: Federico Cappelli Date: Fri, 9 Aug 2024 12:48:38 +0100 Subject: [PATCH 10/20] enforce_os_log_wrapper removed --- PacketTunnelProvider/NetworkProtection/VPNLogger.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PacketTunnelProvider/NetworkProtection/VPNLogger.swift b/PacketTunnelProvider/NetworkProtection/VPNLogger.swift index e45a30a8eb..2fa2a2b26f 100644 --- a/PacketTunnelProvider/NetworkProtection/VPNLogger.swift +++ b/PacketTunnelProvider/NetworkProtection/VPNLogger.swift @@ -19,7 +19,7 @@ import Foundation import NetworkProtection -// swiftlint:disable:next enforce_os_log_wrapper + import OSLog /// Logger for the VPN From 88681550a427afdd9206b30865eb70d01cf21243 Mon Sep 17 00:00:00 2001 From: Federico Cappelli Date: Fri, 9 Aug 2024 14:03:58 +0100 Subject: [PATCH 11/20] DRK 3.2.0, code cleanup --- DuckDuckGo.xcodeproj/project.pbxproj | 4 +--- .../xcshareddata/swiftpm/Package.resolved | 11 +++++++++- DuckDuckGo/SettingsSubscriptionView.swift | 16 +-------------- .../Contents.json | 12 +++++++++++ .../PrivacyProHeaderAlert.pdf | Bin 0 -> 6815 bytes .../Views/SubscriptionFlowView.swift | 1 - .../SubscriptionSettingsHeaderView.swift | 19 +++++++++--------- .../Views/SubscriptionSettingsView.swift | 9 +++++---- LocalPackages/SyncUI/Package.swift | 2 +- LocalPackages/Waitlist/Package.swift | 2 +- 10 files changed, 41 insertions(+), 35 deletions(-) create mode 100644 DuckDuckGo/Subscription/Subscription.xcassets/PrivacyProHeaderAlert.imageset/Contents.json create mode 100644 DuckDuckGo/Subscription/Subscription.xcassets/PrivacyProHeaderAlert.imageset/PrivacyProHeaderAlert.pdf diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 325b51e1cc..6a523a3010 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -2752,7 +2752,6 @@ F114C55A1E66EB020018F95F /* NibLoading.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NibLoading.swift; sourceTree = ""; }; F130D7391E5776C500C45811 /* OmniBarDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OmniBarDelegate.swift; sourceTree = ""; }; F132D6A42C62239B00D85426 /* SubscriptionSettingsHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionSettingsHeaderView.swift; sourceTree = ""; }; - F132D6A62C62294000D85426 /* DesignResourcesKit */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = DesignResourcesKit; path = ../DesignResourcesKit; sourceTree = ""; }; F132D6A72C6524B600D85426 /* Logger+Subscription.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Logger+Subscription.swift"; sourceTree = ""; }; F1386BA31E6846C40062FC3C /* TabDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TabDelegate.swift; sourceTree = ""; }; F13B4BBF1F180D8A00814661 /* TabsModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TabsModel.swift; sourceTree = ""; }; @@ -3934,7 +3933,6 @@ 84E341891E2F7EFB00BDBA6F = { isa = PBXGroup; children = ( - F132D6A62C62294000D85426 /* DesignResourcesKit */, EE3B98EB2A963515002F63A0 /* WidgetsExtensionAlpha.entitlements */, 6FB030C7234331B400A10DB9 /* Configuration.xcconfig */, EEB8FDB92A990AEE00EBEDCF /* Configuration-Alpha.xcconfig */, @@ -10527,7 +10525,7 @@ repositoryURL = "https://github.com/duckduckgo/DesignResourcesKit"; requirement = { kind = exactVersion; - version = 3.1.0; + version = 3.2.0; }; }; F486D2EF25069482002D07D7 /* XCRemoteSwiftPackageReference "Kingfisher" */ = { diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index b874916490..0a8532fe44 100644 --- a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -45,6 +45,15 @@ "version" : "6.4.0" } }, + { + "identity" : "designresourceskit", + "kind" : "remoteSourceControl", + "location" : "https://github.com/duckduckgo/DesignResourcesKit", + "state" : { + "revision" : "fe3c383b5e21e0a419e0f979f7f2cd4e67385b15", + "version" : "3.2.0" + } + }, { "identity" : "duckduckgo-autofill", "kind" : "remoteSourceControl", @@ -129,7 +138,7 @@ { "identity" : "swift-argument-parser", "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-argument-parser.git", + "location" : "https://github.com/apple/swift-argument-parser", "state" : { "revision" : "0fbc8848e389af3bb55c182bc19ca9d5dc2f255b", "version" : "1.4.0" diff --git a/DuckDuckGo/SettingsSubscriptionView.swift b/DuckDuckGo/SettingsSubscriptionView.swift index f3ed16e4cf..0bd3ed6d91 100644 --- a/DuckDuckGo/SettingsSubscriptionView.swift +++ b/DuckDuckGo/SettingsSubscriptionView.swift @@ -91,31 +91,23 @@ struct SettingsSubscriptionView: View { @ViewBuilder private var disabledFeaturesView: some View { - // if viewModel.state.subscription.entitlements.contains(.networkProtection) { SettingsCellView(label: UserText.settingsPProVPNTitle, image: Image("SettingsPrivacyProVPN"), statusIndicator: StatusIndicatorView(status: .off), isGreyedOut: true ) - // } - - // if viewModel.state.subscription.entitlements.contains(.dataBrokerProtection) { SettingsCellView( label: UserText.settingsPProDBPTitle, image: Image("SettingsPrivacyProPIR"), statusIndicator: StatusIndicatorView(status: .off), isGreyedOut: true ) - // } - - // if viewModel.state.subscription.entitlements.contains(.identityTheftRestoration) { SettingsCellView( label: UserText.settingsPProITRTitle, image: Image("SettingsPrivacyProITP"), statusIndicator: StatusIndicatorView(status: .off), isGreyedOut: true ) - // } } @ViewBuilder @@ -217,6 +209,7 @@ struct SettingsSubscriptionView: View { Section(header: Text(UserText.settingsPProSection), footer: !isSignedIn ? footerLink : nil ) { + switch (isSignedIn, hasActiveSubscription, hasNoEntitlements) { // Signed In, Subscription Expired @@ -242,13 +235,6 @@ struct SettingsSubscriptionView: View { subscriptionNavigationCoordinator.shouldPushSubscriptionWebView = false } } -// .sheet(isPresented: $isShowingSubscribeFlow, -// content: { -// SubscriptionContainerViewFactory.makeSubscribeFlow(origin: nil, -// navigationCoordinator: subscriptionNavigationCoordinator, -// subscriptionManager: AppDependencyProvider.shared.subscriptionManager, -// privacyProDataReporter: viewModel.privacyProDataReporter) -// }) } } .onReceive(viewModel.$state) { state in diff --git a/DuckDuckGo/Subscription/Subscription.xcassets/PrivacyProHeaderAlert.imageset/Contents.json b/DuckDuckGo/Subscription/Subscription.xcassets/PrivacyProHeaderAlert.imageset/Contents.json new file mode 100644 index 0000000000..f0862b234b --- /dev/null +++ b/DuckDuckGo/Subscription/Subscription.xcassets/PrivacyProHeaderAlert.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "PrivacyProHeaderAlert.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/DuckDuckGo/Subscription/Subscription.xcassets/PrivacyProHeaderAlert.imageset/PrivacyProHeaderAlert.pdf b/DuckDuckGo/Subscription/Subscription.xcassets/PrivacyProHeaderAlert.imageset/PrivacyProHeaderAlert.pdf new file mode 100644 index 0000000000000000000000000000000000000000..290601ea60638849e1ef0c175a8facdad6b9e55f GIT binary patch literal 6815 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 Z-cSFRodSWu|Dprr6XZdsqmxpW{(q|@iNpW^ literal 0 HcmV?d00001 diff --git a/DuckDuckGo/Subscription/Views/SubscriptionFlowView.swift b/DuckDuckGo/Subscription/Views/SubscriptionFlowView.swift index fffcf42c7a..430475bbe4 100644 --- a/DuckDuckGo/Subscription/Views/SubscriptionFlowView.swift +++ b/DuckDuckGo/Subscription/Views/SubscriptionFlowView.swift @@ -25,7 +25,6 @@ import Core struct SubscriptionFlowView: View { @Environment(\.dismiss) var dismiss -// @EnvironmentObject var subscriptionNavigationCoordinator: SubscriptionNavigationCoordinator @StateObject var viewModel: SubscriptionFlowViewModel @State private var isPurchaseInProgress = false diff --git a/DuckDuckGo/Subscription/Views/SubscriptionSettingsHeaderView.swift b/DuckDuckGo/Subscription/Views/SubscriptionSettingsHeaderView.swift index f56bf8a620..ef424f6537 100644 --- a/DuckDuckGo/Subscription/Views/SubscriptionSettingsHeaderView.swift +++ b/DuckDuckGo/Subscription/Views/SubscriptionSettingsHeaderView.swift @@ -31,7 +31,12 @@ struct SubscriptionSettingsHeaderView: View { var body: some View { VStack(alignment: .center, spacing: 8) { - Image("PrivacyProHeader") + switch state { + case .expired: + Image("PrivacyProHeaderAlert") + default: + Image("PrivacyProHeader") + } Text(UserText.subscriptionTitle) .daxTitle2() .foregroundColor(Color(designSystemColor: .textPrimary)) @@ -47,13 +52,9 @@ struct SubscriptionSettingsHeaderView: View { .foregroundColor(Color(designSystemColor: .textSecondary)) } case .expired(let details): - HStack(spacing: 4) { - Image("Exclamation-Color-16") - .frame(width: 16, height: 16) - Text(details) - .daxBodyRegular() - .foregroundColor(Color(designSystemColor: .textSecondary)) - } + Text(details) + .daxBodyRegular() + .foregroundColor(Color(designSystemColor: .textSecondary)) case .activating: HStack(spacing: 4) { Circle() @@ -74,7 +75,7 @@ struct SubscriptionSettingsHeaderView: View { } #Preview { - VStack { + VStack(spacing: 8) { SubscriptionSettingsHeaderView(state: .subscribed) SubscriptionSettingsHeaderView(state: .expired("Your subscription expired on April 20, 2027")) SubscriptionSettingsHeaderView(state: .activating) diff --git a/DuckDuckGo/Subscription/Views/SubscriptionSettingsView.swift b/DuckDuckGo/Subscription/Views/SubscriptionSettingsView.swift index 5f2be76b58..328fd45455 100644 --- a/DuckDuckGo/Subscription/Views/SubscriptionSettingsView.swift +++ b/DuckDuckGo/Subscription/Views/SubscriptionSettingsView.swift @@ -61,8 +61,6 @@ struct SubscriptionSettingsView: View { // MARK: - @ViewBuilder private var headerSection: some View { -// let isExpired = !(viewModel.state.subscriptionInfo?.isActive ?? false) -// let isSubscribed = viewModel.state.subscriptionInfo?.isActive ?? false Section { switch configuration { case .subscribed: @@ -230,10 +228,12 @@ struct SubscriptionSettingsView: View { NavigationLink(destination: SubscriptionGoogleView(), isActive: $isShowingGoogleView) { EmptyView() - } - + }.hidden() + List { headerSection + .padding(.horizontal, -20) + .padding(.vertical, -10) if configuration == .subscribed || configuration == .expired { devicesSection .alert(isPresented: $isShowingRemovalNotice) { @@ -253,6 +253,7 @@ struct SubscriptionSettingsView: View { helpSection privacyPolicySection } + .padding(.top, -20) .navigationTitle(UserText.settingsPProManageSubscription) .applyInsetGroupedListStyle() .onChange(of: viewModel.state.shouldDismissView) { value in diff --git a/LocalPackages/SyncUI/Package.swift b/LocalPackages/SyncUI/Package.swift index 0fab03538e..c3fd0e2ff4 100644 --- a/LocalPackages/SyncUI/Package.swift +++ b/LocalPackages/SyncUI/Package.swift @@ -33,7 +33,7 @@ let package = Package( ], dependencies: [ .package(path: "../DuckUI"), - .package(url: "https://github.com/duckduckgo/DesignResourcesKit", exact: "3.1.0"), + .package(url: "https://github.com/duckduckgo/DesignResourcesKit", exact: "3.2.0"), .package(url: "https://github.com/duckduckgo/apple-toolbox.git", exact: "3.1.2"), ], targets: [ diff --git a/LocalPackages/Waitlist/Package.swift b/LocalPackages/Waitlist/Package.swift index 977911513d..72ad52d8b2 100644 --- a/LocalPackages/Waitlist/Package.swift +++ b/LocalPackages/Waitlist/Package.swift @@ -15,7 +15,7 @@ let package = Package( targets: ["Waitlist", "WaitlistMocks"]) ], dependencies: [ - .package(url: "https://github.com/duckduckgo/DesignResourcesKit", exact: "3.1.0"), + .package(url: "https://github.com/duckduckgo/DesignResourcesKit", exact: "3.2.0"), .package(url: "https://github.com/duckduckgo/apple-toolbox.git", exact: "3.1.2"), ], targets: [ From 82907dc2016500bf36646f949d89bb0aa028166d Mon Sep 17 00:00:00 2001 From: Federico Cappelli Date: Fri, 9 Aug 2024 14:07:07 +0100 Subject: [PATCH 12/20] unused var removed --- Core/LegacyBookmarksCoreDataStorage.swift | 3 --- 1 file changed, 3 deletions(-) diff --git a/Core/LegacyBookmarksCoreDataStorage.swift b/Core/LegacyBookmarksCoreDataStorage.swift index 782af6b8a5..3949818c3d 100644 --- a/Core/LegacyBookmarksCoreDataStorage.swift +++ b/Core/LegacyBookmarksCoreDataStorage.swift @@ -264,9 +264,6 @@ extension LegacyBookmarksCoreDataStorage { private func deleteExtraOrphanedFolders(_ orphanedFolders: [BookmarkFolderManagedObject], onContext context: NSManagedObjectContext, withFolderType folderType: TopLevelFolderType) { - let count = orphanedFolders.count - let pixelParam = [PixelParameters.bookmarkErrorOrphanedFolderCount: "\(count)"] - // Sort all orphaned folders by number of children let sorted = orphanedFolders.sorted { ($0.children?.count ?? 0) > ($1.children?.count ?? 0) } From 355af6153456f0917f4c14832c21189e39f369b1 Mon Sep 17 00:00:00 2001 From: Federico Cappelli Date: Fri, 9 Aug 2024 14:57:34 +0100 Subject: [PATCH 13/20] swiftlint rules updated for next version --- .swiftlint.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.swiftlint.yml b/.swiftlint.yml index 91c91139bc..a8e8c08795 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -12,6 +12,8 @@ disabled_rules: - function_body_length - file_length - type_body_length + - non_optional_string_data_conversion + - static_over_final_class opt_in_rules: - closure_end_indentation From c5e4e311fb58043bac8e292bfa8bc93ec110909a Mon Sep 17 00:00:00 2001 From: Federico Cappelli Date: Fri, 9 Aug 2024 14:58:33 +0100 Subject: [PATCH 14/20] lint --- DuckDuckGo/StatusIndicator.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DuckDuckGo/StatusIndicator.swift b/DuckDuckGo/StatusIndicator.swift index f65a4289a2..9613cb88b9 100644 --- a/DuckDuckGo/StatusIndicator.swift +++ b/DuckDuckGo/StatusIndicator.swift @@ -1,5 +1,5 @@ // -// SettingsStatus.swift +// StatusIndicator.swift // DuckDuckGo // // Copyright © 2024 DuckDuckGo. All rights reserved. From ede9433e482f32864adc7d1ad728aff73f763f90 Mon Sep 17 00:00:00 2001 From: Federico Cappelli Date: Mon, 12 Aug 2024 10:53:23 +0100 Subject: [PATCH 15/20] VPN status fixed --- DuckDuckGo/SettingsPrivacyProtectionsView.swift | 2 +- DuckDuckGo/SettingsState.swift | 14 +++++++------- DuckDuckGo/SettingsSubscriptionView.swift | 2 +- DuckDuckGo/SettingsViewModel.swift | 14 +++++++------- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/DuckDuckGo/SettingsPrivacyProtectionsView.swift b/DuckDuckGo/SettingsPrivacyProtectionsView.swift index f7a96c9c48..7de152bc90 100644 --- a/DuckDuckGo/SettingsPrivacyProtectionsView.swift +++ b/DuckDuckGo/SettingsPrivacyProtectionsView.swift @@ -63,7 +63,7 @@ struct SettingsPrivacyProtectionsView: View { } // Network Protection - if viewModel.state.networkProtection.enabled { + if viewModel.state.networkProtectionConnected { SettingsCellView(label: UserText.netPSettingsTitle, image: Image("SettingsNetworkProtection"), action: { viewModel.presentLegacyView(.netP) }, diff --git a/DuckDuckGo/SettingsState.swift b/DuckDuckGo/SettingsState.swift index f8283fe22a..3f0ea24f11 100644 --- a/DuckDuckGo/SettingsState.swift +++ b/DuckDuckGo/SettingsState.swift @@ -36,10 +36,10 @@ struct SettingsState { var size: Int } - struct NetworkProtection { - var enabled: Bool - var status: String - } +// struct NetworkProtection { +// var enabled: Bool +//// var status: String +// } struct Subscription: Codable { var enabled: Bool @@ -92,8 +92,8 @@ struct SettingsState { var loginsEnabled: Bool // Network Protection properties - var networkProtection: NetworkProtection - + var networkProtectionConnected: Bool + // Subscriptions Properties var subscription: Subscription @@ -127,7 +127,7 @@ struct SettingsState { voiceSearchEnabled: false, speechRecognitionAvailable: false, loginsEnabled: false, - networkProtection: NetworkProtection(enabled: false, status: ""), + networkProtectionConnected: false, subscription: Subscription(enabled: false, canPurchase: false, isSignedIn: false, diff --git a/DuckDuckGo/SettingsSubscriptionView.swift b/DuckDuckGo/SettingsSubscriptionView.swift index 0bd3ed6d91..af39a0cc0a 100644 --- a/DuckDuckGo/SettingsSubscriptionView.swift +++ b/DuckDuckGo/SettingsSubscriptionView.swift @@ -158,7 +158,7 @@ struct SettingsSubscriptionView: View { SettingsCellView(label: UserText.settingsPProVPNTitle, image: Image("SettingsPrivacyProVPN"), action: { viewModel.presentLegacyView(.netP) }, - statusIndicator: StatusIndicatorView(status: viewModel.state.networkProtection.enabled ? .on : .off), + statusIndicator: StatusIndicatorView(status: viewModel.state.networkProtectionConnected ? .on : .off), disclosureIndicator: true, isButton: true) } diff --git a/DuckDuckGo/SettingsViewModel.swift b/DuckDuckGo/SettingsViewModel.swift index 4c9d00e35c..1d82c8de0e 100644 --- a/DuckDuckGo/SettingsViewModel.swift +++ b/DuckDuckGo/SettingsViewModel.swift @@ -396,7 +396,7 @@ extension SettingsViewModel { voiceSearchEnabled: AppDependencyProvider.shared.voiceSearchHelper.isVoiceSearchEnabled, speechRecognitionAvailable: AppDependencyProvider.shared.voiceSearchHelper.isSpeechRecognizerAvailable, loginsEnabled: featureFlagger.isFeatureOn(.autofillAccessCredentialManagement), - networkProtection: getNetworkProtectionState(), + networkProtectionConnected: false, subscription: SettingsState.defaults.subscription, sync: getSyncState(), duckPlayerEnabled: featureFlagger.isFeatureOn(.duckPlayer) || shouldDisplayDuckPlayerContingencyMessage, @@ -422,9 +422,9 @@ extension SettingsViewModel { } } - private func getNetworkProtectionState() -> SettingsState.NetworkProtection { - return SettingsState.NetworkProtection(enabled: false, status: "") - } +// private func getNetworkProtectionState() -> SettingsState.NetworkProtection { +// return SettingsState.NetworkProtection(enabled: false, status: "") +// } private func getSyncState() -> SettingsState.SyncSettings { SettingsState.SyncSettings(enabled: legacyViewProvider.syncService.featureFlags.contains(.userInterface), @@ -461,12 +461,12 @@ extension SettingsViewModel { if AppDependencyProvider.shared.vpnFeatureVisibility.isPrivacyProLaunched() { switch connectionStatus { case .connected: - self.state.networkProtection.status = UserText.netPCellConnected + self.state.networkProtectionConnected = true default: - self.state.networkProtection.status = UserText.netPCellDisconnected + self.state.networkProtectionConnected = false } } else { - self.state.networkProtection.status = "" + self.state.networkProtectionConnected = false } } From 87c04cf40f88d5a81f6ee2da4f2f0bc94f75da59 Mon Sep 17 00:00:00 2001 From: Federico Cappelli Date: Mon, 12 Aug 2024 10:54:51 +0100 Subject: [PATCH 16/20] code cleanup --- DuckDuckGo/SettingsState.swift | 5 ----- DuckDuckGo/SettingsViewModel.swift | 4 ---- 2 files changed, 9 deletions(-) diff --git a/DuckDuckGo/SettingsState.swift b/DuckDuckGo/SettingsState.swift index 3f0ea24f11..8f7edee144 100644 --- a/DuckDuckGo/SettingsState.swift +++ b/DuckDuckGo/SettingsState.swift @@ -36,11 +36,6 @@ struct SettingsState { var size: Int } -// struct NetworkProtection { -// var enabled: Bool -//// var status: String -// } - struct Subscription: Codable { var enabled: Bool var canPurchase: Bool diff --git a/DuckDuckGo/SettingsViewModel.swift b/DuckDuckGo/SettingsViewModel.swift index 1d82c8de0e..b4e5108c67 100644 --- a/DuckDuckGo/SettingsViewModel.swift +++ b/DuckDuckGo/SettingsViewModel.swift @@ -422,10 +422,6 @@ extension SettingsViewModel { } } -// private func getNetworkProtectionState() -> SettingsState.NetworkProtection { -// return SettingsState.NetworkProtection(enabled: false, status: "") -// } - private func getSyncState() -> SettingsState.SyncSettings { SettingsState.SyncSettings(enabled: legacyViewProvider.syncService.featureFlags.contains(.userInterface), title: { From a8c73fc5a27f2d3fa95b8daa429e18a6a6e7107a Mon Sep 17 00:00:00 2001 From: Federico Cappelli Date: Mon, 12 Aug 2024 10:55:55 +0100 Subject: [PATCH 17/20] var name typo fixed --- DuckDuckGo/SettingsAppearanceView.swift | 2 +- DuckDuckGo/SettingsNextStepsView.swift | 2 +- DuckDuckGo/SettingsState.swift | 6 +++--- DuckDuckGo/SettingsViewModel.swift | 6 +++--- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/DuckDuckGo/SettingsAppearanceView.swift b/DuckDuckGo/SettingsAppearanceView.swift index 1163a96416..e29a2d2169 100644 --- a/DuckDuckGo/SettingsAppearanceView.swift +++ b/DuckDuckGo/SettingsAppearanceView.swift @@ -43,7 +43,7 @@ struct SettingsAppearanceView: View { } Section(header: Text(UserText.addressBar)) { - if viewModel.state.addressbar.enabled { + if viewModel.state.addressBar.enabled { // Address Bar Position SettingsPickerCellView(label: UserText.settingsAddressBar, options: AddressBarPosition.allCases, diff --git a/DuckDuckGo/SettingsNextStepsView.swift b/DuckDuckGo/SettingsNextStepsView.swift index 45c5b3c322..b08b518f25 100644 --- a/DuckDuckGo/SettingsNextStepsView.swift +++ b/DuckDuckGo/SettingsNextStepsView.swift @@ -39,7 +39,7 @@ struct SettingsNextStepsView: View { } // Set Your Address Bar Position - if viewModel.state.addressbar.enabled { + if viewModel.state.addressBar.enabled { NavigationLink(destination: SettingsAppearanceView().environmentObject(viewModel)) { SettingsCellView(label: UserText.setYourAddressBarPosition, image: Image("SettingsAddressBarPosition")) diff --git a/DuckDuckGo/SettingsState.swift b/DuckDuckGo/SettingsState.swift index 8f7edee144..755ae10d13 100644 --- a/DuckDuckGo/SettingsState.swift +++ b/DuckDuckGo/SettingsState.swift @@ -35,7 +35,7 @@ struct SettingsState { var enabled: Bool var size: Int } - + struct Subscription: Codable { var enabled: Bool var canPurchase: Bool @@ -58,7 +58,7 @@ struct SettingsState { var appIcon: AppIcon var fireButtonAnimation: FireButtonAnimationType var textSize: TextSize - var addressbar: AddressBar + var addressBar: AddressBar var showsFullURL: Bool // Privacy properties @@ -105,7 +105,7 @@ struct SettingsState { appIcon: AppIconManager.shared.appIcon, fireButtonAnimation: .fireRising, textSize: TextSize(enabled: false, size: 100), - addressbar: AddressBar(enabled: false, position: .top), + addressBar: AddressBar(enabled: false, position: .top), showsFullURL: false, sendDoNotSell: true, autoconsentEnabled: false, diff --git a/DuckDuckGo/SettingsViewModel.swift b/DuckDuckGo/SettingsViewModel.swift index b4e5108c67..a977475e1d 100644 --- a/DuckDuckGo/SettingsViewModel.swift +++ b/DuckDuckGo/SettingsViewModel.swift @@ -127,11 +127,11 @@ final class SettingsViewModel: ObservableObject { var addressBarPositionBinding: Binding { Binding( get: { - self.state.addressbar.position + self.state.addressBar.position }, set: { self.appSettings.currentAddressBarPosition = $0 - self.state.addressbar.position = $0 + self.state.addressBar.position = $0 } ) } @@ -379,7 +379,7 @@ extension SettingsViewModel { appIcon: AppIconManager.shared.appIcon, fireButtonAnimation: appSettings.currentFireButtonAnimation, textSize: SettingsState.TextSize(enabled: !isPad, size: appSettings.textSize), - addressbar: SettingsState.AddressBar(enabled: !isPad, position: appSettings.currentAddressBarPosition), + addressBar: SettingsState.AddressBar(enabled: !isPad, position: appSettings.currentAddressBarPosition), showsFullURL: appSettings.showFullSiteAddress, sendDoNotSell: appSettings.sendDoNotSell, autoconsentEnabled: appSettings.autoconsentEnabled, From 5d48a3234d3a1b1214d87f02901bf4cbd5bf3d54 Mon Sep 17 00:00:00 2001 From: Federico Cappelli Date: Mon, 12 Aug 2024 11:08:57 +0100 Subject: [PATCH 18/20] removed vpn menu under privacy protections --- DuckDuckGo/SettingsPrivacyProtectionsView.swift | 9 --------- 1 file changed, 9 deletions(-) diff --git a/DuckDuckGo/SettingsPrivacyProtectionsView.swift b/DuckDuckGo/SettingsPrivacyProtectionsView.swift index 7de152bc90..49bba931c1 100644 --- a/DuckDuckGo/SettingsPrivacyProtectionsView.swift +++ b/DuckDuckGo/SettingsPrivacyProtectionsView.swift @@ -61,15 +61,6 @@ struct SettingsPrivacyProtectionsView: View { image: Image("SettingsEmailProtection"), statusIndicator: StatusIndicatorView(status: viewModel.emailProtectionStatus)) } - - // Network Protection - if viewModel.state.networkProtectionConnected { - SettingsCellView(label: UserText.netPSettingsTitle, - image: Image("SettingsNetworkProtection"), - action: { viewModel.presentLegacyView(.netP) }, - disclosureIndicator: true, - isButton: true) - } } } From c48a12a817d48d40a8c7bb0a3a784a3f1fa4a2e2 Mon Sep 17 00:00:00 2001 From: Federico Cappelli Date: Mon, 12 Aug 2024 12:32:50 +0100 Subject: [PATCH 19/20] unused strings removed --- DuckDuckGo/UserText.swift | 2 -- DuckDuckGo/en.lproj/Localizable.strings | 6 ------ 2 files changed, 8 deletions(-) diff --git a/DuckDuckGo/UserText.swift b/DuckDuckGo/UserText.swift index 68a949d88e..4d404626ee 100644 --- a/DuckDuckGo/UserText.swift +++ b/DuckDuckGo/UserText.swift @@ -475,8 +475,6 @@ public struct UserText { public static let netPSettingsTitle = NSLocalizedString("netP.settings.title", value: "VPN", comment: "Title for the DuckDuckGo VPN feature in settings") public static let netPNavTitle = NSLocalizedString("netP.title", value: "DuckDuckGo VPN", comment: "Title for the DuckDuckGo VPN feature") - public static let netPCellConnected = NSLocalizedString("netP.cell.connected", value: "Connected", comment: "String indicating NetP is connected when viewed from the settings screen") - public static let netPCellDisconnected = NSLocalizedString("netP.cell.disconnected", value: "Not connected", comment: "String indicating NetP is disconnected when viewed from the settings screen") static let netPInviteTitle = NSLocalizedString("network.protection.invite.dialog.title", value: "You’re invited to try DuckDuckGo VPN", comment: "Title for the network protection invite screen") static let netPInviteMessage = NSLocalizedString("network.protection.invite.dialog.message", value: "Enter your invite code to get started.", comment: "Message for the network protection invite dialog") diff --git a/DuckDuckGo/en.lproj/Localizable.strings b/DuckDuckGo/en.lproj/Localizable.strings index 8f6b6a21ea..f9bf845070 100644 --- a/DuckDuckGo/en.lproj/Localizable.strings +++ b/DuckDuckGo/en.lproj/Localizable.strings @@ -1477,12 +1477,6 @@ https://duckduckgo.com/mac"; /* Edit button */ "navigation.title.edit" = "Edit"; -/* String indicating NetP is connected when viewed from the settings screen */ -"netP.cell.connected" = "Connected"; - -/* String indicating NetP is disconnected when viewed from the settings screen */ -"netP.cell.disconnected" = "Not connected"; - /* Title for the DuckDuckGo VPN feature in settings */ "netP.settings.title" = "VPN"; From 18254622d7498712f755e22ee00c277a3662f2bc Mon Sep 17 00:00:00 2001 From: Federico Cappelli Date: Mon, 12 Aug 2024 17:18:58 +0100 Subject: [PATCH 20/20] VPN .snoozing state is treated as deactivated --- DuckDuckGo/SettingsViewModel.swift | 2 -- 1 file changed, 2 deletions(-) diff --git a/DuckDuckGo/SettingsViewModel.swift b/DuckDuckGo/SettingsViewModel.swift index 26e54d729b..2de260a871 100644 --- a/DuckDuckGo/SettingsViewModel.swift +++ b/DuckDuckGo/SettingsViewModel.swift @@ -458,8 +458,6 @@ extension SettingsViewModel { switch connectionStatus { case .connected: self.state.networkProtectionConnected = true - case .snoozing: - self.state.networkProtectionConnected = true // TODO: change with @sam default: self.state.networkProtectionConnected = false }