Skip to content

Commit

Permalink
Onboarding intro - Browsers Comparison Chart (#2984)
Browse files Browse the repository at this point in the history
  • Loading branch information
alessandroboron committed Jul 2, 2024
1 parent 1a8978e commit d3c7393
Show file tree
Hide file tree
Showing 8 changed files with 323 additions and 4 deletions.
16 changes: 16 additions & 0 deletions DuckDuckGo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -607,7 +607,9 @@
9FA5E44E2BF1B16400BDEF02 /* SubscriptionContainerViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FA5E44D2BF1B16400BDEF02 /* SubscriptionContainerViewModelTests.swift */; };
9FB027122C2526DD009EA190 /* DaxDialogIntroView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FB027112C2526DD009EA190 /* DaxDialogIntroView.swift */; };
9FB027142C252E0C009EA190 /* DaxDialogBrowsersComparisonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FB027132C252E0C009EA190 /* DaxDialogBrowsersComparisonView.swift */; };
9FB027192C26BC29009EA190 /* BrowsersComparisonModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FB027182C26BC29009EA190 /* BrowsersComparisonModel.swift */; };
9FF7E9822C22A1F100902BE5 /* DaxDialogView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FF7E9812C22A1F100902BE5 /* DaxDialogView.swift */; };
9FF7E9862C23D10300902BE5 /* BrowsersComparisonChart.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FF7E9852C23D10300902BE5 /* BrowsersComparisonChart.swift */; };
AA3D854523D9942200788410 /* AppIconSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA3D854423D9942200788410 /* AppIconSettingsViewController.swift */; };
AA3D854723D9E88E00788410 /* AppIconSettingsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA3D854623D9E88E00788410 /* AppIconSettingsCell.swift */; };
AA3D854923DA1DFB00788410 /* AppIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA3D854823DA1DFB00788410 /* AppIcon.swift */; };
Expand Down Expand Up @@ -2242,7 +2244,9 @@
9FA5E44D2BF1B16400BDEF02 /* SubscriptionContainerViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionContainerViewModelTests.swift; sourceTree = "<group>"; };
9FB027112C2526DD009EA190 /* DaxDialogIntroView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DaxDialogIntroView.swift; sourceTree = "<group>"; };
9FB027132C252E0C009EA190 /* DaxDialogBrowsersComparisonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DaxDialogBrowsersComparisonView.swift; sourceTree = "<group>"; };
9FB027182C26BC29009EA190 /* BrowsersComparisonModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrowsersComparisonModel.swift; sourceTree = "<group>"; };
9FF7E9812C22A1F100902BE5 /* DaxDialogView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DaxDialogView.swift; sourceTree = "<group>"; };
9FF7E9852C23D10300902BE5 /* BrowsersComparisonChart.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrowsersComparisonChart.swift; sourceTree = "<group>"; };
AA3D854423D9942200788410 /* AppIconSettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppIconSettingsViewController.swift; sourceTree = "<group>"; };
AA3D854623D9E88E00788410 /* AppIconSettingsCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppIconSettingsCell.swift; sourceTree = "<group>"; };
AA3D854823DA1DFB00788410 /* AppIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppIcon.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -4204,9 +4208,19 @@
path = DaxDialogs;
sourceTree = "<group>";
};
9FB027172C26BC0F009EA190 /* BrowsersComparison */ = {
isa = PBXGroup;
children = (
9FB027182C26BC29009EA190 /* BrowsersComparisonModel.swift */,
9FF7E9852C23D10300902BE5 /* BrowsersComparisonChart.swift */,
);
path = BrowsersComparison;
sourceTree = "<group>";
};
9FF7E9802C22A19800902BE5 /* OnboardingExperiment */ = {
isa = PBXGroup;
children = (
9FB027172C26BC0F009EA190 /* BrowsersComparison */,
9FB027102C2526A8009EA190 /* DaxDialogs */,
);
path = OnboardingExperiment;
Expand Down Expand Up @@ -6693,6 +6707,7 @@
981FED6E22025151008488D7 /* BlankSnapshotViewController.swift in Sources */,
D66F683D2BB333C100AE93E2 /* SubscriptionContainerView.swift in Sources */,
851B128822200575004781BC /* Onboarding.swift in Sources */,
9FB027192C26BC29009EA190 /* BrowsersComparisonModel.swift in Sources */,
3151F0EE2735800800226F58 /* VoiceSearchFeedbackView.swift in Sources */,
37CF91642BB4A82A00BADCAE /* CrashCollectionOnboardingViewModel.swift in Sources */,
857EEB752095FFAC008A005C /* HomeRowInstructionsViewController.swift in Sources */,
Expand Down Expand Up @@ -6789,6 +6804,7 @@
CB5418632BD90CD000C2CD26 /* BrokenSitePromptViewModel.swift in Sources */,
F1D477C61F2126CC0031ED49 /* OmniBarState.swift in Sources */,
85F2FFCD2211F615006BB258 /* MainViewController+KeyCommands.swift in Sources */,
9FF7E9862C23D10300902BE5 /* BrowsersComparisonChart.swift in Sources */,
4B274F602AFEAECC003F0745 /* NetworkProtectionWidgetRefreshModel.swift in Sources */,
858650D9246B0D3C00C36F8A /* DaxOnboardingViewController.swift in Sources */,
312E5746283BB04A00C18FA0 /* AutofillEmptySearchView.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "DDGBrowserIcon.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "SafariBrowserIcon.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
//
// BrowsersComparisonChart.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

// MARK: - Chart View

struct BrowsersComparisonChart: View {
let privacyFeatures: [BrowsersComparisonModel.PrivacyFeature]

var body: some View {
VStack(spacing: Metrics.stackSpacing) {
Header(browsers: BrowsersComparisonModel.Browser.allCases)
.frame(height: Metrics.headerHeight)

ForEach(privacyFeatures, id: \.type) { feature in
Row(feature: feature)
}

}
}
}

// MARK: - Header

extension BrowsersComparisonChart {

struct Header: View {
let browsers: [BrowsersComparisonModel.Browser]

var body: some View {
HStack(alignment: .top) {
Spacer()

ForEach(Array(browsers.enumerated()), id: \.offset) { index, browser in
Image(browser.image)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: Metrics.imageContainerSize.width, height: Metrics.imageContainerSize.height)

if index < browsers.count - 1 {
Divider()
}
}
}
}
}

}

// MARK: - Row

extension BrowsersComparisonChart {

struct Row: View {
let feature: BrowsersComparisonModel.PrivacyFeature

var body: some View {
HStack {
Text(verbatim: feature.type.title)
.font(Metrics.font)
.foregroundColor(.primary)
.lineLimit(nil)
.lineSpacing(3)
.multilineTextAlignment(.leading)
.fixedSize(horizontal: false, vertical: true)

Spacer()

BrowsersSupport(browsersSupport: feature.browsersSupport)
}
Divider()
}
}

}

// MARK: - Row + BrowsersSupport

extension BrowsersComparisonChart.Row {

struct BrowsersSupport: View {
let browsersSupport: [BrowsersComparisonModel.PrivacyFeature.BrowserSupport]

var body: some View {
ForEach(Array(browsersSupport.enumerated()), id: \.offset) { index, browserSupport in
Image(browserSupport.availability.image)
.frame(width: Metrics.imageContainerSize.width, height: Metrics.imageContainerSize.height)

if index < browsersSupport.count - 1 {
Divider()
.frame(height: Metrics.imageContainerSize.height)
}
}
}
}

}

// MARK: - Metrics

private enum Metrics {
static let stackSpacing: CGFloat = 0.0
static let headerHeight: CGFloat = 60
static let imageContainerSize = CGSize(width: 50.0, height: 50.0)
static let font = Font.system(size: 15.0)
}

#Preview {
BrowsersComparisonChart(privacyFeatures: BrowsersComparisonModel.privacyFeatures)
.padding()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
//
// BrowsersComparisonModel.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

struct BrowsersComparisonModel {

static let privacyFeatures: [PrivacyFeature] = {
PrivacyFeature.FeatureType.allCases.map { featureType in
PrivacyFeature(type: featureType, browsersSupport: browsersSupport(for: featureType))
}
}()

private static func browsersSupport(for feature: PrivacyFeature.FeatureType) -> [PrivacyFeature.BrowserSupport] {
Browser.allCases.map { browser in
let availability: PrivacyFeature.Availability
switch feature {
case .privateSearch:
switch browser {
case .ddg:
availability = .available
case .safari:
availability = .unavailable
}
case .blockThirdPartyTrackers:
switch browser {
case .ddg:
availability = .available
case .safari:
availability = .partiallyAvailable
}
case .blockCookiePopups:
switch browser {
case .ddg:
availability = .available
case .safari:
availability = .unavailable
}
case .blockCreepyAds:
switch browser {
case .ddg:
availability = .available
case .safari:
availability = .unavailable
}
case .eraseBrowsingData:
switch browser {
case .ddg:
availability = .available
case .safari:
availability = .unavailable
}
}

return PrivacyFeature.BrowserSupport(browser: browser, availability: availability)
}
}

}

// MARK: - Browser

extension BrowsersComparisonModel {

enum Browser: CaseIterable {
case safari
case ddg

var image: ImageResource {
switch self {
case .safari: .safariBrowserIcon
case .ddg: .ddgBrowserIcon
}
}
}

}

// MARK: - Privacy Feature

extension BrowsersComparisonModel {

struct PrivacyFeature {
let type: FeatureType
let browsersSupport: [BrowserSupport]
}

}

extension BrowsersComparisonModel.PrivacyFeature {

struct BrowserSupport {
let browser: BrowsersComparisonModel.Browser
let availability: Availability
}

enum FeatureType: CaseIterable {
case privateSearch
case blockThirdPartyTrackers
case blockCookiePopups
case blockCreepyAds
case eraseBrowsingData

var title: String {
switch self {
case .privateSearch:
UserText.DaxOnboardingExperiment.BrowsersComparison.Features.privateSearch
case .blockThirdPartyTrackers:
UserText.DaxOnboardingExperiment.BrowsersComparison.Features.trackerBlockers
case .blockCookiePopups:
UserText.DaxOnboardingExperiment.BrowsersComparison.Features.cookiePopups
case .blockCreepyAds:
UserText.DaxOnboardingExperiment.BrowsersComparison.Features.creepyAds
case .eraseBrowsingData:
UserText.DaxOnboardingExperiment.BrowsersComparison.Features.eraseBrowsingData
}
}
}

enum Availability: Identifiable {
case available
case partiallyAvailable
case unavailable

var id: Self {
self
}

var image: ImageResource {
switch self {
case .available: .checkGreen
case .partiallyAvailable: .stop
case .unavailable: .cross
}
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,7 @@ struct DaxDialogBrowsersComparisonView: View {
.foregroundColor(.primary)
.font(Font.system(size: 20, weight: .bold))

// Replace with BrowsersComparisonView
RoundedRectangle(cornerRadius: 15.0)
.foregroundColor(.blue)
.overlay(Text("Placeholder"), alignment: .center)
BrowsersComparisonChart(privacyFeatures: BrowsersComparisonModel.privacyFeatures)

Button(action: action) {
Text(UserText.DaxOnboardingExperiment.BrowsersComparison.cta)
Expand Down

0 comments on commit d3c7393

Please sign in to comment.