diff --git a/Passepartout.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Passepartout.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 82373e6d..d0a2897a 100644 --- a/Passepartout.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Passepartout.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -41,7 +41,7 @@ "kind" : "remoteSourceControl", "location" : "git@github.com:passepartoutvpn/passepartoutkit-source", "state" : { - "revision" : "db4c0f0c01c237a7b50aa0f5c4acb33316c799f0" + "revision" : "0d7f912460ca5365740d7196fe5db5a38e23d3e1" } }, { diff --git a/Passepartout/Library/Package.swift b/Passepartout/Library/Package.swift index 65d3ffff..aff8a6a6 100644 --- a/Passepartout/Library/Package.swift +++ b/Passepartout/Library/Package.swift @@ -48,7 +48,7 @@ let package = Package( ], dependencies: [ // .package(url: "git@github.com:passepartoutvpn/passepartoutkit-source", from: "0.11.0"), - .package(url: "git@github.com:passepartoutvpn/passepartoutkit-source", revision: "db4c0f0c01c237a7b50aa0f5c4acb33316c799f0"), + .package(url: "git@github.com:passepartoutvpn/passepartoutkit-source", revision: "0d7f912460ca5365740d7196fe5db5a38e23d3e1"), // .package(path: "../../../passepartoutkit-source"), .package(url: "git@github.com:passepartoutvpn/passepartoutkit-source-openvpn-openssl", from: "0.9.1"), // .package(url: "git@github.com:passepartoutvpn/passepartoutkit-source-openvpn-openssl", revision: "031863a1cd683962a7dfe68e20b91fa820a1ecce"), diff --git a/Passepartout/Library/Sources/AppUIMain/Views/App/AppCoordinator.swift b/Passepartout/Library/Sources/AppUIMain/Views/App/AppCoordinator.swift index 8e00bd99..6cd0261a 100644 --- a/Passepartout/Library/Sources/AppUIMain/Views/App/AppCoordinator.swift +++ b/Passepartout/Library/Sources/AppUIMain/Views/App/AppCoordinator.swift @@ -168,10 +168,10 @@ extension AppCoordinator { enterDetail(of: profile) }, onEditProviderEntity: { - guard let pair = $0.firstProviderModuleWithMetadata else { + guard let pair = $0.selectedProvider else { return } - present(.editProviderEntity($0, pair.0, pair.1)) + present(.editProviderEntity($0, pair.module, pair.selection)) }, onMigrateProfiles: { modalRoute = .migrateProfiles diff --git a/Passepartout/Library/Sources/AppUIMain/Views/App/InstalledProfileView.swift b/Passepartout/Library/Sources/AppUIMain/Views/App/InstalledProfileView.swift index 0c7420da..68390ba1 100644 --- a/Passepartout/Library/Sources/AppUIMain/Views/App/InstalledProfileView.swift +++ b/Passepartout/Library/Sources/AppUIMain/Views/App/InstalledProfileView.swift @@ -134,12 +134,12 @@ private extension InstalledProfileView { var providerSelectorButton: some View { profile? - .firstProviderModuleWithMetadata - .map { _, provider in + .selectedProvider + .map { _, selection in Button { flow?.onEditProviderEntity(profile!) } label: { - providerSelectorLabel(with: provider) + providerSelectorLabel(with: selection) } .buttonStyle(.plain) } diff --git a/Passepartout/Library/Sources/AppUIMain/Views/App/ProfileContextMenu.swift b/Passepartout/Library/Sources/AppUIMain/Views/App/ProfileContextMenu.swift index 2d7929ac..995ff0ea 100644 --- a/Passepartout/Library/Sources/AppUIMain/Views/App/ProfileContextMenu.swift +++ b/Passepartout/Library/Sources/AppUIMain/Views/App/ProfileContextMenu.swift @@ -87,7 +87,7 @@ private extension ProfileContextMenu { var providerConnectToButton: some View { profile? - .firstProviderModuleWithMetadata + .selectedProvider .map { _ in Button(Strings.Ui.ProfileContext.connectTo) { flow?.onEditProviderEntity(profile!) diff --git a/Passepartout/Library/Sources/AppUIMain/Views/Diagnostics/ReportIssueButton.swift b/Passepartout/Library/Sources/AppUIMain/Views/Diagnostics/ReportIssueButton.swift index 4213f4fd..84176883 100644 --- a/Passepartout/Library/Sources/AppUIMain/Views/Diagnostics/ReportIssueButton.swift +++ b/Passepartout/Library/Sources/AppUIMain/Views/Diagnostics/ReportIssueButton.swift @@ -58,7 +58,7 @@ struct ReportIssueButton { } var currentProvider: (ProviderID, Date?)? { - guard let id = installedProfile?.firstProviderModuleWithMetadata?.1.id else { + guard let id = installedProfile?.selectedProvider?.selection.id else { return nil } let lastUpdate = providerManager.lastUpdate(for: id) diff --git a/Passepartout/Library/Sources/AppUITV/Views/Profile/ActiveProfileView.swift b/Passepartout/Library/Sources/AppUITV/Views/Profile/ActiveProfileView.swift index ef601246..52245c83 100644 --- a/Passepartout/Library/Sources/AppUITV/Views/Profile/ActiveProfileView.swift +++ b/Passepartout/Library/Sources/AppUITV/Views/Profile/ActiveProfileView.swift @@ -54,27 +54,31 @@ struct ActiveProfileView: View { var errorHandler: ErrorHandler var body: some View { - VStack { + VStack(spacing: .zero) { + Spacer() + VStack { - currentProfileView - statusView - } - .padding(.bottom) + VStack { + currentProfileView + statusView + } + .padding(.bottom) - profile.map { - detailView(for: $0) - } - .padding(.bottom) + profile.map { + detailView(for: $0) + } + .padding(.bottom) - Group { - toggleConnectionButton - switchProfileButton + Group { + toggleConnectionButton + switchProfileButton + } + .clipShape(RoundedRectangle(cornerRadius: 50)) } - .clipShape(RoundedRectangle(cornerRadius: 50)) + .padding(.horizontal, 100) + + Spacer() } - .padding([.top, .horizontal], 100) - - Spacer() } } @@ -96,28 +100,19 @@ private extension ActiveProfileView { func detailView(for profile: Profile) -> some View { VStack(spacing: 10) { - if let connectionModule = profile.modules.first(where: { $0 is ConnectionModule }) { - HStack { - Text(Strings.Global.protocol) - .fontWeight(.light) - Spacer() + if let connectionModule = profile.firstConnectionModule(ifActive: true) { + DetailRowView(title: Strings.Global.protocol) { Text(connectionModule.moduleHandler.id.name) } } - if let providerPair = profile.firstProviderModuleWithMetadata { - if let provider = providerManager.provider(withId: providerPair.1.id) { - HStack { - Text(Strings.Global.provider) - .fontWeight(.light) - Spacer() - Text(provider.description) + if let pair = profile.selectedProvider { + if let metadata = providerManager.provider(withId: pair.selection.id) { + DetailRowView(title: Strings.Global.provider) { + Text(metadata.description) } } - if let entity = providerPair.1.entity { - HStack { - Text(Strings.Global.country) - .fontWeight(.light) - Spacer() + if let entity = pair.selection.entity { + DetailRowView(title: Strings.Global.country) { ThemeCountryText(entity.header.countryCode) } } @@ -133,12 +128,8 @@ private extension ActiveProfileView { nextProfileId: .constant(nil), interactiveManager: interactiveManager, errorHandler: errorHandler, - onProviderEntityRequired: { _ in - // FIXME: #788, TV missing provider entity - }, - onPurchaseRequired: { _ in - // FIXME: #788, TV purchase required - }, + onProviderEntityRequired: onProviderEntityRequired, + onPurchaseRequired: onPurchaseRequired, label: { Text($0 ? Strings.Global.connect : Strings.Global.disconnect) .frame(maxWidth: .infinity) @@ -174,6 +165,36 @@ private extension ActiveProfileView { // MARK: - +private extension ActiveProfileView { + func onProviderEntityRequired(_ profile: Profile) { + // FIXME: #788, TV missing provider entity + } + + func onPurchaseRequired(_ features: Set) { + // FIXME: #788, TV purchase required + } +} + +// MARK: - Subviews + +private struct DetailRowView: View where Content: View { + let title: String + + @ViewBuilder + let content: Content + + var body: some View { + HStack { + Text(title) + .fontWeight(.light) + Spacer() + content + } + } +} + +// MARK: - Previews + #Preview("Host") { let profile: Profile = { do { diff --git a/Passepartout/Library/Sources/AppUITV/Views/Profile/ProfileListView.swift b/Passepartout/Library/Sources/AppUITV/Views/Profile/ProfileListView.swift index 1d5e68e3..21011937 100644 --- a/Passepartout/Library/Sources/AppUITV/Views/Profile/ProfileListView.swift +++ b/Passepartout/Library/Sources/AppUITV/Views/Profile/ProfileListView.swift @@ -81,12 +81,8 @@ private extension ProfileListView { nextProfileId: .constant(nil), interactiveManager: interactiveManager, errorHandler: errorHandler, - onProviderEntityRequired: { _ in - // FIXME: #788, TV missing provider entity - }, - onPurchaseRequired: { _ in - // FIXME: #788, TV purchase required - }, + onProviderEntityRequired: onProviderEntityRequired, + onPurchaseRequired: onPurchaseRequired, label: { _ in toggleView(for: header) } @@ -107,6 +103,18 @@ private extension ProfileListView { // MARK: - +private extension ProfileListView { + func onProviderEntityRequired(_ profile: Profile) { + // FIXME: #788, TV missing provider entity + } + + func onPurchaseRequired(_ features: Set) { + // FIXME: #788, TV purchase required + } +} + +// MARK: - Previews + #Preview("List") { ContentPreview(profileManager: .mock) } diff --git a/Passepartout/Library/Sources/AppUITV/Views/Profile/ProfileView.swift b/Passepartout/Library/Sources/AppUITV/Views/Profile/ProfileView.swift index 48d54518..2da4f628 100644 --- a/Passepartout/Library/Sources/AppUITV/Views/Profile/ProfileView.swift +++ b/Passepartout/Library/Sources/AppUITV/Views/Profile/ProfileView.swift @@ -48,7 +48,7 @@ struct ProfileView: View, TunnelInstallationProviding { var tunnel: ExtendedTunnel @State - private var showsSidePanel = false + var showsSidePanel = false @FocusState private var focusedField: Field? @@ -185,10 +185,20 @@ private extension ProfileView { // MARK: - -#Preview { +#Preview("List") { ProfileView( profileManager: .mock, - tunnel: .mock + tunnel: .mock, + showsSidePanel: true + ) + .withMockEnvironment() +} + +#Preview("Empty") { + ProfileView( + profileManager: ProfileManager(profiles: []), + tunnel: .mock, + showsSidePanel: true ) .withMockEnvironment() } diff --git a/Passepartout/Library/Sources/UILibrary/Views/UI/TunnelToggleButton.swift b/Passepartout/Library/Sources/UILibrary/Views/UI/TunnelToggleButton.swift index 65e218c0..8d6d9ced 100644 --- a/Passepartout/Library/Sources/UILibrary/Views/UI/TunnelToggleButton.swift +++ b/Passepartout/Library/Sources/UILibrary/Views/UI/TunnelToggleButton.swift @@ -113,7 +113,7 @@ private extension TunnelToggleButton { if canConnect { // provider module -> check requirements - if let providerModule = profile.firstProviderModule, + if let providerModule = profile.activeProviderModule, providerModule.isProviderRequired { // missing required provider -> show error