Implemented phase 1 for the POC prototype in the Website service.
This commit is contained in:
1 parent
6d7ab4bcba
commit
ea89ea892a
37 files changed
+4173
-43
No files matched your search
@@ -0,0 +1,14 @@
|
||||
import Foundation
|
||||
|
||||
extension Int {
|
||||
|
||||
// MARK: Computed
|
||||
|
||||
/// The amount of euro cents as a price: whole euros bare (`€240`), anything else to the cent (`€12.50`).
|
||||
var euros: String {
|
||||
isMultiple(of: 100)
|
||||
? "€\(self / 100)"
|
||||
: String(format: "€%d.%02d", self / 100, self % 100)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
import Foundation
|
||||
import Persistence
|
||||
|
||||
extension Listing {
|
||||
|
||||
// MARK: Computed
|
||||
|
||||
/// The chip stating the copy's grade or condition.
|
||||
///
|
||||
/// A slab reads `CGC 9.8` — good from 9.0 up, a warning below 7.0 — a raw issue `Raw · NM`, and a collected edition its condition.
|
||||
var chip: (label: String, tone: Chip.Tone, isCertified: Bool) {
|
||||
guard let grader, let grade else {
|
||||
return (
|
||||
kind == .singleIssues ? "Raw · \(condition)" : condition,
|
||||
.neutral,
|
||||
false
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
"\(grader) \(grade.gradeLabel)",
|
||||
grade >= 9 ? .good : grade < 7 ? .warn : .neutral,
|
||||
true
|
||||
)
|
||||
}
|
||||
|
||||
/// The format the listing is published in, or `nil` when its token names none.
|
||||
var kind: Format? {
|
||||
.init(rawValue: format)
|
||||
}
|
||||
|
||||
/// The path of the listing's detail screen.
|
||||
var path: String {
|
||||
.Route.listing(slug)
|
||||
}
|
||||
|
||||
/// The line under a card's title: the publisher, year, and printing, led by the format for a collected edition.
|
||||
var summary: String {
|
||||
let lead = kind == .singleIssues ? nil : kind?.singular
|
||||
|
||||
return [lead, publisher, String(year), printing]
|
||||
.compactMap(\.self)
|
||||
.joined(separator: " · ")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension Shop {
|
||||
|
||||
// MARK: Computed
|
||||
|
||||
/// The shop's location, e.g. `Amsterdam, NL`.
|
||||
var location: String {
|
||||
"\(city), \(country)"
|
||||
}
|
||||
|
||||
/// The uppercased initials of the first two words of the shop's name that start with a letter.
|
||||
var monogram: String {
|
||||
name
|
||||
.split(separator: " ")
|
||||
.filter { $0.first?.isLetter == true }
|
||||
.prefix(2)
|
||||
.compactMap(\.first)
|
||||
.map { String($0).uppercased() }
|
||||
.joined()
|
||||
}
|
||||
|
||||
/// The path of the shop's public screen.
|
||||
var path: String {
|
||||
.Route.shop(slug)
|
||||
}
|
||||
|
||||
/// The shop's standing in a line: where it is, since when, and how it is rated.
|
||||
var standing: String {
|
||||
"\(location) · selling on CCN since \(sinceYear) · \(String(format: "%.1f", rating)) from \(ratedSales) rated sales"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension Double {
|
||||
|
||||
// MARK: Computed
|
||||
|
||||
/// The grade to one decimal place, e.g. `9.8`.
|
||||
var gradeLabel: String {
|
||||
String(format: "%.1f", self)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
import Foundation
|
||||
|
||||
extension String {
|
||||
/// A namespace for the paths of the site's screens.
|
||||
///
|
||||
@@ -35,5 +37,36 @@ extension String {
|
||||
static let terms = "/terms"
|
||||
/// The want list.
|
||||
static let wants = "/wants"
|
||||
|
||||
/// The path of a listing's detail screen.
|
||||
/// - Parameter slug: the listing's URL slug.
|
||||
static func listing(
|
||||
_ slug: String
|
||||
) -> String {
|
||||
"/listing/\(slug)"
|
||||
}
|
||||
|
||||
/// The path of a shop's public screen.
|
||||
/// - Parameter slug: the shop's URL slug.
|
||||
static func shop(
|
||||
_ slug: String
|
||||
) -> String {
|
||||
"/shop/\(slug)"
|
||||
}
|
||||
|
||||
/// The path of the sign-in screen, returning to the given path afterwards.
|
||||
/// - Parameter path: the path the visitor's gated action started from.
|
||||
static func signIn(
|
||||
returningTo path: String
|
||||
) -> String {
|
||||
"\(signIn)?return=\(path.addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed) ?? "")"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
private extension CharacterSet {
|
||||
/// The characters a query value carries unescaped.
|
||||
static let urlQueryValueAllowed = CharacterSet.urlQueryAllowed.subtracting(.init(charactersIn: "&=+?#"))
|
||||
}
|
||||
Reference in new issue
Block a user