Skip to content

Commit

Permalink
PIA-1298: Home screen design (#79)
Browse files Browse the repository at this point in the history
* PIA-1298: Add publisher to retrieve the selected location

* PIA-1298: Add single source of truth for connectivity state to show in UI

* PIA-1298: Update dashboard selected location section

* PIA-1298: Quick connect section and update regions list styles

* PIA-1298: Add Connection state monitor tests
  • Loading branch information
kp-laura-sempere authored Feb 14, 2024
1 parent aa4daa6 commit 8493427
Show file tree
Hide file tree
Showing 56 changed files with 1,155 additions and 313 deletions.
6 changes: 6 additions & 0 deletions PIA VPN-tvOS/Assets.xcassets/Dashboard/Contents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"images" : [
{
"filename" : "connect.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true,
"template-rendering-intent" : "template"
}
}
Binary file not shown.
6 changes: 6 additions & 0 deletions PIA VPN-tvOS/Assets.xcassets/Regions/Contents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"images" : [
{
"filename" : "icon-smart-location-highlighted.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true
}
}
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"images" : [
{
"filename" : "icon-smart-location.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true
}
}
Binary file not shown.
27 changes: 7 additions & 20 deletions PIA VPN-tvOS/Dashboard/CompositionRoot/DashboardFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,8 @@ class DashboardFactory {
return DashboardViewModel(accountProvider: defaultAccountProvider, appRouter: AppRouterFactory.makeAppRouter(), navigationDestination: RegionsDestinations.serversList)
}

static func makePIAConnectionButton(size: CGFloat = 160, lineWidth: CGFloat = 6) -> PIAConnectionButton {
static func makePIAConnectionButton() -> PIAConnectionButton {
return PIAConnectionButton(
size: size,
lineWidth: lineWidth,
viewModel: makePIAConnectionButtonViewModel()
)
}
Expand All @@ -29,20 +27,17 @@ class DashboardFactory {

extension DashboardFactory {
private static func makePIAConnectionButtonViewModel() -> PIAConnectionButtonViewModel {
return PIAConnectionButtonViewModel(useCase: makeVpnConnectionUseCase(),
vpnStatusMonitor: StateMonitorsFactory.makeVPNStatusMonitor())
return PIAConnectionButtonViewModel(useCase: VpnConnectionFactory.makeVpnConnectionUseCase, connectionStateMonitor: StateMonitorsFactory.makeConnectionStateMonitor)
}

private static func makeVpnConnectionUseCase() -> VpnConnectionUseCaseType {
return VpnConnectionUseCase(serverProvider: makeServerProvider())
}


private static func makeSelectedServerUserCase() -> SelectedServerUseCaseType {
return SelectedServerUseCase(serverProvider: makeServerProvider(), clientPreferences: Client.preferences)
return SelectedServerUseCase(serverProvider: VpnConnectionFactory.makeServerProvider(), clientPreferences: RegionsSelectionFactory.makeClientPreferences)
}

private static func makeSelectedServerViewModel() -> SelectedServerViewModel {
return SelectedServerViewModel(useCase: makeSelectedServerUserCase())
return SelectedServerViewModel(useCase: makeSelectedServerUserCase(), routerAction: .navigate(router: AppRouterFactory.makeAppRouter(), destination: RegionsDestinations.serversList))
}

internal static func makeSelectedServerView() -> SelectedServerView {
Expand All @@ -56,15 +51,7 @@ extension DashboardFactory {

extension DashboardFactory {

static func makeServerProvider() -> ServerProviderType {
guard let defaultServerProvider: DefaultServerProvider =
Client.providers.serverProvider as? DefaultServerProvider else {
fatalError("Incorrect server provider type")
}

return defaultServerProvider

}


static internal func makeQuickConnectButtonViewModel(for server: ServerType, delegate: QuickConnectButtonViewModelDelegate?) -> QuickConnectButtonViewModel {
QuickConnectButtonViewModel(server: server, delegate: delegate)
Expand All @@ -75,7 +62,7 @@ extension DashboardFactory {
}

static internal func makeQuickConnectViewModel() -> QuickConnectViewModel {
QuickConnectViewModel(connectUseCase: makeVpnConnectionUseCase(), selectedServerUseCase: makeSelectedServerUserCase())
QuickConnectViewModel(selectedServerUseCase: makeSelectedServerUserCase(), regionsUseCase: RegionsSelectionFactory.makeRegionsListUseCase())
}

static func makeQuickConnectView() -> QuickConnectView {
Expand Down
4 changes: 0 additions & 4 deletions PIA VPN-tvOS/Dashboard/Presentation/DashboardViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,6 @@ class DashboardViewModel: ObservableObject {
self.navigationDestination = navigationDestination
}

func regionSelectionSectionWasTapped() {
appRouter.navigate(to: navigationDestination)
}


// TODO: Remove this functionality from Dashboard when we have it on the settings menu
func logOut() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,64 +5,69 @@ import SwiftUI
import PIALibrary
import Combine

extension VPNStatus {
func toConnectionButtonState() -> PIAConnectionButtonViewModel.State {
switch self {
case .connected:
return .connected
case .connecting:
return .connecting
case .disconnecting:
return .disconnecting
default:
return .disconnected
}
}
}


class PIAConnectionButtonViewModel: ObservableObject {
enum State {
case disconnected
case connecting
case connected
case disconnecting


@Published var state: ConnectionState = .disconnected
var animating: Bool {
state == .connecting || state == .disconnecting
}

@Published var state: State = .disconnected
@Published var isShowingErrorAlert: Bool = false

private let vpnConnectionUseCase: VpnConnectionUseCaseType
private let vpnStatusMonitor: VPNStatusMonitorType
private let connectionStateMonitor: ConnectionStateMonitorType
private var cancellables = Set<AnyCancellable>()

init(useCase: VpnConnectionUseCaseType, vpnStatusMonitor: VPNStatusMonitorType) {
init(useCase: VpnConnectionUseCaseType, connectionStateMonitor: ConnectionStateMonitorType) {
self.vpnConnectionUseCase = useCase
self.vpnStatusMonitor = vpnStatusMonitor
self.connectionStateMonitor = connectionStateMonitor

addObservers()
}

var errorAlertTitle: String {
L10n.Localizable.ErrorAlert.ConnectionError.NoNetwork.title
}

var errorAlertMessage: String {
L10n.Localizable.ErrorAlert.ConnectionError.NoNetwork.message
}

var errorAlertRetryActionTitle: String {
L10n.Localizable.ErrorAlert.ConnectionError.NoNetwork.RetryAction.title
}

var errorAlertCloseActionTitle: String {
L10n.Localizable.Global.close
}


private func addObservers() {
vpnStatusMonitor.getStatus().sink { [weak self] vpnStatus in
self?.state = vpnStatus.toConnectionButtonState()
}.store(in: &cancellables)
connectionStateMonitor.connectionStatePublisher
.sink { newConnectionState in
self.state = newConnectionState
}.store(in: &cancellables)

}

// Inner ring color and outer ring color
var tintColor: (Color, Color) {
var tintColor: Color {
switch state {
case .disconnected:
return (.pia_red_dark, .pia_red_dark)
return .pia_yellow_dark
case .connecting, .disconnecting:
return (.pia_yellow_dark, .pia_surface_container_secondary)
return .pia_yellow_dark
case .connected:
return (.pia_primary, .pia_primary)
return .pia_primary
case .error:
return .pia_red
default:
return .pia_yellow_dark
}
}

var animating: Bool {
state == .connecting ||
state == .disconnecting
}
}

// MARK: Connection
Expand All @@ -74,20 +79,33 @@ extension PIAConnectionButtonViewModel {
case .disconnected:
connect()
case .connecting:
break
disconnect()
case .connected:
disconnect()
case .disconnecting:
case .disconnecting, .unkown:
break
case .error:
connect()
}
}

private func connect() {
vpnConnectionUseCase.connect()
Task {
do {
try await vpnConnectionUseCase.connect()
} catch {
DispatchQueue.main.async {
self.isShowingErrorAlert = true
}
}
}
}

private func disconnect() {
vpnConnectionUseCase.disconnect()
Task {
try await vpnConnectionUseCase.disconnect()
}

}
}

Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,28 @@ class QuickConnectButtonViewModel: ObservableObject {

private let server: ServerType

@Published var flagName = ""

var flagName: String {
"flag-\(server.country.lowercased())"
}

var titleText: String {
server.country.uppercased()
}

weak var delegate: QuickConnectButtonViewModelDelegate?

init(server: ServerType, delegate: QuickConnectButtonViewModelDelegate?) {
self.server = server
self.delegate = delegate
updateStatus()

}

func connectButtonDidTap() {
delegate?.quickConnectButtonViewModel(didSelect: server)
}

private func updateStatus() {
flagName = "flag-\(server.country.lowercased())"
}



}
14 changes: 8 additions & 6 deletions PIA VPN-tvOS/Dashboard/Presentation/QuickConnectViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,28 @@ class QuickConnectViewModel: ObservableObject {

@Published private (set) var servers: [ServerType] = []

private let connectUseCase: VpnConnectionUseCaseType
private let selectedServerUseCase: SelectedServerUseCaseType
private let regionsUseCase: RegionsListUseCaseType


init(connectUseCase: VpnConnectionUseCaseType,
selectedServerUseCase: SelectedServerUseCaseType) {
self.connectUseCase = connectUseCase
init(selectedServerUseCase: SelectedServerUseCaseType,
regionsUseCase: RegionsListUseCaseType) {
self.selectedServerUseCase = selectedServerUseCase
self.regionsUseCase = regionsUseCase
}

func updateStatus() {
servers = selectedServerUseCase.getHistoricalServers().reversed()
let allHistoricalServers = selectedServerUseCase.getHistoricalServers().reversed().dropFirst()
servers = Array(allHistoricalServers.prefix(4))
}

}

extension QuickConnectViewModel: QuickConnectButtonViewModelDelegate {

func quickConnectButtonViewModel(didSelect server: ServerType) {
connectUseCase.connect(to: server)
regionsUseCase.select(server: server)
updateStatus()
}

}
Expand Down
Loading

0 comments on commit 8493427

Please sign in to comment.