diff --git a/.build/workspace-state.json b/.build/workspace-state.json index aa952d4..d79d44d 100644 --- a/.build/workspace-state.json +++ b/.build/workspace-state.json @@ -174,7 +174,10 @@ }, "subpath" : "Yams" } + ], + "prebuilts" : [ + ] }, - "version" : 6 + "version" : 7 } \ No newline at end of file diff --git a/readeck/Data/API/API.swift b/readeck/Data/API/API.swift index 1225feb..70bc9a1 100644 --- a/readeck/Data/API/API.swift +++ b/readeck/Data/API/API.swift @@ -10,7 +10,7 @@ import Foundation protocol PAPI { var tokenProvider: TokenProvider { get } func login(username: String, password: String) async throws -> UserDto - func getBookmarks(state: BookmarkState?) async throws -> [BookmarkDto] + func getBookmarks(state: BookmarkState?, limit: Int?, offset: Int?, search: String?) async throws -> [BookmarkDto] func getBookmark(id: String) async throws -> BookmarkDetailDto func getBookmarkArticle(id: String) async throws -> String func createBookmark(createRequest: CreateBookmarkRequestDto) async throws -> CreateBookmarkResponseDto @@ -131,21 +131,39 @@ class API: PAPI { return userDto } - func getBookmarks(state: BookmarkState? = nil) async throws -> [BookmarkDto] { + func getBookmarks(state: BookmarkState? = nil, limit: Int? = nil, offset: Int? = nil, search: String? = nil) async throws -> [BookmarkDto] { var endpoint = "/api/bookmarks" + var queryItems: [URLQueryItem] = [] // Query-Parameter basierend auf State hinzufügen if let state = state { switch state { case .unread: - endpoint += "?is_archived=false&is_marked=false" + queryItems.append(URLQueryItem(name: "is_archived", value: "false")) + queryItems.append(URLQueryItem(name: "is_marked", value: "false")) case .favorite: - endpoint += "?is_marked=true" + queryItems.append(URLQueryItem(name: "is_marked", value: "true")) case .archived: - endpoint += "?is_archived=true" + queryItems.append(URLQueryItem(name: "is_archived", value: "true")) } } + if let limit = limit { + queryItems.append(URLQueryItem(name: "limit", value: "\(limit)")) + } + if let offset = offset { + queryItems.append(URLQueryItem(name: "offset", value: "\(offset)")) + } + + if let search = search { + queryItems.append(URLQueryItem(name: "search", value: search)) + } + + if !queryItems.isEmpty { + let queryString = queryItems.map { "\($0.name)=\($0.value ?? "")" }.joined(separator: "&") + endpoint += "?\(queryString)" + } + return try await makeJSONRequest( endpoint: endpoint, responseType: [BookmarkDto].self diff --git a/readeck/Data/Repository/BookmarksRepository.swift b/readeck/Data/Repository/BookmarksRepository.swift index 421f76d..80e0080 100644 --- a/readeck/Data/Repository/BookmarksRepository.swift +++ b/readeck/Data/Repository/BookmarksRepository.swift @@ -1,7 +1,7 @@ import Foundation protocol PBookmarksRepository { - func fetchBookmarks(state: BookmarkState?) async throws -> [Bookmark] + func fetchBookmarks(state: BookmarkState?, limit: Int?, offset: Int?, search: String?) async throws -> [Bookmark] func fetchBookmark(id: String) async throws -> BookmarkDetail func fetchBookmarkArticle(id: String) async throws -> String func createBookmark(createRequest: CreateBookmarkRequest) async throws -> String @@ -16,8 +16,8 @@ class BookmarksRepository: PBookmarksRepository { self.api = api } - func fetchBookmarks(state: BookmarkState? = nil) async throws -> [Bookmark] { - let bookmarkDtos = try await api.getBookmarks(state: state) + func fetchBookmarks(state: BookmarkState? = nil, limit: Int? = nil, offset: Int? = nil, search: String? = nil) async throws -> [Bookmark] { + let bookmarkDtos = try await api.getBookmarks(state: state, limit: limit, offset: offset, search: search) return bookmarkDtos.map { $0.toDomain() } } diff --git a/readeck/Domain/UseCase/GetBookmarksUseCase.swift b/readeck/Domain/UseCase/GetBookmarksUseCase.swift index 2602ae8..3b8c553 100644 --- a/readeck/Domain/UseCase/GetBookmarksUseCase.swift +++ b/readeck/Domain/UseCase/GetBookmarksUseCase.swift @@ -7,8 +7,8 @@ class GetBookmarksUseCase { self.repository = repository } - func execute(state: BookmarkState? = nil) async throws -> [Bookmark] { - let allBookmarks = try await repository.fetchBookmarks(state: state) + func execute(state: BookmarkState? = nil, limit: Int? = nil, offset: Int? = nil, search: String? = nil) async throws -> [Bookmark] { + let allBookmarks = try await repository.fetchBookmarks(state: state, limit: limit, offset: offset, search: search) // Fallback-Filterung auf Client-Seite falls API keine Query-Parameter unterstützt if let state = state { diff --git a/readeck/UI/Bookmarks/BookmarksView.swift b/readeck/UI/Bookmarks/BookmarksView.swift index 31c7438..6440c3b 100644 --- a/readeck/UI/Bookmarks/BookmarksView.swift +++ b/readeck/UI/Bookmarks/BookmarksView.swift @@ -40,6 +40,13 @@ struct BookmarksView: View { } } ) + .onAppear { + if bookmark.id == viewModel.bookmarks.last?.id { + Task { + await viewModel.loadMoreBookmarks() + } + } + } } .buttonStyle(PlainButtonStyle()) .listRowInsets(EdgeInsets(top: 8, leading: 16, bottom: 8, trailing: 16)) @@ -60,6 +67,7 @@ struct BookmarksView: View { ) } } + } // FAB Button - nur bei "Ungelesen" anzeigen diff --git a/readeck/UI/Bookmarks/BookmarksViewModel.swift b/readeck/UI/Bookmarks/BookmarksViewModel.swift index 8adacae..ec209b9 100644 --- a/readeck/UI/Bookmarks/BookmarksViewModel.swift +++ b/readeck/UI/Bookmarks/BookmarksViewModel.swift @@ -1,5 +1,6 @@ import Foundation import Combine +import SwiftUI @Observable class BookmarksViewModel { @@ -18,6 +19,11 @@ class BookmarksViewModel { private var cancellables = Set() + // Pagination-Variablen + private var limit = 20 + private var offset = 0 + private var hasMoreData = true + init() { setupNotificationObserver() } @@ -52,12 +58,35 @@ class BookmarksViewModel { isLoading = true errorMessage = nil currentState = state + offset = 0 // Offset zurücksetzen + hasMoreData = true // Pagination zurücksetzen do { - bookmarks = try await getBooksmarksUseCase.execute(state: state) + let newBookmarks = try await getBooksmarksUseCase.execute(state: state, limit: limit, offset: offset) + bookmarks = newBookmarks + hasMoreData = newBookmarks.count == limit // Prüfen, ob weitere Daten verfügbar sind } catch { errorMessage = "Fehler beim Laden der Bookmarks" - bookmarks = [] + bookmarks = [] + } + + isLoading = false + } + + @MainActor + func loadMoreBookmarks() async { + guard !isLoading && hasMoreData else { return } // Verhindern, dass mehrfach geladen wird + + isLoading = true + errorMessage = nil + + do { + offset += limit // Offset erhöhen + let newBookmarks = try await getBooksmarksUseCase.execute(state: currentState, limit: limit, offset: offset) + bookmarks.append(contentsOf: newBookmarks) // Neue Bookmarks hinzufügen + hasMoreData = newBookmarks.count == limit // Prüfen, ob weitere Daten verfügbar sind + } catch { + errorMessage = "Fehler beim Nachladen der Bookmarks" } isLoading = false diff --git a/readeck/UI/Bookmarks/File.swift b/readeck/UI/Bookmarks/File.swift new file mode 100644 index 0000000..d05637f --- /dev/null +++ b/readeck/UI/Bookmarks/File.swift @@ -0,0 +1,7 @@ +// +// File.swift +// readeck +// +// Created by Ilyas Hallak on 25.06.25. +// +