diff --git a/ios/CHANGELOG.md b/ios/CHANGELOG.md index e496d5b23cda..c24541319636 100644 --- a/ios/CHANGELOG.md +++ b/ios/CHANGELOG.md @@ -23,6 +23,9 @@ Line wrap the file at 100 chars. Th ## [Unreleased] +### Added +- Add option to block gambling and adult content. + ### Fixed - Improve random port distribution. Should be less biased towards port 53. diff --git a/ios/MullvadVPN/DNSSettings.swift b/ios/MullvadVPN/DNSSettings.swift index bee5b1d7f148..f841b6cb1b7f 100644 --- a/ios/MullvadVPN/DNSSettings.swift +++ b/ios/MullvadVPN/DNSSettings.swift @@ -18,6 +18,8 @@ struct DNSBlockingOptions: OptionSet, Codable { static let blockAdvertising = DNSBlockingOptions(rawValue: 1 << 0) static let blockTracking = DNSBlockingOptions(rawValue: 1 << 1) static let blockMalware = DNSBlockingOptions(rawValue: 1 << 2) + static let blockAdultContent = DNSBlockingOptions(rawValue: 1 << 3) + static let blockGambling = DNSBlockingOptions(rawValue: 1 << 4) var serverAddress: IPv4Address? { if isEmpty { diff --git a/ios/MullvadVPN/PreferencesDataSource.swift b/ios/MullvadVPN/PreferencesDataSource.swift index 63482db47f2e..46c0dc54702c 100644 --- a/ios/MullvadVPN/PreferencesDataSource.swift +++ b/ios/MullvadVPN/PreferencesDataSource.swift @@ -49,6 +49,8 @@ class PreferencesDataSource: NSObject, UITableViewDataSource, UITableViewDelegat case blockAdvertising case blockTracking case blockMalware + case blockAdultContent + case blockGambling case useCustomDNS case addDNSServer case dnsServer(_ uniqueID: UUID) @@ -309,7 +311,7 @@ class PreferencesDataSource: NSObject, UITableViewDataSource, UITableViewDelegat private func updateSnapshot() { var newSnapshot = DataSourceSnapshot() newSnapshot.appendSections([.mullvadDNS, .customDNS]) - newSnapshot.appendItems([.blockAdvertising, .blockTracking, .blockMalware], in: .mullvadDNS) + newSnapshot.appendItems([.blockAdvertising, .blockTracking, .blockMalware, .blockAdultContent, .blockGambling], in: .mullvadDNS) newSnapshot.appendItems([.useCustomDNS], in: .customDNS) let dnsServerItems = viewModel.customDNSDomains.map { entry in @@ -377,6 +379,40 @@ class PreferencesDataSource: NSObject, UITableViewDataSource, UITableViewDelegat return cell + case .blockAdultContent: + let cell = tableView.dequeueReusableCell(withIdentifier: CellReuseIdentifiers.settingSwitch.rawValue, for: indexPath) as! SettingsSwitchCell + + cell.titleLabel.text = NSLocalizedString( + "BLOCK_ADULT_CELL_LABEL", + tableName: "Preferences", + value: "Block adult content", + comment: "" + ) + cell.accessibilityHint = nil + cell.setOn(viewModel.blockAdultContent, animated: false) + cell.action = { [weak self] isOn in + self?.setBlockAdultContent(isOn) + } + + return cell + + case .blockGambling: + let cell = tableView.dequeueReusableCell(withIdentifier: CellReuseIdentifiers.settingSwitch.rawValue, for: indexPath) as! SettingsSwitchCell + + cell.titleLabel.text = NSLocalizedString( + "BLOCK_GAMBLING_CELL_LABEL", + tableName: "Preferences", + value: "Block gambling", + comment: "" + ) + cell.accessibilityHint = nil + cell.setOn(viewModel.blockGambling, animated: false) + cell.action = { [weak self] isOn in + self?.setBlockGambling(isOn) + } + + return cell + case .useCustomDNS: let cell = tableView.dequeueReusableCell(withIdentifier: CellReuseIdentifiers.settingSwitch.rawValue, for: indexPath) as! SettingsSwitchCell @@ -476,6 +512,34 @@ class PreferencesDataSource: NSObject, UITableViewDataSource, UITableViewDelegat } } + private func setBlockAdultContent(_ isEnabled: Bool) { + let oldViewModel = viewModel + + viewModel.setBlockAdultContent(isEnabled) + + if oldViewModel.customDNSPrecondition != viewModel.customDNSPrecondition { + reloadCustomDNSFooter() + } + + if !isEditing { + delegate?.preferencesDataSource(self, didChangeViewModel: viewModel) + } + } + + private func setBlockGambling(_ isEnabled: Bool) { + let oldViewModel = viewModel + + viewModel.setBlockGambling(isEnabled) + + if oldViewModel.customDNSPrecondition != viewModel.customDNSPrecondition { + reloadCustomDNSFooter() + } + + if !isEditing { + delegate?.preferencesDataSource(self, didChangeViewModel: viewModel) + } + } + private func setEnableCustomDNS(_ isEnabled: Bool) { viewModel.setEnableCustomDNS(isEnabled) diff --git a/ios/MullvadVPN/PreferencesViewModel.swift b/ios/MullvadVPN/PreferencesViewModel.swift index e303e5bae41c..123331f4a86d 100644 --- a/ios/MullvadVPN/PreferencesViewModel.swift +++ b/ios/MullvadVPN/PreferencesViewModel.swift @@ -35,13 +35,13 @@ enum CustomDNSPrecondition { case .emptyDNSDomains: if isEditing { return NSAttributedString( - markdownString: NSLocalizedString( + string: NSLocalizedString( "CUSTOM_DNS_NO_DNS_ENTRIES_EDITING_ON_FOOTNOTE", tableName: "Preferences", value: "To enable this setting, add at least one server.", comment: "Foot note displayed if there are no DNS entries and table view is in editing mode." ), - font: preferredFont + attributes: [.font: preferredFont] ) } else { return NSAttributedString( @@ -57,13 +57,13 @@ enum CustomDNSPrecondition { case .conflictsWithOtherSettings: return NSAttributedString( - markdownString: NSLocalizedString( - "CUSTOM_DNS_DISABLE_ADTRACKER_BLOCKING_FOOTNOTE", + string: NSLocalizedString( + "CUSTOM_DNS_DISABLE_CONTENT_BLOCKERS_FOOTNOTE", tableName: "Preferences", - value: "Disable **Block ads**, **Block trackers** and **Block malware** to activate this setting.", - comment: "Foot note displayed when custom DNS cannot be enabled, because ad/tracker/malware blockers features should be disabled first." + value: "Disable all content blockers (under Preferences) to activate this setting.", + comment: "Foot note displayed when custom DNS cannot be enabled, because content blockers should be disabled first." ), - font: preferredFont + attributes: [.font: preferredFont] ) } } @@ -78,6 +78,8 @@ struct PreferencesViewModel: Equatable { private(set) var blockAdvertising: Bool private(set) var blockTracking: Bool private(set) var blockMalware: Bool + private(set) var blockAdultContent: Bool + private(set) var blockGambling: Bool private(set) var enableCustomDNS: Bool var customDNSDomains: [DNSServerEntry] @@ -96,6 +98,16 @@ struct PreferencesViewModel: Equatable { enableCustomDNS = false } + mutating func setBlockAdultContent(_ newValue: Bool) { + blockAdultContent = newValue + enableCustomDNS = false + } + + mutating func setBlockGambling(_ newValue: Bool) { + blockGambling = newValue + enableCustomDNS = false + } + mutating func setEnableCustomDNS(_ newValue: Bool) { blockTracking = false blockAdvertising = false @@ -104,7 +116,7 @@ struct PreferencesViewModel: Equatable { /// Precondition for enabling Custom DNS. var customDNSPrecondition: CustomDNSPrecondition { - if blockAdvertising || blockTracking || blockMalware { + if blockAdvertising || blockTracking || blockMalware || blockAdultContent || blockGambling { return .conflictsWithOtherSettings } else { let hasValidDNSDomains = customDNSDomains.contains { entry in @@ -128,6 +140,8 @@ struct PreferencesViewModel: Equatable { 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) enableCustomDNS = dnsSettings.enableCustomDNS customDNSDomains = dnsSettings.customDNSDomains.map { ipAddress in return DNSServerEntry(identifier: UUID(), address: "\(ipAddress)") @@ -141,6 +155,8 @@ struct PreferencesViewModel: Equatable { mergedViewModel.blockAdvertising = other.blockAdvertising mergedViewModel.blockTracking = other.blockTracking mergedViewModel.blockMalware = other.blockMalware + mergedViewModel.blockAdultContent = other.blockAdultContent + mergedViewModel.blockGambling = other.blockGambling mergedViewModel.enableCustomDNS = other.enableCustomDNS var oldDNSDomains = customDNSDomains @@ -218,6 +234,14 @@ struct PreferencesViewModel: Equatable { blockingOptions.insert(.blockMalware) } + if blockAdultContent { + blockingOptions.insert(.blockAdultContent) + } + + if blockGambling { + blockingOptions.insert(.blockGambling) + } + var dnsSettings = DNSSettings() dnsSettings.blockingOptions = blockingOptions dnsSettings.enableCustomDNS = enableCustomDNS