Skip to content

Commit

Permalink
Add toggle in connection view
Browse files Browse the repository at this point in the history
  • Loading branch information
rablador committed Dec 16, 2024
1 parent 55ede8b commit ed14e91
Show file tree
Hide file tree
Showing 5 changed files with 196 additions and 15 deletions.
8 changes: 8 additions & 0 deletions ios/MullvadVPN.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -704,6 +704,7 @@
85EC620C2B838D10005AFFB5 /* MullvadAPIWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85557B132B5983CF00795FE1 /* MullvadAPIWrapper.swift */; };
85FB5A0C2B6903990015DCED /* WelcomePage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85FB5A0B2B6903990015DCED /* WelcomePage.swift */; };
85FB5A102B6960A30015DCED /* AccountDeletionPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85FB5A0F2B6960A30015DCED /* AccountDeletionPage.swift */; };
A90216DB2D0C3E03001626E3 /* HeaderBarSwiftUIHostedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A90216DA2D0C3E03001626E3 /* HeaderBarSwiftUIHostedView.swift */; };
A90763B02B2857D50045ADF0 /* Socks5ConnectCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = A90763A02B2857D50045ADF0 /* Socks5ConnectCommand.swift */; };
A90763B12B2857D50045ADF0 /* Socks5Endpoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = A90763A12B2857D50045ADF0 /* Socks5Endpoint.swift */; };
A90763B22B2857D50045ADF0 /* Socks5EndpointReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = A90763A22B2857D50045ADF0 /* Socks5EndpointReader.swift */; };
Expand Down Expand Up @@ -755,6 +756,7 @@
A988A3E22AFE54AC0008D2C7 /* AccountExpiry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A6F2FA62AFBB9AE006D0856 /* AccountExpiry.swift */; };
A988DF272ADE86ED00D807EF /* WireGuardObfuscationSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = A988DF252ADE86ED00D807EF /* WireGuardObfuscationSettings.swift */; };
A988DF2A2ADE880300D807EF /* TunnelSettingsV3.swift in Sources */ = {isa = PBXBuildFile; fileRef = A988DF282ADE880300D807EF /* TunnelSettingsV3.swift */; };
A98E31752D0B1CDC00C092B7 /* FeatureIndicatorsScrollContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A98E31742D0B1CDC00C092B7 /* FeatureIndicatorsScrollContainerView.swift */; };
A992DA202C24709F00DE7CE5 /* MullvadRustRuntime.h in Headers */ = {isa = PBXBuildFile; fileRef = A992DA1F2C24709F00DE7CE5 /* MullvadRustRuntime.h */; settings = {ATTRIBUTES = (Public, ); }; };
A992DA232C24709F00DE7CE5 /* MullvadRustRuntime.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A992DA1D2C24709F00DE7CE5 /* MullvadRustRuntime.framework */; };
A992DA242C24709F00DE7CE5 /* MullvadRustRuntime.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = A992DA1D2C24709F00DE7CE5 /* MullvadRustRuntime.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
Expand Down Expand Up @@ -2083,6 +2085,7 @@
A900E9BB2ACC609200C95F67 /* DevicesProxy+Stubs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DevicesProxy+Stubs.swift"; sourceTree = "<group>"; };
A900E9BD2ACC654100C95F67 /* APIProxy+Stubs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "APIProxy+Stubs.swift"; sourceTree = "<group>"; };
A900E9BF2ACC661900C95F67 /* AccessTokenManager+Stubs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AccessTokenManager+Stubs.swift"; sourceTree = "<group>"; };
A90216DA2D0C3E03001626E3 /* HeaderBarSwiftUIHostedView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeaderBarSwiftUIHostedView.swift; sourceTree = "<group>"; };
A90763A02B2857D50045ADF0 /* Socks5ConnectCommand.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Socks5ConnectCommand.swift; sourceTree = "<group>"; };
A90763A12B2857D50045ADF0 /* Socks5Endpoint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Socks5Endpoint.swift; sourceTree = "<group>"; };
A90763A22B2857D50045ADF0 /* Socks5EndpointReader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Socks5EndpointReader.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2137,6 +2140,7 @@
A98502022B627B120061901E /* LocalNetworkProbe.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalNetworkProbe.swift; sourceTree = "<group>"; };
A988DF252ADE86ED00D807EF /* WireGuardObfuscationSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WireGuardObfuscationSettings.swift; sourceTree = "<group>"; };
A988DF282ADE880300D807EF /* TunnelSettingsV3.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelSettingsV3.swift; sourceTree = "<group>"; };
A98E31742D0B1CDC00C092B7 /* FeatureIndicatorsScrollContainerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeatureIndicatorsScrollContainerView.swift; sourceTree = "<group>"; };
A98F1B502C19C48D003C869E /* EphemeralPeerExchangeActorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EphemeralPeerExchangeActorTests.swift; sourceTree = "<group>"; };
A992DA1D2C24709F00DE7CE5 /* MullvadRustRuntime.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = MullvadRustRuntime.framework; sourceTree = BUILT_PRODUCTS_DIR; };
A992DA1F2C24709F00DE7CE5 /* MullvadRustRuntime.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MullvadRustRuntime.h; sourceTree = "<group>"; };
Expand Down Expand Up @@ -4088,6 +4092,8 @@
F0B4957B2D03154200CFEC2A /* FeatureIndicatorsView.swift */,
F0ADF1D22D01B6B400299F09 /* FeatureIndicatorsViewModel.swift */,
7AFBE3882D08915D002335FC /* FI_TunnelViewController.swift */,
A98E31742D0B1CDC00C092B7 /* FeatureIndicatorsScrollContainerView.swift */,
A90216DA2D0C3E03001626E3 /* HeaderBarSwiftUIHostedView.swift */,
);
path = FeatureIndicators;
sourceTree = "<group>";
Expand Down Expand Up @@ -6113,6 +6119,7 @@
58CE5E66224146200008646E /* LoginViewController.swift in Sources */,
F0C6FA852A6A733700F521F0 /* InAppPurchaseInteractor.swift in Sources */,
58CEB2F92AFD136E00E6E088 /* UIBackgroundConfiguration+Extensions.swift in Sources */,
A98E31752D0B1CDC00C092B7 /* FeatureIndicatorsScrollContainerView.swift in Sources */,
5878F50029CDA742003D4BE2 /* UIView+AutoLayoutBuilder.swift in Sources */,
A98502032B627B120061901E /* LocalNetworkProbe.swift in Sources */,
7A6F2FA92AFD0842006D0856 /* CustomDNSDataSource.swift in Sources */,
Expand Down Expand Up @@ -6224,6 +6231,7 @@
5827B0BF2B14B37D00CCBBA1 /* Publisher+PreviousValue.swift in Sources */,
7A9CCCB62A96302800DD6A34 /* OutOfTimeCoordinator.swift in Sources */,
5827B0AA2B0F4C9100CCBBA1 /* EditAccessMethodViewControllerDelegate.swift in Sources */,
A90216DB2D0C3E03001626E3 /* HeaderBarSwiftUIHostedView.swift in Sources */,
F050AE5E2B739A73003F4EDB /* LocationDataSourceProtocol.swift in Sources */,
A99E5EE22B762ED30033F241 /* ProblemReportViewController+ViewManagement.swift in Sources */,
7A5869A22B502EA800640D27 /* MethodSettingsSectionIdentifier.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import UIKit
enum HeaderBarStyle {
case transparent, `default`, unsecured, secured

fileprivate func backgroundColor() -> UIColor {
func backgroundColor() -> UIColor {
switch self {
case .transparent:
return UIColor.clear
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,22 @@
// Copyright © 2024 Mullvad VPN AB. All rights reserved.
//

import MullvadREST
import MullvadSettings
import MullvadTypes
import Network
import SwiftUI

typealias ButtonAction = (ConnectionViewViewModel.TunnelControlAction) -> Void

struct ConnectionView: View {
@StateObject var viewModel: ConnectionViewViewModel
@StateObject var indicatorsViewModel: FeatureIndicatorsViewModel
@State var expandConnectionDetails = false

var action: ButtonAction?
var onContentUpdate: (() -> Void)?
var onChevronToggle: (() -> Void)?

var body: some View {
VStack(spacing: 22) {
Expand All @@ -33,6 +38,17 @@ struct ConnectionView: View {
if !indicatorsViewModel.chips.isEmpty {
FeatureIndicatorsView(viewModel: indicatorsViewModel)
}
ConnectionPanel(viewModel: viewModel, onChevronToggle: {
expandConnectionDetails.toggle()
}, isExpanded: $expandConnectionDetails)
Divider()
.background(UIColor.secondaryTextColor.color)
FeatureIndicatorsScrollContainerView(
isExpanded: $expandConnectionDetails,
content: { Text("Hello") }
)
.frame(maxWidth: .infinity)
.border(.white)

ButtonPanel(viewModel: viewModel, action: action)
}
Expand Down Expand Up @@ -60,31 +76,83 @@ struct ConnectionView: View {
indicatorsViewModel: FeatureIndicatorsViewModel(tunnelSettings: LatestTunnelSettings(), ipOverrides: [])
) { action in
print(action)
let selectedRelays = SelectedRelays(
entry: nil,
exit: SelectedRelay(
endpoint: MullvadEndpoint(
ipv4Relay: IPv4Endpoint(ip: .loopback, port: 42),
ipv4Gateway: IPv4Address.loopback,
ipv6Gateway: IPv6Address.loopback,
publicKey: Data()
),
hostname: "se-got-wg-001",
location: Location(
country: "Sweden",
countryCode: "se",
city: "Gothenburg",
cityCode: "got",
latitude: 42,
longitude: 42
)
),
retryAttempt: 0
)
let connectedState = TunnelState.connected(selectedRelays, isPostQuantum: true, isDaita: true)

return ZStack {
VStack {
HeaderBarSwiftUIHostedView()
.frame(maxHeight: 100)
ConnectionView(
viewModel: ConnectionViewViewModel(tunnelState: connectedState),
action: { action in print(action) },
onContentUpdate: { print("On content Update") },
onChevronToggle: { print("Chevron toggle") }
)
}
}
.background(UIColor.secondaryColor.color)
}

private struct ConnectionPanel: View {
@StateObject var viewModel: ConnectionViewViewModel
var onChevronToggle: (() -> Void)?
var isExpanded: Binding<Bool>

var body: some View {
VStack(alignment: .leading) {
Text(viewModel.localizedTitleForSecureLabel)
.textCase(.uppercase)
.font(.title3.weight(.semibold))
.foregroundStyle(viewModel.textColorForSecureLabel.color)
.padding(.bottom, 4)

if let countryAndCity = viewModel.titleForCountryAndCity, let server = viewModel.titleForServer {
Text(countryAndCity)
HStack(alignment: .top) {
VStack(alignment: .leading) {
Text(viewModel.localizedTitleForSecureLabel)
.textCase(.uppercase)
.font(.title3.weight(.semibold))
.foregroundStyle(UIColor.primaryTextColor.color)
Text(server)
.font(.body)
.foregroundStyle(UIColor.primaryTextColor.color.opacity(0.6))
.foregroundStyle(viewModel.textColorForSecureLabel.color)
.padding(.bottom, 4)
if let countryAndCity = viewModel.titleForCountryAndCity, let server = viewModel.titleForServer {
Text(countryAndCity)
.font(.title3.weight(.semibold))
.foregroundStyle(UIColor.primaryTextColor.color)
Text(server)
.font(.body)
.foregroundStyle(UIColor.primaryTextColor.color.opacity(0.6))
}
}
.accessibilityLabel(viewModel.localizedAccessibilityLabel)
if case .connected = viewModel.tunnelState {
if let onChevronToggle {
Spacer()
Button(action: onChevronToggle) {
Image(.iconChevron)
.renderingMode(.template)
.rotationEffect(isExpanded.wrappedValue ? .degrees(-90) : .degrees(90))
.frame(width: 44, height: 44, alignment: .topTrailing)
.foregroundStyle(.white)
.transaction { transaction in
transaction.animation = nil
}
}
}
}
}
.accessibilityLabel(viewModel.localizedAccessibilityLabel)
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
//
// FeatureIndicatorsScrollContainerView.swift
// MullvadVPN
//
// Created by Marco Nikic on 2024-12-12.
// Copyright © 2024 Mullvad VPN AB. All rights reserved.
//

import SwiftUI

struct FeatureIndicatorsScrollContainerView<ContentView: View>: View {
var isExpanded: Binding<Bool>
@ViewBuilder
let content: ContentView

var body: some View {
ScrollView {
content
.frame(maxWidth: .infinity)
.background(.blue)
}
.frame(maxHeight: isExpanded.wrappedValue ? .infinity : 40)
}
}

#Preview {
ExampleView().background(UIColor.secondaryColor.color)
}

private struct ExampleView: View {
@State var isExpanded = false

var body: some View {
VStack {
Button(action: {
isExpanded.toggle()
}, label: {
Text("Toggle layout")
})
FeatureIndicatorsScrollContainerView(isExpanded: $isExpanded) {
if isExpanded {
BigLayoutView()
} else {
SmallLayoutView()
}
}
}
}
}

private struct SmallLayoutView: View {
var body: some View {
HStack {
ForEach(0 ..< 3) { index in
Text("hehehjr \(index)")
}
}
}
}

private struct BigLayoutView: View {
var body: some View {
Group {
VStack {
ForEach(0 ..< 5) { _ in
HStack {
ForEach(0 ..< 8) { index in
Text("hello \(index)")
}
}
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//
// HeaderBarSwiftUIHostedView.swift
// MullvadVPN
//
// Created by Marco Nikic on 2024-12-13.
// Copyright © 2024 Mullvad VPN AB. All rights reserved.
//

import Foundation
import SwiftUI

struct HeaderBarSwiftUIHostedView: UIViewRepresentable {
typealias UIViewType = HeaderBarView

func makeUIView(context: Context) -> HeaderBarView {
let headerBarView = HeaderBarView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
headerBarView.translatesAutoresizingMaskIntoConstraints = false
headerBarView.insetsLayoutMarginsFromSafeArea = false

var headerBarPresentation = HeaderBarPresentation.default
headerBarView.backgroundColor = headerBarPresentation.style.backgroundColor()
headerBarView.showsDivider = headerBarPresentation.showsDivider

return headerBarView
}

func updateUIView(_ uiView: HeaderBarView, context: Context) {
print("update")
}
}

0 comments on commit ed14e91

Please sign in to comment.