Ferrite: Format

Signed-off-by: kingbri <bdashore3@proton.me>
This commit is contained in:
kingbri 2023-03-02 16:23:53 -05:00
parent 8c8e9d0215
commit 438e48be66
29 changed files with 165 additions and 192 deletions

View file

@ -6,8 +6,8 @@
//
//
import Foundation
import CoreData
import Foundation
@objc(Action)
public class Action: NSManagedObject, Plugin {}

View file

@ -6,66 +6,61 @@
//
//
import Foundation
import CoreData
import Foundation
extension Action {
@nonobjc public class func fetchRequest() -> NSFetchRequest<Action> {
return NSFetchRequest<Action>(entityName: "Action")
public extension Action {
@nonobjc class func fetchRequest() -> NSFetchRequest<Action> {
NSFetchRequest<Action>(entityName: "Action")
}
@NSManaged public var id: UUID
@NSManaged public var listId: UUID?
@NSManaged public var name: String
@NSManaged public var deeplink: String?
@NSManaged public var version: Int16
@NSManaged public var requires: [String]
@NSManaged public var author: String
@NSManaged public var enabled: Bool
@NSManaged public var tags: NSOrderedSet?
@NSManaged var id: UUID
@NSManaged var listId: UUID?
@NSManaged var name: String
@NSManaged var deeplink: String?
@NSManaged var version: Int16
@NSManaged var requires: [String]
@NSManaged var author: String
@NSManaged var enabled: Bool
@NSManaged var tags: NSOrderedSet?
public func getTags() -> [PluginTagJson] {
return requires.map { PluginTagJson(name: $0, colorHex: nil) } + tagArray.map { $0.toJson() }
func getTags() -> [PluginTagJson] {
requires.map { PluginTagJson(name: $0, colorHex: nil) } + tagArray.map { $0.toJson() }
}
}
// MARK: Generated accessors for tags
extension Action {
public extension Action {
@objc(insertObject:inTagsAtIndex:)
@NSManaged public func insertIntoTags(_ value: PluginTag, at idx: Int)
@NSManaged func insertIntoTags(_ value: PluginTag, at idx: Int)
@objc(removeObjectFromTagsAtIndex:)
@NSManaged public func removeFromTags(at idx: Int)
@NSManaged func removeFromTags(at idx: Int)
@objc(insertTags:atIndexes:)
@NSManaged public func insertIntoTags(_ values: [PluginTag], at indexes: NSIndexSet)
@NSManaged func insertIntoTags(_ values: [PluginTag], at indexes: NSIndexSet)
@objc(removeTagsAtIndexes:)
@NSManaged public func removeFromTags(at indexes: NSIndexSet)
@NSManaged func removeFromTags(at indexes: NSIndexSet)
@objc(replaceObjectInTagsAtIndex:withObject:)
@NSManaged public func replaceTags(at idx: Int, with value: PluginTag)
@NSManaged func replaceTags(at idx: Int, with value: PluginTag)
@objc(replaceTagsAtIndexes:withTags:)
@NSManaged public func replaceTags(at indexes: NSIndexSet, with values: [PluginTag])
@NSManaged func replaceTags(at indexes: NSIndexSet, with values: [PluginTag])
@objc(addTagsObject:)
@NSManaged public func addToTags(_ value: PluginTag)
@NSManaged func addToTags(_ value: PluginTag)
@objc(removeTagsObject:)
@NSManaged public func removeFromTags(_ value: PluginTag)
@NSManaged func removeFromTags(_ value: PluginTag)
@objc(addTags:)
@NSManaged public func addToTags(_ values: NSOrderedSet)
@NSManaged func addToTags(_ values: NSOrderedSet)
@objc(removeTags:)
@NSManaged public func removeFromTags(_ values: NSOrderedSet)
@NSManaged func removeFromTags(_ values: NSOrderedSet)
}
extension Action : Identifiable {
}
extension Action: Identifiable {}

View file

@ -6,10 +6,8 @@
//
//
import Foundation
import CoreData
import Foundation
@objc(PluginList)
public class PluginList: NSManagedObject {
}
public class PluginList: NSManagedObject {}

View file

@ -6,23 +6,18 @@
//
//
import Foundation
import CoreData
import Foundation
extension PluginList {
@nonobjc public class func fetchRequest() -> NSFetchRequest<PluginList> {
return NSFetchRequest<PluginList>(entityName: "PluginList")
public extension PluginList {
@nonobjc class func fetchRequest() -> NSFetchRequest<PluginList> {
NSFetchRequest<PluginList>(entityName: "PluginList")
}
@NSManaged public var author: String
@NSManaged public var id: UUID
@NSManaged public var name: String
@NSManaged public var urlString: String
@NSManaged var author: String
@NSManaged var id: UUID
@NSManaged var name: String
@NSManaged var urlString: String
}
extension PluginList : Identifiable {
}
extension PluginList: Identifiable {}

View file

@ -6,9 +6,8 @@
//
//
import Foundation
import CoreData
import Foundation
@objc(PluginTag)
public class PluginTag: NSManagedObject {
}
public class PluginTag: NSManagedObject {}

View file

@ -6,26 +6,22 @@
//
//
import Foundation
import CoreData
import Foundation
extension PluginTag {
@nonobjc public class func fetchRequest() -> NSFetchRequest<PluginTag> {
return NSFetchRequest<PluginTag>(entityName: "PluginTag")
public extension PluginTag {
@nonobjc class func fetchRequest() -> NSFetchRequest<PluginTag> {
NSFetchRequest<PluginTag>(entityName: "PluginTag")
}
@NSManaged public var colorHex: String?
@NSManaged public var name: String
@NSManaged public var parentAction: Action?
@NSManaged public var parentSource: Source?
@NSManaged var colorHex: String?
@NSManaged var name: String
@NSManaged var parentAction: Action?
@NSManaged var parentSource: Source?
func toJson() -> PluginTagJson {
return PluginTagJson(name: name, colorHex: colorHex)
internal func toJson() -> PluginTagJson {
PluginTagJson(name: name, colorHex: colorHex)
}
}
extension PluginTag : Identifiable {
}
extension PluginTag: Identifiable {}

View file

@ -6,73 +6,68 @@
//
//
import Foundation
import CoreData
import Foundation
extension Source {
@nonobjc public class func fetchRequest() -> NSFetchRequest<Source> {
return NSFetchRequest<Source>(entityName: "Source")
public extension Source {
@nonobjc class func fetchRequest() -> NSFetchRequest<Source> {
NSFetchRequest<Source>(entityName: "Source")
}
@NSManaged public var id: UUID
@NSManaged public var baseUrl: String?
@NSManaged public var fallbackUrls: [String]?
@NSManaged public var dynamicBaseUrl: Bool
@NSManaged public var enabled: Bool
@NSManaged public var name: String
@NSManaged public var author: String
@NSManaged public var listId: UUID?
@NSManaged public var preferredParser: Int16
@NSManaged public var version: Int16
@NSManaged public var htmlParser: SourceHtmlParser?
@NSManaged public var rssParser: SourceRssParser?
@NSManaged public var jsonParser: SourceJsonParser?
@NSManaged public var api: SourceApi?
@NSManaged public var trackers: [String]?
@NSManaged public var tags: NSOrderedSet?
@NSManaged var id: UUID
@NSManaged var baseUrl: String?
@NSManaged var fallbackUrls: [String]?
@NSManaged var dynamicBaseUrl: Bool
@NSManaged var enabled: Bool
@NSManaged var name: String
@NSManaged var author: String
@NSManaged var listId: UUID?
@NSManaged var preferredParser: Int16
@NSManaged var version: Int16
@NSManaged var htmlParser: SourceHtmlParser?
@NSManaged var rssParser: SourceRssParser?
@NSManaged var jsonParser: SourceJsonParser?
@NSManaged var api: SourceApi?
@NSManaged var trackers: [String]?
@NSManaged var tags: NSOrderedSet?
public func getTags() -> [PluginTagJson] {
return tagArray.map { $0.toJson() }
func getTags() -> [PluginTagJson] {
tagArray.map { $0.toJson() }
}
}
// MARK: Generated accessors for tags
extension Source {
public extension Source {
@objc(insertObject:inTagsAtIndex:)
@NSManaged public func insertIntoTags(_ value: PluginTag, at idx: Int)
@NSManaged func insertIntoTags(_ value: PluginTag, at idx: Int)
@objc(removeObjectFromTagsAtIndex:)
@NSManaged public func removeFromTags(at idx: Int)
@NSManaged func removeFromTags(at idx: Int)
@objc(insertTags:atIndexes:)
@NSManaged public func insertIntoTags(_ values: [PluginTag], at indexes: NSIndexSet)
@NSManaged func insertIntoTags(_ values: [PluginTag], at indexes: NSIndexSet)
@objc(removeTagsAtIndexes:)
@NSManaged public func removeFromTags(at indexes: NSIndexSet)
@NSManaged func removeFromTags(at indexes: NSIndexSet)
@objc(replaceObjectInTagsAtIndex:withObject:)
@NSManaged public func replaceTags(at idx: Int, with value: PluginTag)
@NSManaged func replaceTags(at idx: Int, with value: PluginTag)
@objc(replaceTagsAtIndexes:withTags:)
@NSManaged public func replaceTags(at indexes: NSIndexSet, with values: [PluginTag])
@NSManaged func replaceTags(at indexes: NSIndexSet, with values: [PluginTag])
@objc(addTagsObject:)
@NSManaged public func addToTags(_ value: PluginTag)
@NSManaged func addToTags(_ value: PluginTag)
@objc(removeTagsObject:)
@NSManaged public func removeFromTags(_ value: PluginTag)
@NSManaged func removeFromTags(_ value: PluginTag)
@objc(addTags:)
@NSManaged public func addToTags(_ values: NSOrderedSet)
@NSManaged func addToTags(_ values: NSOrderedSet)
@objc(removeTags:)
@NSManaged public func removeFromTags(_ values: NSOrderedSet)
@NSManaged func removeFromTags(_ values: NSOrderedSet)
}
extension Source : Identifiable {
}
extension Source: Identifiable {}

View file

@ -37,11 +37,11 @@ extension View {
modifier(ViewDidAppearModifier(callback: callback))
}
func customScopeBar<Content: View>(_ content: Content) -> some View {
func customScopeBar(_ content: some View) -> some View {
modifier(CustomScopeBarModifier(hostingContent: content))
}
func customScopeBar<Content: View>(_ content: @escaping () -> Content) -> some View {
func customScopeBar(_ content: @escaping () -> some View) -> some View {
modifier(CustomScopeBarModifier(hostingContent: content()))
}
}

View file

@ -7,7 +7,7 @@
import Foundation
public struct ActionJson: Codable, Hashable, PluginJson {
public struct ActionJson: Codable, Hashable, PluginJson {
public let name: String
public let version: Int16
let minVersion: String?
@ -18,11 +18,11 @@ public struct ActionJson: Codable, Hashable, PluginJson {
public var tags: [PluginTagJson]?
}
extension ActionJson {
public extension ActionJson {
// Fetches all tags without optional requirement
// Avoids the need for extra tag additions in DB
public func getTags() -> [PluginTagJson] {
return requires.map { PluginTagJson(name: $0.rawValue, colorHex: nil) } + (tags.map { $0 } ?? [])
func getTags() -> [PluginTagJson] {
requires.map { PluginTagJson(name: $0.rawValue, colorHex: nil) } + (tags.map { $0 } ?? [])
}
}

View file

@ -17,6 +17,7 @@ public struct Backup: Codable {
var pluginListUrls: [String]?
// MARK: Remove once v1 backups are unsupported
var sourceLists: [PluginListBackupJson]?
}

View file

@ -29,10 +29,10 @@ public struct SourceJson: Codable, Hashable, Sendable, PluginJson {
public var tags: [PluginTagJson]?
}
extension SourceJson {
public extension SourceJson {
// Fetches all tags without optional requirement
public func getTags() -> [PluginTagJson] {
return tags ?? []
func getTags() -> [PluginTagJson] {
tags ?? []
}
}

View file

@ -21,7 +21,7 @@ public protocol Plugin: ObservableObject, NSManagedObject {
extension Plugin {
var tagArray: [PluginTag] {
return self.tags?.array as? [PluginTag] ?? []
tags?.array as? [PluginTag] ?? []
}
}

View file

@ -93,19 +93,18 @@ public class PluginManager: ObservableObject {
}
// forType required to guide generic inferences
func fetchFilteredPlugins<P: Plugin, PJ: PluginJson>(
forType: PJ.Type,
installedPlugins: FetchedResults<P>,
searchText: String
) -> [PJ] {
func fetchFilteredPlugins<PJ: PluginJson>(forType: PJ.Type,
installedPlugins: FetchedResults<some Plugin>,
searchText: String) -> [PJ]
{
let availablePlugins: [PJ] = fetchCastedPlugins(forType)
return availablePlugins
.filter { availablePlugin in
let pluginExists = installedPlugins.contains(where: {
availablePlugin.name == $0.name &&
availablePlugin.listId == $0.listId &&
availablePlugin.author == $0.author
availablePlugin.listId == $0.listId &&
availablePlugin.author == $0.author
})
if searchText.isEmpty {
@ -116,19 +115,18 @@ public class PluginManager: ObservableObject {
}
}
func fetchUpdatedPlugins<P: Plugin, PJ: PluginJson>(
forType: PJ.Type,
installedPlugins: FetchedResults<P>,
searchText: String
) -> [PJ] {
func fetchUpdatedPlugins<PJ: PluginJson>(forType: PJ.Type,
installedPlugins: FetchedResults<some Plugin>,
searchText: String) -> [PJ]
{
var updatedPlugins: [PJ] = []
let availablePlugins: [PJ] = fetchCastedPlugins(forType)
for plugin in installedPlugins {
if let availablePlugin = availablePlugins.first(where: {
plugin.listId == $0.listId &&
plugin.name == $0.name &&
plugin.author == $0.author
plugin.name == $0.name &&
plugin.author == $0.author
}),
availablePlugin.version > plugin.version
{
@ -257,7 +255,7 @@ public class PluginManager: ObservableObject {
if actionJson.requires.count < 1 {
await toastModel?.updateToastDescription("Action addition error: actions must require an input. Please contact the action dev!")
print("Action name \(actionJson.name) does not have a requires parameter")
return
}
@ -289,7 +287,7 @@ public class PluginManager: ObservableObject {
newAction.version = actionJson.version
newAction.author = actionJson.author ?? "Unknown"
newAction.listId = actionJson.listId
newAction.requires = actionJson.requires.map { $0.rawValue }
newAction.requires = actionJson.requires.map(\.rawValue)
newAction.enabled = true
if let jsonTags = actionJson.tags {
@ -325,7 +323,7 @@ public class PluginManager: ObservableObject {
if !dynamicBaseUrl, sourceJson.baseUrl == nil {
await toastModel?.updateToastDescription("Not adding this source because base URL parameters are malformed. Please contact the source dev.")
print("Not adding source \(sourceJson.name) because base URL parameters are malformed")
return
}

View file

@ -483,7 +483,6 @@ class ScrapingViewModel: ObservableObject {
}
for item in items {
//print(item)
// Parse magnet link or translate hash
var magnetHash: String?
if let magnetHashParser = rssParser.magnetHash {

View file

@ -36,7 +36,7 @@ class ToastViewModel: ObservableObject {
@Published var showToast: Bool = false
@Published var indeterminateToastDescription: String? = nil
@Published var indeterminateCancelAction: (() -> ())? = nil
@Published var indeterminateCancelAction: (() -> Void)? = nil
@Published var showIndeterminateToast: Bool = false
public func updateToastDescription(_ description: String, newToastType: ToastType? = nil) {
@ -47,7 +47,7 @@ class ToastViewModel: ObservableObject {
toastDescription = description
}
public func updateIndeterminateToast(_ description: String, cancelAction: (() -> ())?) {
public func updateIndeterminateToast(_ description: String, cancelAction: (() -> Void)?) {
indeterminateToastDescription = description
if let cancelAction {

View file

@ -5,8 +5,8 @@
// Created by Brian Dashore on 9/29/22.
//
import SwiftUI
import Introspect
import SwiftUI
public struct Backport<Content> {
public let content: Content
@ -21,12 +21,11 @@ extension View {
}
extension Backport where Content: View {
@ViewBuilder func alert(
isPresented: Binding<Bool>,
title: String,
message: String?,
buttons: [AlertButton] = []
) -> some View {
@ViewBuilder func alert(isPresented: Binding<Bool>,
title: String,
message: String?,
buttons: [AlertButton] = []) -> some View
{
if #available(iOS 15, *) {
content
.alert(
@ -69,11 +68,10 @@ extension Backport where Content: View {
}
}
@ViewBuilder func confirmationDialog(
isPresented: Binding<Bool>,
title: String, message: String?,
buttons: [AlertButton]
) -> some View {
@ViewBuilder func confirmationDialog(isPresented: Binding<Bool>,
title: String, message: String?,
buttons: [AlertButton]) -> some View
{
if #available(iOS 15, *) {
content
.confirmationDialog(
@ -125,7 +123,7 @@ extension Backport where Content: View {
}
}
@ViewBuilder func introspectSearchController(customize: @escaping (UISearchController) -> ()) -> some View {
@ViewBuilder func introspectSearchController(customize: @escaping (UISearchController) -> Void) -> some View {
if #available(iOS 15, *) {
content.introspectSearchController(customize: customize)
} else {

View file

@ -11,7 +11,5 @@ struct LibraryHeaderView: View {
@EnvironmentObject var debridManager: DebridManager
@Binding var selectedSegment: LibraryPickerSegment
var body: some View {
}
var body: some View {}
}

View file

@ -5,8 +5,8 @@
// Created by Brian Dashore on 2/14/23.
//
import SwiftUI
import Introspect
import SwiftUI
struct CustomScopeBarModifier<V: View>: ViewModifier {
let hostingContent: V
@ -25,6 +25,7 @@ struct CustomScopeBarModifier<V: View>: ViewModifier {
searchController.searchBar.autocapitalizationType = autocorrectSearch ? .sentences : .none
// MARK: One-time setup
guard hostingController == nil else { return }
searchController.hidesNavigationBarDuringPresentation = true

View file

@ -28,7 +28,7 @@ struct SearchableContent<Content: View>: View {
lastHeight = newHeight
}
.transaction {
if geom.size.height != lastHeight && searching {
if geom.size.height != lastHeight, searching {
$0.animation = .default.speed(2)
}
}

View file

@ -24,7 +24,7 @@ struct TestHostingView: View {
Text(textName)
.opacity(0.6)
.foregroundColor(.primary)
Image(systemName: "chevron.down")
.foregroundColor(.tertiaryLabel)
}
@ -49,7 +49,7 @@ struct TestHostingView: View {
Text(secondTextName)
.opacity(0.6)
.foregroundColor(.primary)
Image(systemName: "chevron.down")
.foregroundColor(.tertiaryLabel)
}

View file

@ -17,7 +17,7 @@ struct DebridPickerView<Content: View>: View {
Picker("", selection: $debridManager.selectedDebridType) {
Text("None")
.tag(nil as DebridType?)
ForEach(DebridType.allCases, id: \.self) { (debridType: DebridType) in
if debridManager.enabledDebrids.contains(debridType) {
Text(debridType.toString())

View file

@ -26,30 +26,30 @@ struct BookmarksView: View {
sortDescriptors: [NSSortDescriptor(keyPath: \Bookmark.orderNum, ascending: true)]
) { (bookmarks: FetchedResults<Bookmark>) in
List {
if !bookmarks.isEmpty {
ForEach(bookmarks, id: \.self) { bookmark in
SearchResultButtonView(result: bookmark.toSearchResult(), existingBookmark: bookmark)
}
.onDelete { offsets in
for index in offsets {
if let bookmark = bookmarks[safe: index] {
PersistenceController.shared.delete(bookmark, context: backgroundContext)
NotificationCenter.default.post(name: .didDeleteBookmark, object: bookmark)
}
if !bookmarks.isEmpty {
ForEach(bookmarks, id: \.self) { bookmark in
SearchResultButtonView(result: bookmark.toSearchResult(), existingBookmark: bookmark)
}
.onDelete { offsets in
for index in offsets {
if let bookmark = bookmarks[safe: index] {
PersistenceController.shared.delete(bookmark, context: backgroundContext)
NotificationCenter.default.post(name: .didDeleteBookmark, object: bookmark)
}
}
.onMove { source, destination in
var changedBookmarks = bookmarks.map { $0 }
changedBookmarks.move(fromOffsets: source, toOffset: destination)
for reverseIndex in stride(from: changedBookmarks.count - 1, through: 0, by: -1) {
changedBookmarks[reverseIndex].orderNum = Int16(reverseIndex)
}
PersistenceController.shared.save()
}
}
.onMove { source, destination in
var changedBookmarks = bookmarks.map { $0 }
changedBookmarks.move(fromOffsets: source, toOffset: destination)
for reverseIndex in stride(from: changedBookmarks.count - 1, through: 0, by: -1) {
changedBookmarks[reverseIndex].orderNum = Int16(reverseIndex)
}
PersistenceController.shared.save()
}
}
}
.listStyle(.insetGrouped)
.inlinedList(inset: Application.shared.osVersion.majorVersion > 14 ? 15 : -25)

View file

@ -37,7 +37,7 @@ struct SearchFilterHeaderView: View {
}
.id(scrapingModel.filteredSource)
DebridPickerView() {
DebridPickerView {
FilterLabelView(name: debridManager.selectedDebridType?.toString() ?? "Debrid")
}
.id(debridManager.selectedDebridType)

View file

@ -44,12 +44,12 @@ struct ContentView: View {
.listStyle(.insetGrouped)
.inlinedList(inset: Application.shared.osVersion.majorVersion > 14 ? 20 : -20)
.overlay {
if scrapingModel.searchResults.isEmpty && isSearching && scrapingModel.runningSearchTask == nil {
if scrapingModel.searchResults.isEmpty, isSearching, scrapingModel.runningSearchTask == nil {
Text("No results found")
}
}
.onChange(of: searchText) { newText in
if newText.isEmpty && isSearching {
if newText.isEmpty, isSearching {
searchBarText = getSearchBarText()
}
}
@ -124,7 +124,7 @@ struct ContentView: View {
// Fetches random searchbar text if enabled, otherwise deinit the last case value
func getSearchBarText() -> String {
if usesRandomSearchText {
let num = Int.random(in: 0..<searchBarTextArray.count - 1)
let num = Int.random(in: 0 ..< searchBarTextArray.count - 1)
if num == lastSearchTextIndex {
lastSearchTextIndex = num + 1
return searchBarTextArray[safe: num + 1] ?? "Search"

View file

@ -69,7 +69,7 @@ struct LibraryView: View {
switch navModel.libraryPickerSelection {
case .bookmarks, .debridCloud:
DebridPickerView() {
DebridPickerView {
Text(debridManager.selectedDebridType?.toString(abbreviated: true) ?? "Debrid")
}
.transaction {

View file

@ -109,7 +109,7 @@ struct MainView: View {
isPresented: $backupManager.showRestoreAlert,
title: "Restore backup?",
message:
"Merge (preferred): Will merge your current data with the backup \n\n" +
"Merge (preferred): Will merge your current data with the backup \n\n" +
"Overwrite: Will delete and replace all your data \n\n" +
"If Merge causes app instability, uninstall Ferrite and use the Overwrite option.",
buttons: [
@ -140,7 +140,7 @@ struct MainView: View {
isPresented: $showUpdateAlert,
title: "Update available",
message:
"Ferrite \(releaseVersionString) can be downloaded. \n\n" +
"Ferrite \(releaseVersionString) can be downloaded. \n\n" +
"This alert can be disabled in Settings.",
buttons: [
.init("Download") {

View file

@ -48,11 +48,11 @@ struct PluginsView: View {
if checkedForPlugins {
switch navModel.pluginPickerSelection {
case .sources:
if sources.isEmpty && pluginManager.availableSources.isEmpty {
if sources.isEmpty, pluginManager.availableSources.isEmpty {
EmptyInstructionView(title: "No Sources", message: "Add a plugin list in Settings")
}
case .actions:
if actions.isEmpty && pluginManager.availableActions.isEmpty {
if actions.isEmpty, pluginManager.availableActions.isEmpty {
EmptyInstructionView(title: "No Actions", message: "Add a plugin list in Settings")
}
}

View file

@ -97,7 +97,7 @@ struct SettingsView: View {
}
Section(header: InlineHeader("Default actions")) {
//if debridManager.enabledDebrids.count > 0 {
if debridManager.enabledDebrids.count > 0 {
NavigationLink(
destination: DefaultActionPickerView(
actionRequirement: .debrid,
@ -115,7 +115,7 @@ struct SettingsView: View {
}
}
)
//}
}
NavigationLink(
destination: DefaultActionPickerView(

View file

@ -116,7 +116,7 @@ struct ActionChoiceView: View {
isPresented: $pluginManager.showBrokenDefaultActionAlert,
title: "Action not found",
message:
"The default action could not be run. The action choice sheet has been opened. \n\n" +
"The default action could not be run. The action choice sheet has been opened. \n\n" +
"Please check your default actions in Settings"
)
.onDisappear {