From 0f081d071633c0372611bbb17b354f4f4056e69a Mon Sep 17 00:00:00 2001 From: kingbri Date: Fri, 10 Feb 2023 00:40:36 -0500 Subject: [PATCH] Ferrite: Modify UI The overall UI of Ferrite has been changed to make animations smoother and streamline the experiences. A new search filter interface has been added for all iOS versions, but iOS 15 and up have smooth UI applied due to bugs with searchbars in iOS 14 (which shouldn't even have a searchbar in the first place). Also fix the plugin fetching logic to not listen to a combine publisher and instead use a notification that is easier to control. Signed-off-by: kingbri --- Ferrite.xcodeproj/project.pbxproj | 66 +++++--- Ferrite/API/RealDebridWrapper.swift | 18 ++- Ferrite/Extensions/NotificationCenter.swift | 4 + Ferrite/Extensions/UIDevice.swift | 18 +++ Ferrite/Extensions/View.swift | 36 ++--- Ferrite/ViewModels/NavigationViewModel.swift | 36 ++++- Ferrite/ViewModels/PluginManager.swift | 2 +- Ferrite/Views/CommonViews/Backport.swift | 14 ++ .../Views/CommonViews/FilterLabelView.swift | 27 ++++ .../Views/CommonViews/LibraryHeaderView.swift | 17 +++ .../CommonViews/Modifiers/InlinedList.swift | 6 +- .../Modifiers/SearchAppearance.swift | 63 ++++++++ .../Views/CommonViews/SearchableContent.swift | 37 +++++ .../Views/CommonViews/SectionHeaderView.swift | 20 +++ .../Views/CommonViews/TestHostingView.swift | 75 +++++++++ ...hoiceView.swift => DebridPickerView.swift} | 15 +- .../Library/BookmarksView.swift | 39 +++-- .../Library/LibraryPickerView.swift | 31 ++++ .../Buttons/InstalledPluginButtonView.swift | 2 + .../Plugin/PluginListView.swift | 5 +- .../Plugin/PluginPickerView.swift | 27 ++++ .../SearchResult/SearchFilterHeaderView.swift | 49 ++++++ .../ComponentViews/Settings/BackupsView.swift | 2 +- .../Settings/DefaultActionsPickerViews.swift | 4 +- .../Settings/SettingsPluginListView.swift | 2 +- Ferrite/Views/ContentView.swift | 142 +++++------------- Ferrite/Views/LibraryView.swift | 105 ++++++------- Ferrite/Views/MainView.swift | 8 +- Ferrite/Views/PluginsView.swift | 60 +++----- Ferrite/Views/SearchResultsView.swift | 2 +- .../Views/SheetViews/BatchChoiceView.swift | 4 +- 31 files changed, 634 insertions(+), 302 deletions(-) create mode 100644 Ferrite/Extensions/UIDevice.swift create mode 100644 Ferrite/Views/CommonViews/FilterLabelView.swift create mode 100644 Ferrite/Views/CommonViews/LibraryHeaderView.swift create mode 100644 Ferrite/Views/CommonViews/Modifiers/SearchAppearance.swift create mode 100644 Ferrite/Views/CommonViews/SearchableContent.swift create mode 100644 Ferrite/Views/CommonViews/SectionHeaderView.swift create mode 100644 Ferrite/Views/CommonViews/TestHostingView.swift rename Ferrite/Views/ComponentViews/Debrid/{DebridChoiceView.swift => DebridPickerView.swift} (72%) create mode 100644 Ferrite/Views/ComponentViews/Library/LibraryPickerView.swift create mode 100644 Ferrite/Views/ComponentViews/Plugin/PluginPickerView.swift create mode 100644 Ferrite/Views/ComponentViews/SearchResult/SearchFilterHeaderView.swift diff --git a/Ferrite.xcodeproj/project.pbxproj b/Ferrite.xcodeproj/project.pbxproj index ca19997..94d1b5e 100644 --- a/Ferrite.xcodeproj/project.pbxproj +++ b/Ferrite.xcodeproj/project.pbxproj @@ -32,7 +32,7 @@ 0C41BC6528C2AEB900B47DD6 /* SearchModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C41BC6428C2AEB900B47DD6 /* SearchModels.swift */; }; 0C422E7E293542EA00486D65 /* PremiumizeWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C422E7D293542EA00486D65 /* PremiumizeWrapper.swift */; }; 0C422E80293542F300486D65 /* PremiumizeModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C422E7F293542F300486D65 /* PremiumizeModels.swift */; }; - 0C42B5962932F2D5008057A0 /* DebridChoiceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C42B5952932F2D5008057A0 /* DebridChoiceView.swift */; }; + 0C42B5962932F2D5008057A0 /* DebridPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C42B5952932F2D5008057A0 /* DebridPickerView.swift */; }; 0C42B5982932F6DD008057A0 /* Set.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C42B5972932F6DD008057A0 /* Set.swift */; }; 0C445C62293F9A0B0060744D /* Bundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C445C61293F9A0B0060744D /* Bundle.swift */; }; 0C44E2A828D4DDDC007711AE /* Application.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C44E2A728D4DDDC007711AE /* Application.swift */; }; @@ -44,6 +44,7 @@ 0C5005522992B6750064606A /* PluginTagsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C5005512992B6750064606A /* PluginTagsView.swift */; }; 0C50055A2992BA6A0064606A /* PluginTag+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C5005582992BA6A0064606A /* PluginTag+CoreDataClass.swift */; }; 0C50055B2992BA6A0064606A /* PluginTag+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C5005592992BA6A0064606A /* PluginTag+CoreDataProperties.swift */; }; + 0C50B7D0299DF63C00A9FA3C /* UIDevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C50B7CF299DF63C00A9FA3C /* UIDevice.swift */; }; 0C54D36328C5086E00BFEEE2 /* History+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C54D36128C5086E00BFEEE2 /* History+CoreDataClass.swift */; }; 0C54D36428C5086E00BFEEE2 /* History+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C54D36228C5086E00BFEEE2 /* History+CoreDataProperties.swift */; }; 0C572D4C2993FC2A003EEC05 /* ViewDidAppearHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C572D4B2993FC2A003EEC05 /* ViewDidAppearHandler.swift */; }; @@ -78,6 +79,7 @@ 0C84F4832895BFED0074B7C9 /* Source+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C84F47B2895BFED0074B7C9 /* Source+CoreDataProperties.swift */; }; 0C84F4842895BFED0074B7C9 /* SourceHtmlParser+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C84F47C2895BFED0074B7C9 /* SourceHtmlParser+CoreDataClass.swift */; }; 0C84F4852895BFED0074B7C9 /* SourceHtmlParser+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C84F47D2895BFED0074B7C9 /* SourceHtmlParser+CoreDataProperties.swift */; }; + 0C871BDF29994D9D005279AC /* FilterLabelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C871BDE29994D9D005279AC /* FilterLabelView.swift */; }; 0C95D8D828A55B03005E22B3 /* DefaultActionsPickerViews.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C95D8D728A55B03005E22B3 /* DefaultActionsPickerViews.swift */; }; 0C95D8DA28A55BB6005E22B3 /* SettingsModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C95D8D928A55BB6005E22B3 /* SettingsModels.swift */; }; 0CA05457288EE58200850554 /* SettingsPluginListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CA05456288EE58200850554 /* SettingsPluginListView.swift */; }; @@ -112,7 +114,6 @@ 0CAF9319296399190050812A /* PremiumizeCloudView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CAF9318296399190050812A /* PremiumizeCloudView.swift */; }; 0CB6516328C5A57300DCA721 /* ConditionalId.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CB6516228C5A57300DCA721 /* ConditionalId.swift */; }; 0CB6516528C5A5D700DCA721 /* InlinedList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CB6516428C5A5D700DCA721 /* InlinedList.swift */; }; - 0CB6516828C5A5EC00DCA721 /* Introspect in Frameworks */ = {isa = PBXBuildFile; productRef = 0CB6516728C5A5EC00DCA721 /* Introspect */; }; 0CB6516A28C5B4A600DCA721 /* InlineHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CB6516928C5B4A600DCA721 /* InlineHeader.swift */; }; 0CBAB83628D12ED500AC903E /* DisableInteraction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CBAB83528D12ED500AC903E /* DisableInteraction.swift */; }; 0CBC76FD288D914F0054BE44 /* BatchChoiceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CBC76FC288D914F0054BE44 /* BatchChoiceView.swift */; }; @@ -122,11 +123,16 @@ 0CC389542970AD900066D06F /* Action+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CC389522970AD900066D06F /* Action+CoreDataProperties.swift */; }; 0CD4CAC628C980EB0046E1DC /* HistoryActionsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CD4CAC528C980EB0046E1DC /* HistoryActionsView.swift */; }; 0CD5E78928CD932B001BF684 /* DisabledAppearance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CD5E78828CD932B001BF684 /* DisabledAppearance.swift */; }; + 0CD5F1FB299BEFBE00476DDB /* SearchAppearance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CD5F1FA299BEFBE00476DDB /* SearchAppearance.swift */; }; + 0CD5F1FD299C083B00476DDB /* PluginPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CD5F1FC299C083B00476DDB /* PluginPickerView.swift */; }; 0CD72E17293D9928001A7EA4 /* Array.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CD72E16293D9928001A7EA4 /* Array.swift */; }; 0CDCB91828C662640098B513 /* EmptyInstructionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CDCB91728C662640098B513 /* EmptyInstructionView.swift */; }; 0CDDDE052935235E006810B1 /* BetterSafariView in Frameworks */ = {isa = PBXBuildFile; productRef = 0CDDDE042935235E006810B1 /* BetterSafariView */; }; 0CE1C4182981E8D700418F20 /* Plugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CE1C4172981E8D700418F20 /* Plugin.swift */; }; 0CE66B3A28E640D200F69346 /* Backport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CE66B3928E640D200F69346 /* Backport.swift */; }; + 0CEC8AAC299B03E5007BFE8F /* Introspect in Frameworks */ = {isa = PBXBuildFile; productRef = 0CEC8AAB299B03E5007BFE8F /* Introspect */; }; + 0CEC8AAE299B31B6007BFE8F /* SearchFilterHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CEC8AAD299B31B6007BFE8F /* SearchFilterHeaderView.swift */; }; + 0CEC8AB2299B3B57007BFE8F /* LibraryPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CEC8AB1299B3B57007BFE8F /* LibraryPickerView.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -156,7 +162,7 @@ 0C41BC6428C2AEB900B47DD6 /* SearchModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchModels.swift; sourceTree = ""; }; 0C422E7D293542EA00486D65 /* PremiumizeWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PremiumizeWrapper.swift; sourceTree = ""; }; 0C422E7F293542F300486D65 /* PremiumizeModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PremiumizeModels.swift; sourceTree = ""; }; - 0C42B5952932F2D5008057A0 /* DebridChoiceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebridChoiceView.swift; sourceTree = ""; }; + 0C42B5952932F2D5008057A0 /* DebridPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebridPickerView.swift; sourceTree = ""; }; 0C42B5972932F6DD008057A0 /* Set.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Set.swift; sourceTree = ""; }; 0C445C61293F9A0B0060744D /* Bundle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bundle.swift; sourceTree = ""; }; 0C44E2A728D4DDDC007711AE /* Application.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Application.swift; sourceTree = ""; }; @@ -167,6 +173,7 @@ 0C5005512992B6750064606A /* PluginTagsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PluginTagsView.swift; sourceTree = ""; }; 0C5005582992BA6A0064606A /* PluginTag+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PluginTag+CoreDataClass.swift"; sourceTree = ""; }; 0C5005592992BA6A0064606A /* PluginTag+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PluginTag+CoreDataProperties.swift"; sourceTree = ""; }; + 0C50B7CF299DF63C00A9FA3C /* UIDevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIDevice.swift; sourceTree = ""; }; 0C54D36128C5086E00BFEEE2 /* History+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "History+CoreDataClass.swift"; sourceTree = ""; }; 0C54D36228C5086E00BFEEE2 /* History+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "History+CoreDataProperties.swift"; sourceTree = ""; }; 0C572D4B2993FC2A003EEC05 /* ViewDidAppearHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewDidAppearHandler.swift; sourceTree = ""; }; @@ -197,6 +204,7 @@ 0C84F47B2895BFED0074B7C9 /* Source+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Source+CoreDataProperties.swift"; sourceTree = ""; }; 0C84F47C2895BFED0074B7C9 /* SourceHtmlParser+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SourceHtmlParser+CoreDataClass.swift"; sourceTree = ""; }; 0C84F47D2895BFED0074B7C9 /* SourceHtmlParser+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SourceHtmlParser+CoreDataProperties.swift"; sourceTree = ""; }; + 0C871BDE29994D9D005279AC /* FilterLabelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilterLabelView.swift; sourceTree = ""; }; 0C95D8D728A55B03005E22B3 /* DefaultActionsPickerViews.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultActionsPickerViews.swift; sourceTree = ""; }; 0C95D8D928A55BB6005E22B3 /* SettingsModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsModels.swift; sourceTree = ""; }; 0CA05456288EE58200850554 /* SettingsPluginListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsPluginListView.swift; sourceTree = ""; }; @@ -241,10 +249,14 @@ 0CC6E4D428A45BA000AF2BCC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; 0CD4CAC528C980EB0046E1DC /* HistoryActionsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryActionsView.swift; sourceTree = ""; }; 0CD5E78828CD932B001BF684 /* DisabledAppearance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisabledAppearance.swift; sourceTree = ""; }; + 0CD5F1FA299BEFBE00476DDB /* SearchAppearance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchAppearance.swift; sourceTree = ""; }; + 0CD5F1FC299C083B00476DDB /* PluginPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PluginPickerView.swift; sourceTree = ""; }; 0CD72E16293D9928001A7EA4 /* Array.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Array.swift; sourceTree = ""; }; 0CDCB91728C662640098B513 /* EmptyInstructionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyInstructionView.swift; sourceTree = ""; }; 0CE1C4172981E8D700418F20 /* Plugin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Plugin.swift; sourceTree = ""; }; 0CE66B3928E640D200F69346 /* Backport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Backport.swift; sourceTree = ""; }; + 0CEC8AAD299B31B6007BFE8F /* SearchFilterHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchFilterHeaderView.swift; sourceTree = ""; }; + 0CEC8AB1299B3B57007BFE8F /* LibraryPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibraryPickerView.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -256,8 +268,8 @@ 0C64A4B4288903680079976D /* Base32 in Frameworks */, 0C4CFC462897030D00AD9FAD /* Regex in Frameworks */, 0C7376F028A97D1400D60918 /* SwiftUIX in Frameworks */, - 0CB6516828C5A5EC00DCA721 /* Introspect in Frameworks */, 0C64A4B7288903880079976D /* KeychainSwift in Frameworks */, + 0CEC8AAC299B03E5007BFE8F /* Introspect in Frameworks */, 0CAF1C7B286F5C8600296F86 /* SwiftSoup in Frameworks */, 0CDDDE052935235E006810B1 /* BetterSafariView in Frameworks */, ); @@ -290,7 +302,7 @@ 0C0755C42934245800ECA142 /* Debrid */ = { isa = PBXGroup; children = ( - 0C42B5952932F2D5008057A0 /* DebridChoiceView.swift */, + 0C42B5952932F2D5008057A0 /* DebridPickerView.swift */, 0C0755C5293424A200ECA142 /* DebridLabelView.swift */, ); path = Debrid; @@ -360,6 +372,7 @@ 0C794B65289DAC9F00DD1CC8 /* Source */, 0C0D50E6288DFF850035ECC8 /* PluginListView.swift */, 0C5005512992B6750064606A /* PluginTagsView.swift */, + 0CD5F1FC299C083B00476DDB /* PluginPickerView.swift */, ); path = Plugin; sourceTree = ""; @@ -380,6 +393,7 @@ 0CD5E78828CD932B001BF684 /* DisabledAppearance.swift */, 0CBAB83528D12ED500AC903E /* DisableInteraction.swift */, 0CB6516428C5A5D700DCA721 /* InlinedList.swift */, + 0CD5F1FA299BEFBE00476DDB /* SearchAppearance.swift */, 0C572D4D299403B7003EEC05 /* ViewDidAppearModifier.swift */, ); path = Modifiers; @@ -399,6 +413,7 @@ children = ( 0C41BC6228C2AD0F00B47DD6 /* SearchResultButtonView.swift */, 0C57D4CB289032ED008534E8 /* SearchResultInfoView.swift */, + 0CEC8AAD299B31B6007BFE8F /* SearchFilterHeaderView.swift */, ); path = SearchResult; sourceTree = ""; @@ -459,6 +474,7 @@ 0C391ECA28CAA44B009F1CA1 /* AlertButton.swift */, 0C360C5B28C7DF1400884ED3 /* DynamicFetchRequest.swift */, 0CDCB91728C662640098B513 /* EmptyInstructionView.swift */, + 0C871BDE29994D9D005279AC /* FilterLabelView.swift */, 0CA148C1288903F000DE2211 /* NavView.swift */, 0CA3FB1F28B91D9500FA10A8 /* IndeterminateProgressView.swift */, 0CB6516928C5B4A600DCA721 /* InlineHeader.swift */, @@ -491,6 +507,7 @@ 0C7ED14228D65518009E29AD /* FileManager.swift */, 0C42B5972932F6DD008057A0 /* Set.swift */, 0C7C128528DAA3CD00381CD1 /* URL.swift */, + 0C50B7CF299DF63C00A9FA3C /* UIDevice.swift */, ); path = Extensions; sourceTree = ""; @@ -556,6 +573,7 @@ 0CA3B23628C2660700616D3A /* HistoryView.swift */, 0CD4CAC528C980EB0046E1DC /* HistoryActionsView.swift */, 0C12D43C28CC332A000195BF /* HistoryButtonView.swift */, + 0CEC8AB1299B3B57007BFE8F /* LibraryPickerView.swift */, ); path = Library; sourceTree = ""; @@ -610,8 +628,8 @@ 0C4CFC452897030D00AD9FAD /* Regex */, 0C7376EF28A97D1400D60918 /* SwiftUIX */, 0C7506D628B1AC9A008BEE38 /* SwiftyJSON */, - 0CB6516728C5A5EC00DCA721 /* Introspect */, 0CDDDE042935235E006810B1 /* BetterSafariView */, + 0CEC8AAB299B03E5007BFE8F /* Introspect */, ); productName = Torrenter; productReference = 0CAF1C68286F5C0E00296F86 /* Ferrite.app */; @@ -648,8 +666,8 @@ 0C4CFC442897030D00AD9FAD /* XCRemoteSwiftPackageReference "Regex" */, 0C7376EE28A97D1400D60918 /* XCRemoteSwiftPackageReference "SwiftUIX" */, 0C7506D528B1AC9A008BEE38 /* XCRemoteSwiftPackageReference "SwiftyJSON" */, - 0CB6516628C5A5EC00DCA721 /* XCRemoteSwiftPackageReference "SwiftUI-Introspect" */, 0CDDDE032935235E006810B1 /* XCRemoteSwiftPackageReference "BetterSafariView" */, + 0CEC8AAA299B03E5007BFE8F /* XCRemoteSwiftPackageReference "SwiftUI-Introspect" */, ); productRefGroup = 0CAF1C69286F5C0E00296F86 /* Products */; projectDirPath = ""; @@ -725,7 +743,7 @@ 0CA429F828C5098D000D0610 /* DateFormatter.swift in Sources */, 0C5005522992B6750064606A /* PluginTagsView.swift in Sources */, 0CE66B3A28E640D200F69346 /* Backport.swift in Sources */, - 0C42B5962932F2D5008057A0 /* DebridChoiceView.swift in Sources */, + 0C42B5962932F2D5008057A0 /* DebridPickerView.swift in Sources */, 0C2886D72960C50900D6FC16 /* RealDebridCloudView.swift in Sources */, 0C794B6B289DACF100DD1CC8 /* PluginCatalogButtonView.swift in Sources */, 0C54D36428C5086E00BFEEE2 /* History+CoreDataProperties.swift in Sources */, @@ -735,9 +753,11 @@ 0C7D11FE28AA03FE00ED92DB /* View.swift in Sources */, 0CA3B23728C2660700616D3A /* HistoryView.swift in Sources */, 0C70E40228C3CE9C00A5C72D /* ConditionalContextMenu.swift in Sources */, + 0C50B7D0299DF63C00A9FA3C /* UIDevice.swift in Sources */, 0C0D50E7288DFF850035ECC8 /* PluginListView.swift in Sources */, 0CA3B23428C2658700616D3A /* LibraryView.swift in Sources */, 0CD72E17293D9928001A7EA4 /* Array.swift in Sources */, + 0CD5F1FB299BEFBE00476DDB /* SearchAppearance.swift in Sources */, 0C44E2A828D4DDDC007711AE /* Application.swift in Sources */, 0CC389542970AD900066D06F /* Action+CoreDataProperties.swift in Sources */, 0C7ED14128D61BBA009E29AD /* BackupModels.swift in Sources */, @@ -789,6 +809,7 @@ 0C31133C28B1ABFA004DCB0D /* SourceJsonParser+CoreDataClass.swift in Sources */, 0CBC76FF288DAAD00054BE44 /* NavigationViewModel.swift in Sources */, 0C50055B2992BA6A0064606A /* PluginTag+CoreDataProperties.swift in Sources */, + 0C871BDF29994D9D005279AC /* FilterLabelView.swift in Sources */, 0C3E00D0296F4DB200ECECB2 /* ActionModels.swift in Sources */, 0C44E2AD28D51C63007711AE /* BackupManager.swift in Sources */, 0C422E80293542F300486D65 /* PremiumizeModels.swift in Sources */, @@ -800,12 +821,15 @@ 0C54D36328C5086E00BFEEE2 /* History+CoreDataClass.swift in Sources */, 0CBAB83628D12ED500AC903E /* DisableInteraction.swift in Sources */, 0CE1C4182981E8D700418F20 /* Plugin.swift in Sources */, + 0CEC8AB2299B3B57007BFE8F /* LibraryPickerView.swift in Sources */, 0C84F4842895BFED0074B7C9 /* SourceHtmlParser+CoreDataClass.swift in Sources */, 0C32FB572890D1F2002BD219 /* ListRowViews.swift in Sources */, + 0CEC8AAE299B31B6007BFE8F /* SearchFilterHeaderView.swift in Sources */, 0C84F4822895BFED0074B7C9 /* Source+CoreDataClass.swift in Sources */, 0C391ECB28CAA44B009F1CA1 /* AlertButton.swift in Sources */, 0C7C128628DAA3CD00381CD1 /* URL.swift in Sources */, 0C84F4852895BFED0074B7C9 /* SourceHtmlParser+CoreDataProperties.swift in Sources */, + 0CD5F1FD299C083B00476DDB /* PluginPickerView.swift in Sources */, 0C2886D22960AC2800D6FC16 /* DebridCloudView.swift in Sources */, 0CA148EB288903F000DE2211 /* SearchResultsView.swift in Sources */, 0CA148D7288903F000DE2211 /* LoginWebView.swift in Sources */, @@ -1072,14 +1096,6 @@ minimumVersion = 2.0.0; }; }; - 0CB6516628C5A5EC00DCA721 /* XCRemoteSwiftPackageReference "SwiftUI-Introspect" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/siteline/SwiftUI-Introspect"; - requirement = { - branch = master; - kind = branch; - }; - }; 0CDDDE032935235E006810B1 /* XCRemoteSwiftPackageReference "BetterSafariView" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/stleamist/BetterSafariView"; @@ -1088,6 +1104,14 @@ kind = branch; }; }; + 0CEC8AAA299B03E5007BFE8F /* XCRemoteSwiftPackageReference "SwiftUI-Introspect" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/siteline/SwiftUI-Introspect/"; + requirement = { + branch = master; + kind = branch; + }; + }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ @@ -1121,16 +1145,16 @@ package = 0CAF1C79286F5C8600296F86 /* XCRemoteSwiftPackageReference "SwiftSoup" */; productName = SwiftSoup; }; - 0CB6516728C5A5EC00DCA721 /* Introspect */ = { - isa = XCSwiftPackageProductDependency; - package = 0CB6516628C5A5EC00DCA721 /* XCRemoteSwiftPackageReference "SwiftUI-Introspect" */; - productName = Introspect; - }; 0CDDDE042935235E006810B1 /* BetterSafariView */ = { isa = XCSwiftPackageProductDependency; package = 0CDDDE032935235E006810B1 /* XCRemoteSwiftPackageReference "BetterSafariView" */; productName = BetterSafariView; }; + 0CEC8AAB299B03E5007BFE8F /* Introspect */ = { + isa = XCSwiftPackageProductDependency; + package = 0CEC8AAA299B03E5007BFE8F /* XCRemoteSwiftPackageReference "SwiftUI-Introspect" */; + productName = Introspect; + }; /* End XCSwiftPackageProductDependency section */ /* Begin XCVersionGroup section */ diff --git a/Ferrite/API/RealDebridWrapper.swift b/Ferrite/API/RealDebridWrapper.swift index 9b92a58..a81f69c 100644 --- a/Ferrite/API/RealDebridWrapper.swift +++ b/Ferrite/API/RealDebridWrapper.swift @@ -18,6 +18,16 @@ public class RealDebrid { var authTask: Task? + @MainActor + func setUserDefaultsValue(_ value: Any, forKey: String) { + UserDefaults.standard.set(value, forKey: forKey) + } + + @MainActor + func removeUserDefaultsValue(forKey: String) { + UserDefaults.standard.removeObject(forKey: forKey) + } + // Fetches the device code from RD public func getVerificationInfo() async throws -> DeviceCodeResponse { var urlComponents = URLComponents(string: "\(baseAuthUrl)/device/code")! @@ -72,7 +82,7 @@ public class RealDebrid { // If there's a client ID from the response, end the task successfully if let clientId = rawResponse?.clientID, let clientSecret = rawResponse?.clientSecret { - UserDefaults.standard.set(clientId, forKey: "RealDebrid.ClientId") + await setUserDefaultsValue(clientId, forKey: "RealDebrid.ClientId") keychain.set(clientSecret, forKey: "RealDebrid.ClientSecret") try await getTokens(deviceCode: deviceCode) @@ -124,7 +134,7 @@ public class RealDebrid { keychain.set(rawResponse.refreshToken, forKey: "RealDebrid.RefreshToken") let accessTimestamp = Date().timeIntervalSince1970 + Double(rawResponse.expiresIn) - UserDefaults.standard.set(accessTimestamp, forKey: "RealDebrid.AccessTokenStamp") + await setUserDefaultsValue(accessTimestamp, forKey: "RealDebrid.AccessTokenStamp") } public func fetchToken() async -> String? { @@ -147,8 +157,8 @@ public class RealDebrid { public func deleteTokens() async throws { keychain.delete("RealDebrid.RefreshToken") keychain.delete("RealDebrid.ClientSecret") - UserDefaults.standard.removeObject(forKey: "RealDebrid.ClientId") - UserDefaults.standard.removeObject(forKey: "RealDebrid.AccessTokenStamp") + await removeUserDefaultsValue(forKey: "RealDebrid.ClientId") + await removeUserDefaultsValue(forKey: "RealDebrid.AccessTokenStamp") // Run the request, doesn't matter if it fails if let token = keychain.get("RealDebrid.AccessToken") { diff --git a/Ferrite/Extensions/NotificationCenter.swift b/Ferrite/Extensions/NotificationCenter.swift index 7bc0db5..63599a9 100644 --- a/Ferrite/Extensions/NotificationCenter.swift +++ b/Ferrite/Extensions/NotificationCenter.swift @@ -11,4 +11,8 @@ extension Notification.Name { static var didDeleteBookmark: Notification.Name { Notification.Name("Deleted bookmark") } + + static var didDeletePlugin: Notification.Name { + Notification.Name("Deleted plugin") + } } diff --git a/Ferrite/Extensions/UIDevice.swift b/Ferrite/Extensions/UIDevice.swift new file mode 100644 index 0000000..f2ae7e6 --- /dev/null +++ b/Ferrite/Extensions/UIDevice.swift @@ -0,0 +1,18 @@ +// +// UIDevice.swift +// Ferrite +// +// Created by Brian Dashore on 2/16/23. +// + +import SwiftUI + +extension UIDevice { + var hasNotch: Bool { + if #available(iOS 11.0, *) { + let keyWindow = UIApplication.shared.windows.filter(\.isKeyWindow).first + return keyWindow?.safeAreaInsets.bottom ?? 0 > 0 + } + return false + } +} diff --git a/Ferrite/Extensions/View.swift b/Ferrite/Extensions/View.swift index b7910aa..f92064c 100644 --- a/Ferrite/Extensions/View.swift +++ b/Ferrite/Extensions/View.swift @@ -9,30 +9,6 @@ import Introspect import SwiftUI extension View { - // MARK: Custom introspect functions - - func introspectCollectionView(customize: @escaping (UICollectionView) -> Void) -> some View { - inject(UIKitIntrospectionView( - selector: { introspectionView in - guard let viewHost = Introspect.findViewHost(from: introspectionView) else { - return nil - } - return Introspect.previousSibling(containing: UICollectionView.self, from: viewHost) - }, - customize: customize - )) - } - - // From https://github.com/siteline/SwiftUI-Introspect/pull/129 - public func introspectSearchController(customize: @escaping (UISearchController) -> Void) -> some View { - introspectNavigationController { navigationController in - let navigationBar = navigationController.navigationBar - if let searchController = navigationBar.topItem?.searchController { - customize(searchController) - } - } - } - // MARK: Modifiers func conditionalContextMenu(id: some Hashable, @@ -53,11 +29,19 @@ extension View { modifier(DisableInteraction(disabled: disabled)) } - func inlinedList() -> some View { - modifier(InlinedList()) + func inlinedList(inset: CGFloat) -> some View { + modifier(InlinedList(inset: inset)) } func viewDidAppear(_ callback: @escaping () -> Void) -> some View { modifier(ViewDidAppearModifier(callback: callback)) } + + func searchAppearance(_ content: Content) -> some View { + modifier(SearchAppearance(hostingContent: content)) + } + + func searchAppearance(_ content: @escaping () -> Content) -> some View { + modifier(SearchAppearance(hostingContent: content())) + } } diff --git a/Ferrite/ViewModels/NavigationViewModel.swift b/Ferrite/ViewModels/NavigationViewModel.swift index 41e6e4f..d3460e2 100644 --- a/Ferrite/ViewModels/NavigationViewModel.swift +++ b/Ferrite/ViewModels/NavigationViewModel.swift @@ -7,13 +7,6 @@ import SwiftUI -enum ViewTab { - case search - case plugins - case settings - case library -} - @MainActor class NavigationViewModel: ObservableObject { var toastModel: ToastViewModel? @@ -29,6 +22,24 @@ class NavigationViewModel: ObservableObject { case activity } + enum ViewTab { + case search + case plugins + case settings + case library + } + + enum LibraryPickerSegment { + case bookmarks + case history + case debridCloud + } + + enum PluginPickerSegment { + case sources + case actions + } + @Published var isEditingSearch: Bool = false @Published var isSearching: Bool = false @@ -57,10 +68,16 @@ class NavigationViewModel: ObservableObject { @Published var showSourceListEditor: Bool = false + @Published var libraryPickerSelection: LibraryPickerSegment = .bookmarks + @Published var pluginPickerSelection: PluginPickerSegment = .sources + @AppStorage("Actions.DefaultDebrid") var defaultDebridAction: DefaultDebridActionType = .none @AppStorage("Actions.DefaultMagnet") var defaultMagnetAction: DefaultMagnetActionType = .none + // TODO: Fix for new Actions API public func runDebridAction(urlString: String, _ action: DefaultDebridActionType? = nil) { + currentChoiceSheet = .magnet + /* let selectedAction = action ?? defaultDebridAction switch selectedAction { @@ -92,10 +109,14 @@ class NavigationViewModel: ObservableObject { toastModel?.updateToastDescription("Could not create object for sharing") } } + */ } + // TODO: Fix for new Actions API public func runMagnetAction(magnet: Magnet?, _ action: DefaultMagnetActionType? = nil) { + currentChoiceSheet = .magnet // Fall back to selected magnet if the provided magnet is nil + /* let magnet = magnet ?? selectedMagnet guard let magnetLink = magnet?.link else { toastModel?.updateToastDescription("Could not run your action because the magnet link is invalid.") @@ -125,5 +146,6 @@ class NavigationViewModel: ObservableObject { toastModel?.updateToastDescription("Could not create object for sharing") } } + */ } } diff --git a/Ferrite/ViewModels/PluginManager.swift b/Ferrite/ViewModels/PluginManager.swift index 02629c1..2bac4d6 100644 --- a/Ferrite/ViewModels/PluginManager.swift +++ b/Ferrite/ViewModels/PluginManager.swift @@ -87,7 +87,7 @@ public class PluginManager: ObservableObject { } print("Plugin fetch error: \(error)") - } + } } // Check if underlying type is Source or Action diff --git a/Ferrite/Views/CommonViews/Backport.swift b/Ferrite/Views/CommonViews/Backport.swift index 139897b..2c7221d 100644 --- a/Ferrite/Views/CommonViews/Backport.swift +++ b/Ferrite/Views/CommonViews/Backport.swift @@ -6,6 +6,7 @@ // import SwiftUI +import Introspect public struct Backport { public let content: Content @@ -123,4 +124,17 @@ extension Backport where Content: View { } } } + + @ViewBuilder func introspectSearchController(customize: @escaping (UISearchController) -> ()) -> some View { + if #available(iOS 15, *) { + content.introspectSearchController(customize: customize) + } else { + content.introspectNavigationController { navigationController in + let navigationBar = navigationController.navigationBar + if let searchController = navigationBar.topItem?.searchController { + customize(searchController) + } + } + } + } } diff --git a/Ferrite/Views/CommonViews/FilterLabelView.swift b/Ferrite/Views/CommonViews/FilterLabelView.swift new file mode 100644 index 0000000..deb36a2 --- /dev/null +++ b/Ferrite/Views/CommonViews/FilterLabelView.swift @@ -0,0 +1,27 @@ +// +// FilterLabelView.swift +// Ferrite +// +// Created by Brian Dashore on 2/12/23. +// + +import SwiftUI + +struct FilterLabelView: View { + var name: String + + var body: some View { + HStack(spacing: 4) { + Text(name) + .opacity(0.6) + .foregroundColor(.primary) + + Image(systemName: "chevron.down") + .foregroundColor(.tertiaryLabel) + } + .padding(.horizontal, 9) + .padding(.vertical, 7) + .font(.caption, weight: .medium) + .background(Capsule().foregroundColor(.secondarySystemFill)) + } +} diff --git a/Ferrite/Views/CommonViews/LibraryHeaderView.swift b/Ferrite/Views/CommonViews/LibraryHeaderView.swift new file mode 100644 index 0000000..e6009e1 --- /dev/null +++ b/Ferrite/Views/CommonViews/LibraryHeaderView.swift @@ -0,0 +1,17 @@ +// +// LibraryHeaderView.swift +// Ferrite +// +// Created by Brian Dashore on 2/12/23. +// + +import SwiftUI + +struct LibraryHeaderView: View { + @EnvironmentObject var debridManager: DebridManager + + @Binding var selectedSegment: LibraryPickerSegment + var body: some View { + + } +} diff --git a/Ferrite/Views/CommonViews/Modifiers/InlinedList.swift b/Ferrite/Views/CommonViews/Modifiers/InlinedList.swift index 3e2cac2..4c0b2dd 100644 --- a/Ferrite/Views/CommonViews/Modifiers/InlinedList.swift +++ b/Ferrite/Views/CommonViews/Modifiers/InlinedList.swift @@ -12,16 +12,18 @@ import Introspect import SwiftUI struct InlinedList: ViewModifier { + let inset: CGFloat + func body(content: Content) -> some View { if #available(iOS 16, *) { content .introspectCollectionView { collectionView in - collectionView.contentInset.top = -20 + collectionView.contentInset.top = inset } } else { content .introspectTableView { tableView in - tableView.tableHeaderView = UIView(frame: CGRect(x: 0, y: 0, width: 0, height: 20)) + tableView.contentInset.top = inset } } } diff --git a/Ferrite/Views/CommonViews/Modifiers/SearchAppearance.swift b/Ferrite/Views/CommonViews/Modifiers/SearchAppearance.swift new file mode 100644 index 0000000..22d232e --- /dev/null +++ b/Ferrite/Views/CommonViews/Modifiers/SearchAppearance.swift @@ -0,0 +1,63 @@ +// +// SearchAppearance.swift +// Ferrite +// +// Created by Brian Dashore on 2/14/23. +// + +import SwiftUI +import Introspect + +struct SearchAppearance: ViewModifier { + @AppStorage("Behavior.AutocorrectSearch") var autocorrectSearch = true + + let hostingContent: V + let hostingController: UIHostingController + + init(hostingContent: V) { + self.hostingContent = hostingContent + hostingController = UIHostingController(rootView: hostingContent) + } + + func body(content: Content) -> some View { + if #available(iOS 15, *) { + content + .backport.introspectSearchController { searchController in + searchController.hidesNavigationBarDuringPresentation = true + searchController.searchBar.autocorrectionType = autocorrectSearch ? .default : .no + searchController.searchBar.autocapitalizationType = autocorrectSearch ? .sentences : .none + searchController.searchBar.showsScopeBar = true + searchController.searchBar.scopeButtonTitles = [""] + (searchController.searchBar.value(forKey: "_scopeBar") as? UIView)?.isHidden = true + + hostingController.view.translatesAutoresizingMaskIntoConstraints = false + hostingController.view.backgroundColor = .clear + + guard let containerView = searchController.searchBar.value(forKey: "_scopeBarContainerView") as? UIView else { + return + } + containerView.addSubview(hostingController.view) + + NSLayoutConstraint.activate([ + hostingController.view.widthAnchor.constraint(equalTo: containerView.widthAnchor), + hostingController.view.topAnchor.constraint(equalTo: containerView.topAnchor), + hostingController.view.heightAnchor.constraint(equalTo: containerView.heightAnchor) + ]) + } + .introspectNavigationController { navigationController in + navigationController.navigationBar.prefersLargeTitles = true + navigationController.navigationBar.sizeToFit() + } + } else { + VStack { + hostingContent + content + Spacer() + } + .backport.introspectSearchController { searchController in + searchController.searchBar.autocorrectionType = autocorrectSearch ? .default : .no + searchController.searchBar.autocapitalizationType = autocorrectSearch ? .sentences : .none + } + } + } +} diff --git a/Ferrite/Views/CommonViews/SearchableContent.swift b/Ferrite/Views/CommonViews/SearchableContent.swift new file mode 100644 index 0000000..754fa3a --- /dev/null +++ b/Ferrite/Views/CommonViews/SearchableContent.swift @@ -0,0 +1,37 @@ +// +// SearchableContent.swift +// Ferrite +// +// Created by Brian Dashore on 2/11/23. +// +// View to link animations together with searchbar +// Passes through geometry proxy and last height vars for any comparison +// + +import SwiftUI + +struct SearchableContent: View { + @Binding var searching: Bool + + @State private var lastHeight: CGFloat = 0.0 + + @ViewBuilder var content: (Bool) -> Content + + var body: some View { + GeometryReader { geom in + // Return if the height has changed as a closure variable for child transactions + content(geom.size.height != lastHeight) + .backport.onAppear { + lastHeight = geom.size.height + } + .onChange(of: geom.size.height) { newHeight in + lastHeight = newHeight + } + .transaction { + if geom.size.height != lastHeight && searching { + $0.animation = .default.speed(2) + } + } + } + } +} diff --git a/Ferrite/Views/CommonViews/SectionHeaderView.swift b/Ferrite/Views/CommonViews/SectionHeaderView.swift new file mode 100644 index 0000000..c46f60e --- /dev/null +++ b/Ferrite/Views/CommonViews/SectionHeaderView.swift @@ -0,0 +1,20 @@ +// +// SectionHeaderView.swift +// Ferrite +// +// Created by Brian Dashore on 2/15/23. +// + +import SwiftUI + +struct SectionHeaderView: View { + var body: some View { + Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/) + } +} + +struct SectionHeaderView_Previews: PreviewProvider { + static var previews: some View { + SectionHeaderView() + } +} diff --git a/Ferrite/Views/CommonViews/TestHostingView.swift b/Ferrite/Views/CommonViews/TestHostingView.swift new file mode 100644 index 0000000..ebf4ae8 --- /dev/null +++ b/Ferrite/Views/CommonViews/TestHostingView.swift @@ -0,0 +1,75 @@ +// +// TestHostingView.swift +// Ferrite +// +// Created by Brian Dashore on 2/13/23. +// + +import SwiftUI + +struct TestHostingView: View { + @State private var textName = "First" + @State private var secondTextName = "First" + var body: some View { + ScrollView(.horizontal, showsIndicators: false) { + HStack { + Menu { + Picker("", selection: $textName) { + Text("First").tag("First") + Text("Second").tag("Second") + Text("Third").tag("Third") + } + } label: { + HStack(spacing: 2) { + Text(textName) + .opacity(0.6) + .foregroundColor(.primary) + + Image(systemName: "chevron.down") + .foregroundColor(.tertiaryLabel) + } + .padding(.horizontal, 9) + .padding(.vertical, 7) + .font(.caption, weight: .bold) + .background(Capsule().foregroundColor(.secondarySystemFill)) + } + .id(textName) + .transaction { + $0.animation = .none + } + + Menu { + Picker("", selection: $secondTextName) { + Text("First").tag("First") + Text("Second").tag("Second") + Text("Third").tag("Third") + } + } label: { + HStack(spacing: 2) { + Text(secondTextName) + .opacity(0.6) + .foregroundColor(.primary) + + Image(systemName: "chevron.down") + .foregroundColor(.tertiaryLabel) + } + .padding(.horizontal, 9) + .padding(.vertical, 7) + .font(.caption, weight: .bold) + .background(Capsule().foregroundColor(.secondarySystemFill)) + } + .id(secondTextName) + .transaction { + $0.animation = .none + } + } + .padding(.horizontal, 18) + } + } +} + +struct TestHostingView_Previews: PreviewProvider { + static var previews: some View { + TestHostingView() + } +} diff --git a/Ferrite/Views/ComponentViews/Debrid/DebridChoiceView.swift b/Ferrite/Views/ComponentViews/Debrid/DebridPickerView.swift similarity index 72% rename from Ferrite/Views/ComponentViews/Debrid/DebridChoiceView.swift rename to Ferrite/Views/ComponentViews/Debrid/DebridPickerView.swift index f38ddf5..6aaf7f8 100644 --- a/Ferrite/Views/ComponentViews/Debrid/DebridChoiceView.swift +++ b/Ferrite/Views/ComponentViews/Debrid/DebridPickerView.swift @@ -7,15 +7,17 @@ import SwiftUI -struct DebridChoiceView: View { +struct DebridPickerView: View { @EnvironmentObject var debridManager: DebridManager + @ViewBuilder var label: Content + var body: some View { Menu { 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()) @@ -24,14 +26,7 @@ struct DebridChoiceView: View { } } } label: { - Text(debridManager.selectedDebridType?.toString(abbreviated: true) ?? "Debrid") + label } - .animation(.none) - } -} - -struct DebridChoiceView_Previews: PreviewProvider { - static var previews: some View { - DebridChoiceView() } } diff --git a/Ferrite/Views/ComponentViews/Library/BookmarksView.swift b/Ferrite/Views/ComponentViews/Library/BookmarksView.swift index fdf8690..7668409 100644 --- a/Ferrite/Views/ComponentViews/Library/BookmarksView.swift +++ b/Ferrite/Views/ComponentViews/Library/BookmarksView.swift @@ -26,34 +26,33 @@ struct BookmarksView: View { sortDescriptors: [NSSortDescriptor(keyPath: \Bookmark.orderNum, ascending: true)] ) { (bookmarks: FetchedResults) 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 } + .onMove { source, destination in + var changedBookmarks = bookmarks.map { $0 } - changedBookmarks.move(fromOffsets: source, toOffset: destination) + changedBookmarks.move(fromOffsets: source, toOffset: destination) - for reverseIndex in stride(from: changedBookmarks.count - 1, through: 0, by: -1) { - changedBookmarks[reverseIndex].orderNum = Int16(reverseIndex) + for reverseIndex in stride(from: changedBookmarks.count - 1, through: 0, by: -1) { + changedBookmarks[reverseIndex].orderNum = Int16(reverseIndex) + } + + PersistenceController.shared.save() } - - PersistenceController.shared.save() } - } } - .inlinedList() .listStyle(.insetGrouped) + .inlinedList(inset: Application.shared.osVersion.majorVersion > 14 ? 15 : -25) .backport.onAppear { if debridManager.enabledDebrids.count > 0 { viewTask = Task { diff --git a/Ferrite/Views/ComponentViews/Library/LibraryPickerView.swift b/Ferrite/Views/ComponentViews/Library/LibraryPickerView.swift new file mode 100644 index 0000000..8c97e58 --- /dev/null +++ b/Ferrite/Views/ComponentViews/Library/LibraryPickerView.swift @@ -0,0 +1,31 @@ +// +// LibraryPickerView.swift +// Ferrite +// +// Created by Brian Dashore on 2/13/23. +// + +import SwiftUI + +struct LibraryPickerView: View { + @Environment(\.verticalSizeClass) var verticalSizeClass + + @EnvironmentObject var debridManager: DebridManager + @EnvironmentObject var navModel: NavigationViewModel + + var body: some View { + HStack { + Picker("Segments", selection: $navModel.libraryPickerSelection) { + Text("Bookmarks").tag(NavigationViewModel.LibraryPickerSegment.bookmarks) + Text("History").tag(NavigationViewModel.LibraryPickerSegment.history) + + if !debridManager.enabledDebrids.isEmpty { + Text("Cloud").tag(NavigationViewModel.LibraryPickerSegment.debridCloud) + } + } + } + .pickerStyle(.segmented) + .padding(.horizontal, verticalSizeClass == .compact && UIDevice.current.hasNotch ? 65 : 18) + .padding(.vertical, 5) + } +} diff --git a/Ferrite/Views/ComponentViews/Plugin/Buttons/InstalledPluginButtonView.swift b/Ferrite/Views/ComponentViews/Plugin/Buttons/InstalledPluginButtonView.swift index b99061d..0efdce4 100644 --- a/Ferrite/Views/ComponentViews/Plugin/Buttons/InstalledPluginButtonView.swift +++ b/Ferrite/Views/ComponentViews/Plugin/Buttons/InstalledPluginButtonView.swift @@ -54,6 +54,7 @@ struct InstalledPluginButtonView: View { if #available(iOS 15.0, *) { Button(role: .destructive) { PersistenceController.shared.delete(installedPlugin, context: backgroundContext) + NotificationCenter.default.post(name: .didDeletePlugin, object: nil) } label: { Text("Remove") Image(systemName: "trash") @@ -61,6 +62,7 @@ struct InstalledPluginButtonView: View { } else { Button { PersistenceController.shared.delete(installedPlugin, context: backgroundContext) + NotificationCenter.default.post(name: .didDeletePlugin, object: nil) } label: { Text("Remove") Image(systemName: "trash") diff --git a/Ferrite/Views/ComponentViews/Plugin/PluginListView.swift b/Ferrite/Views/ComponentViews/Plugin/PluginListView.swift index ad515b8..5b18abb 100644 --- a/Ferrite/Views/ComponentViews/Plugin/PluginListView.swift +++ b/Ferrite/Views/ComponentViews/Plugin/PluginListView.swift @@ -57,6 +57,7 @@ struct PluginListView: View { } } } + .inlinedList(inset: Application.shared.osVersion.majorVersion > 14 ? 0 : -25) .listStyle(.insetGrouped) .sheet(isPresented: $navModel.showSourceSettings) { if String(describing: P.self) == "Source" { @@ -70,8 +71,10 @@ struct PluginListView: View { } .onChange(of: searchText) { _ in sourcePredicate = searchText.isEmpty ? nil : NSPredicate(format: "name CONTAINS[cd] %@", searchText) + filteredAvailablePlugins = pluginManager.fetchFilteredPlugins(installedPlugins: installedPlugins, searchText: searchText) + filteredUpdatedPlugins = pluginManager.fetchUpdatedPlugins(installedPlugins: installedPlugins, searchText: searchText) } - .onReceive(installedPlugins.publisher.count()) { _ in + .onReceive(NotificationCenter.default.publisher(for: .didDeletePlugin)) { _ in filteredAvailablePlugins = pluginManager.fetchFilteredPlugins(installedPlugins: installedPlugins, searchText: searchText) filteredUpdatedPlugins = pluginManager.fetchUpdatedPlugins(installedPlugins: installedPlugins, searchText: searchText) } diff --git a/Ferrite/Views/ComponentViews/Plugin/PluginPickerView.swift b/Ferrite/Views/ComponentViews/Plugin/PluginPickerView.swift new file mode 100644 index 0000000..bb02771 --- /dev/null +++ b/Ferrite/Views/ComponentViews/Plugin/PluginPickerView.swift @@ -0,0 +1,27 @@ +// +// PluginPickerView.swift +// Ferrite +// +// Created by Brian Dashore on 2/14/23. +// + +import SwiftUI + +struct PluginPickerView: View { + @Environment(\.verticalSizeClass) var verticalSizeClass + + @EnvironmentObject var navModel: NavigationViewModel + + @State private var textName = "" + @State private var secondTextName = "" + + var body: some View { + Picker("Segments", selection: $navModel.pluginPickerSelection) { + Text("Sources").tag(NavigationViewModel.PluginPickerSegment.sources) + Text("Actions").tag(NavigationViewModel.PluginPickerSegment.actions) + } + .pickerStyle(.segmented) + .padding(.horizontal, verticalSizeClass == .compact && UIDevice.current.hasNotch ? 65 : 18) + .padding(.vertical, 5) + } +} diff --git a/Ferrite/Views/ComponentViews/SearchResult/SearchFilterHeaderView.swift b/Ferrite/Views/ComponentViews/SearchResult/SearchFilterHeaderView.swift new file mode 100644 index 0000000..8834060 --- /dev/null +++ b/Ferrite/Views/ComponentViews/SearchResult/SearchFilterHeaderView.swift @@ -0,0 +1,49 @@ +// +// SearchFilterHeaderView.swift +// Ferrite +// +// Created by Brian Dashore on 2/13/23. +// + +import SwiftUI + +struct SearchFilterHeaderView: View { + @Environment(\.verticalSizeClass) var verticalSizeClass + + @EnvironmentObject var scrapingModel: ScrapingViewModel + @EnvironmentObject var debridManager: DebridManager + + @FetchRequest( + entity: Source.entity(), + sortDescriptors: [] + ) var sources: FetchedResults + + var body: some View { + ScrollView(.horizontal, showsIndicators: false) { + HStack(spacing: 6) { + Menu { + Picker("", selection: $scrapingModel.filteredSource) { + Text("None").tag(nil as Source?) + + ForEach(sources, id: \.self) { source in + if let name = source.name, source.enabled { + Text(name) + .tag(Source?.some(source)) + } + } + } + } label: { + FilterLabelView(name: scrapingModel.filteredSource?.name ?? "Source") + } + .id(scrapingModel.filteredSource) + + DebridPickerView() { + FilterLabelView(name: debridManager.selectedDebridType?.toString() ?? "Debrid") + } + .id(debridManager.selectedDebridType) + } + .padding(.horizontal, verticalSizeClass == .compact ? (Application.shared.osVersion.majorVersion > 14 ? 65 : 18) : 18) + .padding(.top, Application.shared.osVersion.majorVersion > 14 ? 0 : 10) + } + } +} diff --git a/Ferrite/Views/ComponentViews/Settings/BackupsView.swift b/Ferrite/Views/ComponentViews/Settings/BackupsView.swift index 8142845..b6488b7 100644 --- a/Ferrite/Views/ComponentViews/Settings/BackupsView.swift +++ b/Ferrite/Views/ComponentViews/Settings/BackupsView.swift @@ -44,7 +44,7 @@ struct BackupsView: View { } } } - .inlinedList() + .inlinedList(inset: -20) .listStyle(.insetGrouped) } } diff --git a/Ferrite/Views/ComponentViews/Settings/DefaultActionsPickerViews.swift b/Ferrite/Views/ComponentViews/Settings/DefaultActionsPickerViews.swift index c9c069c..ab7b52c 100644 --- a/Ferrite/Views/ComponentViews/Settings/DefaultActionsPickerViews.swift +++ b/Ferrite/Views/ComponentViews/Settings/DefaultActionsPickerViews.swift @@ -29,7 +29,7 @@ struct MagnetActionPickerView: View { } } .listStyle(.insetGrouped) - .inlinedList() + .inlinedList(inset: -20) .navigationTitle("Default magnet action") .navigationBarTitleDisplayMode(.inline) } @@ -68,7 +68,7 @@ struct DebridActionPickerView: View { } } .listStyle(.insetGrouped) - .inlinedList() + .inlinedList(inset: -20) .navigationTitle("Default debrid action") .navigationBarTitleDisplayMode(.inline) } diff --git a/Ferrite/Views/ComponentViews/Settings/SettingsPluginListView.swift b/Ferrite/Views/ComponentViews/Settings/SettingsPluginListView.swift index 4abd6ed..1f23b57 100644 --- a/Ferrite/Views/ComponentViews/Settings/SettingsPluginListView.swift +++ b/Ferrite/Views/ComponentViews/Settings/SettingsPluginListView.swift @@ -73,7 +73,7 @@ struct SettingsPluginListView: View { } } .listStyle(.insetGrouped) - .inlinedList() + .inlinedList(inset: -20) } } .sheet(isPresented: $presentSourceSheet) { diff --git a/Ferrite/Views/ContentView.swift b/Ferrite/Views/ContentView.swift index 88e1ba8..56ea9e6 100644 --- a/Ferrite/Views/ContentView.swift +++ b/Ferrite/Views/ContentView.swift @@ -14,118 +14,56 @@ struct ContentView: View { @EnvironmentObject var navModel: NavigationViewModel @EnvironmentObject var pluginManager: PluginManager - @FetchRequest( - entity: Source.entity(), - sortDescriptors: [] - ) var sources: FetchedResults - - @AppStorage("Behavior.AutocorrectSearch") var autocorrectSearch = true - - @State private var selectedSource: Source? { - didSet { - scrapingModel.filteredSource = selectedSource - } - } - var body: some View { NavView { - VStack(spacing: 10) { - HStack(spacing: 6) { - Text("Filter") - .foregroundColor(.secondary) + SearchResultsView() + .listStyle(.insetGrouped) + .navigationTitle("Search") + .navigationSearchBar { + SearchBar("Search", + text: $scrapingModel.searchText, + isEditing: $navModel.isEditingSearch, + onCommit: { + scrapingModel.searchResults = [] + scrapingModel.runningSearchTask = Task { + navModel.isSearching = true + navModel.showSearchProgress = true - Menu { - Button { - selectedSource = nil - } label: { - Text("None") + let sources = pluginManager.fetchInstalledSources() + await scrapingModel.scanSources(sources: sources) - if selectedSource == nil { - Image(systemName: "checkmark") - } - } + if debridManager.enabledDebrids.count > 0, !scrapingModel.searchResults.isEmpty { + debridManager.clearIAValues() - ForEach(sources, id: \.self) { source in - if let name = source.name, source.enabled { - Button { - selectedSource = source - } label: { - if selectedSource == source { - Label(name, systemImage: "checkmark") - } else { - Text(name) - } - } - } - } - } label: { - Text(selectedSource?.name ?? "Source") - .padding(.trailing, -3) - Image(systemName: "chevron.down") - } - .foregroundColor(.primary) - .animation(.none) - - Spacer() - } - .padding(.vertical, 5) - .padding(.horizontal, 20) - - SearchResultsView() - } - .navigationTitle("Search") - .navigationBarTitleDisplayMode( - navModel.isSearching && Application.shared.osVersion.majorVersion > 14 ? .inline : .large - ) - .navigationSearchBar { - SearchBar("Search", - text: $scrapingModel.searchText, - isEditing: $navModel.isEditingSearch, - onCommit: { - scrapingModel.searchResults = [] - scrapingModel.runningSearchTask = Task { - navModel.isSearching = true - navModel.showSearchProgress = true - - let sources = pluginManager.fetchInstalledSources() - await scrapingModel.scanSources(sources: sources) - - if debridManager.enabledDebrids.count > 0, !scrapingModel.searchResults.isEmpty { - debridManager.clearIAValues() - - // Remove magnets that don't have a hash - let magnets = scrapingModel.searchResults.compactMap { - if let magnetHash = $0.magnet.hash { - return Magnet(hash: magnetHash, link: $0.magnet.link) - } else { - return nil + // Remove magnets that don't have a hash + let magnets = scrapingModel.searchResults.compactMap { + if let magnetHash = $0.magnet.hash { + return Magnet(hash: magnetHash, link: $0.magnet.link) + } else { + return nil + } } + await debridManager.populateDebridIA(magnets) } - await debridManager.populateDebridIA(magnets) - } - navModel.showSearchProgress = false + navModel.showSearchProgress = false + } + }) + .showsCancelButton(navModel.isEditingSearch || navModel.isSearching) + .onCancel { + scrapingModel.searchResults = [] + scrapingModel.runningSearchTask?.cancel() + scrapingModel.runningSearchTask = nil + navModel.isSearching = false + scrapingModel.searchText = "" } - }) - .showsCancelButton(navModel.isEditingSearch || navModel.isSearching) - .onCancel { - scrapingModel.searchResults = [] - scrapingModel.runningSearchTask?.cancel() - scrapingModel.runningSearchTask = nil - navModel.isSearching = false - scrapingModel.searchText = "" - } - } - .introspectSearchController { searchController in - searchController.hidesNavigationBarDuringPresentation = false - searchController.searchBar.autocorrectionType = autocorrectSearch ? .default : .no - searchController.searchBar.autocapitalizationType = autocorrectSearch ? .sentences : .none - } - .toolbar { - ToolbarItemGroup(placement: .navigationBarTrailing) { - DebridChoiceView() } - } + .navigationSearchBarHiddenWhenScrolling(false) + .searchAppearance { + SearchFilterHeaderView() + .environmentObject(scrapingModel) + .environmentObject(debridManager) + } } } } diff --git a/Ferrite/Views/LibraryView.swift b/Ferrite/Views/LibraryView.swift index 3c494c1..eaa027f 100644 --- a/Ferrite/Views/LibraryView.swift +++ b/Ferrite/Views/LibraryView.swift @@ -9,14 +9,8 @@ import SwiftUI import SwiftUIX struct LibraryView: View { - enum LibraryPickerSegment { - case bookmarks - case history - case debridCloud - } - - @EnvironmentObject var navModel: NavigationViewModel @EnvironmentObject var debridManager: DebridManager + @EnvironmentObject var navModel: NavigationViewModel @FetchRequest( entity: Bookmark.entity(), @@ -32,7 +26,6 @@ struct LibraryView: View { @AppStorage("Behavior.AutocorrectSearch") var autocorrectSearch = true - @State private var selectedSegment: LibraryPickerSegment = .bookmarks @State private var editMode: EditMode = .inactive @State private var searchText: String = "" @@ -41,20 +34,8 @@ struct LibraryView: View { var body: some View { NavView { - VStack { - Picker("Segments", selection: $selectedSegment) { - Text("Bookmarks").tag(LibraryPickerSegment.bookmarks) - Text("History").tag(LibraryPickerSegment.history) - - if !debridManager.enabledDebrids.isEmpty { - Text("Cloud").tag(LibraryPickerSegment.debridCloud) - } - } - .pickerStyle(.segmented) - .padding(.horizontal) - .padding(.vertical, 5) - - switch selectedSegment { + ZStack { + switch navModel.libraryPickerSelection { case .bookmarks: BookmarksView(searchText: $searchText) case .history: @@ -62,8 +43,27 @@ struct LibraryView: View { case .debridCloud: DebridCloudView(searchText: $searchText) } + } + .navigationTitle("Library") + .toolbar { + ToolbarItem(placement: .navigationBarTrailing) { + HStack(spacing: Application.shared.osVersion.majorVersion > 14 ? 10 : 18) { + Spacer() + EditButton() - Spacer() + switch navModel.libraryPickerSelection { + case .bookmarks, .debridCloud: + DebridPickerView() { + Text(debridManager.selectedDebridType?.toString(abbreviated: true) ?? "Debrid") + } + .transaction { + $0.animation = .none + } + case .history: + HistoryActionsView() + } + } + } } .navigationSearchBar { SearchBar("Search", text: $searchText, isEditing: $isEditingSearch, onCommit: { @@ -75,46 +75,31 @@ struct LibraryView: View { isSearching = false } } - .introspectSearchController { searchController in - searchController.searchBar.autocorrectionType = autocorrectSearch ? .default : .no - searchController.searchBar.autocapitalizationType = autocorrectSearch ? .sentences : .none - } - .overlay { - switch selectedSegment { - case .bookmarks: - if bookmarks.isEmpty { - EmptyInstructionView(title: "No Bookmarks", message: "Add a bookmark from search results") - } - case .history: - if history.isEmpty { - EmptyInstructionView(title: "No History", message: "Start watching to build history") - } - case .debridCloud: - if debridManager.selectedDebridType == nil { - EmptyInstructionView(title: "Cloud Unavailable", message: "Listing is not available for this service") - } - } - } - .navigationTitle("Library") - .toolbar { - ToolbarItem(placement: .navigationBarTrailing) { - HStack(spacing: Application.shared.osVersion.majorVersion > 14 ? 10 : 18) { - Spacer() - EditButton() - - switch selectedSegment { - case .bookmarks, .debridCloud: - DebridChoiceView() - case .history: - HistoryActionsView() - } - } - .animation(.none) - } + .navigationSearchBarHiddenWhenScrolling(false) + .searchAppearance { + LibraryPickerView() + .environmentObject(debridManager) + .environmentObject(navModel) } .environment(\.editMode, $editMode) } - .onChange(of: selectedSegment) { _ in + .overlay { + switch navModel.libraryPickerSelection { + case .bookmarks: + if bookmarks.isEmpty { + EmptyInstructionView(title: "No Bookmarks", message: "Add a bookmark from search results") + } + case .history: + if history.isEmpty { + EmptyInstructionView(title: "No History", message: "Start watching to build history") + } + case .debridCloud: + if debridManager.selectedDebridType == nil { + EmptyInstructionView(title: "Cloud Unavailable", message: "Listing is not available for this service") + } + } + } + .onChange(of: navModel.libraryPickerSelection) { _ in editMode = .inactive } .onDisappear { diff --git a/Ferrite/Views/MainView.swift b/Ferrite/Views/MainView.swift index 003f75b..69b2bd7 100644 --- a/Ferrite/Views/MainView.swift +++ b/Ferrite/Views/MainView.swift @@ -29,25 +29,25 @@ struct MainView: View { .tabItem { Label("Search", systemImage: "magnifyingglass") } - .tag(ViewTab.search) + .tag(NavigationViewModel.ViewTab.search) LibraryView() .tabItem { Label("Library", systemImage: "book.closed") } - .tag(ViewTab.library) + .tag(NavigationViewModel.ViewTab.library) PluginsView() .tabItem { Label("Plugins", systemImage: "doc.text") } - .tag(ViewTab.plugins) + .tag(NavigationViewModel.ViewTab.plugins) SettingsView() .tabItem { Label("Settings", systemImage: "gear") } - .tag(ViewTab.settings) + .tag(NavigationViewModel.ViewTab.settings) } .sheet(item: $navModel.currentChoiceSheet) { item in switch item { diff --git a/Ferrite/Views/PluginsView.swift b/Ferrite/Views/PluginsView.swift index 20f76d0..7f3eb7f 100644 --- a/Ferrite/Views/PluginsView.swift +++ b/Ferrite/Views/PluginsView.swift @@ -9,12 +9,8 @@ import SwiftUI import SwiftUIX struct PluginsView: View { - enum PluginPickerSegment { - case sources - case actions - } - @EnvironmentObject var pluginManager: PluginManager + @EnvironmentObject var navModel: NavigationViewModel @FetchRequest( entity: Source.entity(), @@ -28,7 +24,6 @@ struct PluginsView: View { @AppStorage("Behavior.AutocorrectSearch") var autocorrectSearch = true - @State private var selectedSegment: PluginPickerSegment = .sources @State private var checkedForPlugins = false @State private var isEditingSearch = false @@ -39,41 +34,15 @@ struct PluginsView: View { var body: some View { NavView { - VStack { - Picker("Segments", selection: $selectedSegment) { - Text("Sources").tag(PluginPickerSegment.sources) - Text("Actions").tag(PluginPickerSegment.actions) - } - .pickerStyle(.segmented) - .padding(.horizontal) - .padding(.vertical, 5) - + ZStack { if checkedForPlugins { - switch selectedSegment { + switch navModel.pluginPickerSelection { case .sources: PluginListView(searchText: $searchText) case .actions: PluginListView(searchText: $searchText) } } - - Spacer() - } - .overlay { - if checkedForPlugins { - switch selectedSegment { - case .sources: - 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 { - EmptyInstructionView(title: "No Actions", message: "Add a plugin list in Settings") - } - } - } else { - ProgressView() - } } .backport.onAppear { viewTask = Task { @@ -96,9 +65,26 @@ struct PluginsView: View { isSearching = false } } - .introspectSearchController { searchController in - searchController.searchBar.autocorrectionType = autocorrectSearch ? .default : .no - searchController.searchBar.autocapitalizationType = autocorrectSearch ? .sentences : .none + .navigationSearchBarHiddenWhenScrolling(false) + .searchAppearance { + PluginPickerView() + .environmentObject(navModel) + } + } + .overlay { + if checkedForPlugins { + switch navModel.pluginPickerSelection { + case .sources: + 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 { + EmptyInstructionView(title: "No Actions", message: "Add a plugin list in Settings") + } + } + } else { + ProgressView() } } } diff --git a/Ferrite/Views/SearchResultsView.swift b/Ferrite/Views/SearchResultsView.swift index df775a7..dc2b699 100644 --- a/Ferrite/Views/SearchResultsView.swift +++ b/Ferrite/Views/SearchResultsView.swift @@ -21,7 +21,7 @@ struct SearchResultsView: View { } } .listStyle(.insetGrouped) - .inlinedList() + .inlinedList(inset: Application.shared.osVersion.majorVersion > 14 ? 20 : -20) .overlay { if scrapingModel.searchResults.isEmpty { if navModel.showSearchProgress { diff --git a/Ferrite/Views/SheetViews/BatchChoiceView.swift b/Ferrite/Views/SheetViews/BatchChoiceView.swift index 783a35e..b413b43 100644 --- a/Ferrite/Views/SheetViews/BatchChoiceView.swift +++ b/Ferrite/Views/SheetViews/BatchChoiceView.swift @@ -48,7 +48,7 @@ struct BatchChoiceView: View { } .backport.tint(.primary) .listStyle(.insetGrouped) - .inlinedList() + .inlinedList(inset: -20) .navigationTitle("Select a file") .navigationBarTitleDisplayMode(.inline) .toolbar { @@ -82,7 +82,7 @@ struct BatchChoiceView: View { PersistenceController.shared.createHistory(selectedHistoryInfo, performSave: true) } - navModel.runDebridAction(urlString: debridManager.downloadUrl) + navModel.runDebridAction(urlString: debridManager.downloadUrl, nil) } debridManager.clearSelectedDebridItems()