krz/hutch
an ios client for sourcehut
clone: git clone https://gitbay.org/krz/hutch.git
v2.1: Hutch/Views/Pastes/PasteListViewModel.swift · raw
1import Foundation
2
3@Observable
4@MainActor
5final class PasteListViewModel {
6 private(set) var pastes: [Paste] = []
7 private(set) var isLoading = false
8 private(set) var isLoadingMore = false
9 private(set) var isRefreshing = false
10 private(set) var isCreatingPaste = false
11 var error: String?
12
13 private var cursor: String?
14 private var hasMore = true
15 private let service: PasteService
16
17 init(service: PasteService) {
18 self.service = service
19 }
20
21 func loadPastes() async {
22 if pastes.isEmpty, let cached = service.loadCachedPastes() {
23 pastes = cached.results
24 cursor = cached.cursor
25 hasMore = cached.cursor != nil
26 }
27
28 if pastes.isEmpty {
29 isLoading = true
30 } else {
31 isRefreshing = true
32 }
33 error = nil
34 cursor = nil
35 hasMore = true
36
37 do {
38 let page = try await service.listPastes(cursor: nil, useCache: true)
39 pastes = page.results
40 cursor = page.cursor
41 hasMore = page.cursor != nil
42 } catch {
43 if pastes.isEmpty {
44 self.error = error.userFacingMessage
45 }
46 }
47
48 isLoading = false
49 isRefreshing = false
50 }
51
52 func loadMoreIfNeeded(currentItem: Paste) async {
53 guard let last = pastes.last,
54 last.id == currentItem.id,
55 hasMore,
56 !isLoadingMore else {
57 return
58 }
59
60 isLoadingMore = true
61 defer { isLoadingMore = false }
62
63 do {
64 let page = try await service.listPastes(cursor: cursor, useCache: false)
65 pastes.append(contentsOf: page.results)
66 cursor = page.cursor
67 hasMore = page.cursor != nil
68 } catch {
69 self.error = error.userFacingMessage
70 }
71 }
72
73 func createPaste(files: [PasteUploadDraft], visibility: Visibility) async -> Paste? {
74 guard !isCreatingPaste else { return nil }
75
76 let normalizedFiles = files.filter {
77 !$0.contents.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
78 }
79 guard !normalizedFiles.isEmpty else {
80 error = "Add at least one file with text content."
81 return nil
82 }
83
84 isCreatingPaste = true
85 error = nil
86 defer { isCreatingPaste = false }
87
88 do {
89 let paste = try await service.createPaste(files: normalizedFiles, visibility: visibility)
90 upsertPaste(paste)
91 return paste
92 } catch {
93 self.error = error.userFacingMessage
94 return nil
95 }
96 }
97
98 func upsertPaste(_ paste: Paste) {
99 if let index = pastes.firstIndex(where: { $0.id == paste.id }) {
100 pastes[index] = paste
101 } else {
102 pastes.insert(paste, at: 0)
103 }
104 }
105
106 func removePaste(id: String) {
107 pastes.removeAll { $0.id == id }
108 }
109}