Skip to content

Commit 1348437

Browse files
authored
SwiftUI: Refactor OPDS facets (#501)
1 parent 165440b commit 1348437

11 files changed

+365
-140
lines changed

TestApp/Sources/OPDS/Base.lproj/OPDS.storyboard

+29-32
Large diffs are not rendered by default.

TestApp/Sources/OPDS/OPDSCatalogs/OPDSCatalogsView.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@
77
import SwiftUI
88

99
struct OPDSCatalogsView: View {
10-
@StateObject private var viewModel: OPDSCatalogsViewModel
10+
@State private var viewModel: OPDSCatalogsViewModel
1111

1212
init(viewModel: OPDSCatalogsViewModel) {
13-
_viewModel = StateObject(wrappedValue: viewModel)
13+
self.viewModel = viewModel
1414
}
1515

1616
var body: some View {

TestApp/Sources/OPDS/OPDSCatalogs/OPDSCatalogsViewModel.swift

+4-3
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@
66

77
import Foundation
88

9-
final class OPDSCatalogsViewModel: ObservableObject {
10-
@Published var catalogs: [OPDSCatalog] = [] {
9+
@Observable
10+
final class OPDSCatalogsViewModel {
11+
var catalogs: [OPDSCatalog] = [] {
1112
didSet {
1213
UserDefaults.standard.set(
1314
catalogs.map(\.toDictionary),
@@ -16,7 +17,7 @@ final class OPDSCatalogsViewModel: ObservableObject {
1617
}
1718
}
1819

19-
@Published var editingCatalog: OPDSCatalog?
20+
var editingCatalog: OPDSCatalog?
2021

2122
var openCatalog: ((URL, IndexPath) -> Void)?
2223

TestApp/Sources/OPDS/OPDSFacetViewController.swift

-77
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
//
2+
// Copyright 2024 Readium Foundation. All rights reserved.
3+
// Use of this source code is governed by the BSD-style license
4+
// available in the top-level LICENSE file of the project.
5+
//
6+
7+
import Foundation
8+
import ReadiumOPDS
9+
import ReadiumShared
10+
11+
extension Feed {
12+
static var preview: Feed {
13+
try! OPDS2Parser.parse(
14+
jsonData: .preview,
15+
url: URL(string: "http://opds-spec.org/opds.json")!,
16+
response: URLResponse()
17+
).feed!
18+
}
19+
}
20+
21+
private extension Data {
22+
static var preview: Data {
23+
let jsonString = """
24+
{
25+
"@context": "http://opds-spec.org/opds.json",
26+
"metadata": {
27+
"title": "Example Library",
28+
"modified": "2024-11-05T12:00:00Z",
29+
"numberOfItems": 5000,
30+
"itemsPerPage": 30
31+
},
32+
"links": [
33+
{
34+
"rel": "self",
35+
"href": "/opds",
36+
"type": "application/opds+json"
37+
}
38+
],
39+
"facets": [
40+
{
41+
"metadata": {
42+
"title": "Genre"
43+
},
44+
"links": [
45+
{
46+
"rel": "http://opds-spec.org/facet",
47+
"href": "/opds/books/new?genre=fiction",
48+
"title": "Fiction",
49+
"type": "application/opds+json",
50+
"properties": {
51+
"numberOfItems": 1250
52+
}
53+
},
54+
{
55+
"rel": "http://opds-spec.org/facet",
56+
"href": "/opds/books/new?genre=mystery",
57+
"title": "Mystery & Detective",
58+
"type": "application/opds+json",
59+
"properties": {
60+
"numberOfItems": 850
61+
}
62+
},
63+
{
64+
"rel": "http://opds-spec.org/facet",
65+
"href": "/opds/books/new?genre=scifi",
66+
"title": "Science Fiction",
67+
"type": "application/opds+json",
68+
"properties": {
69+
"numberOfItems": 725
70+
}
71+
},
72+
{
73+
"rel": "http://opds-spec.org/facet",
74+
"href": "/opds/books/new?genre=non-fiction",
75+
"title": "Non-Fiction",
76+
"type": "application/opds+json",
77+
"properties": {
78+
"numberOfItems": 2175
79+
}
80+
}
81+
]
82+
},
83+
{
84+
"metadata": {
85+
"title": "Language"
86+
},
87+
"links": [
88+
{
89+
"rel": "http://opds-spec.org/facet",
90+
"href": "/opds/books/new?language=en",
91+
"title": "English",
92+
"type": "application/opds+json",
93+
"properties": {
94+
"numberOfItems": 3000
95+
}
96+
},
97+
{
98+
"rel": "http://opds-spec.org/facet",
99+
"href": "/opds/books/new?language=es",
100+
"title": "Spanish",
101+
"type": "application/opds+json",
102+
"properties": {
103+
"numberOfItems": 1000
104+
}
105+
},
106+
{
107+
"rel": "http://opds-spec.org/facet",
108+
"href": "/opds/books/new?language=ru",
109+
"title": "Russian",
110+
"type": "application/opds+json",
111+
"properties": {
112+
"numberOfItems": 800
113+
}
114+
}
115+
]
116+
},
117+
{
118+
"metadata": {
119+
"title": "Availability"
120+
},
121+
"links": [
122+
{
123+
"rel": "http://opds-spec.org/facet",
124+
"href": "/opds/books/new?availability=free",
125+
"title": "Free",
126+
"type": "application/opds+json",
127+
"properties": {
128+
"numberOfItems": 1500
129+
}
130+
},
131+
{
132+
"rel": "http://opds-spec.org/facet",
133+
"href": "/opds/books/new?availability=subscription",
134+
"title": "Subscription",
135+
"type": "application/opds+json",
136+
"properties": {
137+
"numberOfItems": 2500
138+
}
139+
},
140+
{
141+
"rel": "http://opds-spec.org/facet",
142+
"href": "/opds/books/new?availability=buy",
143+
"title": "Purchase Required",
144+
"type": "application/opds+json",
145+
"properties": {
146+
"numberOfItems": 1000
147+
}
148+
}
149+
]
150+
},
151+
{
152+
"metadata": {
153+
"title": "Reading Age"
154+
},
155+
"links": [
156+
{
157+
"rel": "http://opds-spec.org/facet",
158+
"href": "/opds/books/new?age=children",
159+
"title": "Children (0-11)",
160+
"type": "application/opds+json",
161+
"properties": {
162+
"numberOfItems": 800
163+
}
164+
},
165+
{
166+
"rel": "http://opds-spec.org/facet",
167+
"href": "/opds/books/new?age=teen",
168+
"title": "Teen (12-18)",
169+
"type": "application/opds+json",
170+
"properties": {
171+
"numberOfItems": 1200
172+
}
173+
},
174+
{
175+
"rel": "http://opds-spec.org/facet",
176+
"href": "/opds/books/new?age=adult",
177+
"title": "Adult (18+)",
178+
"type": "application/opds+json",
179+
"properties": {
180+
"numberOfItems": 3000
181+
}
182+
}
183+
]
184+
}
185+
],
186+
"publications": [
187+
{
188+
"metadata": {
189+
"title": "Sample Book",
190+
"identifier": "urn:uuid:6409a00b-7bf2-405e-826c-3fdff0fd0734",
191+
"modified": "2024-11-05T12:00:00Z",
192+
"language": ["en"],
193+
"published": "2024",
194+
"author": [
195+
{
196+
"name": "Sample Author"
197+
}
198+
],
199+
"subject": [
200+
{
201+
"name": "Fiction",
202+
"code": "fiction"
203+
}
204+
]
205+
},
206+
"links": [
207+
{
208+
"rel": "http://opds-spec.org/acquisition",
209+
"href": "/books/sample.epub",
210+
"type": "application/epub+zip"
211+
}
212+
]
213+
}
214+
]
215+
}
216+
"""
217+
guard let data = jsonString.data(using: .utf8) else {
218+
return Data()
219+
}
220+
return data
221+
}
222+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
//
2+
// Copyright 2024 Readium Foundation. All rights reserved.
3+
// Use of this source code is governed by the BSD-style license
4+
// available in the top-level LICENSE file of the project.
5+
//
6+
7+
import ReadiumShared
8+
import SwiftUI
9+
10+
struct OPDSFacetLink: View {
11+
let link: ReadiumShared.Link
12+
13+
var body: some View {
14+
HStack {
15+
if let title = link.title {
16+
Text(title)
17+
.foregroundStyle(Color.primary)
18+
}
19+
20+
Spacer()
21+
22+
if let count = link.properties.numberOfItems {
23+
Text("\(count)")
24+
.foregroundStyle(Color.secondary)
25+
.font(.subheadline)
26+
}
27+
28+
Image(systemName: "chevron.right")
29+
}
30+
.font(.body)
31+
}
32+
}
33+
34+
#Preview {
35+
OPDSFacetLink(
36+
link: Feed.preview.facets[0].links[0]
37+
)
38+
.padding()
39+
}

0 commit comments

Comments
 (0)