Skip to content

Commit

Permalink
PIA-886: Dashboard selected region and Quick Connect sections (#54)
Browse files Browse the repository at this point in the history
* PIA-886: Selected region dashboard section (UI)

* PIA-886: Quick connect  dashboard section (UI)
  • Loading branch information
kp-laura-sempere authored Jan 4, 2024
1 parent da934a7 commit 14d53bc
Show file tree
Hide file tree
Showing 23 changed files with 745 additions and 70 deletions.
52 changes: 47 additions & 5 deletions PIA VPN-tvOS/Dashboard/CompositionRoot/DashboardFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,7 @@ import PIALibrary
class DashboardFactory {

static func makeDashboardView() -> DashboardView {
return DashboardView(
viewModel: makeDashboardViewModel(),
connectionButton: makePIAConnectionButton()
)
return DashboardView(viewModel: makeDashboardViewModel())
}

static func makeDashboardViewModel() -> DashboardViewModel {
Expand Down Expand Up @@ -36,7 +33,52 @@ extension DashboardFactory {
}

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

private static func makeSelectedServerUserCase() -> SelectedServerUseCaseType {
return SelectedServerUseCase(serverProvider: makeServerProvider())
}

private static func makeSelectedServerViewModel() -> SelectedServerViewModel {
return SelectedServerViewModel(useCase: makeSelectedServerUserCase())
}

internal static func makeSelectedServerView() -> SelectedServerView {
SelectedServerView(viewModel: makeSelectedServerViewModel())
}

}


// MARK: QuickConnect section

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)
}

static func makeQuickConnectButton(for server: ServerType, delegate: QuickConnectButtonViewModelDelegate?) -> QuickConnectButton {
QuickConnectButton(viewModel: makeQuickConnectButtonViewModel(for: server, delegate: delegate))
}

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

static func makeQuickConnectView() -> QuickConnectView {
QuickConnectView(viewModel: makeQuickConnectViewModel())
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@

import Foundation

protocol QuickConnectButtonViewModelDelegate: AnyObject {
func quickConnectButtonViewModel(didSelect server: ServerType)
}

class QuickConnectButtonViewModel: ObservableObject {

private let server: ServerType

@Published var flagName = ""

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())"
}


}
33 changes: 33 additions & 0 deletions PIA VPN-tvOS/Dashboard/Presentation/QuickConnectViewModel.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@

import Foundation

class QuickConnectViewModel: ObservableObject {

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

private let connectUseCase: VpnConnectionUseCaseType
private let selectedServerUseCase: SelectedServerUseCaseType


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

updateStatus()
}

func updateStatus() {
servers = selectedServerUseCase.getHistoricalServers()
}

}

extension QuickConnectViewModel: QuickConnectButtonViewModelDelegate {

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

}

20 changes: 20 additions & 0 deletions PIA VPN-tvOS/Dashboard/Presentation/SelectedServerViewModel.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@

import Foundation


class SelectedServerViewModel: ObservableObject {
let selectedSeverSectionTitle = L10n.Localizable.Tiles.Region.title.uppercased()
@Published var serverName: String = ""

let useCase: SelectedServerUseCaseType

init(useCase: SelectedServerUseCaseType) {
self.useCase = useCase
updateState()
}

private func updateState() {
serverName = useCase.getSelectedServer().name
}

}
53 changes: 47 additions & 6 deletions PIA VPN-tvOS/Dashboard/UI/DashboardView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,32 @@ struct DashboardView: View {
let viewHeight = UIScreen.main.bounds.height

let viewModel: DashboardViewModel
let connectionButton: PIAConnectionButton

var body: some View {
VStack {
VStack(spacing: 20) {
connectionButton
VStack(alignment: .leading) {
DashboardConnectionButtonSection()
.padding()

Divider()

SelectedServerSection()
.padding()

Divider()

QuickConnectSection()
.frame(width: (viewWidth/2))

Divider()

// TODO: Remove logout button from the Dashboard
// when we have it in the settings screen
Button {
viewModel.logOut()
} label: {
Text("LogOut")
}
.padding()

}
.frame(maxWidth: (viewWidth/2))
Expand All @@ -33,9 +45,38 @@ struct DashboardView: View {
}
}

// MARK: Dashboard sections

fileprivate struct DashboardConnectionButtonSection: View {
var body: some View {
HStack {
Spacer()
DashboardFactory.makePIAConnectionButton()
Spacer()
}
}
}

fileprivate struct SelectedServerSection: View {
var body: some View {
Button {
// TODO: Navigate to the regions list
} label: {
DashboardFactory.makeSelectedServerView()
}
.buttonStyle(.plain)
.buttonBorderShape(.roundedRectangle(radius: 4))
}
}

fileprivate struct QuickConnectSection: View {
var body: some View {
DashboardFactory.makeQuickConnectView()
}
}

#Preview {
DashboardView(
viewModel: DashboardFactory.makeDashboardViewModel(),
connectionButton: DashboardFactory.makePIAConnectionButton()
viewModel: DashboardFactory.makeDashboardViewModel()
)
}
29 changes: 29 additions & 0 deletions PIA VPN-tvOS/Dashboard/UI/QuickConnectButton.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
//
// QuickConnectButton.swift
// PIA VPN-tvOS
//
// Created by Laura S on 12/26/23.
// Copyright © 2023 Private Internet Access Inc. All rights reserved.
//

import SwiftUI

struct QuickConnectButton: View {
@ObservedObject var viewModel: QuickConnectButtonViewModel

var size: CGFloat = 65

var body: some View {
Button {
viewModel.connectButtonDidTap()
} label: {
Image(viewModel.flagName)
.resizable()
.frame(width: size, height: size*0.75)
}
.buttonStyle(.card)
.buttonBorderShape(.roundedRectangle(radius: 2))

}
}

22 changes: 22 additions & 0 deletions PIA VPN-tvOS/Dashboard/UI/QuickConnectView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@

import SwiftUI

struct QuickConnectView: View {
@ObservedObject var viewModel: QuickConnectViewModel

let rows = [ GridItem(.adaptive(minimum: 80)) ]
var body: some View {
ScrollView(.horizontal) {
LazyHGrid(rows: rows) {
ForEach(viewModel.servers, id: \.regionIdentifier) { item in
DashboardFactory.makeQuickConnectButton(for: item, delegate: viewModel)
}
}
}

}
}

#Preview {
QuickConnectView(viewModel: DashboardFactory.makeQuickConnectViewModel())
}
38 changes: 38 additions & 0 deletions PIA VPN-tvOS/Dashboard/UI/SelectedServerView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@

import SwiftUI

struct SelectedServerView: View {
@Environment(\.colorScheme) var colorScheme

@ObservedObject var viewModel: SelectedServerViewModel

var body: some View {

HStack(alignment: .top) {
VStack(alignment: .leading) {
Text(viewModel.selectedSeverSectionTitle)
.font(.callout)
Text(viewModel.serverName)
.font(.caption)
}

Spacer()

HStack(alignment: .center) {
if colorScheme == .light {
Image.map
} else {
Image.map
.blendMode(.hardLight)
}

Image(systemName: "chevron.forward")
.foregroundStyle(Color.gray)
.frame(width: 50)

}

}
}
}

67 changes: 67 additions & 0 deletions PIA VPN-tvOS/Dashboard/UseCases/SelectedServerUseCase.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@

import Foundation
import PIALibrary

protocol SelectedServerUseCaseType {
func getSelectedServer() -> ServerType
func getHistoricalServers() -> [ServerType]
}

class SelectedServerUseCase: SelectedServerUseCaseType {

let serverProvider: ServerProviderType

init(serverProvider: ServerProviderType) {
self.serverProvider = serverProvider
}

func getSelectedServer() -> ServerType {
// TODO: get real selected server
return automaticServer()
}

func getHistoricalServers() -> [ServerType] {
// TODO: Remove this `guard` statement when we get
// the real historical servers
guard !serverProvider.historicalServers.isEmpty else {
return Self.generateDemoServers()
}
return serverProvider.historicalServers
}

private func automaticServer() -> ServerType {
Server(
serial: "",
name: L10n.Localizable.Global.automatic,
country: "universal",
hostname: "auto.bogus.domain",
pingAddress: nil,
regionIdentifier: "auto"
)
}
}

// TODO: Remove this extension when we have implemented
// getting the real historical servers (for QuickConnect)
extension SelectedServerUseCase {
private static func generateDemoServers() -> [ServerType] {
var servers: [ServerType] = []
func createDemoServer(name: String, country: String, regionIdentifier: String) -> ServerType {
Server(serial: "", name: name, country: country, hostname: "auto.bogus.domain", pingAddress: nil, regionIdentifier: regionIdentifier)
}

servers.append(createDemoServer(name: "Spain", country: "ES", regionIdentifier: "Spain"))
servers.append(createDemoServer(name: "France", country: "FR", regionIdentifier: "France"))
servers.append(createDemoServer(name: "Germany", country: "DE", regionIdentifier: "Germany"))
servers.append(createDemoServer(name: "Netherlands", country: "NL", regionIdentifier: "Netherlands"))
servers.append(createDemoServer(name: "Australia", country: "AU", regionIdentifier: "Australia"))
servers.append(createDemoServer(name: "Portugal", country: "PT", regionIdentifier: "Portugal"))
servers.append(createDemoServer(name: "Ireland", country: "IR", regionIdentifier: "Ireland"))
servers.append(createDemoServer(name: "Italy", country: "IT", regionIdentifier: "Italy"))
servers.append(createDemoServer(name: "Canada", country: "CA", regionIdentifier: "Canada"))


return servers

}
}
Loading

0 comments on commit 14d53bc

Please sign in to comment.