auth and projects
This commit is contained in:
1 parent
558bb005f4
commit
0ba2db0ec2
14 files changed
+1207
-94
No files matched your search
@@ -0,0 +1,77 @@
|
||||
//
|
||||
// ProjectListModel.swift
|
||||
// Snail
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Observation
|
||||
|
||||
/// Постраничная загрузка списка проектов: следующая страница подгружается при прокрутке.
|
||||
@Observable
|
||||
final class ProjectListModel {
|
||||
private static let pageSize = 20
|
||||
|
||||
private(set) var projects: [Project] = []
|
||||
private(set) var isLoading = false
|
||||
private(set) var hasLoaded = false
|
||||
private(set) var error: APIError?
|
||||
|
||||
private var loadedPage = 0
|
||||
private var totalPages = 1
|
||||
private let service: ProjectService
|
||||
|
||||
init(service: ProjectService = ProjectService()) {
|
||||
self.service = service
|
||||
}
|
||||
|
||||
var hasMorePages: Bool {
|
||||
loadedPage < totalPages
|
||||
}
|
||||
|
||||
/// Загружает первую страницу заново (первый показ, pull-to-refresh, «Повторить»).
|
||||
func reload() async {
|
||||
await load(page: 1)
|
||||
}
|
||||
|
||||
/// Вызывается при появлении строки: если это последняя строка, грузим следующую страницу.
|
||||
func loadMoreIfNeeded(after project: Project) async {
|
||||
guard project.id == projects.last?.id, hasMorePages, !isLoading else { return }
|
||||
await load(page: loadedPage + 1)
|
||||
}
|
||||
|
||||
func create(key: String, name: String) async throws {
|
||||
let project = try await service.create(key: key, name: name)
|
||||
projects.insert(project, at: 0)
|
||||
}
|
||||
|
||||
func rename(_ project: Project, to name: String) async throws {
|
||||
let updated = try await service.update(id: project.id, name: name)
|
||||
if let index = projects.firstIndex(where: { $0.id == updated.id }) {
|
||||
projects[index] = updated
|
||||
}
|
||||
}
|
||||
|
||||
private func load(page: Int) async {
|
||||
isLoading = true
|
||||
defer { isLoading = false }
|
||||
|
||||
do {
|
||||
let result = try await service.list(page: page, limit: Self.pageSize)
|
||||
if page == 1 {
|
||||
projects = result.list
|
||||
} else {
|
||||
// Список мог сдвинуться между запросами — отбрасываем дубликаты.
|
||||
let known = Set(projects.map(\.id))
|
||||
projects += result.list.filter { !known.contains($0.id) }
|
||||
}
|
||||
loadedPage = page
|
||||
totalPages = result.meta.totalPages
|
||||
error = nil
|
||||
} catch let apiError as APIError {
|
||||
error = apiError
|
||||
} catch {
|
||||
self.error = .invalidResponse
|
||||
}
|
||||
hasLoaded = true
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user