summaryrefslogtreecommitdiffhomepage
path: root/ios
diff options
context:
space:
mode:
authorJon Petersson <jon.petersson@kvadrat.se>2024-09-17 11:26:44 +0200
committerBug Magnet <marco.nikic@mullvad.net>2024-09-20 15:09:12 +0200
commite77fba76ce9ac9fb082fa3cf531869a8032329de (patch)
tree471a3b02fa13255f7b497cb90720d272aaa8e1f2 /ios
parent8ed275c7e5db137a8389701266b9b527b46cc1e8 (diff)
downloadmullvadvpn-e77fba76ce9ac9fb082fa3cf531869a8032329de.tar.xz
mullvadvpn-e77fba76ce9ac9fb082fa3cf531869a8032329de.zip
Add counter and 'all' switch to DNS content blockers
Diffstat (limited to 'ios')
-rw-r--r--ios/MullvadVPN/Classes/AccessbilityIdentifier.swift1
-rw-r--r--ios/MullvadVPN/View controllers/Settings/SettingsCell.swift5
-rw-r--r--ios/MullvadVPN/View controllers/VPNSettings/CustomDNSCellFactory.swift17
-rw-r--r--ios/MullvadVPN/View controllers/VPNSettings/CustomDNSDataSource.swift112
-rw-r--r--ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsViewController.swift1
-rw-r--r--ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsViewModel.swift43
6 files changed, 128 insertions, 51 deletions
diff --git a/ios/MullvadVPN/Classes/AccessbilityIdentifier.swift b/ios/MullvadVPN/Classes/AccessbilityIdentifier.swift
index c673fdfe57..b828221a88 100644
--- a/ios/MullvadVPN/Classes/AccessbilityIdentifier.swift
+++ b/ios/MullvadVPN/Classes/AccessbilityIdentifier.swift
@@ -179,6 +179,7 @@ public enum AccessibilityIdentifier: String {
case wireGuardPort
// Custom DNS
+ case blockAll
case blockAdvertising
case blockTracking
case blockMalware
diff --git a/ios/MullvadVPN/View controllers/Settings/SettingsCell.swift b/ios/MullvadVPN/View controllers/Settings/SettingsCell.swift
index 8b4db565d0..c55b5f0aa2 100644
--- a/ios/MullvadVPN/View controllers/Settings/SettingsCell.swift
+++ b/ios/MullvadVPN/View controllers/Settings/SettingsCell.swift
@@ -59,6 +59,7 @@ class SettingsCell: UITableViewCell, CustomCellDisclosureHandling {
}
}
+ private var subCellLeadingIndentation: CGFloat = 0
private let buttonWidth: CGFloat = 24
private let infoButton: UIButton = {
let button = UIButton(type: .custom)
@@ -85,6 +86,8 @@ class SettingsCell: UITableViewCell, CustomCellDisclosureHandling {
infoButton.isHidden = true
infoButton.addTarget(self, action: #selector(handleInfoButton(_:)), for: .touchUpInside)
+ subCellLeadingIndentation = contentView.layoutMargins.left + UIMetrics.TableView.cellIndentationWidth
+
titleLabel.translatesAutoresizingMaskIntoConstraints = false
titleLabel.font = UIFont.systemFont(ofSize: 17)
titleLabel.textColor = UIColor.Cell.titleTextColor
@@ -149,7 +152,7 @@ class SettingsCell: UITableViewCell, CustomCellDisclosureHandling {
}
func applySubCellStyling() {
- contentView.layoutMargins.left += UIMetrics.TableView.cellIndentationWidth
+ contentView.layoutMargins.left = subCellLeadingIndentation
backgroundView?.backgroundColor = UIColor.Cell.Background.indentationLevelOne
}
diff --git a/ios/MullvadVPN/View controllers/VPNSettings/CustomDNSCellFactory.swift b/ios/MullvadVPN/View controllers/VPNSettings/CustomDNSCellFactory.swift
index e1e6b7680f..11536c55f3 100644
--- a/ios/MullvadVPN/View controllers/VPNSettings/CustomDNSCellFactory.swift
+++ b/ios/MullvadVPN/View controllers/VPNSettings/CustomDNSCellFactory.swift
@@ -46,7 +46,7 @@ final class CustomDNSCellFactory: CellFactoryProtocol {
cell.titleLabel.text = title
cell.accessibilityIdentifier = preference.accessibilityIdentifier
cell.applySubCellStyling()
- cell.setOn(toggleSetting, animated: false)
+ cell.setOn(toggleSetting, animated: true)
cell.action = { [weak self] isOn in
self?.delegate?.didChangeState(
for: preference,
@@ -58,6 +58,21 @@ final class CustomDNSCellFactory: CellFactoryProtocol {
// swiftlint:disable:next function_body_length
func configureCell(_ cell: UITableViewCell, item: CustomDNSDataSource.Item, indexPath: IndexPath) {
switch item {
+ case .blockAll:
+ let localizedString = NSLocalizedString(
+ "BLOCK_ALL_CELL_LABEL",
+ tableName: "VPNSettings",
+ value: "All",
+ comment: ""
+ )
+
+ configure(
+ cell,
+ toggleSetting: viewModel.blockAll,
+ title: localizedString,
+ for: .blockAll
+ )
+
case .blockAdvertising:
let localizedString = NSLocalizedString(
"BLOCK_ADS_CELL_LABEL",
diff --git a/ios/MullvadVPN/View controllers/VPNSettings/CustomDNSDataSource.swift b/ios/MullvadVPN/View controllers/VPNSettings/CustomDNSDataSource.swift
index a2bce16dac..d6ea9cbc60 100644
--- a/ios/MullvadVPN/View controllers/VPNSettings/CustomDNSDataSource.swift
+++ b/ios/MullvadVPN/View controllers/VPNSettings/CustomDNSDataSource.swift
@@ -49,6 +49,7 @@ final class CustomDNSDataSource: UITableViewDiffableDataSource<
}
enum Item: Hashable {
+ case blockAll
case blockAdvertising
case blockTracking
case blockMalware
@@ -61,11 +62,21 @@ final class CustomDNSDataSource: UITableViewDiffableDataSource<
case dnsServerInfo
static var contentBlockers: [Item] {
- [.blockAdvertising, .blockTracking, .blockMalware, .blockGambling, .blockAdultContent, .blockSocialMedia]
+ [
+ .blockAll,
+ .blockAdvertising,
+ .blockTracking,
+ .blockMalware,
+ .blockGambling,
+ .blockAdultContent,
+ .blockSocialMedia,
+ ]
}
var accessibilityIdentifier: AccessibilityIdentifier {
switch self {
+ case .blockAll:
+ return .blockAll
case .blockAdvertising:
return .blockAdvertising
case .blockTracking:
@@ -403,85 +414,74 @@ final class CustomDNSDataSource: UITableViewDiffableDataSource<
}
}
- private func setBlockAdvertising(_ isEnabled: Bool) {
+ private func setBlockAll(_ isEnabled: Bool) {
let oldViewModel = viewModel
+ viewModel.setBlockAll(isEnabled)
+ reloadBlockerData(isEnabled, oldViewModel: oldViewModel)
- viewModel.setBlockAdvertising(isEnabled)
-
- if oldViewModel.customDNSPrecondition != viewModel.customDNSPrecondition {
- reloadDnsServerInfo()
+ [
+ .blockAdvertising,
+ .blockTracking,
+ .blockMalware,
+ .blockAdultContent,
+ .blockGambling,
+ .blockSocialMedia,
+ ].forEach { item in
+ reload(item: item)
}
+ }
- if !isEditing {
- delegate?.didChangeViewModel(viewModel)
- }
+ private func setBlockAdvertising(_ isEnabled: Bool) {
+ let oldViewModel = viewModel
+ viewModel.setBlockAdvertising(isEnabled)
+ reloadBlockerData(isEnabled, oldViewModel: oldViewModel)
}
private func setBlockTracking(_ isEnabled: Bool) {
let oldViewModel = viewModel
-
viewModel.setBlockTracking(isEnabled)
-
- if oldViewModel.customDNSPrecondition != viewModel.customDNSPrecondition {
- reloadDnsServerInfo()
- }
-
- if !isEditing {
- delegate?.didChangeViewModel(viewModel)
- }
+ reloadBlockerData(isEnabled, oldViewModel: oldViewModel)
}
private func setBlockMalware(_ isEnabled: Bool) {
let oldViewModel = viewModel
-
viewModel.setBlockMalware(isEnabled)
-
- if oldViewModel.customDNSPrecondition != viewModel.customDNSPrecondition {
- reloadDnsServerInfo()
- }
-
- if !isEditing {
- delegate?.didChangeViewModel(viewModel)
- }
+ reloadBlockerData(isEnabled, oldViewModel: oldViewModel)
}
private func setBlockAdultContent(_ isEnabled: Bool) {
let oldViewModel = viewModel
-
viewModel.setBlockAdultContent(isEnabled)
-
- if oldViewModel.customDNSPrecondition != viewModel.customDNSPrecondition {
- reloadDnsServerInfo()
- }
-
- if !isEditing {
- delegate?.didChangeViewModel(viewModel)
- }
+ reloadBlockerData(isEnabled, oldViewModel: oldViewModel)
}
private func setBlockGambling(_ isEnabled: Bool) {
let oldViewModel = viewModel
-
viewModel.setBlockGambling(isEnabled)
-
- if oldViewModel.customDNSPrecondition != viewModel.customDNSPrecondition {
- reloadDnsServerInfo()
- }
-
- if !isEditing {
- delegate?.didChangeViewModel(viewModel)
- }
+ reloadBlockerData(isEnabled, oldViewModel: oldViewModel)
}
private func setBlockSocialMedia(_ isEnabled: Bool) {
let oldViewModel = viewModel
-
viewModel.setBlockSocialMedia(isEnabled)
+ reloadBlockerData(isEnabled, oldViewModel: oldViewModel)
+ }
+ private func reloadBlockerData(_ isEnabled: Bool, oldViewModel: VPNSettingsViewModel) {
if oldViewModel.customDNSPrecondition != viewModel.customDNSPrecondition {
reloadDnsServerInfo()
}
+ if !isEnabled || viewModel.allBlockersEnabled {
+ reload(item: .blockAll)
+ }
+
+ if
+ let index = snapshot().sectionIdentifiers.firstIndex(of: .contentBlockers),
+ let headerView = tableView?.headerView(forSection: index) as? SettingsHeaderView {
+ configureContentBlockersHeader(headerView)
+ }
+
if !isEditing {
delegate?.didChangeViewModel(viewModel)
}
@@ -588,8 +588,23 @@ final class CustomDNSDataSource: UITableViewDiffableDataSource<
comment: ""
)
- header.titleLabel.text = title
+ let enabledBlockersCount = viewModel.enabledBlockersCount
+ let attributedTitle = NSMutableAttributedString(string: title)
+ let blockerCountText = NSAttributedString(string: " (\(enabledBlockersCount))", attributes: [
+ .foregroundColor: UIColor.primaryTextColor.withAlphaComponent(0.6),
+ ])
+
+ if enabledBlockersCount > 0 {
+ attributedTitle.append(blockerCountText)
+ }
+
+ UIView.transition(with: header.titleLabel, duration: 0.2, options: .transitionCrossDissolve) {
+ header.titleLabel.attributedText = attributedTitle
+ }
+ header.titleLabel.sizeToFit()
+
header.accessibilityCustomActionName = title
+ header.accessibilityValue = "\(enabledBlockersCount)"
header.accessibilityIdentifier = .dnsContentBlockersHeaderView
header.infoButtonHandler = { [weak self] in
@@ -618,6 +633,9 @@ final class CustomDNSDataSource: UITableViewDiffableDataSource<
extension CustomDNSDataSource: CustomDNSCellEventHandler {
func didChangeState(for preference: Item, isOn: Bool) {
switch preference {
+ case .blockAll:
+ setBlockAll(isOn)
+
case .blockAdvertising:
setBlockAdvertising(isOn)
diff --git a/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsViewController.swift b/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsViewController.swift
index 32facd0eca..5ce99f689f 100644
--- a/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsViewController.swift
+++ b/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsViewController.swift
@@ -128,6 +128,7 @@ extension VPNSettingsViewController: VPNSettingsDataSourceDelegate {
interactor.evaluateDaitaSettingsCompatibility(settings)
}
+ // swiftlint:disable:next function_body_length
func showPrompt(
for item: VPNSettingsPromptAlertItem,
onSave: @escaping () -> Void,
diff --git a/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsViewModel.swift b/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsViewModel.swift
index 94fde408b6..1c125e19fd 100644
--- a/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsViewModel.swift
+++ b/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsViewModel.swift
@@ -84,6 +84,7 @@ struct DNSServerEntry: Equatable, Hashable {
}
struct VPNSettingsViewModel: Equatable {
+ private(set) var blockAll: Bool
private(set) var blockAdvertising: Bool
private(set) var blockTracking: Bool
private(set) var blockMalware: Bool
@@ -104,39 +105,69 @@ struct VPNSettingsViewModel: Equatable {
static let defaultWireGuardPorts: [UInt16] = [51820, 53]
+ var enabledBlockersCount: Int {
+ [
+ blockAdvertising,
+ blockTracking,
+ blockMalware,
+ blockAdultContent,
+ blockGambling,
+ blockSocialMedia,
+ ].filter { $0 }.count
+ }
+
+ var allBlockersEnabled: Bool {
+ enabledBlockersCount == CustomDNSDataSource.Item.contentBlockers.filter { $0 != .blockAll }.count
+ }
+
+ mutating func setBlockAll(_ newValue: Bool) {
+ blockAll = newValue
+ blockAdvertising = newValue
+ blockTracking = newValue
+ blockMalware = newValue
+ blockAdultContent = newValue
+ blockGambling = newValue
+ blockSocialMedia = newValue
+ enableCustomDNS = false
+ }
+
mutating func setBlockAdvertising(_ newValue: Bool) {
blockAdvertising = newValue
+ blockAll = allBlockersEnabled
enableCustomDNS = false
}
mutating func setBlockTracking(_ newValue: Bool) {
blockTracking = newValue
+ blockAll = allBlockersEnabled
enableCustomDNS = false
}
mutating func setBlockMalware(_ newValue: Bool) {
blockMalware = newValue
+ blockAll = allBlockersEnabled
enableCustomDNS = false
}
mutating func setBlockAdultContent(_ newValue: Bool) {
blockAdultContent = newValue
+ blockAll = allBlockersEnabled
enableCustomDNS = false
}
mutating func setBlockGambling(_ newValue: Bool) {
blockGambling = newValue
+ blockAll = allBlockersEnabled
enableCustomDNS = false
}
mutating func setBlockSocialMedia(_ newValue: Bool) {
blockSocialMedia = newValue
+ blockAll = allBlockersEnabled
enableCustomDNS = false
}
mutating func setEnableCustomDNS(_ newValue: Bool) {
- blockTracking = false
- blockAdvertising = false
enableCustomDNS = newValue
}
@@ -195,12 +226,20 @@ struct VPNSettingsViewModel: Equatable {
init(from tunnelSettings: LatestTunnelSettings = LatestTunnelSettings()) {
let dnsSettings = tunnelSettings.dnsSettings
+
blockAdvertising = dnsSettings.blockingOptions.contains(.blockAdvertising)
blockTracking = dnsSettings.blockingOptions.contains(.blockTracking)
blockMalware = dnsSettings.blockingOptions.contains(.blockMalware)
blockAdultContent = dnsSettings.blockingOptions.contains(.blockAdultContent)
blockGambling = dnsSettings.blockingOptions.contains(.blockGambling)
blockSocialMedia = dnsSettings.blockingOptions.contains(.blockSocialMedia)
+ blockAll = blockAdvertising
+ && blockTracking
+ && blockMalware
+ && blockAdultContent
+ && blockGambling
+ && blockSocialMedia
+
enableCustomDNS = dnsSettings.enableCustomDNS
customDNSDomains = dnsSettings.customDNSDomains.map { ipAddress in
DNSServerEntry(identifier: UUID(), address: "\(ipAddress)")