Skip to content

Commit

Permalink
VPN Geoswitching - look and feel tweaks + Pixels (#2045)
Browse files Browse the repository at this point in the history
Task/Issue URL: https://app.asana.com/0/0/1206317271028889/f

**Description**:

Second PR for the bulk of the VPN Geoswitching feature. 

- Makes some tweaks to the styling (font sizes, images, colours)
- adds the correct separators
- fixes the click area of the country items.
- preloads the ViewModel so the countries are already loaded when the
sheet is shown
- adds the pixels listed in the [macOS Geoswitching Pixels
subtask](https://app.asana.com/0/0/1206112407389115/f).

**Steps to test this PR**:
1. Make sure you’re an internal user
2. Navigate to Settings -> VPN
3. Check this screen and the VPN Location that the Location item links
to match [the
designs](https://www.figma.com/file/d7d8ZGkWD0W5ExhBI0tESv/O-N---VPN-Settings-Templates?type=design&node-id=1342-7329&mode=design&t=Wfh4vgagnGqTtVWo-0)
4. Check that switching countries and/or cities updates the VPN location
5. Check that everything looks and feels good.
6. Check that the appropriate pixels are being sent

—
###### Internal references:
[Pull Request Review
Checklist](https://app.asana.com/0/1202500774821704/1203764234894239/f)
[Software Engineering
Expectations](https://app.asana.com/0/59792373528535/199064865822552)
[Technical Design
Template](https://app.asana.com/0/59792373528535/184709971311943)
[Pull Request
Documentation](https://app.asana.com/0/1202500774821704/1204012835277482/f)
  • Loading branch information
graeme authored Jan 15, 2024
1 parent 2c90bf0 commit f766bd6
Show file tree
Hide file tree
Showing 10 changed files with 279 additions and 141 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"images" : [
{
"filename" : "Location-16-Solid.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"template-rendering-intent" : "template"
}
}
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ enum NetworkProtectionPixelEvent: PixelKitEvent {
case networkProtectionClientInvalidInviteCode
case networkProtectionClientFailedToRedeemInviteCode(error: Error?)
case networkProtectionClientFailedToParseRedeemResponse(error: Error)
case networkProtectionClientFailedToFetchLocations(error: Error?)
case networkProtectionClientFailedToParseLocationsResponse(error: Error?)
case networkProtectionClientInvalidAuthToken

case networkProtectionServerListStoreFailedToEncodeServerList
Expand Down Expand Up @@ -158,6 +160,12 @@ enum NetworkProtectionPixelEvent: PixelKitEvent {
case .networkProtectionClientFailedToParseRedeemResponse:
return "m_mac_netp_backend_api_error_parsing_redeem_response_failed"

case .networkProtectionClientFailedToFetchLocations:
return "m_mac_netp_backend_api_error_failed_to_fetch_location_list"

case .networkProtectionClientFailedToParseLocationsResponse:
return "m_mac_netp_backend_api_error_parsing_location_list_response_failed"

case .networkProtectionClientInvalidAuthToken:
return "m_mac_netp_backend_api_error_invalid_auth_token"

Expand Down Expand Up @@ -251,6 +259,12 @@ enum NetworkProtectionPixelEvent: PixelKitEvent {
case .networkProtectionClientFailedToRedeemInviteCode(error: let error):
return error?.pixelParameters

case .networkProtectionClientFailedToFetchLocations(error: let error):
return error?.pixelParameters

case .networkProtectionClientFailedToParseLocationsResponse(error: let error):
return error?.pixelParameters

case .networkProtectionUnhandledError(let function, let line, let error):
var parameters = error.pixelParameters
parameters[PixelKit.Parameters.function] = function
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,28 +26,45 @@ import PixelKit
extension EventMapping where Event == NetworkProtectionError {
static var networkProtectionAppDebugEvents: EventMapping<NetworkProtectionError> = .init { event, _, _, _ in
let domainEvent: NetworkProtectionPixelEvent
let frequency: PixelKit.Frequency

switch event {
case .failedToEncodeRedeemRequest:
domainEvent = .networkProtectionClientFailedToEncodeRedeemRequest
frequency = .standard
case .invalidInviteCode:
domainEvent = .networkProtectionClientInvalidInviteCode
frequency = .standard
case .failedToRedeemInviteCode(let error):
domainEvent = .networkProtectionClientFailedToRedeemInviteCode(error: error)
frequency = .standard
case .failedToParseRedeemResponse(let error):
domainEvent = .networkProtectionClientFailedToParseRedeemResponse(error: error)
frequency = .standard
case .invalidAuthToken:
domainEvent = .networkProtectionClientInvalidAuthToken
frequency = .standard
case .failedToCastKeychainValueToData(field: let field):
domainEvent = .networkProtectionKeychainErrorFailedToCastKeychainValueToData(field: field)
frequency = .standard
case .keychainReadError(field: let field, status: let status):
domainEvent = .networkProtectionKeychainReadError(field: field, status: status)
frequency = .standard
case .keychainWriteError(field: let field, status: let status):
domainEvent = .networkProtectionKeychainWriteError(field: field, status: status)
frequency = .standard
case .keychainDeleteError(status: let status):
domainEvent = .networkProtectionKeychainDeleteError(status: status)
frequency = .standard
case .noAuthTokenFound:
domainEvent = .networkProtectionNoAuthTokenFoundError
frequency = .standard
case .failedToFetchLocationList(let error):
domainEvent = .networkProtectionClientFailedToFetchLocations(error: error)
frequency = .dailyAndContinuous
case .failedToParseLocationListResponse(let error):
domainEvent = .networkProtectionClientFailedToParseLocationsResponse(error: error)
frequency = .dailyAndContinuous
case .noServerRegistrationInfo,
.couldNotSelectClosestServer,
.couldNotGetPeerPublicKey,
Expand All @@ -70,14 +87,13 @@ extension EventMapping where Event == NetworkProtectionError {
.wireGuardDnsResolution,
.wireGuardSetNetworkSettings,
.startWireGuardBackend,
.failedToRetrieveAuthToken,
.failedToFetchLocationList,
.failedToParseLocationListResponse:
.failedToRetrieveAuthToken:
domainEvent = .networkProtectionUnhandledError(function: #function, line: #line, error: event)
frequency = .standard
return
case .unhandledError(function: let function, line: let line, error: let error):
domainEvent = .networkProtectionUnhandledError(function: function, line: line, error: error)

frequency = .standard
return
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,22 +22,21 @@ import Foundation
import SwiftUI

struct VPNLocationPreferenceItem: View {
let model: VPNLocationPreferenceItemModel
@ObservedObject var model: VPNLocationPreferenceItemModel
@State private var isShowingLocationSheet: Bool = false

var body: some View {
VStack(alignment: .leading) {
HStack(spacing: 10) {
switch model.icon {
case .defaultIcon:
Image(systemName: "location.fill")
.resizable()
.frame(width: 18, height: 18)
Image("Location-16-Solid")
.foregroundColor(Color("BlackWhite100").opacity(0.9))
case .emoji(let string):
Text(string).font(.system(size: 20))
Text(string).font(.system(size: 16))
}

VStack(alignment: .leading) {
VStack(alignment: .leading, spacing: 2) {
Text(model.title)
.font(.system(size: 13))
.foregroundColor(.primary)
Expand All @@ -52,13 +51,15 @@ struct VPNLocationPreferenceItem: View {
isShowingLocationSheet = true
}
.sheet(isPresented: $isShowingLocationSheet) {
VPNLocationView(isPresented: $isShowingLocationSheet)
VPNLocationView(model: model.locationsViewModel, isPresented: $isShowingLocationSheet)
}
}
}
.frame(idealWidth: .infinity, maxWidth: .infinity, alignment: .topLeading)
.padding(10)
.frame(height: 52)
.padding(.horizontal, 10)
.background(Color("BlackWhite1"))
.animation(.default)
.roundedBorder()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
import Foundation
import NetworkProtection

struct VPNLocationPreferenceItemModel {
final class VPNLocationPreferenceItemModel: ObservableObject {
enum LocationIcon {
case defaultIcon
case emoji(String)
Expand All @@ -31,6 +31,9 @@ struct VPNLocationPreferenceItemModel {
let subtitle: String?
let icon: LocationIcon

// This is preloaded so the user doesn't have to wait for the list to load on presentation
let locationsViewModel = VPNLocationViewModel()

init(selectedLocation: VPNSettings.SelectedLocation) {
switch selectedLocation {
case .nearest:
Expand Down
Loading

0 comments on commit f766bd6

Please sign in to comment.