diff --git a/readeck.xcodeproj/project.pbxproj b/readeck.xcodeproj/project.pbxproj index 7a89528..71e7733 100644 --- a/readeck.xcodeproj/project.pbxproj +++ b/readeck.xcodeproj/project.pbxproj @@ -8,6 +8,7 @@ /* Begin PBXBuildFile section */ 5D2B7FB92DFA27A400EBDB2B /* URLShare.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 5D2B7FAF2DFA27A400EBDB2B /* URLShare.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + 5D348CC32E0C9F4F00D0AF21 /* netfox in Frameworks */ = {isa = PBXBuildFile; productRef = 5D348CC22E0C9F4F00D0AF21 /* netfox */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -133,6 +134,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 5D348CC32E0C9F4F00D0AF21 /* netfox in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -220,6 +222,7 @@ ); name = readeck; packageProductDependencies = ( + 5D348CC22E0C9F4F00D0AF21 /* netfox */, ); productName = readeck; productReference = 5D45F9C82DF858680048D5B8 /* readeck.app */; @@ -306,6 +309,9 @@ ); mainGroup = 5D45F9BF2DF858680048D5B8; minimizedProjectReferenceProxies = 1; + packageReferences = ( + 5D348CC12E0C9F4F00D0AF21 /* XCRemoteSwiftPackageReference "netfox" */, + ); preferredProjectObjectVersion = 77; productRefGroup = 5D45F9C92DF858680048D5B8 /* Products */; projectDirPath = ""; @@ -790,6 +796,25 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + 5D348CC12E0C9F4F00D0AF21 /* XCRemoteSwiftPackageReference "netfox" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/kasketis/netfox"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 1.21.0; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 5D348CC22E0C9F4F00D0AF21 /* netfox */ = { + isa = XCSwiftPackageProductDependency; + package = 5D348CC12E0C9F4F00D0AF21 /* XCRemoteSwiftPackageReference "netfox" */; + productName = netfox; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 5D45F9C02DF858680048D5B8 /* Project object */; } diff --git a/readeck.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/readeck.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 0000000..1b9f250 --- /dev/null +++ b/readeck.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,15 @@ +{ + "originHash" : "7374154e7686de69a9f88fbafb081b646b02140f8d82770f46fa750840581e0e", + "pins" : [ + { + "identity" : "netfox", + "kind" : "remoteSourceControl", + "location" : "https://github.com/kasketis/netfox", + "state" : { + "revision" : "557576032736fd3140422baefb68b8f76c55088f", + "version" : "1.21.0" + } + } + ], + "version" : 3 +} diff --git a/readeck.xcodeproj/xcuserdata/ilyashallak.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/readeck.xcodeproj/xcuserdata/ilyashallak.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist index 401b5be..5770a4d 100644 --- a/readeck.xcodeproj/xcuserdata/ilyashallak.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ b/readeck.xcodeproj/xcuserdata/ilyashallak.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -3,4 +3,52 @@ uuid = "FAD7B3BD-946C-4129-A614-E1823F18EC12" type = "1" version = "2.0"> + + + + + + + + + + + + diff --git a/readeck/UI/Bookmarks/BookmarksView.swift b/readeck/UI/Bookmarks/BookmarksView.swift index 6440c3b..e98fefe 100644 --- a/readeck/UI/Bookmarks/BookmarksView.swift +++ b/readeck/UI/Bookmarks/BookmarksView.swift @@ -1,3 +1,5 @@ +import Foundation +import Combine import SwiftUI struct BookmarksView: View { @@ -6,10 +8,15 @@ struct BookmarksView: View { @State private var selectedBookmarkId: String? let state: BookmarkState + @Binding var selectedBookmark: Bookmark? + @State private var showingAddBookmarkFromShare = false @State private var shareURL = "" @State private var shareTitle = "" + @Environment(\.horizontalSizeClass) var horizontalSizeClass + @Environment(\.verticalSizeClass) var verticalSizeClass + var body: some View { NavigationStack { ZStack { @@ -19,7 +26,11 @@ struct BookmarksView: View { List { ForEach(viewModel.bookmarks, id: \.id) { bookmark in Button(action: { - selectedBookmarkId = bookmark.id + if UIDevice.isPhone { + selectedBookmarkId = bookmark.id + } else { + selectedBookmark = bookmark + } }) { BookmarkCardView( bookmark: bookmark, @@ -105,13 +116,13 @@ struct BookmarksView: View { .sheet(isPresented: $showingAddBookmark) { AddBookmarkView(prefilledURL: shareURL, prefilledTitle: shareTitle) } - .alert("Fehler", isPresented: .constant(viewModel.errorMessage != nil)) { + /*.alert("Fehler", isPresented: .constant(viewModel.errorMessage != nil)) { Button("OK", role: .cancel) { viewModel.errorMessage = nil } } message: { Text(viewModel.errorMessage ?? "") - } + }*/ .task { await viewModel.loadBookmarks(state: state) } @@ -123,24 +134,8 @@ struct BookmarksView: View { } } } - .onReceive(NotificationCenter.default.publisher(for: NSNotification.Name("AddBookmarkFromShare"))) { notification in - handleShareNotification(notification) - } } - } - - private func handleShareNotification(_ notification: Notification) { - guard let userInfo = notification.userInfo, - let url = userInfo["url"] as? String, - !url.isEmpty else { - return - } - - shareURL = url - shareTitle = userInfo["title"] as? String ?? "" - showingAddBookmark = true - - print("Received share notification - URL: \(url), Title: \(shareTitle)") + .searchable(text: $viewModel.searchQuery, placement: .automatic, prompt: "Search...") } } diff --git a/readeck/UI/Bookmarks/BookmarksViewModel.swift b/readeck/UI/Bookmarks/BookmarksViewModel.swift index ec209b9..3d974ad 100644 --- a/readeck/UI/Bookmarks/BookmarksViewModel.swift +++ b/readeck/UI/Bookmarks/BookmarksViewModel.swift @@ -24,6 +24,14 @@ class BookmarksViewModel { private var offset = 0 private var hasMoreData = true + var searchQuery: String = "" { + didSet { + throttleSearch() + } + } + + private var searchWorkItem: DispatchWorkItem? + init() { setupNotificationObserver() } @@ -53,6 +61,20 @@ class BookmarksViewModel { print("Received share notification - URL: \(url)") } + private func throttleSearch() { + searchWorkItem?.cancel() + + let workItem = DispatchWorkItem { [weak self] in + guard let self = self else { return } + Task { + await self.loadBookmarks(state: self.currentState) + } + } + + searchWorkItem = workItem + DispatchQueue.main.asyncAfter(deadline: .now() + 0.3, execute: workItem) + } + @MainActor func loadBookmarks(state: BookmarkState = .unread) async { isLoading = true @@ -62,7 +84,12 @@ class BookmarksViewModel { hasMoreData = true // Pagination zurücksetzen do { - let newBookmarks = try await getBooksmarksUseCase.execute(state: state, limit: limit, offset: offset) + let newBookmarks = try await getBooksmarksUseCase.execute( + state: state, + limit: limit, + offset: offset, + search: searchQuery // Suche integrieren + ) bookmarks = newBookmarks hasMoreData = newBookmarks.count == limit // Prüfen, ob weitere Daten verfügbar sind } catch { diff --git a/readeck/UI/TabView.swift b/readeck/UI/TabView.swift index b8cc0bd..7990aff 100644 --- a/readeck/UI/TabView.swift +++ b/readeck/UI/TabView.swift @@ -1,7 +1,7 @@ import SwiftUI import Foundation -enum BookmarkState: String, CaseIterable { +enum BookmarkState: String, CaseIterable { case unread = "unread" case favorite = "favorite" case archived = "archived" @@ -31,22 +31,40 @@ enum BookmarkState: String, CaseIterable { struct MainTabView: View { @State private var selectedTab: String = "Ungelesen" - + + // sizeClass + @Environment(\.horizontalSizeClass) + var horizontalSizeClass + + @Environment(\.verticalSizeClass) + var verticalSizeClass + + @State var selectedBookmark: Bookmark? + var body: some View { + if UIDevice.isPhone { + PhoneView() + } else { + PadView() + } + } + + @ViewBuilder + private func PhoneView() -> some View { TabView(selection: $selectedTab) { - BookmarksView(state: .unread) + BookmarksView(state: .unread, selectedBookmark: .constant(nil)) .tabItem { Label("Ungelesen", systemImage: "house") } .tag("Ungelesen") - - BookmarksView(state: .favorite) + + BookmarksView(state: .favorite, selectedBookmark: .constant(nil)) .tabItem { Label("Favoriten", systemImage: "heart") } .tag("Favoriten") - BookmarksView(state: .archived) + BookmarksView(state: .archived, selectedBookmark: .constant(nil)) .tabItem { Label("Archiv", systemImage: "archivebox") } @@ -56,12 +74,83 @@ struct MainTabView: View { .tabItem { Label("Settings", systemImage: "gear") } - .tag("Settings") + .tag("Settings") + } + .accentColor(.accentColor) + } + + @ViewBuilder + private func PadView() -> some View { + TabView(selection: $selectedTab) { + // Ungelesen Tab + NavigationSplitView { + BookmarksView(state: .unread, selectedBookmark: $selectedBookmark) + } detail: { + if let selectedBookmark = selectedBookmark { + BookmarkDetailView(bookmarkId: selectedBookmark.id) + } else { + Text("Select a bookmark") + .foregroundColor(.gray) + } + } + .tabItem { + Label("Unread", systemImage: "house") + } + .tag("Unread") + + NavigationSplitViewContainer(state: .favorite, selectedBookmark: $selectedBookmark) + .tabItem { + Label("Favoriten", systemImage: "heart") + } + .tag("Favorite") + + NavigationSplitViewContainer(state: .archived, selectedBookmark: $selectedBookmark) + .tabItem { + Label("Archive", systemImage: "archivebox") + } + .tag("Archive") + + SettingsView() + .tabItem { + Label("Settings", systemImage: "gear") + } + .tag("Settings") + } + .accentColor(.accentColor) + } +} + +// Container für NavigationSplitView +struct NavigationSplitViewContainer: View { + let state: BookmarkState + @Binding var selectedBookmark: Bookmark? + + var body: some View { + NavigationSplitView { + BookmarksView(state: state, selectedBookmark: $selectedBookmark) + } detail: { + if let selectedBookmark = selectedBookmark { + BookmarkDetailView(bookmarkId: selectedBookmark.id) + } else { + Text("Select a bookmark") + .foregroundColor(.gray) + } } - .accentColor(.blue) } } #Preview { MainTabView() } + + + +extension UIDevice { + static var isPad: Bool { + return UIDevice.current.userInterfaceIdiom == .pad + } + + static var isPhone: Bool { + return UIDevice.current.userInterfaceIdiom == .phone + } +} diff --git a/readeck/UI/readeckApp.swift b/readeck/UI/readeckApp.swift index 6c2dd3e..03a9e3b 100644 --- a/readeck/UI/readeckApp.swift +++ b/readeck/UI/readeckApp.swift @@ -6,6 +6,7 @@ // import SwiftUI +import netfox @main struct readeckApp: App { @@ -18,6 +19,11 @@ struct readeckApp: App { .onOpenURL { url in handleIncomingURL(url) } + .onAppear { + #if DEBUG + NFX.sharedInstance().start() + #endif + } } }