Unit tests, Lokalisierung, ShareExtension
This commit is contained in:
@@ -0,0 +1,149 @@
|
||||
import Foundation
|
||||
import Combine
|
||||
|
||||
// MARK: - ViewModel
|
||||
|
||||
@MainActor
|
||||
final class ShareViewModel: ObservableObject {
|
||||
|
||||
// MARK: Published state
|
||||
|
||||
@Published var shelves: [ShelfSummary] = []
|
||||
@Published var books: [BookSummary] = []
|
||||
@Published var chapters: [ChapterSummary] = []
|
||||
|
||||
@Published var selectedShelf: ShelfSummary?
|
||||
@Published var selectedBook: BookSummary?
|
||||
@Published var selectedChapter: ChapterSummary?
|
||||
|
||||
@Published var pageTitle: String = ""
|
||||
@Published var isLoading: Bool = false
|
||||
@Published var errorMessage: String?
|
||||
@Published var isSaved: Bool = false
|
||||
|
||||
// MARK: Read-only
|
||||
|
||||
let sharedText: String
|
||||
let isConfigured: Bool
|
||||
let serverURL: String
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let apiService: ShareAPIServiceProtocol
|
||||
private let defaults: UserDefaults?
|
||||
|
||||
private let lastShelfIDKey = "shareExtension.lastShelfID"
|
||||
private let lastBookIDKey = "shareExtension.lastBookID"
|
||||
|
||||
// MARK: Computed
|
||||
|
||||
var isSaveDisabled: Bool {
|
||||
pageTitle.trimmingCharacters(in: .whitespaces).isEmpty
|
||||
|| selectedBook == nil
|
||||
|| isLoading
|
||||
}
|
||||
|
||||
// MARK: - Init
|
||||
|
||||
init(
|
||||
sharedText: String,
|
||||
apiService: ShareAPIServiceProtocol,
|
||||
serverURL: String = "",
|
||||
isConfigured: Bool = true,
|
||||
defaults: UserDefaults? = nil
|
||||
) {
|
||||
self.sharedText = sharedText
|
||||
self.isConfigured = isConfigured
|
||||
self.serverURL = serverURL
|
||||
self.apiService = apiService
|
||||
self.defaults = defaults
|
||||
|
||||
// Auto-populate title from the first non-empty line of the shared text.
|
||||
let firstLine = sharedText
|
||||
.components(separatedBy: .newlines)
|
||||
.first { !$0.trimmingCharacters(in: .whitespaces).isEmpty } ?? ""
|
||||
self.pageTitle = String(firstLine.prefix(100))
|
||||
}
|
||||
|
||||
// MARK: - Actions
|
||||
|
||||
/// Loads all shelves and restores the last used shelf/book selection.
|
||||
func loadShelves() async {
|
||||
isLoading = true
|
||||
errorMessage = nil
|
||||
defer { isLoading = false }
|
||||
|
||||
do {
|
||||
shelves = try await apiService.fetchShelves()
|
||||
|
||||
// Restore last selected shelf
|
||||
let lastShelfID = defaults?.integer(forKey: lastShelfIDKey) ?? 0
|
||||
if lastShelfID != 0, let match = shelves.first(where: { $0.id == lastShelfID }) {
|
||||
await selectShelf(match)
|
||||
}
|
||||
} catch {
|
||||
errorMessage = error.localizedDescription
|
||||
}
|
||||
}
|
||||
|
||||
func selectShelf(_ shelf: ShelfSummary) async {
|
||||
selectedShelf = shelf
|
||||
selectedBook = nil
|
||||
selectedChapter = nil
|
||||
books = []
|
||||
chapters = []
|
||||
defaults?.set(shelf.id, forKey: lastShelfIDKey)
|
||||
|
||||
isLoading = true
|
||||
defer { isLoading = false }
|
||||
|
||||
do {
|
||||
books = try await apiService.fetchBooks(shelfId: shelf.id)
|
||||
|
||||
// Restore last selected book
|
||||
let lastBookID = defaults?.integer(forKey: lastBookIDKey) ?? 0
|
||||
if lastBookID != 0, let match = books.first(where: { $0.id == lastBookID }) {
|
||||
await selectBook(match)
|
||||
}
|
||||
} catch {
|
||||
errorMessage = error.localizedDescription
|
||||
}
|
||||
}
|
||||
|
||||
func selectBook(_ book: BookSummary) async {
|
||||
selectedBook = book
|
||||
selectedChapter = nil
|
||||
chapters = []
|
||||
defaults?.set(book.id, forKey: lastBookIDKey)
|
||||
|
||||
isLoading = true
|
||||
defer { isLoading = false }
|
||||
|
||||
do {
|
||||
chapters = try await apiService.fetchChapters(bookId: book.id)
|
||||
} catch {
|
||||
errorMessage = error.localizedDescription
|
||||
}
|
||||
}
|
||||
|
||||
func savePage() async {
|
||||
guard let book = selectedBook,
|
||||
!pageTitle.trimmingCharacters(in: .whitespaces).isEmpty else { return }
|
||||
|
||||
isLoading = true
|
||||
errorMessage = nil
|
||||
defer { isLoading = false }
|
||||
|
||||
do {
|
||||
_ = try await apiService.createPage(
|
||||
bookId: book.id,
|
||||
chapterId: selectedChapter?.id,
|
||||
title: pageTitle.trimmingCharacters(in: .whitespaces),
|
||||
markdown: sharedText
|
||||
)
|
||||
isSaved = true
|
||||
} catch {
|
||||
errorMessage = error.localizedDescription
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user