From a697b03d787288caea50fb8748f24f2d03097ef6 Mon Sep 17 00:00:00 2001 From: Laura Sempere Date: Thu, 14 Dec 2023 09:23:13 +0100 Subject: [PATCH] PIA-885: Dashboard connect button UI --- .../CompositionRoot/DashboardFactory.swift | 33 +++++ .../PIAConnectionButtonViewModel.swift | 82 +++++++++++ PIA VPN-tvOS/Dashboard/UI/DashboardView.swift | 37 +++++ .../Dashboard/UI/PIAConnectionButton.swift | 49 +++++++ .../UseCases/VpnConnectionUseCase.swift | 20 +++ PIA VPN-tvOS/PIA VPN-tvOS.entitlements | 5 + PIA VPN-tvOS/PIA_VPN_tvOSApp.swift | 1 + .../Shared/UI/Colors.xcassets/Contents.json | 6 + .../app_background.colorset/Contents.json | 33 +++++ .../Contents.json | 28 ++++ .../pia_green.colorset/Contents.json | 20 +++ .../pia_green_dark_20.colorset/Contents.json | 20 +++ .../pia_green_dark_50.colorset/Contents.json | 20 +++ .../pia_red.colorset/Contents.json | 20 +++ .../pia_red_dark.colorset/Contents.json | 20 +++ .../pia_yellow.colorset/Contents.json | 20 +++ .../pia_yellow_dark.colorset/Contents.json | 20 +++ PIA VPN-tvOS/Shared/UI/PIA+Animation.swift | 12 ++ PIA VPN-tvOS/Shared/UI/PIAColors+SwitUI.swift | 14 ++ .../Mocks/VpnConnectionUseCaseTypeMock.swift | 31 +++++ .../PIAConnectionButtonViewModelTests.swift | 102 ++++++++++++++ PIA VPN.xcodeproj/project.pbxproj | 128 +++++++++++++++++- .../vpn-button.imageset/Contents.json | 13 +- .../vpn-button.imageset/Contents.json | 3 + 24 files changed, 725 insertions(+), 12 deletions(-) create mode 100644 PIA VPN-tvOS/Dashboard/CompositionRoot/DashboardFactory.swift create mode 100644 PIA VPN-tvOS/Dashboard/Presentation/PIAConnectionButtonViewModel.swift create mode 100644 PIA VPN-tvOS/Dashboard/UI/DashboardView.swift create mode 100644 PIA VPN-tvOS/Dashboard/UI/PIAConnectionButton.swift create mode 100644 PIA VPN-tvOS/Dashboard/UseCases/VpnConnectionUseCase.swift create mode 100644 PIA VPN-tvOS/PIA VPN-tvOS.entitlements create mode 100644 PIA VPN-tvOS/Shared/UI/Colors.xcassets/Contents.json create mode 100644 PIA VPN-tvOS/Shared/UI/Colors.xcassets/app_background.colorset/Contents.json create mode 100644 PIA VPN-tvOS/Shared/UI/Colors.xcassets/pia_connect_button_grey.colorset/Contents.json create mode 100644 PIA VPN-tvOS/Shared/UI/Colors.xcassets/pia_green.colorset/Contents.json create mode 100644 PIA VPN-tvOS/Shared/UI/Colors.xcassets/pia_green_dark_20.colorset/Contents.json create mode 100644 PIA VPN-tvOS/Shared/UI/Colors.xcassets/pia_green_dark_50.colorset/Contents.json create mode 100644 PIA VPN-tvOS/Shared/UI/Colors.xcassets/pia_red.colorset/Contents.json create mode 100644 PIA VPN-tvOS/Shared/UI/Colors.xcassets/pia_red_dark.colorset/Contents.json create mode 100644 PIA VPN-tvOS/Shared/UI/Colors.xcassets/pia_yellow.colorset/Contents.json create mode 100644 PIA VPN-tvOS/Shared/UI/Colors.xcassets/pia_yellow_dark.colorset/Contents.json create mode 100644 PIA VPN-tvOS/Shared/UI/PIA+Animation.swift create mode 100644 PIA VPN-tvOS/Shared/UI/PIAColors+SwitUI.swift create mode 100644 PIA VPN-tvOSTests/Dashboard/Mocks/VpnConnectionUseCaseTypeMock.swift create mode 100644 PIA VPN-tvOSTests/Dashboard/PIAConnectionButtonViewModelTests.swift diff --git a/PIA VPN-tvOS/Dashboard/CompositionRoot/DashboardFactory.swift b/PIA VPN-tvOS/Dashboard/CompositionRoot/DashboardFactory.swift new file mode 100644 index 000000000..fb0c75e20 --- /dev/null +++ b/PIA VPN-tvOS/Dashboard/CompositionRoot/DashboardFactory.swift @@ -0,0 +1,33 @@ + +import Foundation + +class DashboardFactory { + + static func makeDashboardView() -> DashboardView { + return DashboardView( + connectionButton: makePIAConnectionButton() + ) + } + + static func makePIAConnectionButton(size: CGFloat = 160, lineWidth: CGFloat = 6) -> PIAConnectionButton { + return PIAConnectionButton( + size: size, + lineWidth: lineWidth, + viewModel: makePIAConnectionButtonViewModel() + ) + } + +} + +// MARK: Private + +extension DashboardFactory { + private static func makePIAConnectionButtonViewModel() -> PIAConnectionButtonViewModel { + return PIAConnectionButtonViewModel(useCase: makeVpnConnectionUseCase()) + } + + private static func makeVpnConnectionUseCase() -> VpnConnectionUseCaseType { + return VpnConnectionUseCase() + } + +} diff --git a/PIA VPN-tvOS/Dashboard/Presentation/PIAConnectionButtonViewModel.swift b/PIA VPN-tvOS/Dashboard/Presentation/PIAConnectionButtonViewModel.swift new file mode 100644 index 000000000..3b2d3f3ce --- /dev/null +++ b/PIA VPN-tvOS/Dashboard/Presentation/PIAConnectionButtonViewModel.swift @@ -0,0 +1,82 @@ + + +import Foundation +import SwiftUI + +class PIAConnectionButtonViewModel: ObservableObject { + enum State { + case disconnected + case connecting + case connected + case disconnecting + } + + @Published var state: State = .disconnected + + private let vpnConnectionUseCase: VpnConnectionUseCaseType + + init(useCase: VpnConnectionUseCaseType) { + self.vpnConnectionUseCase = useCase + } + + // Inner ring color and outer ring color + var tintColor: (Color, Color) { + switch state { + case .disconnected: + return (.pia_red_dark, .pia_red_dark) + case .connecting, .disconnecting: + return (.pia_yellow_dark, .pia_connect_button_grey) + case .connected: + return (.pia_green, .pia_green) + } + } + + var animating: Bool { + state == .connecting || + state == .disconnecting + } +} + +// MARK: Connection + +extension PIAConnectionButtonViewModel { + + func toggleConnection() { + switch state { + case .disconnected: + connect() + case .connecting: + break + case .connected: + disconnect() + case .disconnecting: + break + } + } + + private func connect() { + // TODO: Take the state from the real VpnManager state monitor + state = .connecting + + vpnConnectionUseCase.connect() + + // TODO: Take the state from the real VpnManager state monitor + DispatchQueue.main.asyncAfter(deadline: .now()+0.2) { [weak self] in + self?.state = .connected + } + } + + private func disconnect() { + // TODO: Take the state from the real VpnManager state monitor + state = .disconnecting + + vpnConnectionUseCase.disconnect() + + // TODO: Take the state from the real VpnManager state monitor + DispatchQueue.main.asyncAfter(deadline: .now()+0.2) { [weak self] in + self?.state = .disconnected + } + } + +} + diff --git a/PIA VPN-tvOS/Dashboard/UI/DashboardView.swift b/PIA VPN-tvOS/Dashboard/UI/DashboardView.swift new file mode 100644 index 000000000..10d956f65 --- /dev/null +++ b/PIA VPN-tvOS/Dashboard/UI/DashboardView.swift @@ -0,0 +1,37 @@ + +import SwiftUI + +struct DashboardView: View { + let viewWidth = UIScreen.main.bounds.width + let viewHeight = UIScreen.main.bounds.height + + let connectionButton: PIAConnectionButton + + var body: some View { + VStack { + VStack(spacing: 20) { + connectionButton + .padding() + + Button { + + } label: { + Text("Other Button") + } + .padding(20) + + } + .frame(maxWidth: (viewWidth/2)) + .padding() + + } + .frame(width: viewWidth, height: viewHeight) + .background(Color.app_background) + + + } +} + +#Preview { + DashboardView(connectionButton: DashboardFactory.makePIAConnectionButton()) +} diff --git a/PIA VPN-tvOS/Dashboard/UI/PIAConnectionButton.swift b/PIA VPN-tvOS/Dashboard/UI/PIAConnectionButton.swift new file mode 100644 index 000000000..1f7aa9f87 --- /dev/null +++ b/PIA VPN-tvOS/Dashboard/UI/PIAConnectionButton.swift @@ -0,0 +1,49 @@ + + +import SwiftUI + +struct PIAConnectionButton: View { + var size: CGFloat = 160 + var lineWidth: CGFloat = 6 + + @ObservedObject var viewModel:PIAConnectionButtonViewModel + + var body: some View { + Button { + viewModel.toggleConnection() + } label: { + ZStack { + connectingRing(for: viewModel.tintColor.0) + .frame(width: size) + .opacity(viewModel.animating ? 1 : 0) + .animation(Animation + .easeOut(duration: 1) + .repeat(while: viewModel.animating), value: viewModel.animating) + Circle() + .stroke(style: StrokeStyle(lineWidth: lineWidth)) + .foregroundStyle(viewModel.animating ? .tertiary : .primary) + .foregroundColor(viewModel.tintColor.1) + .frame(width: size) + + Image("vpn-button") + .resizable() + .renderingMode(.template) + .foregroundColor(viewModel.tintColor.0) + .frame(width: (size-100), height: (size-100)) + } + + } + .buttonStyle(.card) + .buttonBorderShape(ButtonBorderShape.capsule) + + } + + + func connectingRing(for color: Color) -> some View { + Circle() + .trim(from: viewModel.animating ? 0 : 1, to: viewModel.animating ? 1.5 : 1) + .stroke(color.gradient, + style: StrokeStyle(lineWidth: lineWidth, lineCap: .square)) + .rotationEffect(.degrees(-95)) + } +} diff --git a/PIA VPN-tvOS/Dashboard/UseCases/VpnConnectionUseCase.swift b/PIA VPN-tvOS/Dashboard/UseCases/VpnConnectionUseCase.swift new file mode 100644 index 000000000..4e46f4904 --- /dev/null +++ b/PIA VPN-tvOS/Dashboard/UseCases/VpnConnectionUseCase.swift @@ -0,0 +1,20 @@ + +import Foundation + +protocol VpnConnectionUseCaseType { + func connect() + func disconnect() +} + +class VpnConnectionUseCase: VpnConnectionUseCaseType { + + func connect() { + // TODO: Implement + } + + func disconnect() { + // TODO: Implement + } + + +} diff --git a/PIA VPN-tvOS/PIA VPN-tvOS.entitlements b/PIA VPN-tvOS/PIA VPN-tvOS.entitlements new file mode 100644 index 000000000..0c67376eb --- /dev/null +++ b/PIA VPN-tvOS/PIA VPN-tvOS.entitlements @@ -0,0 +1,5 @@ + + + + + diff --git a/PIA VPN-tvOS/PIA_VPN_tvOSApp.swift b/PIA VPN-tvOS/PIA_VPN_tvOSApp.swift index 3982169b0..61a8b0ca7 100644 --- a/PIA VPN-tvOS/PIA_VPN_tvOSApp.swift +++ b/PIA VPN-tvOS/PIA_VPN_tvOSApp.swift @@ -13,6 +13,7 @@ struct PIA_VPN_tvOSApp: App { var body: some Scene { WindowGroup { LoginFactory.makeLoginView() +// DashboardFactory.makeDashboardView() } } } diff --git a/PIA VPN-tvOS/Shared/UI/Colors.xcassets/Contents.json b/PIA VPN-tvOS/Shared/UI/Colors.xcassets/Contents.json new file mode 100644 index 000000000..73c00596a --- /dev/null +++ b/PIA VPN-tvOS/Shared/UI/Colors.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/PIA VPN-tvOS/Shared/UI/Colors.xcassets/app_background.colorset/Contents.json b/PIA VPN-tvOS/Shared/UI/Colors.xcassets/app_background.colorset/Contents.json new file mode 100644 index 000000000..b64592979 --- /dev/null +++ b/PIA VPN-tvOS/Shared/UI/Colors.xcassets/app_background.colorset/Contents.json @@ -0,0 +1,33 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "1.000", + "green" : "1.000", + "red" : "1.000" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "platform" : "ios", + "reference" : "systemGray5Color" + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/PIA VPN-tvOS/Shared/UI/Colors.xcassets/pia_connect_button_grey.colorset/Contents.json b/PIA VPN-tvOS/Shared/UI/Colors.xcassets/pia_connect_button_grey.colorset/Contents.json new file mode 100644 index 000000000..f5f5bf54e --- /dev/null +++ b/PIA VPN-tvOS/Shared/UI/Colors.xcassets/pia_connect_button_grey.colorset/Contents.json @@ -0,0 +1,28 @@ +{ + "colors" : [ + { + "color" : { + "platform" : "ios", + "reference" : "opaqueSeparatorColor" + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "platform" : "ios", + "reference" : "systemGray3Color" + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/PIA VPN-tvOS/Shared/UI/Colors.xcassets/pia_green.colorset/Contents.json b/PIA VPN-tvOS/Shared/UI/Colors.xcassets/pia_green.colorset/Contents.json new file mode 100644 index 000000000..be0ff731b --- /dev/null +++ b/PIA VPN-tvOS/Shared/UI/Colors.xcassets/pia_green.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.353", + "green" : "0.874", + "red" : "0.365" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/PIA VPN-tvOS/Shared/UI/Colors.xcassets/pia_green_dark_20.colorset/Contents.json b/PIA VPN-tvOS/Shared/UI/Colors.xcassets/pia_green_dark_20.colorset/Contents.json new file mode 100644 index 000000000..d093479d4 --- /dev/null +++ b/PIA VPN-tvOS/Shared/UI/Colors.xcassets/pia_green_dark_20.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.286", + "green" : "0.714", + "red" : "0.298" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/PIA VPN-tvOS/Shared/UI/Colors.xcassets/pia_green_dark_50.colorset/Contents.json b/PIA VPN-tvOS/Shared/UI/Colors.xcassets/pia_green_dark_50.colorset/Contents.json new file mode 100644 index 000000000..3508cca86 --- /dev/null +++ b/PIA VPN-tvOS/Shared/UI/Colors.xcassets/pia_green_dark_50.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.039", + "green" : "0.474", + "red" : "0.012" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/PIA VPN-tvOS/Shared/UI/Colors.xcassets/pia_red.colorset/Contents.json b/PIA VPN-tvOS/Shared/UI/Colors.xcassets/pia_red.colorset/Contents.json new file mode 100644 index 000000000..e198c67e3 --- /dev/null +++ b/PIA VPN-tvOS/Shared/UI/Colors.xcassets/pia_red.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.345", + "green" : "0.267", + "red" : "0.949" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/PIA VPN-tvOS/Shared/UI/Colors.xcassets/pia_red_dark.colorset/Contents.json b/PIA VPN-tvOS/Shared/UI/Colors.xcassets/pia_red_dark.colorset/Contents.json new file mode 100644 index 000000000..2b8e122d5 --- /dev/null +++ b/PIA VPN-tvOS/Shared/UI/Colors.xcassets/pia_red_dark.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.176", + "green" : "0.208", + "red" : "0.698" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/PIA VPN-tvOS/Shared/UI/Colors.xcassets/pia_yellow.colorset/Contents.json b/PIA VPN-tvOS/Shared/UI/Colors.xcassets/pia_yellow.colorset/Contents.json new file mode 100644 index 000000000..0510fce0a --- /dev/null +++ b/PIA VPN-tvOS/Shared/UI/Colors.xcassets/pia_yellow.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.039", + "green" : "0.812", + "red" : "0.976" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/PIA VPN-tvOS/Shared/UI/Colors.xcassets/pia_yellow_dark.colorset/Contents.json b/PIA VPN-tvOS/Shared/UI/Colors.xcassets/pia_yellow_dark.colorset/Contents.json new file mode 100644 index 000000000..712183692 --- /dev/null +++ b/PIA VPN-tvOS/Shared/UI/Colors.xcassets/pia_yellow_dark.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.000", + "green" : "0.706", + "red" : "0.902" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/PIA VPN-tvOS/Shared/UI/PIA+Animation.swift b/PIA VPN-tvOS/Shared/UI/PIA+Animation.swift new file mode 100644 index 000000000..f6a9dba25 --- /dev/null +++ b/PIA VPN-tvOS/Shared/UI/PIA+Animation.swift @@ -0,0 +1,12 @@ + +import SwiftUI + +extension Animation { + func `repeat`(while expression: Bool, autoreverses: Bool = true) -> Animation { + if expression { + return self.repeatForever(autoreverses: autoreverses) + } else { + return self + } + } +} diff --git a/PIA VPN-tvOS/Shared/UI/PIAColors+SwitUI.swift b/PIA VPN-tvOS/Shared/UI/PIAColors+SwitUI.swift new file mode 100644 index 000000000..154ef51eb --- /dev/null +++ b/PIA VPN-tvOS/Shared/UI/PIAColors+SwitUI.swift @@ -0,0 +1,14 @@ + +import SwiftUI + +extension Color { + static var app_background = Color("app_background") + static var pia_green = Color("pia_green") + static var pia_green_dark_20 = Color("pia_green_dark_20") + static var pia_green_dark_50 = Color("pia_green_dark_50") + static var pia_yellow = Color("pia_yellow") + static var pia_yellow_dark = Color("pia_yellow_dark") + static var pia_red = Color("pia_red") + static var pia_red_dark = Color("pia_red_dark") + static var pia_connect_button_grey = Color("pia_connect_button_grey") +} diff --git a/PIA VPN-tvOSTests/Dashboard/Mocks/VpnConnectionUseCaseTypeMock.swift b/PIA VPN-tvOSTests/Dashboard/Mocks/VpnConnectionUseCaseTypeMock.swift new file mode 100644 index 000000000..f6aaff84f --- /dev/null +++ b/PIA VPN-tvOSTests/Dashboard/Mocks/VpnConnectionUseCaseTypeMock.swift @@ -0,0 +1,31 @@ +// +// VpnConnectionUseCaseTypeMock.swift +// PIA VPN-tvOSTests +// +// Created by Laura S on 12/14/23. +// Copyright © 2023 Private Internet Access Inc. All rights reserved. +// + +import Foundation +@testable import PIA_VPN_tvOS + +class VpnConnectionUseCaseMock: VpnConnectionUseCaseType { + + var connectCalled: Bool = false + var connectCalledAttempt: Int = 0 + + func connect() { + connectCalled = true + connectCalledAttempt += 1 + } + + var disconnectCalled: Bool = false + var disconnectCalledAttempt: Int = 0 + + func disconnect() { + disconnectCalled = true + disconnectCalledAttempt += 1 + } + + +} diff --git a/PIA VPN-tvOSTests/Dashboard/PIAConnectionButtonViewModelTests.swift b/PIA VPN-tvOSTests/Dashboard/PIAConnectionButtonViewModelTests.swift new file mode 100644 index 000000000..3d05d2c08 --- /dev/null +++ b/PIA VPN-tvOSTests/Dashboard/PIAConnectionButtonViewModelTests.swift @@ -0,0 +1,102 @@ +// +// PIAConnectionButtonViewModelTests.swift +// PIA VPN-tvOSTests +// +// Created by Laura S on 12/14/23. +// Copyright © 2023 Private Internet Access Inc. All rights reserved. +// + +import XCTest +import Combine +@testable import PIA_VPN_tvOS + +final class PIAConnectionButtonViewModelTests: XCTestCase { + + final class Fixture { + let vpnConnectionUseCaseMock = VpnConnectionUseCaseMock() + } + + var fixture: Fixture! + var sut: PIAConnectionButtonViewModel! + var cancellables: Set! + + override func setUp() { + fixture = Fixture() + sut = PIAConnectionButtonViewModel(useCase: fixture.vpnConnectionUseCaseMock) + cancellables = Set() + } + + override func tearDown() { + fixture = nil + sut = nil + cancellables = nil + } + + func test_toggleConnection_when_disconnected() { + // GIVEN that the vpn state is disconnected + XCTAssertTrue(sut.state == .disconnected) + + let connectingExpectation = expectation(description: "vpn is connecting") + + let connectedExpectation = expectation(description: "vpn is connected") + + sut.$state.sink { _ in + } receiveValue: { state in + switch state { + case .connecting: + connectingExpectation.fulfill() + case .connected: + connectedExpectation.fulfill() + default: break + } + }.store(in: &cancellables) + + + // WHEN calling the toggle connection method + sut.toggleConnection() + + // THEN the vpnConnectionUseCase is called once to connect the vpn + XCTAssertTrue(fixture.vpnConnectionUseCaseMock.connectCalled) + XCTAssertTrue(fixture.vpnConnectionUseCaseMock.connectCalledAttempt == 1) + + // AND the Vpn state becomes 'connecting' and 'connected' + wait(for: [connectingExpectation, connectedExpectation], timeout: 1) + XCTAssertEqual(sut.state, .connected) + + } + + func test_toggleConnection_when_connected() { + // GIVEN that the vpn state is connected + sut.state = .connected + XCTAssertTrue(sut.state == .connected) + + let disconnectingExpectation = expectation(description: "vpn is disconnecting") + + let disconnectedExpectation = expectation(description: "vpn is disconnected") + + sut.$state.sink { _ in + } receiveValue: { state in + switch state { + case .disconnecting: + disconnectingExpectation.fulfill() + case .disconnected: + disconnectedExpectation.fulfill() + default: break + } + }.store(in: &cancellables) + + + // WHEN calling the toggle connection method + sut.toggleConnection() + + // THEN the vpnConnectionUseCase is called once to disconnect the vpn + XCTAssertTrue(fixture.vpnConnectionUseCaseMock.disconnectCalled) + XCTAssertTrue(fixture.vpnConnectionUseCaseMock.disconnectCalledAttempt == 1) + + // AND the Vpn state becomes 'disconnecting' and 'disconnected' + wait(for: [disconnectingExpectation, disconnectedExpectation], timeout: 1) + XCTAssertEqual(sut.state, .disconnected) + + } + +} diff --git a/PIA VPN.xcodeproj/project.pbxproj b/PIA VPN.xcodeproj/project.pbxproj index 440a3ce2f..29fb7c12d 100644 --- a/PIA VPN.xcodeproj/project.pbxproj +++ b/PIA VPN.xcodeproj/project.pbxproj @@ -167,8 +167,15 @@ 692483202AB05F18002A0407 /* PIAConnectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6924831F2AB05F18002A0407 /* PIAConnectionView.swift */; }; 692483222AB05F37002A0407 /* PIACircleIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = 692483212AB05F37002A0407 /* PIACircleIcon.swift */; }; 692483262AB05F85002A0407 /* PIAConnectionActivityWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 692483252AB05F85002A0407 /* PIAConnectionActivityWidget.swift */; }; + 694AC74E2B17AB9C007E7B56 /* DashboardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 694AC74D2B17AB9C007E7B56 /* DashboardView.swift */; }; + 694AC74F2B17ADF9007E7B56 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 291C6397183EBC210039EC03 /* Images.xcassets */; }; 695BF81D2AC30EFB00D1139C /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 0E7EC043209326E30029811E /* Localizable.strings */; }; 695BF81F2AC410E000D1139C /* (null) in Sources */ = {isa = PBXBuildFile; }; + 698C3B492B2B33650012D527 /* VpnConnectionUseCaseTypeMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 698C3B482B2B33650012D527 /* VpnConnectionUseCaseTypeMock.swift */; }; + 698C3B4B2B2B34760012D527 /* PIAConnectionButtonViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 698C3B4A2B2B34760012D527 /* PIAConnectionButtonViewModelTests.swift */; }; + 698C3B4C2B2B3CA10012D527 /* Stubs+PIALibrary.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5AB28822B2902E200744E5F /* Stubs+PIALibrary.swift */; }; + 698C3B4D2B2B3CA60012D527 /* LoginProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5AB28842B2902E700744E5F /* LoginProviderTests.swift */; }; + 698C3B4E2B2B3CBE0012D527 /* LoginIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5AB28742B279BB600744E5F /* LoginIntegrationTests.swift */; }; 698F4F2B2AB8A2080010B2B0 /* PIAWidgetExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 8269A6D5251CB5E0000B4DBF /* PIAWidgetExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 698F4F2D2AB978BF0010B2B0 /* PIACircleImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 698F4F2C2AB978BF0010B2B0 /* PIACircleImageView.swift */; }; 698F4F2E2AB97BAD0010B2B0 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 291C6397183EBC210039EC03 /* Images.xcassets */; }; @@ -188,6 +195,13 @@ 69B70ABF2ACC2CFE0072A09D /* AccessibilityId.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69B70ABD2ACC2CFE0072A09D /* AccessibilityId.swift */; }; 69B70AC02ACC2CFE0072A09D /* AccessibilityId.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69B70ABD2ACC2CFE0072A09D /* AccessibilityId.swift */; }; 69C587FD2AD00C6300B95EF9 /* PIAExampleWithAuthenticatedAppTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69C587FC2AD00C6300B95EF9 /* PIAExampleWithAuthenticatedAppTest.swift */; }; + 69F053472B1E056400AE0665 /* PIAColors+SwitUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69F053462B1E056400AE0665 /* PIAColors+SwitUI.swift */; }; + 69F0534A2B1F1D2800AE0665 /* Colors.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 69F053492B1F1D2800AE0665 /* Colors.xcassets */; }; + 69F1C2932B2B10D400E924AE /* DashboardFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69F1C2922B2B10D400E924AE /* DashboardFactory.swift */; }; + 69F1C2952B2B216100E924AE /* PIA+Animation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69F1C2942B2B216100E924AE /* PIA+Animation.swift */; }; + 69F1C2972B2B239000E924AE /* PIAConnectionButtonViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69F1C2962B2B239000E924AE /* PIAConnectionButtonViewModel.swift */; }; + 69F1C2992B2B28DA00E924AE /* PIAConnectionButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69F1C2982B2B28DA00E924AE /* PIAConnectionButton.swift */; }; + 69F1C29C2B2B299300E924AE /* VpnConnectionUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69F1C29B2B2B299300E924AE /* VpnConnectionUseCase.swift */; }; 7ECBB8DD27B6C4F500C0C774 /* UserSurveyManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ECBB8DC27B6C4F500C0C774 /* UserSurveyManager.swift */; }; 7ECBB8DE27BA5FCE00C0C774 /* UserSurveyManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ECBB8DC27B6C4F500C0C774 /* UserSurveyManager.swift */; }; 821674F12678A1BE0028E4FD /* SettingsDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 821674F02678A1BE0028E4FD /* SettingsDelegate.swift */; }; @@ -527,14 +541,11 @@ E5AB286F2B27977000744E5F /* LoginFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5AB286E2B27977000744E5F /* LoginFactory.swift */; }; E5AB28712B279B4000744E5F /* LoginProviderType.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5AB28702B279B4000744E5F /* LoginProviderType.swift */; }; E5AB28732B279B7000744E5F /* LoginProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5AB28722B279B7000744E5F /* LoginProvider.swift */; }; - E5AB28752B279BB600744E5F /* LoginIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5AB28742B279BB600744E5F /* LoginIntegrationTests.swift */; }; E5AB28772B28B49A00744E5F /* UserAccount.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5AB28762B28B49A00744E5F /* UserAccount.swift */; }; E5AB28792B28B4B300744E5F /* AccountInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5AB28782B28B4B300744E5F /* AccountInfo.swift */; }; E5AB287B2B28B4C400744E5F /* Credentials.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5AB287A2B28B4C400744E5F /* Credentials.swift */; }; E5AB287D2B28E4E700744E5F /* UserAccountMapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5AB287C2B28E4E700744E5F /* UserAccountMapper.swift */; }; E5AB287F2B28F6EF00744E5F /* Stubs.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5AB287E2B28F6EF00744E5F /* Stubs.swift */; }; - E5AB28832B2902E200744E5F /* Stubs+PIALibrary.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5AB28822B2902E200744E5F /* Stubs+PIALibrary.swift */; }; - E5AB28852B2902E700744E5F /* LoginProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5AB28842B2902E700744E5F /* LoginProviderTests.swift */; }; E5AB28872B2911C900744E5F /* AccountProviderMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5AB28862B2911C900744E5F /* AccountProviderMock.swift */; }; E5AB288A2B29BDED00744E5F /* Foundation+PIA.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5AB28892B29BDED00744E5F /* Foundation+PIA.swift */; }; E5C507682B0D609300200A6A /* PIALibrary in Frameworks */ = {isa = PBXBuildFile; productRef = E5C507672B0D609300200A6A /* PIALibrary */; }; @@ -853,6 +864,9 @@ 692483212AB05F37002A0407 /* PIACircleIcon.swift */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = sourcecode.swift; path = PIACircleIcon.swift; sourceTree = ""; tabWidth = 4; }; 692483252AB05F85002A0407 /* PIAConnectionActivityWidget.swift */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = sourcecode.swift; path = PIAConnectionActivityWidget.swift; sourceTree = ""; tabWidth = 4; }; 6947AADB2ACDC8AE001BCC66 /* PIA-VPN-e2e-simulator.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = "PIA-VPN-e2e-simulator.xctestplan"; sourceTree = ""; }; + 694AC74D2B17AB9C007E7B56 /* DashboardView.swift */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = sourcecode.swift; path = DashboardView.swift; sourceTree = ""; tabWidth = 4; }; + 698C3B482B2B33650012D527 /* VpnConnectionUseCaseTypeMock.swift */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = sourcecode.swift; path = VpnConnectionUseCaseTypeMock.swift; sourceTree = ""; tabWidth = 4; }; + 698C3B4A2B2B34760012D527 /* PIAConnectionButtonViewModelTests.swift */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = sourcecode.swift; path = PIAConnectionButtonViewModelTests.swift; sourceTree = ""; tabWidth = 4; }; 698F4F2C2AB978BF0010B2B0 /* PIACircleImageView.swift */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = sourcecode.swift; path = PIACircleImageView.swift; sourceTree = ""; tabWidth = 4; }; 698F4F2F2ABA1DA10010B2B0 /* PIAConnectionLiveActivityManager.swift */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = sourcecode.swift; path = PIAConnectionLiveActivityManager.swift; sourceTree = ""; tabWidth = 4; }; 699F23AC2AFBA66000EBC5E6 /* SettingsScreen.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsScreen.swift; sourceTree = ""; }; @@ -866,6 +880,13 @@ 69B70AB42ACBF51C0072A09D /* LoginScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginScreen.swift; sourceTree = ""; }; 69B70ABD2ACC2CFE0072A09D /* AccessibilityId.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccessibilityId.swift; sourceTree = ""; }; 69C587FC2AD00C6300B95EF9 /* PIAExampleWithAuthenticatedAppTest.swift */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = PIAExampleWithAuthenticatedAppTest.swift; sourceTree = ""; tabWidth = 2; }; + 69F053462B1E056400AE0665 /* PIAColors+SwitUI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PIAColors+SwitUI.swift"; sourceTree = ""; }; + 69F053492B1F1D2800AE0665 /* Colors.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Colors.xcassets; sourceTree = ""; }; + 69F1C2922B2B10D400E924AE /* DashboardFactory.swift */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = sourcecode.swift; path = DashboardFactory.swift; sourceTree = ""; tabWidth = 4; }; + 69F1C2942B2B216100E924AE /* PIA+Animation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PIA+Animation.swift"; sourceTree = ""; }; + 69F1C2962B2B239000E924AE /* PIAConnectionButtonViewModel.swift */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = sourcecode.swift; path = PIAConnectionButtonViewModel.swift; sourceTree = ""; tabWidth = 4; }; + 69F1C2982B2B28DA00E924AE /* PIAConnectionButton.swift */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = sourcecode.swift; path = PIAConnectionButton.swift; sourceTree = ""; tabWidth = 4; }; + 69F1C29B2B2B299300E924AE /* VpnConnectionUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VpnConnectionUseCase.swift; sourceTree = ""; }; 7EC2972D27D8B8580061C56A /* CredentialsUtil.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.swift; path = CredentialsUtil.swift; sourceTree = ""; tabWidth = 4; }; 7ECBB8DC27B6C4F500C0C774 /* UserSurveyManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSurveyManager.swift; sourceTree = ""; }; 821674F02678A1BE0028E4FD /* SettingsDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsDelegate.swift; sourceTree = ""; }; @@ -1112,7 +1133,7 @@ E5AB28862B2911C900744E5F /* AccountProviderMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountProviderMock.swift; sourceTree = ""; }; E5AB28892B29BDED00744E5F /* Foundation+PIA.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Foundation+PIA.swift"; sourceTree = ""; }; E5C507772B0E144D00200A6A /* PIA VPN-tvOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "PIA VPN-tvOS.app"; sourceTree = BUILT_PRODUCTS_DIR; }; - E5C507792B0E144E00200A6A /* PIA_VPN_tvOSApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PIA_VPN_tvOSApp.swift; sourceTree = ""; }; + E5C507792B0E144E00200A6A /* PIA_VPN_tvOSApp.swift */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = sourcecode.swift; path = PIA_VPN_tvOSApp.swift; sourceTree = ""; tabWidth = 4; }; E5C5077B2B0E144E00200A6A /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; E5C5077D2B0E145000200A6A /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; E5C507802B0E145000200A6A /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; @@ -1673,6 +1694,43 @@ path = Core; sourceTree = ""; }; + 694AC74B2B17AB73007E7B56 /* Dashboard */ = { + isa = PBXGroup; + children = ( + 69F1C29A2B2B297500E924AE /* UseCases */, + 69F1C2912B2B10B500E924AE /* Presentation */, + 69F1C2902B2B104B00E924AE /* CompositionRoot */, + 694AC74C2B17AB7F007E7B56 /* UI */, + ); + path = Dashboard; + sourceTree = ""; + }; + 694AC74C2B17AB7F007E7B56 /* UI */ = { + isa = PBXGroup; + children = ( + 694AC74D2B17AB9C007E7B56 /* DashboardView.swift */, + 69F1C2982B2B28DA00E924AE /* PIAConnectionButton.swift */, + ); + path = UI; + sourceTree = ""; + }; + 698C3B462B2B33260012D527 /* Dashboard */ = { + isa = PBXGroup; + children = ( + 698C3B472B2B33440012D527 /* Mocks */, + 698C3B4A2B2B34760012D527 /* PIAConnectionButtonViewModelTests.swift */, + ); + path = Dashboard; + sourceTree = ""; + }; + 698C3B472B2B33440012D527 /* Mocks */ = { + isa = PBXGroup; + children = ( + 698C3B482B2B33650012D527 /* VpnConnectionUseCaseTypeMock.swift */, + ); + path = Mocks; + sourceTree = ""; + }; 699F23AB2AFBA66000EBC5E6 /* Settings */ = { isa = PBXGroup; children = ( @@ -1708,6 +1766,48 @@ path = Util; sourceTree = ""; }; + 69F053442B1E054900AE0665 /* Shared */ = { + isa = PBXGroup; + children = ( + 69F053452B1E055300AE0665 /* UI */, + ); + path = Shared; + sourceTree = ""; + }; + 69F053452B1E055300AE0665 /* UI */ = { + isa = PBXGroup; + children = ( + 69F053462B1E056400AE0665 /* PIAColors+SwitUI.swift */, + 69F053492B1F1D2800AE0665 /* Colors.xcassets */, + 69F1C2942B2B216100E924AE /* PIA+Animation.swift */, + ); + path = UI; + sourceTree = ""; + }; + 69F1C2902B2B104B00E924AE /* CompositionRoot */ = { + isa = PBXGroup; + children = ( + 69F1C2922B2B10D400E924AE /* DashboardFactory.swift */, + ); + path = CompositionRoot; + sourceTree = ""; + }; + 69F1C2912B2B10B500E924AE /* Presentation */ = { + isa = PBXGroup; + children = ( + 69F1C2962B2B239000E924AE /* PIAConnectionButtonViewModel.swift */, + ); + path = Presentation; + sourceTree = ""; + }; + 69F1C29A2B2B297500E924AE /* UseCases */ = { + isa = PBXGroup; + children = ( + 69F1C29B2B2B299300E924AE /* VpnConnectionUseCase.swift */, + ); + path = UseCases; + sourceTree = ""; + }; 82183D8425014F940033023F /* Menu */ = { isa = PBXGroup; children = ( @@ -2038,6 +2138,8 @@ isa = PBXGroup; children = ( E5C507A32B153A1800200A6A /* Login */, + 69F053442B1E054900AE0665 /* Shared */, + 694AC74B2B17AB73007E7B56 /* Dashboard */, E5C507792B0E144E00200A6A /* PIA_VPN_tvOSApp.swift */, E5C5077B2B0E144E00200A6A /* ContentView.swift */, E5C5077D2B0E145000200A6A /* Assets.xcassets */, @@ -2057,6 +2159,7 @@ E5C507892B0E145100200A6A /* PIA VPN-tvOSTests */ = { isa = PBXGroup; children = ( + 698C3B462B2B33260012D527 /* Dashboard */, E5C507A62B153E4800200A6A /* Login */, E5C5078A2B0E145100200A6A /* PIA_VPN_tvOSTests.swift */, ); @@ -2758,6 +2861,8 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 694AC74F2B17ADF9007E7B56 /* Images.xcassets in Resources */, + 69F0534A2B1F1D2800AE0665 /* Colors.xcassets in Resources */, E5C507812B0E145000200A6A /* Preview Assets.xcassets in Resources */, E5C5077E2B0E145000200A6A /* Assets.xcassets in Resources */, ); @@ -3270,21 +3375,28 @@ E5C507A52B153A2F00200A6A /* LoginViewModel.swift in Sources */, E5C507B72B17E75B00200A6A /* CheckLoginAvailability.swift in Sources */, E5AB286F2B27977000744E5F /* LoginFactory.swift in Sources */, + 69F1C29C2B2B299300E924AE /* VpnConnectionUseCase.swift in Sources */, E5AB288A2B29BDED00744E5F /* Foundation+PIA.swift in Sources */, E5C507A22B0FE40000200A6A /* LoginView.swift in Sources */, E5AB287D2B28E4E700744E5F /* UserAccountMapper.swift in Sources */, E5AB28792B28B4B300744E5F /* AccountInfo.swift in Sources */, E5C507B12B17E48900200A6A /* LoginWithCredentialsUseCase.swift in Sources */, E5C507B92B17E7A400200A6A /* ValidateCredentialsFormat.swift in Sources */, + 69F1C2992B2B28DA00E924AE /* PIAConnectionButton.swift in Sources */, E5C507AE2B17E45800200A6A /* LoginPresentableErrorMapper.swift in Sources */, E5AB28712B279B4000744E5F /* LoginProviderType.swift in Sources */, E5AB287B2B28B4C400744E5F /* Credentials.swift in Sources */, E5C5077C2B0E144E00200A6A /* ContentView.swift in Sources */, + 694AC74E2B17AB9C007E7B56 /* DashboardView.swift in Sources */, + 69F1C2932B2B10D400E924AE /* DashboardFactory.swift in Sources */, E5C5077A2B0E144E00200A6A /* PIA_VPN_tvOSApp.swift in Sources */, + 69F1C2952B2B216100E924AE /* PIA+Animation.swift in Sources */, E5C507C72B1FABFF00200A6A /* LoginDomainErrorMapperType.swift in Sources */, + 69F1C2972B2B239000E924AE /* PIAConnectionButtonViewModel.swift in Sources */, E5C507B32B17E5AD00200A6A /* LoginError.swift in Sources */, E5C507CA2B1FAC3600200A6A /* LoginDomainErrorMapper.swift in Sources */, E5AB28772B28B49A00744E5F /* UserAccount.swift in Sources */, + 69F053472B1E056400AE0665 /* PIAColors+SwitUI.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -3292,16 +3404,18 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 698C3B492B2B33650012D527 /* VpnConnectionUseCaseTypeMock.swift in Sources */, E5C507CC2B1FACE000200A6A /* LoginWithCredentialsUseCaseTests.swift in Sources */, E5C507A82B153E6B00200A6A /* LoginViewModelTests.swift in Sources */, E5AB286C2B2796E700744E5F /* LoginProviderMock.swift in Sources */, E5C507BE2B1F6FA600200A6A /* Equatable.swift in Sources */, E5AB28872B2911C900744E5F /* AccountProviderMock.swift in Sources */, + 698C3B4E2B2B3CBE0012D527 /* LoginIntegrationTests.swift in Sources */, E5C5078B2B0E145100200A6A /* PIA_VPN_tvOSTests.swift in Sources */, + 698C3B4B2B2B34760012D527 /* PIAConnectionButtonViewModelTests.swift in Sources */, + 698C3B4C2B2B3CA10012D527 /* Stubs+PIALibrary.swift in Sources */, E5AB287F2B28F6EF00744E5F /* Stubs.swift in Sources */, - E5AB28852B2902E700744E5F /* LoginProviderTests.swift in Sources */, - E5AB28832B2902E200744E5F /* Stubs+PIALibrary.swift in Sources */, - E5AB28752B279BB600744E5F /* LoginIntegrationTests.swift in Sources */, + 698C3B4D2B2B3CA60012D527 /* LoginProviderTests.swift in Sources */, E5C507C22B1F702700200A6A /* LoginWithCredentialsUseCaseMock.swift in Sources */, E5C507C42B1F72E700200A6A /* CheckLoginAvailabilityTests.swift in Sources */, E5C507C02B1F700C00200A6A /* CheckLoginAvailabilityMock.swift in Sources */, diff --git a/PIA VPN/Images.xcassets/PIAX/Dashboard/vpn-button.imageset/Contents.json b/PIA VPN/Images.xcassets/PIAX/Dashboard/vpn-button.imageset/Contents.json index fe7cfc82e..ef78548ea 100644 --- a/PIA VPN/Images.xcassets/PIAX/Dashboard/vpn-button.imageset/Contents.json +++ b/PIA VPN/Images.xcassets/PIAX/Dashboard/vpn-button.imageset/Contents.json @@ -1,12 +1,15 @@ { "images" : [ { - "idiom" : "universal", - "filename" : "icon-green.pdf" + "filename" : "icon-green.pdf", + "idiom" : "universal" } ], "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true } -} \ No newline at end of file +} diff --git a/PIAWidget/Assets.xcassets/vpn-button.imageset/Contents.json b/PIAWidget/Assets.xcassets/vpn-button.imageset/Contents.json index a4bb1831c..ef78548ea 100644 --- a/PIAWidget/Assets.xcassets/vpn-button.imageset/Contents.json +++ b/PIAWidget/Assets.xcassets/vpn-button.imageset/Contents.json @@ -8,5 +8,8 @@ "info" : { "author" : "xcode", "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true } }