Add bookmark labels functionality
- Add BookmarkLabel model and DTO - Create LabelsRepository and PLabelsRepository protocol - Add GetLabelsUseCase for fetching labels - Update BookmarkMapper to handle labels - Add LabelsView and LabelsViewModel for UI - Update BookmarksView and BookmarkLabelsView to display labels - Add green2 color asset for labels - Update API and repository layers to support labels
This commit is contained in:
parent
d2e8228903
commit
3e6db364b5
38
readeck/Assets.xcassets/green2.colorset/Contents.json
Normal file
38
readeck/Assets.xcassets/green2.colorset/Contents.json
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0x5B",
|
||||||
|
"green" : "0x4D",
|
||||||
|
"red" : "0x00"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"appearances" : [
|
||||||
|
{
|
||||||
|
"appearance" : "luminosity",
|
||||||
|
"value" : "dark"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0x5B",
|
||||||
|
"green" : "0x4D",
|
||||||
|
"red" : "0x00"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -10,13 +10,14 @@ import Foundation
|
|||||||
protocol PAPI {
|
protocol PAPI {
|
||||||
var tokenProvider: TokenProvider { get }
|
var tokenProvider: TokenProvider { get }
|
||||||
func login(endpoint: String, username: String, password: String) async throws -> UserDto
|
func login(endpoint: String, username: String, password: String) async throws -> UserDto
|
||||||
func getBookmarks(state: BookmarkState?, limit: Int?, offset: Int?, search: String?, type: [BookmarkType]?) async throws -> BookmarksPageDto
|
func getBookmarks(state: BookmarkState?, limit: Int?, offset: Int?, search: String?, type: [BookmarkType]?, tag: String?) async throws -> BookmarksPageDto
|
||||||
func getBookmark(id: String) async throws -> BookmarkDetailDto
|
func getBookmark(id: String) async throws -> BookmarkDetailDto
|
||||||
func getBookmarkArticle(id: String) async throws -> String
|
func getBookmarkArticle(id: String) async throws -> String
|
||||||
func createBookmark(createRequest: CreateBookmarkRequestDto) async throws -> CreateBookmarkResponseDto
|
func createBookmark(createRequest: CreateBookmarkRequestDto) async throws -> CreateBookmarkResponseDto
|
||||||
func updateBookmark(id: String, updateRequest: UpdateBookmarkRequestDto) async throws
|
func updateBookmark(id: String, updateRequest: UpdateBookmarkRequestDto) async throws
|
||||||
func deleteBookmark(id: String) async throws
|
func deleteBookmark(id: String) async throws
|
||||||
func searchBookmarks(search: String) async throws -> BookmarksPageDto
|
func searchBookmarks(search: String) async throws -> BookmarksPageDto
|
||||||
|
func getBookmarkLabels() async throws -> [BookmarkLabelDto]
|
||||||
}
|
}
|
||||||
|
|
||||||
class API: PAPI {
|
class API: PAPI {
|
||||||
@ -180,12 +181,12 @@ class API: PAPI {
|
|||||||
return try JSONDecoder().decode(UserDto.self, from: data)
|
return try JSONDecoder().decode(UserDto.self, from: data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getBookmarks(state: BookmarkState? = nil, limit: Int? = nil, offset: Int? = nil, search: String? = nil, type: [BookmarkType]? = nil) async throws -> BookmarksPageDto {
|
func getBookmarks(state: BookmarkState? = nil, limit: Int? = nil, offset: Int? = nil, search: String? = nil, type: [BookmarkType]? = nil, tag: String? = nil) async throws -> BookmarksPageDto {
|
||||||
var endpoint = "/api/bookmarks"
|
var endpoint = "/api/bookmarks"
|
||||||
var queryItems: [URLQueryItem] = []
|
var queryItems: [URLQueryItem] = []
|
||||||
|
|
||||||
// Query-Parameter basierend auf State hinzufügen
|
// Query-Parameter basierend auf State hinzufügen
|
||||||
if let state = state {
|
if let state {
|
||||||
switch state {
|
switch state {
|
||||||
case .unread:
|
case .unread:
|
||||||
queryItems.append(URLQueryItem(name: "is_archived", value: "false"))
|
queryItems.append(URLQueryItem(name: "is_archived", value: "false"))
|
||||||
@ -199,24 +200,28 @@ class API: PAPI {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let limit = limit {
|
if let limit {
|
||||||
queryItems.append(URLQueryItem(name: "limit", value: "\(limit)"))
|
queryItems.append(URLQueryItem(name: "limit", value: "\(limit)"))
|
||||||
}
|
}
|
||||||
if let offset = offset {
|
if let offset {
|
||||||
queryItems.append(URLQueryItem(name: "offset", value: "\(offset)"))
|
queryItems.append(URLQueryItem(name: "offset", value: "\(offset)"))
|
||||||
}
|
}
|
||||||
|
|
||||||
if let search = search {
|
if let search {
|
||||||
queryItems.append(URLQueryItem(name: "search", value: search))
|
queryItems.append(URLQueryItem(name: "search", value: search))
|
||||||
}
|
}
|
||||||
|
|
||||||
// type-Parameter als Array von BookmarkType
|
// type-Parameter als Array von BookmarkType
|
||||||
if let type = type, !type.isEmpty {
|
if let type, !type.isEmpty {
|
||||||
for t in type {
|
for t in type {
|
||||||
queryItems.append(URLQueryItem(name: "type", value: t.rawValue))
|
queryItems.append(URLQueryItem(name: "type", value: t.rawValue))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let tag {
|
||||||
|
queryItems.append(URLQueryItem(name: "labels", value: tag))
|
||||||
|
}
|
||||||
|
|
||||||
if !queryItems.isEmpty {
|
if !queryItems.isEmpty {
|
||||||
let queryString = queryItems.map { "\($0.name)=\($0.value ?? "")" }.joined(separator: "&")
|
let queryString = queryItems.map { "\($0.name)=\($0.value ?? "")" }.joined(separator: "&")
|
||||||
endpoint += "?\(queryString)"
|
endpoint += "?\(queryString)"
|
||||||
@ -350,6 +355,13 @@ class API: PAPI {
|
|||||||
links: links
|
links: links
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getBookmarkLabels() async throws -> [BookmarkLabelDto] {
|
||||||
|
return try await makeJSONRequest(
|
||||||
|
endpoint: "/api/bookmarks/labels",
|
||||||
|
responseType: [BookmarkLabelDto].self
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum HTTPMethod: String {
|
enum HTTPMethod: String {
|
||||||
|
|||||||
18
readeck/Data/API/DTOs/BookmarkLabelDto.swift
Normal file
18
readeck/Data/API/DTOs/BookmarkLabelDto.swift
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
struct BookmarkLabelDto: Codable, Identifiable {
|
||||||
|
var id: String { get { href } }
|
||||||
|
let name: String
|
||||||
|
let count: Int
|
||||||
|
let href: String
|
||||||
|
|
||||||
|
enum CodingKeys: String, CodingKey {
|
||||||
|
case name, count, href
|
||||||
|
}
|
||||||
|
|
||||||
|
init(name: String, count: Int, href: String) {
|
||||||
|
self.name = name
|
||||||
|
self.count = count
|
||||||
|
self.href = href
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -71,3 +71,9 @@ extension ImageResourceDto {
|
|||||||
return ImageResource(src: src, height: height, width: width)
|
return ImageResource(src: src, height: height, width: width)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension BookmarkLabelDto {
|
||||||
|
func toDomain() -> BookmarkLabel {
|
||||||
|
return BookmarkLabel(name: self.name, count: self.count, href: self.href)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
protocol PBookmarksRepository {
|
protocol PBookmarksRepository {
|
||||||
func fetchBookmarks(state: BookmarkState?, limit: Int?, offset: Int?, search: String?, type: [BookmarkType]?) async throws -> BookmarksPage
|
func fetchBookmarks(state: BookmarkState?, limit: Int?, offset: Int?, search: String?, type: [BookmarkType]?, tag: String?) async throws -> BookmarksPage
|
||||||
func fetchBookmark(id: String) async throws -> BookmarkDetail
|
func fetchBookmark(id: String) async throws -> BookmarkDetail
|
||||||
func fetchBookmarkArticle(id: String) async throws -> String
|
func fetchBookmarkArticle(id: String) async throws -> String
|
||||||
func createBookmark(createRequest: CreateBookmarkRequest) async throws -> String
|
func createBookmark(createRequest: CreateBookmarkRequest) async throws -> String
|
||||||
@ -17,8 +17,8 @@ class BookmarksRepository: PBookmarksRepository {
|
|||||||
self.api = api
|
self.api = api
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchBookmarks(state: BookmarkState? = nil, limit: Int? = nil, offset: Int? = nil, search: String? = nil, type: [BookmarkType]? = nil) async throws -> BookmarksPage {
|
func fetchBookmarks(state: BookmarkState? = nil, limit: Int? = nil, offset: Int? = nil, search: String? = nil, type: [BookmarkType]? = nil, tag: String? = nil) async throws -> BookmarksPage {
|
||||||
let bookmarkDtos = try await api.getBookmarks(state: state, limit: limit, offset: offset, search: search, type: type)
|
let bookmarkDtos = try await api.getBookmarks(state: state, limit: limit, offset: offset, search: search, type: type, tag: tag)
|
||||||
return bookmarkDtos.toDomain()
|
return bookmarkDtos.toDomain()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
14
readeck/Data/Repository/LabelsRepository.swift
Normal file
14
readeck/Data/Repository/LabelsRepository.swift
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
class LabelsRepository: PLabelsRepository {
|
||||||
|
private let api: PAPI
|
||||||
|
|
||||||
|
init(api: PAPI) {
|
||||||
|
self.api = api
|
||||||
|
}
|
||||||
|
|
||||||
|
func getLabels() async throws -> [BookmarkLabel] {
|
||||||
|
let dtos = try await api.getBookmarkLabels()
|
||||||
|
return dtos.map { $0.toDomain() }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -17,6 +17,7 @@ struct BookmarkDetail {
|
|||||||
let labels: [String]
|
let labels: [String]
|
||||||
let thumbnailUrl: String
|
let thumbnailUrl: String
|
||||||
let imageUrl: String
|
let imageUrl: String
|
||||||
|
var content: String?
|
||||||
}
|
}
|
||||||
|
|
||||||
extension BookmarkDetail {
|
extension BookmarkDetail {
|
||||||
|
|||||||
15
readeck/Domain/Model/BookmarkLabel.swift
Normal file
15
readeck/Domain/Model/BookmarkLabel.swift
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
struct BookmarkLabel: Identifiable, Equatable, Hashable {
|
||||||
|
let id: String // kann href oder name sein, je nach Backend
|
||||||
|
let name: String
|
||||||
|
let count: Int
|
||||||
|
let href: String
|
||||||
|
|
||||||
|
init(name: String, count: Int, href: String) {
|
||||||
|
self.name = name
|
||||||
|
self.count = count
|
||||||
|
self.href = href
|
||||||
|
self.id = href // oder name, je nach Backend-Eindeutigkeit
|
||||||
|
}
|
||||||
|
}
|
||||||
5
readeck/Domain/Protocols/PLabelsRepository.swift
Normal file
5
readeck/Domain/Protocols/PLabelsRepository.swift
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
protocol PLabelsRepository {
|
||||||
|
func getLabels() async throws -> [BookmarkLabel]
|
||||||
|
}
|
||||||
@ -7,8 +7,8 @@ class GetBookmarksUseCase {
|
|||||||
self.repository = repository
|
self.repository = repository
|
||||||
}
|
}
|
||||||
|
|
||||||
func execute(state: BookmarkState? = nil, limit: Int? = nil, offset: Int? = nil, search: String? = nil, type: [BookmarkType]? = nil) async throws -> BookmarksPage {
|
func execute(state: BookmarkState? = nil, limit: Int? = nil, offset: Int? = nil, search: String? = nil, type: [BookmarkType]? = nil, tag: String? = nil) async throws -> BookmarksPage {
|
||||||
var allBookmarks = try await repository.fetchBookmarks(state: state, limit: limit, offset: offset, search: search, type: type)
|
var allBookmarks = try await repository.fetchBookmarks(state: state, limit: limit, offset: offset, search: search, type: type, tag: tag)
|
||||||
|
|
||||||
if let state = state {
|
if let state = state {
|
||||||
allBookmarks.bookmarks = allBookmarks.bookmarks.filter { bookmark in
|
allBookmarks.bookmarks = allBookmarks.bookmarks.filter { bookmark in
|
||||||
|
|||||||
13
readeck/Domain/UseCase/GetLabelsUseCase.swift
Normal file
13
readeck/Domain/UseCase/GetLabelsUseCase.swift
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
class GetLabelsUseCase {
|
||||||
|
private let labelsRepository: PLabelsRepository
|
||||||
|
|
||||||
|
init(labelsRepository: PLabelsRepository) {
|
||||||
|
self.labelsRepository = labelsRepository
|
||||||
|
}
|
||||||
|
|
||||||
|
func execute() async throws -> [BookmarkLabel] {
|
||||||
|
return try await labelsRepository.getLabels()
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -30,6 +30,7 @@ struct BookmarkLabelsView: View {
|
|||||||
.navigationBarTitleDisplayMode(.inline)
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
.toolbar {
|
.toolbar {
|
||||||
ToolbarItem(placement: .navigationBarLeading) {
|
ToolbarItem(placement: .navigationBarLeading) {
|
||||||
|
|
||||||
Button("Abbrechen") {
|
Button("Abbrechen") {
|
||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,8 +15,16 @@ struct BookmarksView: View {
|
|||||||
|
|
||||||
let state: BookmarkState
|
let state: BookmarkState
|
||||||
let type: [BookmarkType]
|
let type: [BookmarkType]
|
||||||
|
|
||||||
@Binding var selectedBookmark: Bookmark?
|
@Binding var selectedBookmark: Bookmark?
|
||||||
|
let tag: String?
|
||||||
|
|
||||||
|
// MARK: Initializer
|
||||||
|
init(state: BookmarkState, type: [BookmarkType], selectedBookmark: Binding<Bookmark?>, tag: String? = nil) {
|
||||||
|
self.state = state
|
||||||
|
self.type = type
|
||||||
|
self._selectedBookmark = selectedBookmark
|
||||||
|
self.tag = tag
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: Environments
|
// MARK: Environments
|
||||||
|
|
||||||
@ -147,7 +155,7 @@ struct BookmarksView: View {
|
|||||||
}*/
|
}*/
|
||||||
.onAppear {
|
.onAppear {
|
||||||
Task {
|
Task {
|
||||||
await viewModel.loadBookmarks(state: state, type: type)
|
await viewModel.loadBookmarks(state: state, type: type, tag: tag)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onChange(of: showingAddBookmark) { oldValue, newValue in
|
.onChange(of: showingAddBookmark) { oldValue, newValue in
|
||||||
|
|||||||
@ -13,11 +13,13 @@ class BookmarksViewModel {
|
|||||||
var errorMessage: String?
|
var errorMessage: String?
|
||||||
var currentState: BookmarkState = .unread
|
var currentState: BookmarkState = .unread
|
||||||
var currentType = [BookmarkType.article]
|
var currentType = [BookmarkType.article]
|
||||||
|
var currentTag: String? = nil
|
||||||
|
|
||||||
var showingAddBookmarkFromShare = false
|
var showingAddBookmarkFromShare = false
|
||||||
var shareURL = ""
|
var shareURL = ""
|
||||||
var shareTitle = ""
|
var shareTitle = ""
|
||||||
|
|
||||||
|
|
||||||
private var cancellables = Set<AnyCancellable>()
|
private var cancellables = Set<AnyCancellable>()
|
||||||
private var limit = 20
|
private var limit = 20
|
||||||
private var offset = 0
|
private var offset = 0
|
||||||
@ -74,11 +76,12 @@ class BookmarksViewModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
func loadBookmarks(state: BookmarkState = .unread, type: [BookmarkType] = [.article]) async {
|
func loadBookmarks(state: BookmarkState = .unread, type: [BookmarkType] = [.article], tag: String? = nil) async {
|
||||||
isLoading = true
|
isLoading = true
|
||||||
errorMessage = nil
|
errorMessage = nil
|
||||||
currentState = state
|
currentState = state
|
||||||
currentType = type
|
currentType = type
|
||||||
|
currentTag = tag
|
||||||
|
|
||||||
offset = 0 // Offset zurücksetzen
|
offset = 0 // Offset zurücksetzen
|
||||||
hasMoreData = true // Pagination zurücksetzen
|
hasMoreData = true // Pagination zurücksetzen
|
||||||
@ -89,10 +92,11 @@ class BookmarksViewModel {
|
|||||||
limit: limit,
|
limit: limit,
|
||||||
offset: offset,
|
offset: offset,
|
||||||
search: searchQuery,
|
search: searchQuery,
|
||||||
type: type
|
type: type,
|
||||||
|
tag: tag
|
||||||
)
|
)
|
||||||
bookmarks = newBookmarks
|
bookmarks = newBookmarks
|
||||||
hasMoreData = newBookmarks.bookmarks.count == limit // Prüfen, ob weitere Daten verfügbar sind
|
hasMoreData = newBookmarks.currentPage != newBookmarks.totalPages // Prüfen, ob weitere Daten verfügbar sind
|
||||||
} catch {
|
} catch {
|
||||||
errorMessage = "Fehler beim Laden der Bookmarks"
|
errorMessage = "Fehler beim Laden der Bookmarks"
|
||||||
bookmarks = nil
|
bookmarks = nil
|
||||||
@ -114,9 +118,10 @@ class BookmarksViewModel {
|
|||||||
state: currentState,
|
state: currentState,
|
||||||
limit: limit,
|
limit: limit,
|
||||||
offset: offset,
|
offset: offset,
|
||||||
type: currentType)
|
type: currentType,
|
||||||
bookmarks?.bookmarks.append(contentsOf: newBookmarks.bookmarks) // Neue Bookmarks hinzufügen
|
tag: currentTag)
|
||||||
hasMoreData = newBookmarks.bookmarks.count == limit // Prüfen,
|
bookmarks?.bookmarks.append(contentsOf: newBookmarks.bookmarks)
|
||||||
|
hasMoreData = newBookmarks.currentPage != newBookmarks.totalPages
|
||||||
} catch {
|
} catch {
|
||||||
errorMessage = "Fehler beim Nachladen der Bookmarks"
|
errorMessage = "Fehler beim Nachladen der Bookmarks"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,6 +15,8 @@ protocol UseCaseFactory {
|
|||||||
func makeSaveServerSettingsUseCase() -> SaveServerSettingsUseCase
|
func makeSaveServerSettingsUseCase() -> SaveServerSettingsUseCase
|
||||||
func makeAddLabelsToBookmarkUseCase() -> AddLabelsToBookmarkUseCase
|
func makeAddLabelsToBookmarkUseCase() -> AddLabelsToBookmarkUseCase
|
||||||
func makeRemoveLabelsFromBookmarkUseCase() -> RemoveLabelsFromBookmarkUseCase
|
func makeRemoveLabelsFromBookmarkUseCase() -> RemoveLabelsFromBookmarkUseCase
|
||||||
|
func makeGetLabelsUseCase() -> GetLabelsUseCase
|
||||||
|
func makeAddTextToSpeechQueueUseCase() -> AddTextToSpeechQueueUseCase
|
||||||
}
|
}
|
||||||
|
|
||||||
class DefaultUseCaseFactory: UseCaseFactory {
|
class DefaultUseCaseFactory: UseCaseFactory {
|
||||||
@ -88,4 +90,14 @@ class DefaultUseCaseFactory: UseCaseFactory {
|
|||||||
func makeRemoveLabelsFromBookmarkUseCase() -> RemoveLabelsFromBookmarkUseCase {
|
func makeRemoveLabelsFromBookmarkUseCase() -> RemoveLabelsFromBookmarkUseCase {
|
||||||
return RemoveLabelsFromBookmarkUseCase(repository: bookmarksRepository)
|
return RemoveLabelsFromBookmarkUseCase(repository: bookmarksRepository)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func makeGetLabelsUseCase() -> GetLabelsUseCase {
|
||||||
|
let api = API(tokenProvider: CoreDataTokenProvider())
|
||||||
|
let labelsRepository = LabelsRepository(api: api)
|
||||||
|
return GetLabelsUseCase(labelsRepository: labelsRepository)
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeAddTextToSpeechQueueUseCase() -> AddTextToSpeechQueueUseCase {
|
||||||
|
return AddTextToSpeechQueueUseCase()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
40
readeck/UI/Labels/LabelsView.swift
Normal file
40
readeck/UI/Labels/LabelsView.swift
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct LabelsView: View {
|
||||||
|
@State var viewModel = LabelsViewModel()
|
||||||
|
@State private var selectedTag: String? = nil
|
||||||
|
@State private var selectedBookmark: Bookmark? = nil
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
if viewModel.isLoading {
|
||||||
|
ProgressView()
|
||||||
|
} else if let errorMessage = viewModel.errorMessage {
|
||||||
|
Text("Fehler: \(errorMessage)")
|
||||||
|
.foregroundColor(.red)
|
||||||
|
} else {
|
||||||
|
List {
|
||||||
|
ForEach(viewModel.labels, id: \.href) { label in
|
||||||
|
|
||||||
|
NavigationLink {
|
||||||
|
BookmarksView(state: .all, type: [], selectedBookmark: .constant(nil), tag: label.name)
|
||||||
|
.navigationTitle("\(label.name) (\(label.count))")
|
||||||
|
} label: {
|
||||||
|
HStack {
|
||||||
|
Text(label.name)
|
||||||
|
Spacer()
|
||||||
|
Text("\(label.count)")
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onAppear {
|
||||||
|
Task {
|
||||||
|
await viewModel.loadLabels()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
23
readeck/UI/Labels/LabelsViewModel.swift
Normal file
23
readeck/UI/Labels/LabelsViewModel.swift
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import Foundation
|
||||||
|
import Observation
|
||||||
|
|
||||||
|
@Observable
|
||||||
|
class LabelsViewModel {
|
||||||
|
private let getLabelsUseCase = DefaultUseCaseFactory.shared.makeGetLabelsUseCase()
|
||||||
|
|
||||||
|
var labels: [BookmarkLabel] = []
|
||||||
|
var isLoading = false
|
||||||
|
var errorMessage: String? = nil
|
||||||
|
|
||||||
|
@MainActor
|
||||||
|
func loadLabels() async {
|
||||||
|
isLoading = true
|
||||||
|
errorMessage = nil
|
||||||
|
do {
|
||||||
|
labels = try await getLabelsUseCase.execute()
|
||||||
|
} catch {
|
||||||
|
errorMessage = "Fehler beim Laden der Labels"
|
||||||
|
}
|
||||||
|
isLoading = false
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -86,7 +86,7 @@ struct PadSidebarView: View {
|
|||||||
case .pictures:
|
case .pictures:
|
||||||
BookmarksView(state: .all, type: [.photo], selectedBookmark: $selectedBookmark)
|
BookmarksView(state: .all, type: [.photo], selectedBookmark: $selectedBookmark)
|
||||||
case .tags:
|
case .tags:
|
||||||
Text("Tags")
|
LabelsView()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.navigationTitle(selectedTab.label)
|
.navigationTitle(selectedTab.label)
|
||||||
|
|||||||
@ -126,7 +126,7 @@ struct SettingsServerView: View {
|
|||||||
Button("Debug-Anmeldung") {
|
Button("Debug-Anmeldung") {
|
||||||
viewModel.username = "admin"
|
viewModel.username = "admin"
|
||||||
viewModel.password = "Diggah123"
|
viewModel.password = "Diggah123"
|
||||||
viewModel.endpoint = "https://keep.mnk.any64.de"
|
viewModel.endpoint = "https://readeck.mnk.any64.de"
|
||||||
}
|
}
|
||||||
.font(.caption)
|
.font(.caption)
|
||||||
.foregroundColor(.secondary)
|
.foregroundColor(.secondary)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user