Skip to content
Open
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
51 changes: 35 additions & 16 deletions ios/Assets/Localizable.xcstrings
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,18 @@
}
}
},
"%@ “%@” requires restarting the VPN connection, which will disconnect you and briefly expose your traffic. To prevent this, manually enable Airplane Mode and turn off Wi-Fi before continuing." : {
"comment" : "A warning message that appears when a user is about to perform an action that requires restarting the VPN connection. The argument is the name of the action, and the second argument is the name of the feature.",
"isCommentAutoGenerated" : true,
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "new",
"value" : "%1$@ “%2$@” requires restarting the VPN connection, which will disconnect you and briefly expose your traffic. To prevent this, manually enable Airplane Mode and turn off Wi-Fi before continuing."
}
}
}
},
"%@ (%@) hides patterns in your encrypted VPN traffic." : {
"localizations" : {
"da" : {
Expand Down Expand Up @@ -502,18 +514,6 @@
}
}
},
"%@ %@ requires restarting the VPN connection, which will disconnect you and briefly expose your traffic. To prevent this, manually enable Airplane Mode and turn off Wi-Fi before continuing." : {
"comment" : "A warning message that appears when a user is about to perform an action that requires restarting the VPN connection. The argument is the action being performed, and the second argument is the feature that requires restarting the VPN connection.",
"isCommentAutoGenerated" : true,
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "new",
"value" : "%1$@ %2$@ requires restarting the VPN connection, which will disconnect you and briefly expose your traffic. To prevent this, manually enable Airplane Mode and turn off Wi-Fi before continuing."
}
}
}
},
"%@ cannot be empty" : {
"comment" : "A description of a validation error for a specific field. The argument is the name of the field.",
"isCommentAutoGenerated" : true
Expand Down Expand Up @@ -6453,8 +6453,17 @@
},
"Attention: This will ignore filter settings for the entry server that is being automatically selected." : {

},
"Attention: This will ignore filter settings for the server that is being used as an entry point" : {
"comment" : "Additional information about the automatic location picker.",
"isCommentAutoGenerated" : true
},
"Attention: toggling “%@” requires restarting the VPN connection." : {
"comment" : "A note about a feature that requires restarting the VPN connection when toggled. The argument is the string “Local network sharing”.",
"isCommentAutoGenerated" : true
},
"Attention: toggling “Local network sharing” requires restarting the VPN connection." : {
"extractionState" : "stale",
"localizations" : {
"da" : {
"stringUnit" : {
Expand Down Expand Up @@ -19952,8 +19961,13 @@
}
}
},
"Due to iOS limitations, this is not enabled by default. Our other apps tunnel all traffic via the VPN by default." : {
"comment" : "Explanation of why the \"Include all networks\" feature is not enabled by default in the Mullvad VPN app.",
"isCommentAutoGenerated" : true
},
"Due to iOS limitations, this not enabled by default. Our other apps tunnel all traffic via the VPN by default." : {
"comment" : "Explanation of why the \"Use Include All Networks\" feature is not enabled by default in the IAN settings view.",
"extractionState" : "stale",
"isCommentAutoGenerated" : true,
"localizations" : {
"da" : {
Expand Down Expand Up @@ -30019,6 +30033,10 @@
}
}
},
"Information" : {
"comment" : "A button that provides more information about an item.",
"isCommentAutoGenerated" : true
},
"Install the latest app version to stay up to date." : {
"comment" : "Body text of an in-app notification that reminds the user to update their app to the latest version.",
"isCommentAutoGenerated" : true,
Expand Down Expand Up @@ -36409,9 +36427,6 @@
}
}
}
},
"Mode" : {

},
"Montreal" : {
"localizations" : {
Expand Down Expand Up @@ -43633,8 +43648,12 @@
}
}
},
"Picks a suitable location based on your exit location, this is based on a number of different factors such as distance, provider, and server load." : {
"comment" : "Description of an automatic location in the \"What is an automatic location?\" alert.",
"isCommentAutoGenerated" : true
},
"Please enable notifications to ensure that you do not lose network connectivity. Would you like to continue anyways?" : {
"comment" : "Message in a system alert that asks the user if they want to continue despite having disabled notifications.",
"comment" : "Message in a system alert that asks the user if they want to continue despite having disabled notifications."
},
"Please enter a valid IPv4 or IPv6 address" : {
"comment" : "Error message displayed when the user inputs an invalid IP address.",
Expand Down
24 changes: 12 additions & 12 deletions ios/MullvadREST/Relay/MultihopDecisionFlow.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ protocol MultihopDecisionFlow {
func pick(
entryCandidates: [RelayCandidate],
exitCandidates: [RelayCandidate],
daitaAutomaticRouting: Bool
selectNearbyLocation: Bool
) throws -> SelectedRelays
}

Expand All @@ -31,7 +31,7 @@ struct OneToOne: MultihopDecisionFlow {
func pick(
entryCandidates: [RelayCandidate],
exitCandidates: [RelayCandidate],
daitaAutomaticRouting: Bool
selectNearbyLocation: Bool
) throws -> SelectedRelays {
guard canHandle(entryCandidates: entryCandidates, exitCandidates: exitCandidates) else {
guard let next else {
Expand All @@ -40,7 +40,7 @@ struct OneToOne: MultihopDecisionFlow {
return try next.pick(
entryCandidates: entryCandidates,
exitCandidates: exitCandidates,
daitaAutomaticRouting: daitaAutomaticRouting
selectNearbyLocation: selectNearbyLocation
)
}

Expand All @@ -51,7 +51,7 @@ struct OneToOne: MultihopDecisionFlow {
let exitMatch = try relayPicker.findBestMatch(from: exitCandidates, applyObfuscation: false)
let entryMatch = try relayPicker.findBestMatch(
from: entryCandidates,
closeTo: daitaAutomaticRouting ? exitMatch.location : nil,
closeTo: selectNearbyLocation ? exitMatch.location : nil,
applyObfuscation: true,
)

Expand Down Expand Up @@ -79,7 +79,7 @@ struct OneToMany: MultihopDecisionFlow {
func pick(
entryCandidates: [RelayCandidate],
exitCandidates: [RelayCandidate],
daitaAutomaticRouting: Bool
selectNearbyLocation: Bool
) throws -> SelectedRelays {
guard let multihopPicker = relayPicker as? MultihopPicker else {
fatalError("Could not cast picker to MultihopPicker")
Expand All @@ -92,7 +92,7 @@ struct OneToMany: MultihopDecisionFlow {
return try next.pick(
entryCandidates: entryCandidates,
exitCandidates: exitCandidates,
daitaAutomaticRouting: daitaAutomaticRouting
selectNearbyLocation: selectNearbyLocation
)
}

Expand Down Expand Up @@ -127,7 +127,7 @@ struct ManyToOne: MultihopDecisionFlow {
func pick(
entryCandidates: [RelayCandidate],
exitCandidates: [RelayCandidate],
daitaAutomaticRouting: Bool
selectNearbyLocation: Bool
) throws -> SelectedRelays {
guard let multihopPicker = relayPicker as? MultihopPicker else {
fatalError("Could not cast picker to MultihopPicker")
Expand All @@ -140,7 +140,7 @@ struct ManyToOne: MultihopDecisionFlow {
return try next.pick(
entryCandidates: entryCandidates,
exitCandidates: exitCandidates,
daitaAutomaticRouting: daitaAutomaticRouting
selectNearbyLocation: selectNearbyLocation
)
}

Expand All @@ -151,7 +151,7 @@ struct ManyToOne: MultihopDecisionFlow {
let entryMatch = try multihopPicker.exclude(
relay: exitMatch,
from: entryCandidates,
closeTo: daitaAutomaticRouting ? exitMatch.location : nil,
closeTo: selectNearbyLocation ? exitMatch.location : nil,
applyObfuscation: true
)

Expand Down Expand Up @@ -179,7 +179,7 @@ struct ManyToMany: MultihopDecisionFlow {
func pick(
entryCandidates: [RelayCandidate],
exitCandidates: [RelayCandidate],
daitaAutomaticRouting: Bool
selectNearbyLocation: Bool
) throws -> SelectedRelays {
guard let multihopPicker = relayPicker as? MultihopPicker else {
fatalError("Could not cast picker to MultihopPicker")
Expand All @@ -192,15 +192,15 @@ struct ManyToMany: MultihopDecisionFlow {
return try next.pick(
entryCandidates: entryCandidates,
exitCandidates: exitCandidates,
daitaAutomaticRouting: daitaAutomaticRouting
selectNearbyLocation: selectNearbyLocation
)
}

let exitMatch = try multihopPicker.findBestMatch(from: exitCandidates, applyObfuscation: false)
let entryMatch = try multihopPicker.exclude(
relay: exitMatch,
from: entryCandidates,
closeTo: daitaAutomaticRouting ? exitMatch.location : nil,
closeTo: selectNearbyLocation ? exitMatch.location : nil,
applyObfuscation: true
)

Expand Down
2 changes: 1 addition & 1 deletion ios/MullvadREST/Relay/RelayPicking/MultihopPicker.swift
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ struct MultihopPicker: RelayPicking {
return try decisionFlow.pick(
entryCandidates: entryCandidates,
exitCandidates: exitCandidates,
daitaAutomaticRouting: daitaSettings.isAutomaticRouting
selectNearbyLocation: daitaSettings.isAutomaticRouting || (constraints.entryLocations == .any)
)
}

Expand Down
34 changes: 30 additions & 4 deletions ios/MullvadSettings/RecentConnections.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,43 @@
import MullvadTypes

public struct RecentConnections: Codable, Sendable, Equatable {
private enum CodingKeys: String, CodingKey {
case isEnabled
case entryLocations
case exitLocations
}

public let isEnabled: Bool
public let entryLocations: [UserSelectedRelays]
public let exitLocations: [UserSelectedRelays]
public let entryLocations: [RelayConstraint<UserSelectedRelays>]
public let exitLocations: [RelayConstraint<UserSelectedRelays>]

public init(
isEnabled: Bool = true,
entryLocations: [UserSelectedRelays] = [],
exitLocations: [UserSelectedRelays] = []
entryLocations: [RelayConstraint<UserSelectedRelays>] = [],
exitLocations: [RelayConstraint<UserSelectedRelays>] = []
) {
self.isEnabled = isEnabled
self.entryLocations = entryLocations
self.exitLocations = exitLocations
}

public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
isEnabled = try container.decodeIfPresent(Bool.self, forKey: .isEnabled) ?? true
entryLocations = Self.decodeLocations(from: container, forKey: .entryLocations)
exitLocations = Self.decodeLocations(from: container, forKey: .exitLocations)
}

private static func decodeLocations(
from container: KeyedDecodingContainer<CodingKeys>,
forKey key: CodingKeys
) -> [RelayConstraint<UserSelectedRelays>] {
if let locations = try? container.decode([RelayConstraint<UserSelectedRelays>].self, forKey: key) {
return locations
} else if let locations = try? container.decode([UserSelectedRelays].self, forKey: key) {
return locations.map { .only($0) }
}

return []
}
}
Loading
Loading