Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add UI for DAITA in VPN settings #6667

Merged
merged 1 commit into from
Aug 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions ios/MullvadVPN.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -909,6 +909,7 @@
F08B6B7D2C528C6300D0A121 /* PostQuantumKeyExchangingPipeline.swift in Sources */ = {isa = PBXBuildFile; fileRef = F05919762C453FAF00C301F3 /* PostQuantumKeyExchangingPipeline.swift */; };
F08B6B7E2C528C6300D0A121 /* MultiHopPostQuantumKeyExchanging.swift in Sources */ = {isa = PBXBuildFile; fileRef = F059197C2C454C9200C301F3 /* MultiHopPostQuantumKeyExchanging.swift */; };
F08B6B822C52931600D0A121 /* WireGuardKitTypes in Frameworks */ = {isa = PBXBuildFile; productRef = F08B6B812C52931600D0A121 /* WireGuardKitTypes */; };
F09084682C6E88ED001CD36E /* DaitaPromptAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = F09084672C6E88ED001CD36E /* DaitaPromptAlert.swift */; };
F09A297B2A9F8A9B00EA3B6F /* LogoutDialogueView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F09A29782A9F8A9B00EA3B6F /* LogoutDialogueView.swift */; };
F09A297C2A9F8A9B00EA3B6F /* VoucherTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = F09A29792A9F8A9B00EA3B6F /* VoucherTextField.swift */; };
F09A297D2A9F8A9B00EA3B6F /* RedeemVoucherContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F09A297A2A9F8A9B00EA3B6F /* RedeemVoucherContentView.swift */; };
Expand Down Expand Up @@ -2106,6 +2107,7 @@
F07BF2572A26112D00042943 /* InputTextFormatterTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InputTextFormatterTests.swift; sourceTree = "<group>"; };
F07BF2612A26279100042943 /* RedeemVoucherOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RedeemVoucherOperation.swift; sourceTree = "<group>"; };
F07CFF1F29F2720E008C0343 /* RegisteredDeviceInAppNotificationProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RegisteredDeviceInAppNotificationProvider.swift; sourceTree = "<group>"; };
F09084672C6E88ED001CD36E /* DaitaPromptAlert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DaitaPromptAlert.swift; sourceTree = "<group>"; };
F09A29782A9F8A9B00EA3B6F /* LogoutDialogueView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LogoutDialogueView.swift; sourceTree = "<group>"; };
F09A29792A9F8A9B00EA3B6F /* VoucherTextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VoucherTextField.swift; sourceTree = "<group>"; };
F09A297A2A9F8A9B00EA3B6F /* RedeemVoucherContentView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RedeemVoucherContentView.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -3932,6 +3934,7 @@
85021CAD2BDBC4290098B400 /* AppLogsPage.swift */,
8587A05C2B84D43100152938 /* ChangeLogAlert.swift */,
A9BFB0002BD00B7F00F2BCA1 /* CustomListPage.swift */,
F09084672C6E88ED001CD36E /* DaitaPromptAlert.swift */,
85A42B872BB44D31007BABF7 /* DeviceManagementPage.swift */,
852A26452BA9C9CB006EB9C8 /* DNSSettingsPage.swift */,
8585CBE22BC684180015B6A4 /* EditAccessMethodPage.swift */,
Expand Down Expand Up @@ -6067,6 +6070,7 @@
85EC620C2B838D10005AFFB5 /* MullvadAPIWrapper.swift in Sources */,
A9DF789D2B7D1E8B0094E4AD /* LoggedInWithTimeUITestCase.swift in Sources */,
85D2B0B12B6BD32400DF9DA7 /* BaseUITestCase.swift in Sources */,
F09084682C6E88ED001CD36E /* DaitaPromptAlert.swift in Sources */,
8529693C2B4F0257007EAD4C /* Alert.swift in Sources */,
8542F7532BCFBD050035C042 /* SelectLocationFilterPage.swift in Sources */,
850201DD2B503D8C00EF8C96 /* SelectLocationPage.swift in Sources */,
Expand Down
9 changes: 6 additions & 3 deletions ios/MullvadVPN/Classes/AccessbilityIdentifier.swift
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,8 @@ public enum AccessibilityIdentifier: String {
case cityLocationCell
case relayLocationCell
case customListLocationCell
case multihopConfirmAlertBackButton
case multihopConfirmAlertEnableButton
case daitaConfirmAlertBackButton
case daitaConfirmAlertEnableButton

// Labels
case accountPageDeviceNameLabel
Expand Down Expand Up @@ -190,14 +190,17 @@ public enum AccessibilityIdentifier: String {
case dnsServer
case dnsServerInfo

// DAITA
case daitaSwitch
case daitaPromptAlert

// Quantum resistance
case quantumResistanceAutomatic
case quantumResistanceOff
case quantumResistanceOn

// Multihop
case multihopSwitch
case multihopPromptAlert

// Error
case unknown
Expand Down
3 changes: 3 additions & 0 deletions ios/MullvadVPN/View controllers/Login/LoginContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ class LoginContentView: UIView {
let statusActivityView: StatusActivityView = {
let statusActivityView = StatusActivityView(state: .hidden)
statusActivityView.translatesAutoresizingMaskIntoConstraints = false
statusActivityView.clipsToBounds = true
return statusActivityView
}()

Expand Down Expand Up @@ -151,6 +152,8 @@ class LoginContentView: UIView {
createAccountButton.pinEdges(.all().excluding(.top), to: footerContainer.layoutMarginsGuide)

statusActivityView.centerXAnchor.constraint(equalTo: contentContainer.centerXAnchor)
statusActivityView.widthAnchor.constraint(equalToConstant: 60.0)
statusActivityView.heightAnchor.constraint(equalTo: statusActivityView.widthAnchor, multiplier: 1.0)

formContainer.topAnchor.constraint(equalTo: statusActivityView.bottomAnchor, constant: 30)
formContainer.centerYAnchor.constraint(equalTo: contentContainer.centerYAnchor, constant: -20)
Expand Down
11 changes: 0 additions & 11 deletions ios/MullvadVPN/View controllers/Login/LoginViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -256,17 +256,6 @@ class LoginViewController: UIViewController, RootContainment {

private func updateStatusIcon() {
contentView.statusActivityView.state = loginState.statusActivityState

switch loginState {
case .authenticating:
contentView.statusActivityView.accessibilityIdentifier = .loginStatusIconAuthenticating
case .failure:
contentView.statusActivityView.accessibilityIdentifier = .loginStatusIconFailure
case .success:
contentView.statusActivityView.accessibilityIdentifier = .loginStatusIconSuccess
default:
break
}
}

private func beginLogin(_ action: LoginAction) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ protocol VPNSettingsCellEventHandler {
func selectCustomPortEntry(_ port: UInt16) -> Bool
func selectObfuscationState(_ state: WireGuardObfuscationState)
func switchMultihop(_ state: MultihopState)
func switchDaitaState(_ settings: DAITASettings)
}

final class VPNSettingsCellFactory: CellFactoryProtocol {
Expand Down Expand Up @@ -170,7 +171,6 @@ final class VPNSettingsCellFactory: CellFactoryProtocol {
)
cell.accessibilityIdentifier = "\(item.accessibilityIdentifier.rawValue)\(portString)"
cell.applySubCellStyling()

case .quantumResistanceAutomatic:
guard let cell = cell as? SelectableSettingsCell else { return }

Expand Down Expand Up @@ -206,13 +206,34 @@ final class VPNSettingsCellFactory: CellFactoryProtocol {
cell.accessibilityIdentifier = item.accessibilityIdentifier
cell.applySubCellStyling()

case .multihop:
case .daitaSwitch:
guard let cell = cell as? SettingsSwitchCell else { return }

cell.titleLabel.text = NSLocalizedString(
"DAITA_LABEL",
tableName: "VPNSettings",
value: "DAITA",
comment: ""
)
cell.accessibilityIdentifier = item.accessibilityIdentifier
cell.setOn(viewModel.daitaSettings.state.isEnabled, animated: false)

cell.infoButtonHandler = { [weak self] in
self?.delegate?.showInfo(for: .daita)
}

cell.action = { [weak self] isEnabled in
let state: DAITAState = isEnabled ? .on : .off
self?.delegate?.switchDaitaState(DAITASettings(state: state))
}

case .multihopSwitch:
guard let cell = cell as? SettingsSwitchCell else { return }

cell.titleLabel.text = NSLocalizedString(
"MULTIHOP_LABEL",
tableName: "VPNSettings",
value: "Enable multihop",
value: "Multihop",
comment: ""
)
cell.accessibilityIdentifier = item.accessibilityIdentifier
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ final class VPNSettingsDataSource: UITableViewDiffableDataSource<
case wireGuardObfuscationPort
case quantumResistance
case multihop
case daita
var reusableViewClass: AnyClass {
switch self {
case .dnsSettings:
Expand All @@ -42,12 +43,14 @@ final class VPNSettingsDataSource: UITableViewDiffableDataSource<
return SelectableSettingsCell.self
case .multihop:
return SettingsSwitchCell.self
case .daita:
return SettingsSwitchCell.self
}
}
}

private enum HeaderFooterReuseIdentifiers: String, CaseIterable {
case wireGuardPortHeader
case settingsHeaderView

var reusableViewClass: AnyClass {
return SettingsHeaderView.self
Expand All @@ -61,7 +64,7 @@ final class VPNSettingsDataSource: UITableViewDiffableDataSource<
case wireGuardObfuscation
case wireGuardObfuscationPort
case quantumResistance
case multiHop
case privacyAndSecurity
}

enum Item: Hashable {
Expand All @@ -76,7 +79,8 @@ final class VPNSettingsDataSource: UITableViewDiffableDataSource<
case quantumResistanceAutomatic
case quantumResistanceOn
case quantumResistanceOff
case multihop
case multihopSwitch
case daitaSwitch

static var wireGuardPorts: [Item] {
let defaultPorts = VPNSettingsViewModel.defaultWireGuardPorts.map {
Expand Down Expand Up @@ -121,7 +125,9 @@ final class VPNSettingsDataSource: UITableViewDiffableDataSource<
return .quantumResistanceOn
case .quantumResistanceOff:
return .quantumResistanceOff
case .multihop:
case .daitaSwitch:
return .daitaSwitch
case .multihopSwitch:
return .multihopSwitch
}
}
Expand All @@ -142,8 +148,10 @@ final class VPNSettingsDataSource: UITableViewDiffableDataSource<
return .wireGuardObfuscationPort
case .quantumResistanceAutomatic, .quantumResistanceOn, .quantumResistanceOff:
return .quantumResistance
case .multihop:
case .multihopSwitch:
return .multihop
case .daitaSwitch:
return .daita
}
}
}
Expand Down Expand Up @@ -319,8 +327,7 @@ final class VPNSettingsDataSource: UITableViewDiffableDataSource<

guard let view = tableView
.dequeueReusableHeaderFooterView(
withIdentifier: HeaderFooterReuseIdentifiers.wireGuardPortHeader
.rawValue
withIdentifier: HeaderFooterReuseIdentifiers.settingsHeaderView.rawValue
) as? SettingsHeaderView else { return nil }

switch sectionIdentifier {
Expand Down Expand Up @@ -353,8 +360,8 @@ final class VPNSettingsDataSource: UITableViewDiffableDataSource<
let sectionIdentifier = snapshot().sectionIdentifiers[section]

switch sectionIdentifier {
case .dnsSettings, .ipOverrides, .multiHop:
return 0
case .dnsSettings, .ipOverrides, .privacyAndSecurity:
return .leastNonzeroMagnitude
default:
return tableView.estimatedRowHeight
}
Expand All @@ -375,7 +382,7 @@ final class VPNSettingsDataSource: UITableViewDiffableDataSource<
let sectionIdentifier = snapshot().sectionIdentifiers[indexPath.section]

return switch sectionIdentifier {
case .multiHop: false
case .privacyAndSecurity: false
default: true
}
}
Expand All @@ -392,7 +399,7 @@ final class VPNSettingsDataSource: UITableViewDiffableDataSource<

tableView?.register(
SettingsHeaderView.self,
forHeaderFooterViewReuseIdentifier: HeaderFooterReuseIdentifiers.wireGuardPortHeader.rawValue
forHeaderFooterViewReuseIdentifier: HeaderFooterReuseIdentifiers.settingsHeaderView.rawValue
)
}

Expand All @@ -412,7 +419,11 @@ final class VPNSettingsDataSource: UITableViewDiffableDataSource<
snapshot.appendItems([.dnsSettings], toSection: .dnsSettings)
snapshot.appendItems([.ipOverrides], toSection: .ipOverrides)

snapshot.appendItems([.multihop], toSection: .multiHop)
#if DEBUG
snapshot.appendItems([.daitaSwitch, .multihopSwitch], toSection: .privacyAndSecurity)
#else
snapshot.appendItems([.multihopSwitch], toSection: .privacyAndSecurity)
#endif

applySnapshot(snapshot, animated: animated, completion: completion)
}
Expand Down Expand Up @@ -620,6 +631,22 @@ extension VPNSettingsDataSource: VPNSettingsCellEventHandler {
viewModel.setMultihop(state)
delegate?.didChangeViewModel(viewModel)
}

func switchDaitaState(_ settings: DAITASettings) {
if settings.state.isEnabled {
delegate?.showPrompt(for: .daita) { [weak self] in
guard let self else { return }
viewModel.setDAITASettings(settings)
delegate?.didChangeViewModel(viewModel)
} onDiscard: { [weak self] in
guard let self else { return }
tableView?.reloadData()
}
} else {
viewModel.setDAITASettings(settings)
delegate?.didChangeViewModel(viewModel)
}
}
}

// swiftlint:disable:this file_length
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,5 @@ protocol VPNSettingsDataSourceDelegate: AnyObject {
func showDNSSettings()
func showIPOverrides()
func didSelectWireGuardPort(_ port: UInt16?)
func showPrompt(for: VPNSettingsPromptAlertItem, onSave: @escaping () -> Void, onDiscard: @escaping () -> Void)
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,9 @@ enum VPNSettingsInfoButtonItem {
case wireGuardObfuscationPort
case quantumResistance
case multihop
case daita
}

enum VPNSettingsPromptAlertItem {
case daita
}
Loading
Loading