diff --git a/CHANGELOG.md b/CHANGELOG.md index f85a7c48..36e04324 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - OpenVPN: Support for `--remote-random-hostname`. [tunnelkit#286](https://github.com/passepartoutvpn/tunnelkit/pull/286) +### Fixed + +- Restore "Reconnect" action in profiles. [#232](https://github.com/passepartoutvpn/passepartout-apple/pull/232) + ## 2.0.1 (2022-10-17) ### Added diff --git a/Passepartout/App/Constants/Theme.swift b/Passepartout/App/Constants/Theme.swift index de16a0f3..824550ce 100644 --- a/Passepartout/App/Constants/Theme.swift +++ b/Passepartout/App/Constants/Theme.swift @@ -245,6 +245,10 @@ extension View { "ellipsis.circle" } + var themeReconnectImage: String { + "arrow.clockwise" + } + var themeShortcutsImage: String { "mic" } @@ -290,10 +294,6 @@ extension View { "slider.horizontal.3" } - var themeProviderRefreshImage: String { - "arrow.clockwise" - } - var themeNetworkSettingsImage: String { // "network" "globe" diff --git a/Passepartout/App/Views/OrganizerView+Profiles.swift b/Passepartout/App/Views/OrganizerView+Profiles.swift index 991af740..587f7ed6 100644 --- a/Passepartout/App/Views/OrganizerView+Profiles.swift +++ b/Passepartout/App/Views/OrganizerView+Profiles.swift @@ -85,8 +85,7 @@ extension OrganizerView { } label: { profileLabel(forHeader: header) }.contextMenu { - duplicateButton(forHeader: header) - deleteButton(forHeader: header) + ProfileContextMenu(header: header) } } @@ -97,23 +96,6 @@ extension OrganizerView { ) } - private func duplicateButton(forHeader header: Profile.Header) -> some View { - ProfileView.DuplicateButton( - header: header, - setAsCurrent: false - ) - } - - private func deleteButton(forHeader header: Profile.Header) -> some View { - DestructiveButton { - withAnimation { - profileManager.removeProfiles(withIds: [header.id]) - } - } label: { - Label(L10n.Global.Strings.delete, systemImage: themeDeleteImage) - } - } - private var sortedHeaders: [Profile.Header] { profileManager.headers .sorted() @@ -146,3 +128,52 @@ extension OrganizerView { } } } + +extension OrganizerView { + struct ProfileContextMenu: View { + @ObservedObject private var profileManager: ProfileManager + + @ObservedObject private var currentVPNState: ObservableVPNState + + let header: Profile.Header + + init(header: Profile.Header) { + profileManager = .shared + currentVPNState = .shared + self.header = header + } + + var body: some View { + if #available(iOS 16, *), isActiveProfileNotDisconnected { + reconnectButton + } + duplicateButton + deleteButton + } + + private var isActiveProfileNotDisconnected: Bool { + profileManager.isActiveProfile(header.id) && currentVPNState.vpnStatus != .disconnected + } + + private var reconnectButton: some View { + ProfileView.ReconnectButton() + } + + private var duplicateButton: some View { + ProfileView.DuplicateButton( + header: header, + setAsCurrent: false + ) + } + + private var deleteButton: some View { + DestructiveButton { + withAnimation { + profileManager.removeProfiles(withIds: [header.id]) + } + } label: { + Label(L10n.Global.Strings.delete, systemImage: themeDeleteImage) + } + } + } +} diff --git a/Passepartout/App/Views/ProfileView+MainMenu.swift b/Passepartout/App/Views/ProfileView+MainMenu.swift index 1cc736ef..1921bd25 100644 --- a/Passepartout/App/Views/ProfileView+MainMenu.swift +++ b/Passepartout/App/Views/ProfileView+MainMenu.swift @@ -42,6 +42,8 @@ extension ProfileView { @ObservedObject private var vpnManager: VPNManager + @ObservedObject private var currentVPNState: ObservableVPNState + @ObservedObject private var currentProfile: ObservableProfile private var header: Profile.Header { @@ -61,6 +63,7 @@ extension ProfileView { init(currentProfile: ObservableProfile, modalType: Binding) { profileManager = .shared vpnManager = .shared + currentVPNState = .shared self.currentProfile = currentProfile _modalType = modalType } @@ -90,10 +93,20 @@ extension ProfileView { private var mainView: some View { Menu { - shortcutsButton + if isActiveProfileNotDisconnected { + ReconnectButton() + } + ShortcutsButton( + modalType: $modalType + ) Divider() - renameButton - duplicateButton + RenameButton( + modalType: $modalType + ) + DuplicateButton( + header: header, + setAsCurrent: true + ) uninstallVPNButton Divider() deleteProfileButton @@ -102,25 +115,10 @@ extension ProfileView { } } - private var shortcutsButton: some View { - ShortcutsButton( - modalType: $modalType - ) - } - - private var renameButton: some View { - RenameButton( - modalType: $modalType - ) + private var isActiveProfileNotDisconnected: Bool { + profileManager.isActiveProfile(header.id) && currentVPNState.vpnStatus != .disconnected } - private var duplicateButton: some View { - DuplicateButton( - header: currentProfile.value.header, - setAsCurrent: true - ) - } - private var uninstallVPNButton: some View { Button { actionSheetType = .uninstallVPN @@ -149,6 +147,26 @@ extension ProfileView { } } } +} + +extension ProfileView { + struct ReconnectButton: View { + @ObservedObject private var vpnManager: VPNManager + + init() { + vpnManager = .shared + } + + var body: some View { + Button { + Task { @MainActor in + await vpnManager.reconnect() + } + } label: { + Label(L10n.Global.Strings.reconnect, systemImage: themeReconnectImage) + } + } + } struct ShortcutsButton: View { @ObservedObject private var productManager: ProductManager