ReadKeep/readeck/UI/Settings/SettingsServerViewModel.swift
Ilyas Hallak df8a7b64b2 feat: Add Kingfisher caching, card layouts, dynamic tag layout, and undo delete
- Integrate Kingfisher for image caching with CachedAsyncImage component
- Add CacheSettingsView for managing image cache size and clearing cache
- Implement three card layout styles: compact, magazine (default), natural
- Add AppearanceSettingsView with visual layout previews and theme settings
- Create Clean Architecture for card layout with domain models and use cases
- Implement FlowLayout for dynamic label width calculation
- Add skeleton loading animation for initial bookmark loads
- Replace delete confirmation dialogs with immediate delete + 3-second undo
- Support multiple simultaneous undo operations with individual progress bars
- Add grayed-out visual feedback for pending deletions
- Centralize notification names in dedicated NotificationNames file
- Remove pagination logic from label management (replaced with FlowLayout)
- Update AsyncImage usage across BookmarkCardView, BookmarkDetailView, ImageViewerView
- Improve UI consistency and spacing throughout the app
2025-09-04 10:43:27 +02:00

98 lines
3.0 KiB
Swift

import Foundation
import Observation
import SwiftUI
@Observable
class SettingsServerViewModel {
// MARK: - Use Cases
private let loginUseCase: PLoginUseCase
private let logoutUseCase: PLogoutUseCase
private let saveServerSettingsUseCase: PSaveServerSettingsUseCase
private let loadSettingsUseCase: PLoadSettingsUseCase
// MARK: - Server Settings
var endpoint = ""
var username = ""
var password = ""
var isLoading = false
var isLoggedIn = false
// MARK: - Messages
var errorMessage: String?
var successMessage: String?
private var hasFinishedSetup: Bool {
SettingsRepository().hasFinishedSetup
}
init(_ factory: UseCaseFactory = DefaultUseCaseFactory.shared) {
self.loginUseCase = factory.makeLoginUseCase()
self.logoutUseCase = factory.makeLogoutUseCase()
self.saveServerSettingsUseCase = factory.makeSaveServerSettingsUseCase()
self.loadSettingsUseCase = factory.makeLoadSettingsUseCase()
}
var isSetupMode: Bool {
!hasFinishedSetup
}
@MainActor
func loadServerSettings() async {
do {
if let settings = try await loadSettingsUseCase.execute() {
endpoint = settings.endpoint ?? ""
username = settings.username ?? ""
password = settings.password ?? ""
isLoggedIn = settings.isLoggedIn
}
} catch {
errorMessage = "Error loading settings"
}
}
@MainActor
func saveServerSettings() async {
guard canLogin else {
errorMessage = "Please fill in all fields."
return
}
clearMessages()
isLoading = true
defer { isLoading = false }
do {
let user = try await loginUseCase.execute(endpoint: endpoint, username: username.trimmingCharacters(in: .whitespacesAndNewlines), password: password)
try await saveServerSettingsUseCase.execute(endpoint: endpoint, username: username, password: password, token: user.token)
isLoggedIn = true
successMessage = "Server settings saved and successfully logged in."
try await SettingsRepository().saveHasFinishedSetup(true)
NotificationCenter.default.post(name: .setupStatusChanged, object: nil)
} catch {
errorMessage = "Connection or login failed: \(error.localizedDescription)"
isLoggedIn = false
}
}
@MainActor
func logout() async {
do {
try await logoutUseCase.execute()
isLoggedIn = false
successMessage = "Logged out"
NotificationCenter.default.post(name: .setupStatusChanged, object: nil)
} catch {
errorMessage = "Error logging out"
}
}
func clearMessages() {
errorMessage = nil
successMessage = nil
}
var canLogin: Bool {
!username.isEmpty && !password.isEmpty
}
}