1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
import Foundation
/// What the search tab shows before anything is typed: the recent feed as a
/// grid, headed by chips for the most-used cameras and places.
///
/// The chips are kept apart from the galleries because they arrive on their
/// own requests and fail on their own: a camera index that is down should
/// not take the recent galleries with it, and the other way round.
@Observable
@MainActor
final class ExploreViewModel {
var cameras: [CameraItem] = []
var locations: [LocationItem] = []
var galleries: [GrainGallery] = []
var isLoading = true
var isLoadingMore = false
private let client: XRPCClient
private var cursor: String?
private var loaded = false
init(client: XRPCClient) {
self.client = client
}
/// The first load, once. The tab is composed each time it is shown, and
/// a second look should not blank what was already there.
func loadIfNeeded(auth: AuthContext? = nil) async {
guard !loaded else { return }
loaded = true
await load(auth: auth)
}
func refresh(auth: AuthContext? = nil) async {
await load(auth: auth)
}
private func load(auth: AuthContext?) async {
// Three requests, none waiting on another. Each keeps what it had on
// failure: a chip row that empties because one refresh timed out
// would read as the cameras having gone.
async let camerasReq = client.getCameras(auth: auth)
async let locationsReq = client.getLocations(auth: auth)
async let feedReq = client.getFeed(feed: "recent", auth: auth)
if let response = try? await camerasReq {
cameras = response.cameras ?? []
}
if let response = try? await locationsReq {
locations = response.locations ?? []
}
if let response = try? await feedReq {
galleries = response.items ?? []
cursor = response.cursor
}
isLoading = false
}
func loadMore(auth: AuthContext? = nil) async {
guard let cursor, !isLoadingMore, !isLoading else { return }
isLoadingMore = true
do {
let response = try await client.getFeed(feed: "recent", cursor: cursor, auth: auth)
galleries.append(contentsOf: response.items ?? [])
self.cursor = response.cursor
} catch {}
isLoadingMore = false
}
}