Implemented phase 3 for the POC prototype in the Website service.
This commit is contained in:
1 parent
b1ceed7790
commit
0dc3fe6bea
29 files changed
+4089
-66
No files matched your search
@@ -0,0 +1,73 @@
|
||||
import Foundation
|
||||
|
||||
extension Date {
|
||||
|
||||
// MARK: Methods
|
||||
|
||||
/// How long until the date, in a few words: `47 h left`, `2 days left`, `under an hour left`.
|
||||
/// - Parameter now: the moment counted from.
|
||||
func countdown(
|
||||
from now: Date
|
||||
) -> String {
|
||||
let hours = Int(timeIntervalSince(now) / 3_600)
|
||||
|
||||
switch hours {
|
||||
case ..<1: return "under an hour left"
|
||||
case ..<48: return "\(hours) h left"
|
||||
default: return "\(hours / 24) days left"
|
||||
}
|
||||
}
|
||||
|
||||
/// The date as a stamp on a record: `today, 09:14`, `yesterday, 21:38`, `20 Sep, 14:05`, or with the year once it differs.
|
||||
///
|
||||
/// Read on Amsterdam's clock: the marketplace is NL-first.
|
||||
/// - Parameter now: the moment "today" is judged from.
|
||||
func stamp(
|
||||
relativeTo now: Date
|
||||
) -> String {
|
||||
let time = formatted(Self.style.hour(.twoDigits(amPM: .omitted)).minute(.twoDigits))
|
||||
|
||||
if Self.calendar.isDate(self, inSameDayAs: now) {
|
||||
return "today, \(time)"
|
||||
}
|
||||
|
||||
if
|
||||
let yesterday = Self.calendar.date(byAdding: .day, value: -1, to: now),
|
||||
Self.calendar.isDate(self, inSameDayAs: yesterday)
|
||||
{
|
||||
return "yesterday, \(time)"
|
||||
}
|
||||
|
||||
let isSameYear = Self.calendar.isDate(self, equalTo: now, toGranularity: .year)
|
||||
let day = formatted(isSameYear ? Self.style.day().month(.abbreviated) : Self.style.day().month(.abbreviated).year())
|
||||
|
||||
return "\(day), \(time)"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - Helpers
|
||||
|
||||
private extension Date {
|
||||
|
||||
// MARK: Constants
|
||||
|
||||
/// The calendar days are told apart by, on Amsterdam's clock.
|
||||
static let calendar: Calendar = {
|
||||
var calendar = Calendar(identifier: .gregorian)
|
||||
|
||||
calendar.timeZone = timeZone
|
||||
|
||||
return calendar
|
||||
}()
|
||||
|
||||
/// The base format style: British English, on Amsterdam's clock.
|
||||
static let style = Date.FormatStyle(
|
||||
locale: .init(identifier: "en_GB"),
|
||||
calendar: calendar,
|
||||
timeZone: timeZone
|
||||
)
|
||||
|
||||
static let timeZone = TimeZone(identifier: "Europe/Amsterdam") ?? .gmt
|
||||
|
||||
}
|
||||
@@ -40,6 +40,14 @@ extension String {
|
||||
/// The want list.
|
||||
static let wants = "/wants"
|
||||
|
||||
/// The path an offer is answered at.
|
||||
/// - Parameter id: the offer's identifier.
|
||||
static func answer(
|
||||
offer id: UUID
|
||||
) -> String {
|
||||
"\(sellerDashboard)/offers/\(id.uuidString.lowercased())"
|
||||
}
|
||||
|
||||
/// The path buying a listing outright starts at.
|
||||
/// - Parameter slug: the listing's URL slug.
|
||||
static func buy(
|
||||
@@ -48,6 +56,14 @@ extension String {
|
||||
"\(listing(slug))/buy"
|
||||
}
|
||||
|
||||
/// The path of a conversation.
|
||||
/// - Parameter id: the conversation's identifier.
|
||||
static func conversation(
|
||||
_ id: UUID
|
||||
) -> String {
|
||||
"\(messages)/\(id.uuidString.lowercased())"
|
||||
}
|
||||
|
||||
/// The path a gated action leads to: the action itself when signed in, else the sign-in screen, returning to where the action was taken.
|
||||
/// - Parameters:
|
||||
/// - path: the action's own path.
|
||||
@@ -93,6 +109,14 @@ extension String {
|
||||
"\(listing(slug))/offer"
|
||||
}
|
||||
|
||||
/// The path of a sale record.
|
||||
/// - Parameter number: the record's number.
|
||||
static func sale(
|
||||
_ number: Int
|
||||
) -> String {
|
||||
"/sales/\(number)"
|
||||
}
|
||||
|
||||
/// The path of a shop's public screen.
|
||||
/// - Parameter slug: the shop's URL slug.
|
||||
static func shop(
|
||||
|
||||
Reference in new issue
Block a user