mirror of
https://github.com/Ferrite-iOS/Ferrite.git
synced 2026-01-11 20:10:27 +00:00
Library: Add history functionality
Action history is logged and displayed to the user's library. These are triggered whenever the magnet choice sheet is displayed. Also redo alerts and action sheets to avoid deprecation notices for >iOS 14. These will be removed when iOS 14 support is dropped. There was also a problem with sheet presentation not working after a sheet was dismissed. Disable the appropriate view when a sheet is being presented. Signed-off-by: kingbri <bdashore3@proton.me>
This commit is contained in:
parent
2f870b9410
commit
4d3a16f77e
25 changed files with 823 additions and 172 deletions
|
|
@ -10,19 +10,24 @@
|
|||
0C0D50E5288DFE7F0035ECC8 /* SourceModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C0D50E4288DFE7F0035ECC8 /* SourceModels.swift */; };
|
||||
0C0D50E7288DFF850035ECC8 /* SourcesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C0D50E6288DFF850035ECC8 /* SourcesView.swift */; };
|
||||
0C10848B28BD9A38008F0BA6 /* SettingsAppVersionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C10848A28BD9A38008F0BA6 /* SettingsAppVersionView.swift */; };
|
||||
0C12D43D28CC332A000195BF /* HistoryButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C12D43C28CC332A000195BF /* HistoryButtonView.swift */; };
|
||||
0C31133C28B1ABFA004DCB0D /* SourceJsonParser+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C31133A28B1ABFA004DCB0D /* SourceJsonParser+CoreDataClass.swift */; };
|
||||
0C31133D28B1ABFA004DCB0D /* SourceJsonParser+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C31133B28B1ABFA004DCB0D /* SourceJsonParser+CoreDataProperties.swift */; };
|
||||
0C32FB532890D19D002BD219 /* AboutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C32FB522890D19D002BD219 /* AboutView.swift */; };
|
||||
0C32FB552890D1BF002BD219 /* UIApplication.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C32FB542890D1BF002BD219 /* UIApplication.swift */; };
|
||||
0C32FB572890D1F2002BD219 /* ListRowViews.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C32FB562890D1F2002BD219 /* ListRowViews.swift */; };
|
||||
0C360C5C28C7DF1400884ED3 /* DynamicFetchRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C360C5B28C7DF1400884ED3 /* DynamicFetchRequest.swift */; };
|
||||
0C391EC928CA63F0009F1CA1 /* DynamicActionSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C391EC828CA63F0009F1CA1 /* DynamicActionSheet.swift */; };
|
||||
0C391ECB28CAA44B009F1CA1 /* AlertButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C391ECA28CAA44B009F1CA1 /* AlertButton.swift */; };
|
||||
0C41BC6328C2AD0F00B47DD6 /* SearchResultButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C41BC6228C2AD0F00B47DD6 /* SearchResultButtonView.swift */; };
|
||||
0C41BC6528C2AEB900B47DD6 /* SearchModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C41BC6428C2AEB900B47DD6 /* SearchModels.swift */; };
|
||||
0C4CFC462897030D00AD9FAD /* Regex in Frameworks */ = {isa = PBXBuildFile; productRef = 0C4CFC452897030D00AD9FAD /* Regex */; };
|
||||
0C4CFC4D28970C8B00AD9FAD /* SourceComplexQuery+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C4CFC4728970C8B00AD9FAD /* SourceComplexQuery+CoreDataClass.swift */; };
|
||||
0C4CFC4E28970C8B00AD9FAD /* SourceComplexQuery+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C4CFC4828970C8B00AD9FAD /* SourceComplexQuery+CoreDataProperties.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 */; };
|
||||
0C57D4CC289032ED008534E8 /* SearchResultRDView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C57D4CB289032ED008534E8 /* SearchResultRDView.swift */; };
|
||||
0C60B1EF28A1A00000E3FD7E /* SearchProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C60B1EE28A1A00000E3FD7E /* SearchProgressView.swift */; };
|
||||
0C626A9528CADB25003C7129 /* DynamicAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C626A9428CADB25003C7129 /* DynamicAlert.swift */; };
|
||||
0C64A4B4288903680079976D /* Base32 in Frameworks */ = {isa = PBXBuildFile; productRef = 0C64A4B3288903680079976D /* Base32 */; };
|
||||
0C64A4B7288903880079976D /* KeychainSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 0C64A4B6288903880079976D /* KeychainSwift */; };
|
||||
0C68135028BC1A2D00FAD890 /* GithubWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C68134F28BC1A2D00FAD890 /* GithubWrapper.swift */; };
|
||||
|
|
@ -80,14 +85,18 @@
|
|||
0CA3B23C28C2AA5600616D3A /* Bookmark+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CA3B23A28C2AA5600616D3A /* Bookmark+CoreDataClass.swift */; };
|
||||
0CA3B23D28C2AA5600616D3A /* Bookmark+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CA3B23B28C2AA5600616D3A /* Bookmark+CoreDataProperties.swift */; };
|
||||
0CA3FB2028B91D9500FA10A8 /* IndeterminateProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CA3FB1F28B91D9500FA10A8 /* IndeterminateProgressView.swift */; };
|
||||
0CA429F828C5098D000D0610 /* DateFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CA429F728C5098D000D0610 /* DateFormatter.swift */; };
|
||||
0CAF1C7B286F5C8600296F86 /* SwiftSoup in Frameworks */ = {isa = PBXBuildFile; productRef = 0CAF1C7A286F5C8600296F86 /* SwiftSoup */; };
|
||||
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 */; };
|
||||
0CBC76FF288DAAD00054BE44 /* NavigationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CBC76FE288DAAD00054BE44 /* NavigationViewModel.swift */; };
|
||||
0CBC7705288DE7F40054BE44 /* PersistenceController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CBC7704288DE7F40054BE44 /* PersistenceController.swift */; };
|
||||
0CD4CAC628C980EB0046E1DC /* HistoryActionsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CD4CAC528C980EB0046E1DC /* HistoryActionsView.swift */; };
|
||||
0CD5E78928CD932B001BF684 /* DisabledAppearance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CD5E78828CD932B001BF684 /* DisabledAppearance.swift */; };
|
||||
0CDCB91828C662640098B513 /* EmptyInstructionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CDCB91728C662640098B513 /* EmptyInstructionView.swift */; };
|
||||
0CFEFCFD288A006200B3F490 /* GroupBoxStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CFEFCFC288A006200B3F490 /* GroupBoxStyle.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
|
@ -96,18 +105,23 @@
|
|||
0C0D50E4288DFE7F0035ECC8 /* SourceModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SourceModels.swift; sourceTree = "<group>"; };
|
||||
0C0D50E6288DFF850035ECC8 /* SourcesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SourcesView.swift; sourceTree = "<group>"; };
|
||||
0C10848A28BD9A38008F0BA6 /* SettingsAppVersionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsAppVersionView.swift; sourceTree = "<group>"; };
|
||||
0C12D43C28CC332A000195BF /* HistoryButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryButtonView.swift; sourceTree = "<group>"; };
|
||||
0C31133A28B1ABFA004DCB0D /* SourceJsonParser+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SourceJsonParser+CoreDataClass.swift"; sourceTree = "<group>"; };
|
||||
0C31133B28B1ABFA004DCB0D /* SourceJsonParser+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SourceJsonParser+CoreDataProperties.swift"; sourceTree = "<group>"; };
|
||||
0C32FB522890D19D002BD219 /* AboutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutView.swift; sourceTree = "<group>"; };
|
||||
0C32FB542890D1BF002BD219 /* UIApplication.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIApplication.swift; sourceTree = "<group>"; };
|
||||
0C32FB562890D1F2002BD219 /* ListRowViews.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListRowViews.swift; sourceTree = "<group>"; };
|
||||
0C360C5B28C7DF1400884ED3 /* DynamicFetchRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DynamicFetchRequest.swift; sourceTree = "<group>"; };
|
||||
0C391EC828CA63F0009F1CA1 /* DynamicActionSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DynamicActionSheet.swift; sourceTree = "<group>"; };
|
||||
0C391ECA28CAA44B009F1CA1 /* AlertButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertButton.swift; sourceTree = "<group>"; };
|
||||
0C41BC6228C2AD0F00B47DD6 /* SearchResultButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchResultButtonView.swift; sourceTree = "<group>"; };
|
||||
0C41BC6428C2AEB900B47DD6 /* SearchModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchModels.swift; sourceTree = "<group>"; };
|
||||
0C4CFC4728970C8B00AD9FAD /* SourceComplexQuery+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SourceComplexQuery+CoreDataClass.swift"; sourceTree = "<group>"; };
|
||||
0C4CFC4828970C8B00AD9FAD /* SourceComplexQuery+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SourceComplexQuery+CoreDataProperties.swift"; sourceTree = "<group>"; };
|
||||
0C54D36128C5086E00BFEEE2 /* History+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "History+CoreDataClass.swift"; sourceTree = "<group>"; };
|
||||
0C54D36228C5086E00BFEEE2 /* History+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "History+CoreDataProperties.swift"; sourceTree = "<group>"; };
|
||||
0C57D4CB289032ED008534E8 /* SearchResultRDView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchResultRDView.swift; sourceTree = "<group>"; };
|
||||
0C60B1EE28A1A00000E3FD7E /* SearchProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchProgressView.swift; sourceTree = "<group>"; };
|
||||
0C626A9428CADB25003C7129 /* DynamicAlert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DynamicAlert.swift; sourceTree = "<group>"; };
|
||||
0C68134F28BC1A2D00FAD890 /* GithubWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GithubWrapper.swift; sourceTree = "<group>"; };
|
||||
0C68135128BC1A7C00FAD890 /* GithubModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GithubModels.swift; sourceTree = "<group>"; };
|
||||
0C70E40128C3CE9C00A5C72D /* ConditionalContextMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConditionalContextMenu.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -161,14 +175,18 @@
|
|||
0CA3B23A28C2AA5600616D3A /* Bookmark+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Bookmark+CoreDataClass.swift"; sourceTree = "<group>"; };
|
||||
0CA3B23B28C2AA5600616D3A /* Bookmark+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Bookmark+CoreDataProperties.swift"; sourceTree = "<group>"; };
|
||||
0CA3FB1F28B91D9500FA10A8 /* IndeterminateProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IndeterminateProgressView.swift; sourceTree = "<group>"; };
|
||||
0CA429F728C5098D000D0610 /* DateFormatter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateFormatter.swift; sourceTree = "<group>"; };
|
||||
0CAF1C68286F5C0E00296F86 /* Ferrite.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Ferrite.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
0CB6516228C5A57300DCA721 /* ConditionalId.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConditionalId.swift; sourceTree = "<group>"; };
|
||||
0CB6516428C5A5D700DCA721 /* InlinedList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InlinedList.swift; sourceTree = "<group>"; };
|
||||
0CB6516928C5B4A600DCA721 /* InlineHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InlineHeader.swift; sourceTree = "<group>"; };
|
||||
0CBAB83528D12ED500AC903E /* DisableInteraction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisableInteraction.swift; sourceTree = "<group>"; };
|
||||
0CBC76FC288D914F0054BE44 /* BatchChoiceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BatchChoiceView.swift; sourceTree = "<group>"; };
|
||||
0CBC76FE288DAAD00054BE44 /* NavigationViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationViewModel.swift; sourceTree = "<group>"; };
|
||||
0CBC7704288DE7F40054BE44 /* PersistenceController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PersistenceController.swift; sourceTree = "<group>"; };
|
||||
0CC6E4D428A45BA000AF2BCC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
|
||||
0CD4CAC528C980EB0046E1DC /* HistoryActionsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryActionsView.swift; sourceTree = "<group>"; };
|
||||
0CD5E78828CD932B001BF684 /* DisabledAppearance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisabledAppearance.swift; sourceTree = "<group>"; };
|
||||
0CDCB91728C662640098B513 /* EmptyInstructionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyInstructionView.swift; sourceTree = "<group>"; };
|
||||
0CFEFCFC288A006200B3F490 /* GroupBoxStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupBoxStyle.swift; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
|
@ -194,6 +212,8 @@
|
|||
0C0D50DE288DF72D0035ECC8 /* Classes */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0C54D36128C5086E00BFEEE2 /* History+CoreDataClass.swift */,
|
||||
0C54D36228C5086E00BFEEE2 /* History+CoreDataProperties.swift */,
|
||||
0CA3B23A28C2AA5600616D3A /* Bookmark+CoreDataClass.swift */,
|
||||
0CA3B23B28C2AA5600616D3A /* Bookmark+CoreDataProperties.swift */,
|
||||
0C31133A28B1ABFA004DCB0D /* SourceJsonParser+CoreDataClass.swift */,
|
||||
|
|
@ -280,6 +300,11 @@
|
|||
0CDCB91728C662640098B513 /* EmptyInstructionView.swift */,
|
||||
0C360C5B28C7DF1400884ED3 /* DynamicFetchRequest.swift */,
|
||||
0C70E40128C3CE9C00A5C72D /* ConditionalContextMenu.swift */,
|
||||
0C391EC828CA63F0009F1CA1 /* DynamicActionSheet.swift */,
|
||||
0C391ECA28CAA44B009F1CA1 /* AlertButton.swift */,
|
||||
0C626A9428CADB25003C7129 /* DynamicAlert.swift */,
|
||||
0CD5E78828CD932B001BF684 /* DisabledAppearance.swift */,
|
||||
0CBAB83528D12ED500AC903E /* DisableInteraction.swift */,
|
||||
);
|
||||
path = CommonViews;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -302,6 +327,7 @@
|
|||
0C7D11FD28AA03FE00ED92DB /* View.swift */,
|
||||
0C78041C28BFB3EA001E8CA3 /* String.swift */,
|
||||
0C70E40528C40C4E00A5C72D /* NotificationCenter.swift */,
|
||||
0CA429F728C5098D000D0610 /* DateFormatter.swift */,
|
||||
);
|
||||
path = Extensions;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -315,6 +341,7 @@
|
|||
0CA148C0288903F000DE2211 /* CommonViews */,
|
||||
0CA0545C288F7CB200850554 /* SettingsViews */,
|
||||
0CA148D3288903F000DE2211 /* SearchResultsView.swift */,
|
||||
0C41BC6228C2AD0F00B47DD6 /* SearchResultButtonView.swift */,
|
||||
0C57D4CB289032ED008534E8 /* SearchResultRDView.swift */,
|
||||
0CA148D4288903F000DE2211 /* ContentView.swift */,
|
||||
0CA148D1288903F000DE2211 /* MainView.swift */,
|
||||
|
|
@ -324,9 +351,7 @@
|
|||
0CA148BD288903F000DE2211 /* MagnetChoiceView.swift */,
|
||||
0C0D50E6288DFF850035ECC8 /* SourcesView.swift */,
|
||||
0C32FB522890D19D002BD219 /* AboutView.swift */,
|
||||
0C60B1EE28A1A00000E3FD7E /* SearchProgressView.swift */,
|
||||
0CA3B23328C2658700616D3A /* LibraryView.swift */,
|
||||
0C41BC6228C2AD0F00B47DD6 /* SearchResultButtonView.swift */,
|
||||
);
|
||||
path = Views;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -365,6 +390,8 @@
|
|||
children = (
|
||||
0CA3B23828C2660D00616D3A /* BookmarksView.swift */,
|
||||
0CA3B23628C2660700616D3A /* HistoryView.swift */,
|
||||
0CD4CAC528C980EB0046E1DC /* HistoryActionsView.swift */,
|
||||
0C12D43C28CC332A000195BF /* HistoryButtonView.swift */,
|
||||
);
|
||||
path = LibraryViews;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -486,11 +513,11 @@
|
|||
files = (
|
||||
0C0D50E5288DFE7F0035ECC8 /* SourceModels.swift in Sources */,
|
||||
0CB6516528C5A5D700DCA721 /* InlinedList.swift in Sources */,
|
||||
0C60B1EF28A1A00000E3FD7E /* SearchProgressView.swift in Sources */,
|
||||
0C32FB532890D19D002BD219 /* AboutView.swift in Sources */,
|
||||
0CB6516328C5A57300DCA721 /* ConditionalId.swift in Sources */,
|
||||
0C70E40628C40C4E00A5C72D /* NotificationCenter.swift in Sources */,
|
||||
0C84F4832895BFED0074B7C9 /* Source+CoreDataProperties.swift in Sources */,
|
||||
0CD5E78928CD932B001BF684 /* DisabledAppearance.swift in Sources */,
|
||||
0CA148DB288903F000DE2211 /* NavView.swift in Sources */,
|
||||
0CA3B23C28C2AA5600616D3A /* Bookmark+CoreDataClass.swift in Sources */,
|
||||
0C750745289B003E004B3906 /* SourceRssParser+CoreDataProperties.swift in Sources */,
|
||||
|
|
@ -499,8 +526,10 @@
|
|||
0C31133D28B1ABFA004DCB0D /* SourceJsonParser+CoreDataProperties.swift in Sources */,
|
||||
0CA0545B288EEA4E00850554 /* SourceListEditorView.swift in Sources */,
|
||||
0C360C5C28C7DF1400884ED3 /* DynamicFetchRequest.swift in Sources */,
|
||||
0CA429F828C5098D000D0610 /* DateFormatter.swift in Sources */,
|
||||
0C84F4872895BFED0074B7C9 /* SourceList+CoreDataProperties.swift in Sources */,
|
||||
0C794B6B289DACF100DD1CC8 /* SourceCatalogView.swift in Sources */,
|
||||
0C54D36428C5086E00BFEEE2 /* History+CoreDataProperties.swift in Sources */,
|
||||
0CA148E9288903F000DE2211 /* MainView.swift in Sources */,
|
||||
0CBC76FD288D914F0054BE44 /* BatchChoiceView.swift in Sources */,
|
||||
0C32FB552890D1BF002BD219 /* UIApplication.swift in Sources */,
|
||||
|
|
@ -515,6 +544,7 @@
|
|||
0C750744289B003E004B3906 /* SourceRssParser+CoreDataClass.swift in Sources */,
|
||||
0C794B69289DACC800DD1CC8 /* InstalledSourceView.swift in Sources */,
|
||||
0C79DC082899AF3C003F1C5A /* SourceSeedLeech+CoreDataProperties.swift in Sources */,
|
||||
0CD4CAC628C980EB0046E1DC /* HistoryActionsView.swift in Sources */,
|
||||
0CA148DD288903F000DE2211 /* ScrapingViewModel.swift in Sources */,
|
||||
0CB6516A28C5B4A600DCA721 /* InlineHeader.swift in Sources */,
|
||||
0CA148D8288903F000DE2211 /* MagnetChoiceView.swift in Sources */,
|
||||
|
|
@ -523,9 +553,11 @@
|
|||
0C68135028BC1A2D00FAD890 /* GithubWrapper.swift in Sources */,
|
||||
0C95D8DA28A55BB6005E22B3 /* SettingsModels.swift in Sources */,
|
||||
0CA148E3288903F000DE2211 /* Task.swift in Sources */,
|
||||
0C626A9528CADB25003C7129 /* DynamicAlert.swift in Sources */,
|
||||
0CA148E7288903F000DE2211 /* ToastViewModel.swift in Sources */,
|
||||
0C68135228BC1A7C00FAD890 /* GithubModels.swift in Sources */,
|
||||
0CFEFCFD288A006200B3F490 /* GroupBoxStyle.swift in Sources */,
|
||||
0C391EC928CA63F0009F1CA1 /* DynamicActionSheet.swift in Sources */,
|
||||
0CA3B23928C2660D00616D3A /* BookmarksView.swift in Sources */,
|
||||
0C79DC072899AF3C003F1C5A /* SourceSeedLeech+CoreDataClass.swift in Sources */,
|
||||
0C794B67289DACB600DD1CC8 /* SourceUpdateButtonView.swift in Sources */,
|
||||
|
|
@ -539,6 +571,7 @@
|
|||
0C41BC6328C2AD0F00B47DD6 /* SearchResultButtonView.swift in Sources */,
|
||||
0CA05459288EE9E600850554 /* SourceManager.swift in Sources */,
|
||||
0C84F4772895BE680074B7C9 /* FerriteDB.xcdatamodeld in Sources */,
|
||||
0C12D43D28CC332A000195BF /* HistoryButtonView.swift in Sources */,
|
||||
0C733287289C4C820058D1FE /* SourceSettingsView.swift in Sources */,
|
||||
0C10848B28BD9A38008F0BA6 /* SettingsAppVersionView.swift in Sources */,
|
||||
0CA05457288EE58200850554 /* SettingsSourceListView.swift in Sources */,
|
||||
|
|
@ -550,9 +583,12 @@
|
|||
0CA148E8288903F000DE2211 /* RealDebridWrapper.swift in Sources */,
|
||||
0CA148D6288903F000DE2211 /* SettingsView.swift in Sources */,
|
||||
0CA148E5288903F000DE2211 /* DebridManager.swift in Sources */,
|
||||
0C54D36328C5086E00BFEEE2 /* History+CoreDataClass.swift in Sources */,
|
||||
0CBAB83628D12ED500AC903E /* DisableInteraction.swift in Sources */,
|
||||
0C84F4842895BFED0074B7C9 /* SourceHtmlParser+CoreDataClass.swift in Sources */,
|
||||
0C32FB572890D1F2002BD219 /* ListRowViews.swift in Sources */,
|
||||
0C84F4822895BFED0074B7C9 /* Source+CoreDataClass.swift in Sources */,
|
||||
0C391ECB28CAA44B009F1CA1 /* AlertButton.swift in Sources */,
|
||||
0C84F4852895BFED0074B7C9 /* SourceHtmlParser+CoreDataProperties.swift in Sources */,
|
||||
0CA148EB288903F000DE2211 /* SearchResultsView.swift in Sources */,
|
||||
0CA148D7288903F000DE2211 /* LoginWebView.swift in Sources */,
|
||||
|
|
@ -705,6 +741,7 @@
|
|||
PRODUCT_BUNDLE_IDENTIFIER = me.kingbri.Ferrite;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_STRICT_CONCURRENCY = minimal;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
|
|
@ -738,6 +775,7 @@
|
|||
PRODUCT_BUNDLE_IDENTIFIER = me.kingbri.Ferrite;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_STRICT_CONCURRENCY = minimal;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
|
|
|
|||
15
Ferrite/DataManagement/Classes/History+CoreDataClass.swift
Normal file
15
Ferrite/DataManagement/Classes/History+CoreDataClass.swift
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
//
|
||||
// History+CoreDataClass.swift
|
||||
// Ferrite
|
||||
//
|
||||
// Created by Brian Dashore on 9/4/22.
|
||||
//
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import CoreData
|
||||
|
||||
@objc(History)
|
||||
public class History: NSManagedObject {
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
//
|
||||
// History+CoreDataProperties.swift
|
||||
// Ferrite
|
||||
//
|
||||
// Created by Brian Dashore on 9/4/22.
|
||||
//
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import CoreData
|
||||
|
||||
|
||||
extension History {
|
||||
|
||||
@nonobjc public class func fetchRequest() -> NSFetchRequest<History> {
|
||||
return NSFetchRequest<History>(entityName: "History")
|
||||
}
|
||||
|
||||
@NSManaged public var date: Date?
|
||||
@NSManaged public var dateString: String?
|
||||
@NSManaged public var entries: NSSet?
|
||||
|
||||
var entryArray: [HistoryEntry] {
|
||||
let entrySet = entries as? Set<HistoryEntry> ?? []
|
||||
|
||||
return entrySet.sorted {
|
||||
$0.timeStamp > $1.timeStamp
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Generated accessors for entries
|
||||
extension History {
|
||||
|
||||
@objc(addEntriesObject:)
|
||||
@NSManaged public func addToEntries(_ value: HistoryEntry)
|
||||
|
||||
@objc(removeEntriesObject:)
|
||||
@NSManaged public func removeFromEntries(_ value: HistoryEntry)
|
||||
|
||||
@objc(addEntries:)
|
||||
@NSManaged public func addToEntries(_ values: NSSet)
|
||||
|
||||
@objc(removeEntries:)
|
||||
@NSManaged public func removeFromEntries(_ values: NSSet)
|
||||
|
||||
}
|
||||
|
||||
extension History : Identifiable {
|
||||
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="21277" systemVersion="21F79" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
|
||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="21279" systemVersion="21G83" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
|
||||
<entity name="Bookmark" representedClassName="Bookmark" syncable="YES">
|
||||
<attribute name="leechers" optional="YES" attributeType="String"/>
|
||||
<attribute name="magnetHash" optional="YES" attributeType="String"/>
|
||||
|
|
@ -10,13 +10,15 @@
|
|||
<attribute name="source" attributeType="String" defaultValueString=""/>
|
||||
<attribute name="title" optional="YES" attributeType="String"/>
|
||||
</entity>
|
||||
<entity name="History" representedClassName="History" syncable="YES" codeGenerationType="class">
|
||||
<entity name="History" representedClassName="History" syncable="YES">
|
||||
<attribute name="date" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="dateString" optional="YES" attributeType="String"/>
|
||||
<relationship name="entries" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="HistoryEntry" inverseName="parentHistory" inverseEntity="HistoryEntry"/>
|
||||
</entity>
|
||||
<entity name="HistoryEntry" representedClassName="HistoryEntry" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="name" optional="YES" attributeType="String"/>
|
||||
<attribute name="source" optional="YES" attributeType="String"/>
|
||||
<attribute name="subName" optional="YES" attributeType="String"/>
|
||||
<attribute name="timeStamp" optional="YES" attributeType="Double" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="url" optional="YES" attributeType="String"/>
|
||||
<relationship name="parentHistory" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="History" inverseName="entries" inverseEntity="History"/>
|
||||
|
|
|
|||
|
|
@ -7,6 +7,18 @@
|
|||
|
||||
import CoreData
|
||||
|
||||
enum HistoryDeleteRange {
|
||||
case day
|
||||
case week
|
||||
case month
|
||||
case allTime
|
||||
}
|
||||
|
||||
enum HistoryDeleteError: Error {
|
||||
case noDate(String)
|
||||
case unknown(String)
|
||||
}
|
||||
|
||||
// No iCloud until finalized sources
|
||||
struct PersistenceController {
|
||||
static var shared = PersistenceController()
|
||||
|
|
@ -78,4 +90,67 @@ struct PersistenceController {
|
|||
container.viewContext.delete(object)
|
||||
save()
|
||||
}
|
||||
|
||||
|
||||
func getHistoryPredicate(range: HistoryDeleteRange) -> NSPredicate? {
|
||||
if range == .allTime {
|
||||
return nil
|
||||
}
|
||||
|
||||
var components = Calendar.current.dateComponents([.day, .month, .year], from: Date())
|
||||
components.hour = 0
|
||||
components.minute = 0
|
||||
components.second = 0
|
||||
|
||||
guard let today = Calendar.current.date(from: components) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
var offsetComponents = DateComponents(day: 1)
|
||||
guard let tomorrow = Calendar.current.date(byAdding: offsetComponents, to: today) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch range {
|
||||
case .week:
|
||||
offsetComponents.day = -7
|
||||
case .month:
|
||||
offsetComponents.day = -28
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
guard var offsetDate = Calendar.current.date(byAdding: offsetComponents, to: today) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
if TimeZone.current.isDaylightSavingTime(for: offsetDate) {
|
||||
offsetDate = offsetDate.addingTimeInterval(3600)
|
||||
}
|
||||
|
||||
let predicate = NSPredicate(format: "date >= %@ && date < %@", range == .day ? today as NSDate : offsetDate as NSDate, tomorrow as NSDate)
|
||||
|
||||
return predicate
|
||||
}
|
||||
|
||||
// Always use the background context to batch delete
|
||||
// Merge changes into both contexts to update views
|
||||
func batchDeleteHistory(range: HistoryDeleteRange) throws {
|
||||
let predicate = getHistoryPredicate(range: range)
|
||||
|
||||
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "History")
|
||||
|
||||
if let predicate = predicate {
|
||||
fetchRequest.predicate = predicate
|
||||
} else if range != .allTime {
|
||||
throw HistoryDeleteError.noDate("No history date range was provided and you weren't trying to clear everything! Try relaunching the app?")
|
||||
}
|
||||
|
||||
let batchDeleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest)
|
||||
|
||||
batchDeleteRequest.resultType = .resultTypeObjectIDs
|
||||
let result = try backgroundContext.execute(batchDeleteRequest) as? NSBatchDeleteResult
|
||||
let changes: [AnyHashable: Any] = [NSDeletedObjectsKey: result?.result as? [NSManagedObjectID] ?? []]
|
||||
NSManagedObjectContext.mergeChanges(fromRemoteContextSave: changes, into: [container.viewContext, backgroundContext])
|
||||
}
|
||||
}
|
||||
|
|
|
|||
17
Ferrite/Extensions/DateFormatter.swift
Normal file
17
Ferrite/Extensions/DateFormatter.swift
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
//
|
||||
// DateFormatter.swift
|
||||
// Ferrite
|
||||
//
|
||||
// Created by Brian Dashore on 9/4/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
extension DateFormatter {
|
||||
static let historyDateFormatter: DateFormatter = {
|
||||
let df = DateFormatter()
|
||||
df.dateFormat = "ddMMyyyy"
|
||||
|
||||
return df
|
||||
}()
|
||||
}
|
||||
|
|
@ -33,6 +33,10 @@ extension View {
|
|||
modifier(ConditionalId(id: id))
|
||||
}
|
||||
|
||||
func disabledAppearance(_ disabled: Bool, dimmedOpacity: Double? = nil, animation: Animation? = nil) -> some View {
|
||||
modifier(DisabledAppearance(disabled: disabled, dimmedOpacity: dimmedOpacity, animation: animation))
|
||||
}
|
||||
|
||||
func inlinedList() -> some View {
|
||||
modifier(InlinedList())
|
||||
}
|
||||
|
|
@ -43,4 +47,26 @@ extension View {
|
|||
) -> some View {
|
||||
modifier(ConditionalContextMenu(internalContent, id: id))
|
||||
}
|
||||
|
||||
func dynamicActionSheet(
|
||||
isPresented: Binding<Bool>,
|
||||
title: String,
|
||||
message: String? = nil,
|
||||
buttons: [AlertButton]) -> some View
|
||||
{
|
||||
modifier(DynamicActionSheet(isPresented: isPresented, title: title, message: message, buttons: buttons))
|
||||
}
|
||||
|
||||
func dynamicAlert(
|
||||
isPresented: Binding<Bool>,
|
||||
title: String,
|
||||
message: String? = nil,
|
||||
buttons: [AlertButton]) -> some View
|
||||
{
|
||||
modifier(DynamicAlert(isPresented: isPresented, title: title, message: message, buttons: buttons))
|
||||
}
|
||||
|
||||
func disableInteraction(_ disabled: Bool) -> some View {
|
||||
modifier(DisableInteraction(disabled: disabled))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ class NavigationViewModel: ObservableObject {
|
|||
@AppStorage("Actions.DefaultDebrid") var defaultDebridAction: DefaultDebridActionType = .none
|
||||
@AppStorage("Actions.DefaultMagnet") var defaultMagnetAction: DefaultMagnetActionType = .none
|
||||
|
||||
public func runDebridAction(action: DefaultDebridActionType?, urlString: String) {
|
||||
public func runDebridAction(urlString: String, _ action: DefaultDebridActionType? = nil) {
|
||||
let selectedAction = action ?? defaultDebridAction
|
||||
|
||||
switch selectedAction {
|
||||
|
|
@ -89,17 +89,10 @@ class NavigationViewModel: ObservableObject {
|
|||
}
|
||||
}
|
||||
|
||||
public func runMagnetAction(_ action: DefaultMagnetActionType? = nil) {
|
||||
guard let searchResult = selectedSearchResult else {
|
||||
toastModel?.updateToastDescription("Magnet action error: A search result was not selected.")
|
||||
print("Magnet action error: A search result was not selected.")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
public func runMagnetAction(magnetString: String?, _ action: DefaultMagnetActionType? = nil) {
|
||||
let selectedAction = action ?? defaultMagnetAction
|
||||
|
||||
guard let magnetLink = searchResult.magnetLink else {
|
||||
guard let magnetLink = magnetString else {
|
||||
toastModel?.updateToastDescription("Could not run your action because the magnet link is invalid.")
|
||||
print("Magnet action error: The magnet link is invalid.")
|
||||
|
||||
|
|
@ -126,4 +119,48 @@ class NavigationViewModel: ObservableObject {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func addToHistory(name: String?, source: String?, url: String?, subName: String? = nil) {
|
||||
let backgroundContext = PersistenceController.shared.backgroundContext
|
||||
|
||||
let newHistoryEntry = HistoryEntry(context: backgroundContext)
|
||||
newHistoryEntry.name = name
|
||||
newHistoryEntry.source = source
|
||||
newHistoryEntry.url = url
|
||||
newHistoryEntry.subName = subName
|
||||
|
||||
let now = Date()
|
||||
newHistoryEntry.timeStamp = now.timeIntervalSince1970
|
||||
|
||||
let dateString = DateFormatter.historyDateFormatter.string(from: now)
|
||||
|
||||
let historyRequest = History.fetchRequest()
|
||||
historyRequest.predicate = NSPredicate(format: "dateString = %@", dateString)
|
||||
|
||||
if var histories = try? backgroundContext.fetch(historyRequest) {
|
||||
for (i, history) in histories.enumerated() {
|
||||
let existingEntries = history.entryArray.filter { $0.url == newHistoryEntry.url && $0.name == newHistoryEntry.name }
|
||||
|
||||
if !existingEntries.isEmpty {
|
||||
for entry in existingEntries {
|
||||
PersistenceController.shared.delete(entry, context: backgroundContext)
|
||||
}
|
||||
}
|
||||
|
||||
if history.entryArray.isEmpty {
|
||||
PersistenceController.shared.delete(history, context: backgroundContext)
|
||||
histories.remove(at: i)
|
||||
}
|
||||
}
|
||||
|
||||
newHistoryEntry.parentHistory = histories.first ?? History(context: backgroundContext)
|
||||
} else {
|
||||
newHistoryEntry.parentHistory = History(context: backgroundContext)
|
||||
}
|
||||
|
||||
newHistoryEntry.parentHistory?.dateString = dateString
|
||||
newHistoryEntry.parentHistory?.date = now
|
||||
|
||||
PersistenceController.shared.save(backgroundContext)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,12 +8,12 @@
|
|||
import SwiftUI
|
||||
|
||||
struct BatchChoiceView: View {
|
||||
@Environment(\.presentationMode) var presentationMode
|
||||
|
||||
@EnvironmentObject var debridManager: DebridManager
|
||||
@EnvironmentObject var scrapingModel: ScrapingViewModel
|
||||
@EnvironmentObject var navModel: NavigationViewModel
|
||||
|
||||
let backgroundContext = PersistenceController.shared.backgroundContext
|
||||
|
||||
var body: some View {
|
||||
NavView {
|
||||
List {
|
||||
|
|
@ -28,7 +28,8 @@ struct BatchChoiceView: View {
|
|||
if !debridManager.realDebridDownloadUrl.isEmpty {
|
||||
// The download may complete before this sheet dismisses
|
||||
try? await Task.sleep(seconds: 1)
|
||||
navModel.runDebridAction(action: nil, urlString: debridManager.realDebridDownloadUrl)
|
||||
navModel.addToHistory(name: searchResult.title, source: searchResult.source, url: debridManager.realDebridDownloadUrl, subName: file.name)
|
||||
navModel.runDebridAction(urlString: debridManager.realDebridDownloadUrl)
|
||||
}
|
||||
|
||||
debridManager.selectedRealDebridFile = nil
|
||||
|
|
@ -36,7 +37,7 @@ struct BatchChoiceView: View {
|
|||
}
|
||||
}
|
||||
|
||||
presentationMode.wrappedValue.dismiss()
|
||||
navModel.currentChoiceSheet = nil
|
||||
}
|
||||
.dynamicAccentColor(.primary)
|
||||
}
|
||||
|
|
@ -47,9 +48,12 @@ struct BatchChoiceView: View {
|
|||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
Button("Done") {
|
||||
debridManager.selectedRealDebridItem = nil
|
||||
navModel.currentChoiceSheet = nil
|
||||
|
||||
presentationMode.wrappedValue.dismiss()
|
||||
Task {
|
||||
try? await Task.sleep(seconds: 1)
|
||||
debridManager.selectedRealDebridItem = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
69
Ferrite/Views/CommonViews/AlertButton.swift
Normal file
69
Ferrite/Views/CommonViews/AlertButton.swift
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
//
|
||||
// AlertButton.swift
|
||||
// Ferrite
|
||||
//
|
||||
// Created by Brian Dashore on 9/8/22.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct AlertButton: Identifiable {
|
||||
enum Role {
|
||||
case destructive
|
||||
case cancel
|
||||
}
|
||||
|
||||
let id: UUID
|
||||
let label: String
|
||||
let action: () -> Void
|
||||
let role: Role?
|
||||
|
||||
// Used for all buttons
|
||||
init(_ label: String, role: Role? = nil, action: @escaping () -> Void) {
|
||||
self.id = UUID()
|
||||
self.label = label
|
||||
self.action = action
|
||||
self.role = role
|
||||
}
|
||||
|
||||
// Used for buttons with no action
|
||||
init(_ label: String = "Cancel", role: Role? = nil) {
|
||||
self.id = UUID()
|
||||
self.label = label
|
||||
self.action = { }
|
||||
self.role = role
|
||||
}
|
||||
|
||||
func toActionButton() -> Alert.Button {
|
||||
if let role = role {
|
||||
switch role {
|
||||
case .cancel:
|
||||
return .cancel(Text(label))
|
||||
case .destructive:
|
||||
return .destructive(Text(label), action: action)
|
||||
}
|
||||
} else {
|
||||
return .default(Text(label), action: action)
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS 15.0, *)
|
||||
@ViewBuilder
|
||||
func toButtonView() -> some View {
|
||||
Button(label, role: toButtonRole(role), action: action)
|
||||
}
|
||||
|
||||
@available(iOS 15.0, *)
|
||||
func toButtonRole(_ role: Role?) -> ButtonRole? {
|
||||
if let role = role {
|
||||
switch role {
|
||||
case .destructive:
|
||||
return .destructive
|
||||
case .cancel:
|
||||
return .cancel
|
||||
}
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
25
Ferrite/Views/CommonViews/DisableInteraction.swift
Normal file
25
Ferrite/Views/CommonViews/DisableInteraction.swift
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
//
|
||||
// DisableInteraction.swift
|
||||
// Ferrite
|
||||
//
|
||||
// Created by Brian Dashore on 9/13/22.
|
||||
//
|
||||
// Disables interaction without applying the appearance
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct DisableInteraction: ViewModifier {
|
||||
let disabled: Bool
|
||||
|
||||
func body(content: Content) -> some View {
|
||||
content
|
||||
.overlay {
|
||||
if disabled {
|
||||
Color.clear
|
||||
.contentShape(Rectangle())
|
||||
.gesture(TapGesture())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
21
Ferrite/Views/CommonViews/DisabledAppearance.swift
Normal file
21
Ferrite/Views/CommonViews/DisabledAppearance.swift
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
//
|
||||
// DisabledAppearance.swift
|
||||
// Ferrite
|
||||
//
|
||||
// Created by Brian Dashore on 9/10/22.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct DisabledAppearance: ViewModifier {
|
||||
let disabled: Bool
|
||||
let dimmedOpacity: Double?
|
||||
let animation: Animation?
|
||||
|
||||
func body(content: Content) -> some View {
|
||||
content
|
||||
.disabled(disabled)
|
||||
.opacity(disabled ? dimmedOpacity.map { $0 } ?? 0.5 : 1)
|
||||
.animation(animation.map { $0 } ?? .none, value: disabled)
|
||||
}
|
||||
}
|
||||
44
Ferrite/Views/CommonViews/DynamicActionSheet.swift
Normal file
44
Ferrite/Views/CommonViews/DynamicActionSheet.swift
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
//
|
||||
// DynamicActionSheet.swift
|
||||
// Ferrite
|
||||
//
|
||||
// Created by Brian Dashore on 9/8/22.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct DynamicActionSheet: ViewModifier {
|
||||
@Binding var isPresented: Bool
|
||||
|
||||
let title: String
|
||||
let message: String?
|
||||
let buttons: [AlertButton]
|
||||
|
||||
func body(content: Content) -> some View {
|
||||
if #available(iOS 15, *) {
|
||||
content
|
||||
.confirmationDialog(
|
||||
title,
|
||||
isPresented: $isPresented,
|
||||
titleVisibility: .visible
|
||||
) {
|
||||
ForEach(buttons) { button in
|
||||
button.toButtonView()
|
||||
}
|
||||
} message: {
|
||||
if let message = message {
|
||||
Text(message)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
content
|
||||
.actionSheet(isPresented: $isPresented) {
|
||||
ActionSheet(
|
||||
title: Text(title),
|
||||
message: message.map { Text($0) } ?? nil,
|
||||
buttons: [buttons.map { $0.toActionButton() }, [.cancel()]].flatMap{ $0 }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
56
Ferrite/Views/CommonViews/DynamicAlert.swift
Normal file
56
Ferrite/Views/CommonViews/DynamicAlert.swift
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
//
|
||||
// DynamicAlert.swift
|
||||
// Ferrite
|
||||
//
|
||||
// Created by Brian Dashore on 9/8/22.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct DynamicAlert: ViewModifier {
|
||||
@Binding var isPresented: Bool
|
||||
|
||||
let title: String
|
||||
let message: String?
|
||||
let buttons: [AlertButton]
|
||||
|
||||
func body(content: Content) -> some View {
|
||||
if #available(iOS 15, *) {
|
||||
content
|
||||
.alert(
|
||||
title,
|
||||
isPresented: $isPresented,
|
||||
actions: {
|
||||
ForEach(buttons) { button in
|
||||
button.toButtonView()
|
||||
}
|
||||
},
|
||||
message: {
|
||||
if let message = message {
|
||||
Text(message)
|
||||
}
|
||||
}
|
||||
)
|
||||
} else {
|
||||
content
|
||||
.alert(isPresented: $isPresented) {
|
||||
if let primaryButton = buttons[safe: 0],
|
||||
let secondaryButton = buttons[safe: 1]
|
||||
{
|
||||
return Alert(
|
||||
title: Text(title),
|
||||
message: message.map { Text($0) } ?? nil,
|
||||
primaryButton: primaryButton.toActionButton(),
|
||||
secondaryButton: secondaryButton.toActionButton()
|
||||
)
|
||||
} else {
|
||||
return Alert(
|
||||
title: Text(title),
|
||||
message: message.map { Text($0) } ?? nil,
|
||||
dismissButton: buttons[0].toActionButton()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -73,25 +73,23 @@ struct ContentView: View {
|
|||
SearchResultsView()
|
||||
}
|
||||
.sheet(item: $navModel.currentChoiceSheet) { item in
|
||||
Group {
|
||||
switch item {
|
||||
case .magnet:
|
||||
MagnetChoiceView()
|
||||
.environmentObject(debridManager)
|
||||
.environmentObject(scrapingModel)
|
||||
.environmentObject(navModel)
|
||||
case .batch:
|
||||
BatchChoiceView()
|
||||
.environmentObject(debridManager)
|
||||
.environmentObject(scrapingModel)
|
||||
.environmentObject(navModel)
|
||||
case .activity:
|
||||
if #available(iOS 16, *) {
|
||||
AppActivityView(activityItems: navModel.activityItems)
|
||||
.presentationDetents([.medium, .large])
|
||||
} else {
|
||||
AppActivityView(activityItems: navModel.activityItems)
|
||||
}
|
||||
switch item {
|
||||
case .magnet:
|
||||
MagnetChoiceView()
|
||||
.environmentObject(debridManager)
|
||||
.environmentObject(scrapingModel)
|
||||
.environmentObject(navModel)
|
||||
case .batch:
|
||||
BatchChoiceView()
|
||||
.environmentObject(debridManager)
|
||||
.environmentObject(scrapingModel)
|
||||
.environmentObject(navModel)
|
||||
case .activity:
|
||||
if #available(iOS 16, *) {
|
||||
AppActivityView(activityItems: navModel.activityItems)
|
||||
.presentationDetents([.medium, .large])
|
||||
} else {
|
||||
AppActivityView(activityItems: navModel.activityItems)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,13 @@ struct LibraryView: View {
|
|||
]
|
||||
) var bookmarks: FetchedResults<Bookmark>
|
||||
|
||||
@FetchRequest(
|
||||
entity: History.entity(),
|
||||
sortDescriptors: [
|
||||
NSSortDescriptor(keyPath: \History.date, ascending: false)
|
||||
]
|
||||
) var history: FetchedResults<History>
|
||||
|
||||
@State private var historyEmpty = true
|
||||
|
||||
@State private var selectedSegment: LibraryPickerSegment = .bookmarks
|
||||
|
|
@ -42,7 +49,7 @@ struct LibraryView: View {
|
|||
case .bookmarks:
|
||||
BookmarksView(bookmarks: bookmarks)
|
||||
case .history:
|
||||
HistoryView()
|
||||
HistoryView(history: history)
|
||||
}
|
||||
|
||||
Spacer()
|
||||
|
|
@ -54,7 +61,7 @@ struct LibraryView: View {
|
|||
EmptyInstructionView(title: "No Bookmarks", message: "Add a bookmark from search results")
|
||||
}
|
||||
case .history:
|
||||
if historyEmpty {
|
||||
if history.isEmpty {
|
||||
EmptyInstructionView(title: "No History", message: "Start watching to build history")
|
||||
}
|
||||
}
|
||||
|
|
@ -62,7 +69,13 @@ struct LibraryView: View {
|
|||
.navigationTitle("Library")
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
EditButton()
|
||||
HStack {
|
||||
EditButton()
|
||||
|
||||
if selectedSegment == .history {
|
||||
HistoryActionsView()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.environment(\.editMode, $editMode)
|
||||
|
|
|
|||
54
Ferrite/Views/LibraryViews/HistoryActionsView.swift
Normal file
54
Ferrite/Views/LibraryViews/HistoryActionsView.swift
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
//
|
||||
// HistoryActionsView.swift
|
||||
// Ferrite
|
||||
//
|
||||
// Created by Brian Dashore on 9/7/22.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct HistoryActionsView: View {
|
||||
@EnvironmentObject var toastModel: ToastViewModel
|
||||
|
||||
@State private var showActionSheet = false
|
||||
|
||||
var body: some View {
|
||||
Button("Clear") {
|
||||
showActionSheet.toggle()
|
||||
}
|
||||
.dynamicAccentColor(.red)
|
||||
.dynamicActionSheet(
|
||||
isPresented: $showActionSheet,
|
||||
title: "Clear watch history",
|
||||
message: "This is an irreversible action!",
|
||||
buttons: [
|
||||
AlertButton("Past day", role: .destructive) {
|
||||
deleteHistory(.day)
|
||||
},
|
||||
AlertButton("Past week", role: .destructive) {
|
||||
deleteHistory(.week)
|
||||
},
|
||||
AlertButton("Past month", role: .destructive) {
|
||||
deleteHistory(.month)
|
||||
},
|
||||
AlertButton("All time", role: .destructive) {
|
||||
deleteHistory(.allTime)
|
||||
}
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
func deleteHistory(_ deleteRange: HistoryDeleteRange) {
|
||||
do {
|
||||
try PersistenceController.shared.batchDeleteHistory(range: deleteRange)
|
||||
} catch {
|
||||
toastModel.updateToastDescription("History delete error: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct HistoryActionsView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
HistoryActionsView()
|
||||
}
|
||||
}
|
||||
76
Ferrite/Views/LibraryViews/HistoryButtonView.swift
Normal file
76
Ferrite/Views/LibraryViews/HistoryButtonView.swift
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
//
|
||||
// HistoryButtonView.swift
|
||||
// Ferrite
|
||||
//
|
||||
// Created by Brian Dashore on 9/9/22.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct HistoryButtonView: View {
|
||||
@EnvironmentObject var toastModel: ToastViewModel
|
||||
@EnvironmentObject var navModel: NavigationViewModel
|
||||
@EnvironmentObject var debridManager: DebridManager
|
||||
|
||||
let entry: HistoryEntry
|
||||
|
||||
var body: some View {
|
||||
Button {
|
||||
if let url = entry.url {
|
||||
if url.starts(with: "https://") {
|
||||
Task {
|
||||
debridManager.realDebridDownloadUrl = url
|
||||
navModel.runDebridAction(urlString: url)
|
||||
|
||||
if navModel.currentChoiceSheet != .magnet {
|
||||
debridManager.realDebridDownloadUrl = ""
|
||||
}
|
||||
}
|
||||
} else {
|
||||
navModel.runMagnetAction(magnetString: url)
|
||||
}
|
||||
} else {
|
||||
toastModel.updateToastDescription("URL invalid. Cannot load this history entry. Please delete it.")
|
||||
}
|
||||
} label: {
|
||||
VStack(alignment: .leading) {
|
||||
VStack(alignment: .leading, spacing: 3) {
|
||||
Text(entry.name ?? "Unknown title")
|
||||
.font(entry.subName == nil ? .body : .subheadline)
|
||||
|
||||
if let subName = entry.subName {
|
||||
Text(subName)
|
||||
.foregroundColor(.gray)
|
||||
.font(.subheadline)
|
||||
}
|
||||
}
|
||||
|
||||
HStack {
|
||||
Text(entry.source ?? "Unknown source")
|
||||
|
||||
Spacer()
|
||||
|
||||
Text("DEBRID")
|
||||
.fontWeight(.bold)
|
||||
.padding(3)
|
||||
.background {
|
||||
Group {
|
||||
if let url = entry.url, url.starts(with: "https://") {
|
||||
Color.green
|
||||
} else {
|
||||
Color.red
|
||||
}
|
||||
}
|
||||
.cornerRadius(4)
|
||||
.opacity(0.5)
|
||||
}
|
||||
}
|
||||
.font(.caption)
|
||||
}
|
||||
.lineLimit(1)
|
||||
.disabledAppearance(navModel.currentChoiceSheet != nil, dimmedOpacity: 0.7, animation: .easeOut(duration: 0.2))
|
||||
}
|
||||
.dynamicAccentColor(.white)
|
||||
.disableInteraction(navModel.currentChoiceSheet != nil)
|
||||
}
|
||||
}
|
||||
|
|
@ -8,15 +8,58 @@
|
|||
import SwiftUI
|
||||
|
||||
struct HistoryView: View {
|
||||
@EnvironmentObject var navModel: NavigationViewModel
|
||||
|
||||
let backgroundContext = PersistenceController.shared.backgroundContext
|
||||
|
||||
var history: FetchedResults<History>
|
||||
var formatter: DateFormatter = .init()
|
||||
|
||||
@State private var historyIndex = 0
|
||||
|
||||
init(history: FetchedResults<History>) {
|
||||
self.history = history
|
||||
|
||||
formatter.dateStyle = .medium
|
||||
formatter.timeStyle = .none
|
||||
}
|
||||
|
||||
func groupedEntries(_ result: FetchedResults<History>) -> [[History]] {
|
||||
Dictionary(grouping: result) { (element: History) in
|
||||
element.dateString ?? ""
|
||||
}.values.sorted { $0[0].date ?? Date() > $1[0].date ?? Date() }
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
ZStack {
|
||||
EmptyView()
|
||||
if !history.isEmpty {
|
||||
List {
|
||||
ForEach(groupedEntries(history), id: \.self) { (section: [History]) in
|
||||
Section(header: Text(formatter.string(from: section[0].date ?? Date()))) {
|
||||
ForEach(section, id: \.self) { history in
|
||||
ForEach(history.entryArray) { entry in
|
||||
HistoryButtonView(entry: entry)
|
||||
}
|
||||
.onDelete { offsets in
|
||||
removeEntry(at: offsets, from: history)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.listStyle(.insetGrouped)
|
||||
}
|
||||
}
|
||||
|
||||
func removeEntry(at offsets: IndexSet, from history: History) {
|
||||
for index in offsets {
|
||||
if let entry = history.entryArray[safe: index] {
|
||||
history.removeFromEntries(entry)
|
||||
PersistenceController.shared.delete(entry, context: backgroundContext)
|
||||
}
|
||||
|
||||
if history.entryArray.isEmpty {
|
||||
PersistenceController.shared.delete(history, context: backgroundContext)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct HistoryView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
HistoryView()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,31 +23,30 @@ struct MagnetChoiceView: View {
|
|||
var body: some View {
|
||||
NavView {
|
||||
Form {
|
||||
if realDebridEnabled, debridManager.matchSearchResult(result: navModel.selectedSearchResult) != .none {
|
||||
if !debridManager.realDebridDownloadUrl.isEmpty {
|
||||
Section(header: "Real Debrid options") {
|
||||
ListRowButtonView("Play on Outplayer", systemImage: "arrow.up.forward.app.fill") {
|
||||
navModel.runDebridAction(action: .outplayer, urlString: debridManager.realDebridDownloadUrl)
|
||||
navModel.runDebridAction(urlString: debridManager.realDebridDownloadUrl, .outplayer)
|
||||
}
|
||||
|
||||
ListRowButtonView("Play on VLC", systemImage: "arrow.up.forward.app.fill") {
|
||||
navModel.runDebridAction(action: .vlc, urlString: debridManager.realDebridDownloadUrl)
|
||||
navModel.runDebridAction(urlString: debridManager.realDebridDownloadUrl, .vlc)
|
||||
}
|
||||
|
||||
ListRowButtonView("Play on Infuse", systemImage: "arrow.up.forward.app.fill") {
|
||||
navModel.runDebridAction(action: .infuse, urlString: debridManager.realDebridDownloadUrl)
|
||||
navModel.runDebridAction(urlString: debridManager.realDebridDownloadUrl, .infuse)
|
||||
}
|
||||
|
||||
ListRowButtonView("Copy download URL", systemImage: "doc.on.doc.fill") {
|
||||
UIPasteboard.general.string = debridManager.realDebridDownloadUrl
|
||||
showLinkCopyAlert.toggle()
|
||||
}
|
||||
.alert(isPresented: $showLinkCopyAlert) {
|
||||
Alert(
|
||||
title: Text("Copied"),
|
||||
message: Text("Download link copied successfully"),
|
||||
dismissButton: .cancel(Text("OK"))
|
||||
)
|
||||
}
|
||||
.dynamicAlert(
|
||||
isPresented: $showLinkCopyAlert ,
|
||||
title: "Copied",
|
||||
message: "Download link copied successfully",
|
||||
buttons: [AlertButton("OK")]
|
||||
)
|
||||
|
||||
ListRowButtonView("Share download URL", systemImage: "square.and.arrow.up.fill") {
|
||||
if let url = URL(string: debridManager.realDebridDownloadUrl) {
|
||||
|
|
@ -63,13 +62,12 @@ struct MagnetChoiceView: View {
|
|||
UIPasteboard.general.string = navModel.selectedSearchResult?.magnetLink
|
||||
showMagnetCopyAlert.toggle()
|
||||
}
|
||||
.alert(isPresented: $showMagnetCopyAlert) {
|
||||
Alert(
|
||||
title: Text("Copied"),
|
||||
message: Text("Magnet link copied successfully"),
|
||||
dismissButton: .cancel(Text("OK"))
|
||||
)
|
||||
}
|
||||
.dynamicAlert(
|
||||
isPresented: $showMagnetCopyAlert,
|
||||
title: "Copied",
|
||||
message: "Magnet link copied successfully",
|
||||
buttons: [AlertButton("OK")]
|
||||
)
|
||||
|
||||
ListRowButtonView("Share magnet", systemImage: "square.and.arrow.up.fill") {
|
||||
if let result = navModel.selectedSearchResult,
|
||||
|
|
@ -82,7 +80,7 @@ struct MagnetChoiceView: View {
|
|||
}
|
||||
|
||||
ListRowButtonView("Open in WebTor", systemImage: "arrow.up.forward.app.fill") {
|
||||
navModel.runMagnetAction(.webtor)
|
||||
navModel.runMagnetAction(magnetString: navModel.selectedSearchResult?.magnetLink, .webtor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -95,6 +93,9 @@ struct MagnetChoiceView: View {
|
|||
AppActivityView(activityItems: navModel.activityItems)
|
||||
}
|
||||
}
|
||||
.onDisappear {
|
||||
debridManager.realDebridDownloadUrl = ""
|
||||
}
|
||||
.navigationTitle("Link actions")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
|
|
|
|||
|
|
@ -46,20 +46,21 @@ struct MainView: View {
|
|||
}
|
||||
.tag(ViewTab.settings)
|
||||
}
|
||||
.alert(isPresented: $showUpdateAlert) {
|
||||
Alert(
|
||||
title: Text("Update available"),
|
||||
message: Text("Ferrite \(releaseVersionString) can be downloaded. \n\n This alert can be disabled in Settings."),
|
||||
primaryButton: .default(Text("Download")) {
|
||||
.dynamicAlert(
|
||||
isPresented: $showUpdateAlert,
|
||||
title: "Update available",
|
||||
message: "Ferrite \(releaseVersionString) can be downloaded. \n\n This alert can be disabled in Settings.",
|
||||
buttons: [
|
||||
AlertButton("Download") {
|
||||
guard let releaseUrl = URL(string: releaseUrlString) else {
|
||||
return
|
||||
}
|
||||
|
||||
UIApplication.shared.open(releaseUrl)
|
||||
},
|
||||
secondaryButton: .cancel()
|
||||
)
|
||||
}
|
||||
AlertButton(role: .cancel)
|
||||
]
|
||||
)
|
||||
.onAppear {
|
||||
if autoUpdateNotifs {
|
||||
viewTask = Task {
|
||||
|
|
@ -71,7 +72,6 @@ struct MainView: View {
|
|||
|
||||
let releaseVersion = String(latestRelease.tagName.dropFirst())
|
||||
if releaseVersion > UIApplication.shared.appVersion {
|
||||
print("Greater")
|
||||
releaseVersionString = latestRelease.tagName
|
||||
releaseUrlString = latestRelease.htmlUrl
|
||||
showUpdateAlert.toggle()
|
||||
|
|
|
|||
|
|
@ -1,20 +0,0 @@
|
|||
//
|
||||
// SearchProgressView.swift
|
||||
// Ferrite
|
||||
//
|
||||
// Created by Brian Dashore on 8/8/22.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct SearchProgressView: View {
|
||||
var body: some View {
|
||||
Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/)
|
||||
}
|
||||
}
|
||||
|
||||
struct SearchProgressView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
SearchProgressView()
|
||||
}
|
||||
}
|
||||
|
|
@ -14,73 +14,82 @@ struct SearchResultButtonView: View {
|
|||
@EnvironmentObject var navModel: NavigationViewModel
|
||||
@EnvironmentObject var debridManager: DebridManager
|
||||
|
||||
@AppStorage("RealDebrid.Enabled") var realDebridEnabled = false
|
||||
|
||||
var result: SearchResult
|
||||
|
||||
@State private var runOnce = false
|
||||
@State var existingBookmark: Bookmark? = nil
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading) {
|
||||
Button {
|
||||
if debridManager.currentDebridTask == nil {
|
||||
navModel.selectedSearchResult = result
|
||||
|
||||
switch debridManager.matchSearchResult(result: result) {
|
||||
case .full:
|
||||
debridManager.currentDebridTask = Task {
|
||||
await debridManager.fetchRdDownload(searchResult: result)
|
||||
|
||||
if !debridManager.realDebridDownloadUrl.isEmpty {
|
||||
navModel.runDebridAction(action: nil, urlString: debridManager.realDebridDownloadUrl)
|
||||
Button {
|
||||
if debridManager.currentDebridTask == nil {
|
||||
navModel.selectedSearchResult = result
|
||||
|
||||
switch debridManager.matchSearchResult(result: result) {
|
||||
case .full:
|
||||
debridManager.currentDebridTask = Task {
|
||||
await debridManager.fetchRdDownload(searchResult: result)
|
||||
|
||||
if !debridManager.realDebridDownloadUrl.isEmpty {
|
||||
navModel.addToHistory(name: result.title, source: result.source, url: debridManager.realDebridDownloadUrl)
|
||||
navModel.runDebridAction(urlString: debridManager.realDebridDownloadUrl)
|
||||
|
||||
if navModel.currentChoiceSheet != .magnet {
|
||||
debridManager.realDebridDownloadUrl = ""
|
||||
}
|
||||
}
|
||||
case .partial:
|
||||
if debridManager.setSelectedRdResult(result: result) {
|
||||
navModel.currentChoiceSheet = .batch
|
||||
}
|
||||
case .none:
|
||||
navModel.runMagnetAction()
|
||||
}
|
||||
case .partial:
|
||||
if debridManager.setSelectedRdResult(result: result) {
|
||||
navModel.currentChoiceSheet = .batch
|
||||
}
|
||||
case .none:
|
||||
navModel.addToHistory(name: result.title, source: result.source, url: result.magnetLink)
|
||||
navModel.runMagnetAction(magnetString: result.magnetLink)
|
||||
}
|
||||
} label: {
|
||||
}
|
||||
} label: {
|
||||
VStack(alignment: .leading, spacing: 10) {
|
||||
Text(result.title ?? "No title")
|
||||
.font(.callout)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
}
|
||||
.dynamicAccentColor(.primary)
|
||||
.padding(.bottom, 5)
|
||||
.conditionalContextMenu(id: existingBookmark) {
|
||||
if let bookmark = existingBookmark {
|
||||
Button {
|
||||
PersistenceController.shared.delete(bookmark, context: backgroundContext)
|
||||
|
||||
// When the entity is deleted, let other instances know to remove that reference
|
||||
NotificationCenter.default.post(name: .didDeleteBookmark, object: nil)
|
||||
} label: {
|
||||
Text("Remove bookmark")
|
||||
Image(systemName: "bookmark.slash.fill")
|
||||
}
|
||||
} else {
|
||||
Button {
|
||||
let newBookmark = Bookmark(context: backgroundContext)
|
||||
newBookmark.title = result.title
|
||||
newBookmark.source = result.source
|
||||
newBookmark.magnetHash = result.magnetHash
|
||||
newBookmark.magnetLink = result.magnetLink
|
||||
newBookmark.seeders = result.seeders
|
||||
newBookmark.leechers = result.leechers
|
||||
|
||||
existingBookmark = newBookmark
|
||||
|
||||
PersistenceController.shared.save(backgroundContext)
|
||||
} label: {
|
||||
Text("Bookmark")
|
||||
Image(systemName: "bookmark")
|
||||
}
|
||||
SearchResultRDView(result: result)
|
||||
}
|
||||
.disabledAppearance(navModel.currentChoiceSheet != nil, dimmedOpacity: 0.7, animation: .easeOut(duration: 0.2))
|
||||
}
|
||||
.disableInteraction(navModel.currentChoiceSheet != nil)
|
||||
.dynamicAccentColor(.primary)
|
||||
.conditionalContextMenu(id: existingBookmark) {
|
||||
if let bookmark = existingBookmark {
|
||||
Button {
|
||||
PersistenceController.shared.delete(bookmark, context: backgroundContext)
|
||||
|
||||
// When the entity is deleted, let other instances know to remove that reference
|
||||
NotificationCenter.default.post(name: .didDeleteBookmark, object: nil)
|
||||
} label: {
|
||||
Text("Remove bookmark")
|
||||
Image(systemName: "bookmark.slash.fill")
|
||||
}
|
||||
} else {
|
||||
Button {
|
||||
let newBookmark = Bookmark(context: backgroundContext)
|
||||
newBookmark.title = result.title
|
||||
newBookmark.source = result.source
|
||||
newBookmark.magnetHash = result.magnetHash
|
||||
newBookmark.magnetLink = result.magnetLink
|
||||
newBookmark.seeders = result.seeders
|
||||
newBookmark.leechers = result.leechers
|
||||
|
||||
existingBookmark = newBookmark
|
||||
|
||||
PersistenceController.shared.save(backgroundContext)
|
||||
} label: {
|
||||
Text("Bookmark")
|
||||
Image(systemName: "bookmark")
|
||||
}
|
||||
}
|
||||
|
||||
SearchResultRDView(result: result)
|
||||
}
|
||||
.onReceive(NotificationCenter.default.publisher(for: .didDeleteBookmark)) { _ in
|
||||
existingBookmark = nil
|
||||
|
|
|
|||
|
|
@ -37,20 +37,18 @@ struct SearchResultRDView: View {
|
|||
.fontWeight(.bold)
|
||||
.padding(2)
|
||||
.background {
|
||||
switch debridManager.matchSearchResult(result: result) {
|
||||
case .full:
|
||||
Color.green
|
||||
.cornerRadius(4)
|
||||
.opacity(0.5)
|
||||
case .partial:
|
||||
Color.orange
|
||||
.cornerRadius(4)
|
||||
.opacity(0.5)
|
||||
case .none:
|
||||
Color.red
|
||||
.cornerRadius(4)
|
||||
.opacity(0.5)
|
||||
Group {
|
||||
switch debridManager.matchSearchResult(result: result) {
|
||||
case .full:
|
||||
Color.green
|
||||
case .partial:
|
||||
Color.orange
|
||||
case .none:
|
||||
Color.red
|
||||
}
|
||||
}
|
||||
.cornerRadius(4)
|
||||
.opacity(0.5)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,13 +32,12 @@ struct SourceListEditorView: View {
|
|||
sourceUrl = navModel.selectedSourceList?.urlString ?? ""
|
||||
sourceUrlSet = true
|
||||
}
|
||||
.alert(isPresented: $sourceManager.showUrlErrorAlert) {
|
||||
Alert(
|
||||
title: Text("Error"),
|
||||
message: Text(sourceManager.urlErrorAlertText),
|
||||
dismissButton: .default(Text("OK"))
|
||||
)
|
||||
}
|
||||
.dynamicAlert(
|
||||
isPresented: $sourceManager.showUrlErrorAlert,
|
||||
title: "Error",
|
||||
message: sourceManager.urlErrorAlertText,
|
||||
buttons: [AlertButton("OK")]
|
||||
)
|
||||
.navigationTitle("Editing source list")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
|
|
|
|||
Loading…
Reference in a new issue