Make shortcuts contextual in ProfileView

- Add toolbar item

- Target current profile

- Only list relevant shortcuts to profile
This commit is contained in:
Davide De Rosa 2022-04-18 16:42:17 +02:00
parent b1882dcf80
commit 480738d126
8 changed files with 234 additions and 248 deletions

View File

@ -31,6 +31,7 @@
0E3B7FD627E5173A00C66F13 /* ProfileView+VPN.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3B7FD527E5173A00C66F13 /* ProfileView+VPN.swift */; };
0E3B7FDA27E51A0200C66F13 /* ProfileView+Provider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3B7FD927E51A0200C66F13 /* ProfileView+Provider.swift */; };
0E3CD47F280DA14B007075C0 /* OrganizerView+AddMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3CD47E280DA14B007075C0 /* OrganizerView+AddMenu.swift */; };
0E3CD483280DAE92007075C0 /* ProfileView+MenuBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3CD482280DAE92007075C0 /* ProfileView+MenuBar.swift */; };
0E44689627B051C300A14CE4 /* ProfileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E44689527B051C300A14CE4 /* ProfileView.swift */; };
0E44689C27B11B5300A14CE4 /* AboutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E44689B27B11B5300A14CE4 /* AboutView.swift */; };
0E49F6BB27D7638300385834 /* EndpointAdvancedView+OpenVPN.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E49F6BA27D7638300385834 /* EndpointAdvancedView+OpenVPN.swift */; };
@ -105,7 +106,6 @@
0ED89C1727DE0E05008B36D6 /* IntentEditView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED89C1627DE0E05008B36D6 /* IntentEditView.swift */; };
0ED89C1C27DE3ABC008B36D6 /* ShortcutsView+Add.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED89C1B27DE3ABC008B36D6 /* ShortcutsView+Add.swift */; };
0ED89C1E27DE3F8D008B36D6 /* IntentAddView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED89C1D27DE3F8D008B36D6 /* IntentAddView.swift */; };
0ED89C2027DE423B008B36D6 /* ShortcutsView+ConnectTo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED89C1F27DE423B008B36D6 /* ShortcutsView+ConnectTo.swift */; };
0ED89C2527DE45A3008B36D6 /* ProfileHeaderRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED89C2427DE45A3008B36D6 /* ProfileHeaderRow.swift */; };
0EDE02C227F61C79000FBE3C /* EditableTextList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EDE02C127F61C79000FBE3C /* EditableTextList.swift */; };
0EE11CD2280D8317003BE431 /* OrganizerView+SettingsMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EE11CD1280D8317003BE431 /* OrganizerView+SettingsMenu.swift */; };
@ -211,6 +211,7 @@
0E3B7FD527E5173A00C66F13 /* ProfileView+VPN.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProfileView+VPN.swift"; sourceTree = "<group>"; };
0E3B7FD927E51A0200C66F13 /* ProfileView+Provider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProfileView+Provider.swift"; sourceTree = "<group>"; };
0E3CD47E280DA14B007075C0 /* OrganizerView+AddMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OrganizerView+AddMenu.swift"; sourceTree = "<group>"; };
0E3CD482280DAE92007075C0 /* ProfileView+MenuBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProfileView+MenuBar.swift"; sourceTree = "<group>"; };
0E44689527B051C300A14CE4 /* ProfileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileView.swift; sourceTree = "<group>"; };
0E44689B27B11B5300A14CE4 /* AboutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutView.swift; sourceTree = "<group>"; };
0E49F6BA27D7638300385834 /* EndpointAdvancedView+OpenVPN.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "EndpointAdvancedView+OpenVPN.swift"; sourceTree = "<group>"; };
@ -315,7 +316,6 @@
0ED89C1627DE0E05008B36D6 /* IntentEditView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntentEditView.swift; sourceTree = "<group>"; };
0ED89C1B27DE3ABC008B36D6 /* ShortcutsView+Add.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ShortcutsView+Add.swift"; sourceTree = "<group>"; };
0ED89C1D27DE3F8D008B36D6 /* IntentAddView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntentAddView.swift; sourceTree = "<group>"; };
0ED89C1F27DE423B008B36D6 /* ShortcutsView+ConnectTo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ShortcutsView+ConnectTo.swift"; sourceTree = "<group>"; };
0ED89C2427DE45A3008B36D6 /* ProfileHeaderRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileHeaderRow.swift; sourceTree = "<group>"; };
0EDE02C127F61C79000FBE3C /* EditableTextList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditableTextList.swift; sourceTree = "<group>"; };
0EDE8DBF20C86910004C739C /* PassepartoutOpenVPNTunnel.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = PassepartoutOpenVPNTunnel.appex; sourceTree = BUILT_PRODUCTS_DIR; };
@ -398,6 +398,7 @@
0E92D7C527F103300033CB7B /* ProfileView+Configuration.swift */,
0E92D7F327F104B80033CB7B /* ProfileView+Diagnostics.swift */,
0E92D7C827F1042A0033CB7B /* ProfileView+Extra.swift */,
0E3CD482280DAE92007075C0 /* ProfileView+MenuBar.swift */,
0E3B7FD927E51A0200C66F13 /* ProfileView+Provider.swift */,
0E3B7FD527E5173A00C66F13 /* ProfileView+VPN.swift */,
0E0AD48F27BD53CB00FBB520 /* ProfileView+Welcome.swift */,
@ -406,7 +407,6 @@
0EBC075A27EC4FFF00208AD9 /* ReportIssueView.swift */,
0E0BD27827B2EBE500583AC5 /* ShortcutsView.swift */,
0ED89C1B27DE3ABC008B36D6 /* ShortcutsView+Add.swift */,
0ED89C1F27DE423B008B36D6 /* ShortcutsView+ConnectTo.swift */,
0E71ACFA27C12E5300F85C4B /* VersionView.swift */,
);
path = Views;
@ -988,7 +988,6 @@
0ED89C2527DE45A3008B36D6 /* ProfileHeaderRow.swift in Sources */,
0E2C172B27CB63F9007E8488 /* Reviewer.swift in Sources */,
0E71ACDD27C0295C00F85C4B /* View+Extensions.swift in Sources */,
0ED89C2027DE423B008B36D6 /* ShortcutsView+ConnectTo.swift in Sources */,
0E34A2B627CAA8CC00C73B67 /* Core+L10n.swift in Sources */,
0E6059CF27FCC618003F4063 /* SwiftGen+Assets.swift in Sources */,
0E2A8D4927ADF87F00207D04 /* PassepartoutApp.swift in Sources */,
@ -1001,6 +1000,7 @@
0E71ACF927C12E4800F85C4B /* CreditsView.swift in Sources */,
0ED89C1527DE0A0C008B36D6 /* Shortcut.swift in Sources */,
0E34A2B927CAA96A00C73B67 /* OpenVPN+L10n.swift in Sources */,
0E3CD483280DAE92007075C0 /* ProfileView+MenuBar.swift in Sources */,
0EB17EAE27D226CF00D473B5 /* LocalProduct.swift in Sources */,
0E71ACEB27C1060D00F85C4B /* EndpointView.swift in Sources */,
0E53249927D26B51002565C3 /* ProductManager.swift in Sources */,

View File

@ -34,10 +34,6 @@ extension OrganizerView {
@Binding var alertType: AlertType?
private var isEligibleForSiri: Bool {
productManager.isEligible(forFeature: .siriShortcuts)
}
private let redditURL = Constants.URLs.subreddit
private let alternativeToURL = Constants.URLs.alternativeTo
@ -62,7 +58,6 @@ extension OrganizerView {
shareMenu
}
Divider()
shortcutsButton
aboutButton
// RemoveVPNSection()
// betaSection
@ -71,14 +66,6 @@ extension OrganizerView {
}
}
private var shortcutsButton: some View {
Button {
presentShortcutsOrPaywall()
} label: {
Label(L10n.Organizer.Items.SiriShortcuts.caption, systemImage: themeShortcutsImage)
}
}
private var supportMenu: some View {
Group {
Button {
@ -112,16 +99,6 @@ extension OrganizerView {
}
}
private func presentShortcutsOrPaywall() {
// eligibility: enter Siri shortcuts or present paywall
if isEligibleForSiri {
modalType = .shortcuts
} else {
modalType = .presentPaywallShortcuts
}
}
private func shareOnTwitter() {
let url = Unlocalized.Social.twitterIntent(withMessage: shareMessage)
URL.openURL(url)

View File

@ -32,16 +32,12 @@ struct OrganizerView: View {
case addHost(URL, Bool)
case shortcuts
case donate
case share([Any])
case about
case presentPaywallShortcuts
// XXX: alert ids
var id: Int {
switch self {
@ -49,15 +45,11 @@ struct OrganizerView: View {
case .addHost: return 2
case .shortcuts: return 3
case .donate: return 4
case .share: return 5
case .about: return 6
case .presentPaywallShortcuts: return 7
}
}
}
@ -160,11 +152,6 @@ extension OrganizerView {
)
}.themeGlobal()
case .shortcuts:
NavigationView {
ShortcutsView()
}.themeGlobal()
case .donate:
NavigationView {
DonateView()
@ -177,14 +164,6 @@ extension OrganizerView {
NavigationView {
AboutView()
}.themeGlobal()
case .presentPaywallShortcuts:
NavigationView {
PaywallView(
modalType: $modalType,
feature: .siriShortcuts
)
}.themeGlobal()
}
}

View File

@ -0,0 +1,70 @@
//
// ProfileView+MenuBar.swift
// Passepartout
//
// Created by Davide De Rosa on 2/6/22.
// Copyright (c) 2022 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of Passepartout.
//
// Passepartout is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Passepartout is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
//
import SwiftUI
import PassepartoutCore
extension ProfileView {
struct MenuBar: View {
@ObservedObject private var productManager: ProductManager
@ObservedObject private var currentProfile: ObservableProfile
@Binding private var modalType: ModalType?
private var isEligibleForSiri: Bool {
productManager.isEligible(forFeature: .siriShortcuts)
}
init(currentProfile: ObservableProfile, modalType: Binding<ModalType?>) {
productManager = .shared
self.currentProfile = currentProfile
_modalType = modalType
}
var body: some View {
Button {
presentShortcutsOrPaywall()
} label: {
themeShortcutsImage.asSystemImage
}
Button {
modalType = .rename
} label: {
themeRenameProfileImage.asSystemImage
}
}
private func presentShortcutsOrPaywall() {
// eligibility: enter Siri shortcuts or present paywall
if isEligibleForSiri {
modalType = .shortcuts
} else {
modalType = .paywallShortcuts
}
}
}
}

View File

@ -24,13 +24,16 @@
//
import SwiftUI
import Combine
import PassepartoutCore
struct ProfileView: View {
enum ModalType: Int, Identifiable {
case shortcuts
case rename
case paywallShortcuts
case paywallNetworkSettings
case paywallTrustedNetworks
@ -106,11 +109,10 @@ struct ProfileView: View {
private func toolbar() -> some ToolbarContent {
ToolbarItemGroup(placement: .navigationBarTrailing) {
if !isDeleted {
Button {
modalType = .rename
} label: {
Image(systemName: themeRenameProfileImage)
}
MenuBar(
currentProfile: profileManager.currentProfile,
modalType: $modalType
)
}
}
}
@ -118,11 +120,24 @@ struct ProfileView: View {
@ViewBuilder
private func presentedModal(_ modalType: ModalType) -> some View {
switch modalType {
case .shortcuts:
NavigationView {
ShortcutsView(target: profileManager.currentProfile.value)
}.themeGlobal()
case .rename:
NavigationView {
RenameView(currentProfile: profileManager.currentProfile)
}.themeGlobal()
case .paywallShortcuts:
NavigationView {
PaywallView(
modalType: $modalType,
feature: .siriShortcuts
)
}.themeGlobal()
case .paywallNetworkSettings:
NavigationView {
PaywallView(

View File

@ -29,76 +29,131 @@ import PassepartoutCore
extension ShortcutsView {
struct AddView: View {
@ObservedObject private var profileManager: ProfileManager
@ObservedObject private var providerManager: ProviderManager
@StateObject private var pendingProfile = ObservableProfile()
private let target: Profile
@Binding private var pendingShortcut: INShortcut?
init(pendingShortcut: Binding<INShortcut?>) {
profileManager = .shared
@State private var isPresentingProviderLocation = false
init(target: Profile, pendingShortcut: Binding<INShortcut?>) {
providerManager = .shared
self.target = target
_pendingShortcut = pendingShortcut
}
var body: some View {
List {
Section(
header: Text(Unlocalized.VPN.vpn)
) {
NavigationLink(L10n.Shortcuts.Add.Items.Connect.caption) {
ConnectToView(
pendingProfile: pendingProfile,
pendingShortcut: $pendingShortcut
)
}.disabled(profileManager.headers.isEmpty)
ZStack {
List {
Section(
header: Text(Unlocalized.VPN.vpn)
) {
addConnectView
Button(L10n.Shortcuts.Add.Items.EnableVpn.caption, action: addEnableVPN)
Button(L10n.Shortcuts.Add.Items.DisableVpn.caption, action: addDisableVPN)
}
Section(
header: Text(L10n.Shortcuts.Add.Sections.Wifi.header)
) {
Button(L10n.Shortcuts.Add.Items.TrustCurrentWifi.caption, action: addTrustWiFi)
Button(L10n.Shortcuts.Add.Items.UntrustCurrentWifi.caption, action: addUntrustWiFi)
}
Section(
header: Text(L10n.Shortcuts.Add.Sections.Cellular.header)
) {
Button(L10n.Shortcuts.Add.Items.TrustCellular.caption, action: addTrustCellular)
Button(L10n.Shortcuts.Add.Items.UntrustCellular.caption, action: addUntrustCellular)
}
}
Button(L10n.Shortcuts.Add.Items.EnableVpn.caption, action: addEnableVPN)
Button(L10n.Shortcuts.Add.Items.DisableVpn.caption, action: addDisableVPN)
}
Section(
header: Text(L10n.Shortcuts.Add.Sections.Wifi.header)
) {
Button(L10n.Shortcuts.Add.Items.TrustCurrentWifi.caption, action: addTrustWiFi)
Button(L10n.Shortcuts.Add.Items.UntrustCurrentWifi.caption, action: addUntrustWiFi)
}
Section(
header: Text(L10n.Shortcuts.Add.Sections.Cellular.header)
) {
Button(L10n.Shortcuts.Add.Items.TrustCellular.caption, action: addTrustCellular)
Button(L10n.Shortcuts.Add.Items.UntrustCellular.caption, action: addUntrustCellular)
}
providerLocationLink
}.navigationTitle(L10n.Shortcuts.Add.title)
}
private func addEnableVPN() {
addShortcut(with: IntentDispatcher.intentEnable())
}
private func addDisableVPN() {
addShortcut(with: IntentDispatcher.intentDisable())
}
private func addTrustWiFi() {
addShortcut(with: IntentDispatcher.intentTrustWiFi())
}
private func addUntrustWiFi() {
addShortcut(with: IntentDispatcher.intentUntrustWiFi())
}
private func addTrustCellular() {
addShortcut(with: IntentDispatcher.intentTrustCellular())
}
private func addUntrustCellular() {
addShortcut(with: IntentDispatcher.intentUntrustCellular())
}
private func addShortcut(with intent: INIntent) {
guard let shortcut = INShortcut(intent: intent) else {
fatalError("Unable to create INShortcut, intent '\(intent.description)' not exposed by app?")
private var addConnectView: some View {
Button(L10n.Shortcuts.Add.Items.Connect.caption) {
if target.isProvider {
pendingProfile.value = target
isPresentingProviderLocation = true
} else {
addConnect(target.header)
}
}
}
private var providerLocationLink: some View {
NavigationLink("", isActive: $isPresentingProviderLocation) {
ProviderLocationView(
currentProfile: pendingProfile,
isEditable: false,
isPresented: isProviderLocationPresented
)
}
pendingShortcut = shortcut
}
}
}
extension ShortcutsView.AddView {
private var isProviderLocationPresented: Binding<Bool> {
.init {
isPresentingProviderLocation
} set: {
if !$0 {
isPresentingProviderLocation = false
addMoveToPendingProfile()
}
}
}
private func addConnect(_ header: Profile.Header) {
pendingShortcut = INShortcut(intent: IntentDispatcher.intentConnect(
header: header
))
}
private func addMoveToPendingProfile() {
let header = pendingProfile.value.header
guard let server = pendingProfile.value.providerServer(providerManager) else {
return
}
pendingShortcut = INShortcut(intent: IntentDispatcher.intentMoveTo(
header: header,
providerFullName: server.providerMetadata.fullName,
server: server
))
}
private func addEnableVPN() {
addShortcut(with: IntentDispatcher.intentEnable())
}
private func addDisableVPN() {
addShortcut(with: IntentDispatcher.intentDisable())
}
private func addTrustWiFi() {
addShortcut(with: IntentDispatcher.intentTrustWiFi())
}
private func addUntrustWiFi() {
addShortcut(with: IntentDispatcher.intentUntrustWiFi())
}
private func addTrustCellular() {
addShortcut(with: IntentDispatcher.intentTrustCellular())
}
private func addUntrustCellular() {
addShortcut(with: IntentDispatcher.intentUntrustCellular())
}
private func addShortcut(with intent: INIntent) {
guard let shortcut = INShortcut(intent: intent) else {
fatalError("Unable to create INShortcut, intent '\(intent.description)' not exposed by app?")
}
pendingShortcut = shortcut
}
}

View File

@ -1,135 +0,0 @@
//
// ShortcutsView+ConnectTo.swift
// Passepartout
//
// Created by Davide De Rosa on 3/13/22.
// Copyright (c) 2022 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of Passepartout.
//
// Passepartout is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Passepartout is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
//
import SwiftUI
import Intents
import PassepartoutCore
extension ShortcutsView {
struct ConnectToView: View {
@ObservedObject private var profileManager: ProfileManager
@ObservedObject private var providerManager: ProviderManager
@ObservedObject private var pendingProfile: ObservableProfile
@Binding private var pendingShortcut: INShortcut?
@State private var profileIdMakingReady: UUID?
@State private var presentedHeader: Profile.Header?
private var isLocationPresented: Binding<Bool> {
.init {
presentedHeader != nil
} set: {
if !$0 {
presentedHeader = nil
addMoveToPendingProfile()
}
}
}
init(pendingProfile: ObservableProfile, pendingShortcut: Binding<INShortcut?>) {
profileManager = .shared
providerManager = .shared
self.pendingProfile = pendingProfile
_pendingShortcut = pendingShortcut
}
var body: some View {
debugChanges()
return ZStack {
let headers = profileManager.headers
List {
Section {
ForEach(headers.sorted(), content: profileRow)
.disabled(profileIdMakingReady != nil)
}
}
ForEach(Array(headers.filter {
$0.providerName != nil
}), content: providerLocationLink)
}.navigationTitle(L10n.Shortcuts.Add.Items.Connect.caption)
}
private func profileRow(_ header: Profile.Header) -> some View {
Button {
if let _ = header.providerName {
Task {
profileIdMakingReady = header.id
await loadAndSelectProfile(withHeader: header)
profileIdMakingReady = nil
}
} else {
addConnect(header)
}
} label: {
ProfileHeaderRow(header: header)
}.withTrailingProgress(when: profileIdMakingReady == header.id)
}
private func providerLocationLink(_ header: Profile.Header) -> some View {
NavigationLink("", tag: header, selection: $presentedHeader) {
ProviderLocationView(
currentProfile: pendingProfile,
isEditable: false,
isPresented: isLocationPresented
)
}
}
private func loadAndSelectProfile(withHeader header: Profile.Header) async {
do {
let result = try profileManager.loadProfile(withId: header.id)
if !result.isReady {
try await profileManager.makeProfileReady(result.profile)
}
pendingProfile.value = result.profile
presentedHeader = header
} catch {
pp_log.error("Unable to select profile: \(error)")
}
}
private func addConnect(_ header: Profile.Header) {
pendingShortcut = INShortcut(intent: IntentDispatcher.intentConnect(
header: header
))
}
private func addMoveToPendingProfile() {
let header = pendingProfile.value.header
guard let server = pendingProfile.value.providerServer(providerManager) else {
return
}
pendingShortcut = INShortcut(intent: IntentDispatcher.intentMoveTo(
header: header,
providerFullName: server.providerMetadata.fullName,
server: server
))
}
}
}

View File

@ -44,6 +44,8 @@ struct ShortcutsView: View {
}
@ObservedObject private var intentsManager: IntentsManager
private let target: Profile
@State private var modalType: ModalType?
@ -51,8 +53,9 @@ struct ShortcutsView: View {
@State private var pendingShortcut: INShortcut?
init() {
init(target: Profile) {
intentsManager = .shared
self.target = target
}
var body: some View {
@ -75,10 +78,16 @@ struct ShortcutsView: View {
Section(
header: Text(L10n.Shortcuts.Edit.Sections.All.header)
) {
ForEach(intentsManager.shortcuts.values.sorted(), content: rowView)
ForEach(relevantShortcuts, content: rowView)
}
}
private var relevantShortcuts: [Shortcut] {
intentsManager.shortcuts.values.filter {
$0.isRelevant(to: target)
}.sorted()
}
private var addSection: some View {
Section(
// FIXME: l10n, string id
@ -86,6 +95,7 @@ struct ShortcutsView: View {
) {
NavigationLink(isActive: $isNavigationPresented) {
AddView(
target: target,
pendingShortcut: delegatingPendingShortcut
)
} label: {
@ -141,3 +151,18 @@ struct ShortcutsView: View {
modalType = .add(shortcut: shortcut)
}
}
private extension Shortcut {
func isRelevant(to profile: Profile) -> Bool {
guard let intent = native.shortcut.intent else {
return true
}
if let connectIntent = intent as? ConnectVPNIntent {
return connectIntent.profileId == profile.id.uuidString
}
if let moveToIntent = intent as? MoveToLocationIntent {
return moveToIntent.profileId == profile.id.uuidString
}
return true
}
}