Faster / Cached ServerDate decoding
This commit is contained in:
parent
76c0c843cd
commit
013410a80e
|
@ -59,7 +59,7 @@ public struct Account: Codable, Identifiable, Equatable, Hashable {
|
|||
header: URL(string: "https://files.mastodon.social/media_attachments/files/003/134/405/original/04060b07ddf7bb0b.png")!,
|
||||
acct: "johnm@example.com",
|
||||
note: .init(stringValue: "Some content"),
|
||||
createdAt: "2022-12-16T10:20:54.000Z",
|
||||
createdAt: ServerDate(),
|
||||
followersCount: 10,
|
||||
followingCount: 10,
|
||||
statusesCount: 10,
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
import Foundation
|
||||
|
||||
public typealias ServerDate = String
|
||||
fileprivate enum CodingKeys: CodingKey {
|
||||
case asDate, relativeFormatted, shortDateFormatted
|
||||
}
|
||||
|
||||
extension ServerDate {
|
||||
public struct ServerDate: Codable, Hashable, Equatable {
|
||||
public let asDate: Date
|
||||
public let relativeFormatted: String
|
||||
public let shortDateFormatted: String
|
||||
|
||||
public static var sampleDate: ServerDate {
|
||||
createdAtDateFormatter.string(from: Date()-100)
|
||||
}
|
||||
private static let calendar = Calendar(identifier: .gregorian)
|
||||
|
||||
private static var createdAtDateFormatter: DateFormatter = {
|
||||
let dateFormatter = DateFormatter()
|
||||
|
@ -29,17 +32,33 @@ extension ServerDate {
|
|||
return dateFormatter
|
||||
}()
|
||||
|
||||
private static let calendar = Calendar(identifier: .gregorian)
|
||||
|
||||
public var asDate: Date {
|
||||
Self.createdAtDateFormatter.date(from: self)!
|
||||
public init() {
|
||||
asDate = Date()-100
|
||||
relativeFormatted = Self.createdAtRelativeFormatter.localizedString(for: asDate, relativeTo: Date())
|
||||
shortDateFormatted = Self.createdAtShortDateFormatted.string(from: asDate)
|
||||
}
|
||||
|
||||
public var relativeFormatted: String {
|
||||
return Self.createdAtRelativeFormatter.localizedString(for: asDate, relativeTo: Date())
|
||||
public init(from decoder: Decoder) throws {
|
||||
do {
|
||||
// Decode from server
|
||||
let container = try decoder.singleValueContainer()
|
||||
let stringDate = try container.decode(String.self)
|
||||
asDate = Self.createdAtDateFormatter.date(from: stringDate)!
|
||||
relativeFormatted = Self.createdAtRelativeFormatter.localizedString(for: asDate, relativeTo: Date())
|
||||
shortDateFormatted = Self.createdAtShortDateFormatted.string(from: asDate)
|
||||
} catch {
|
||||
// Decode from cache
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
asDate = try container.decode(Date.self, forKey: .asDate)
|
||||
relativeFormatted = try container.decode(String.self, forKey: .relativeFormatted)
|
||||
shortDateFormatted = try container.decode(String.self, forKey: .shortDateFormatted)
|
||||
}
|
||||
}
|
||||
|
||||
public var shortDateFormatted: String {
|
||||
return Self.createdAtShortDateFormatted.string(from: asDate)
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(asDate, forKey: .asDate)
|
||||
try container.encode(relativeFormatted, forKey: .relativeFormatted)
|
||||
try container.encode(shortDateFormatted, forKey: .shortDateFormatted)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ public struct ConsolidatedNotification: Identifiable {
|
|||
public static func placeholder() -> ConsolidatedNotification {
|
||||
.init(notifications: [Notification.placeholder()],
|
||||
type: .favourite,
|
||||
createdAt: "2022-12-16T10:20:54.000Z",
|
||||
createdAt: ServerDate(),
|
||||
accounts: [.placeholder()],
|
||||
status: .placeholder())
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ public struct Notification: Decodable, Identifiable {
|
|||
public static func placeholder() -> Notification {
|
||||
.init(id: UUID().uuidString,
|
||||
type: NotificationType.favourite.rawValue,
|
||||
createdAt: "2022-12-16T10:20:54.000Z",
|
||||
createdAt: ServerDate(),
|
||||
account: .placeholder(),
|
||||
status: .placeholder())
|
||||
}
|
||||
|
|
|
@ -30,12 +30,12 @@ public struct Poll: Codable, Equatable, Hashable {
|
|||
}
|
||||
|
||||
public struct NullableString: Codable, Equatable, Hashable {
|
||||
public let value: String?
|
||||
public let value: ServerDate?
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
do {
|
||||
let container = try decoder.singleValueContainer()
|
||||
value = try container.decode(String.self)
|
||||
value = try container.decode(ServerDate.self)
|
||||
} catch {
|
||||
value = nil
|
||||
}
|
||||
|
|
|
@ -54,15 +54,19 @@ public protocol AnyStatus {
|
|||
var language: String? { get }
|
||||
}
|
||||
|
||||
extension AnyStatus {
|
||||
public var viewId: String {
|
||||
get {
|
||||
"\(id)\(createdAt.asDate.description)\(editedAt?.asDate.description ?? "")"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protocol StatusUI {
|
||||
var userMentioned: Bool? { get set }
|
||||
}
|
||||
|
||||
public struct Status: AnyStatus, Codable, Identifiable, Equatable, Hashable, StatusUI {
|
||||
public var viewId: String {
|
||||
id + createdAt + (editedAt ?? "")
|
||||
}
|
||||
|
||||
public var userMentioned: Bool?
|
||||
|
||||
public static func == (lhs: Status, rhs: Status) -> Bool {
|
||||
|
@ -105,7 +109,7 @@ public struct Status: AnyStatus, Codable, Identifiable, Equatable, Hashable, Sta
|
|||
content: .init(stringValue: "Lorem ipsum [#dolor](#) sit amet\nconsectetur [@adipiscing](#) elit\nAsed do eiusmod tempor incididunt ut labore.", parseMarkdown: forSettings),
|
||||
|
||||
account: .placeholder(),
|
||||
createdAt: ServerDate.sampleDate,
|
||||
createdAt: ServerDate(),
|
||||
editedAt: nil,
|
||||
reblog: nil,
|
||||
mediaAttachments: [],
|
||||
|
@ -137,10 +141,6 @@ public struct Status: AnyStatus, Codable, Identifiable, Equatable, Hashable, Sta
|
|||
}
|
||||
|
||||
public struct ReblogStatus: AnyStatus, Codable, Identifiable, Equatable, Hashable {
|
||||
public var viewId: String {
|
||||
id + createdAt + (editedAt ?? "")
|
||||
}
|
||||
|
||||
public static func == (lhs: ReblogStatus, rhs: ReblogStatus) -> Bool {
|
||||
lhs.id == rhs.id
|
||||
}
|
||||
|
@ -152,7 +152,7 @@ public struct ReblogStatus: AnyStatus, Codable, Identifiable, Equatable, Hashabl
|
|||
public let id: String
|
||||
public let content: HTMLString
|
||||
public let account: Account
|
||||
public let createdAt: String
|
||||
public let createdAt: ServerDate
|
||||
public let editedAt: ServerDate?
|
||||
public let mediaAttachments: [MediaAttachment]
|
||||
public let mentions: [Mention]
|
||||
|
|
|
@ -2,7 +2,7 @@ import Foundation
|
|||
|
||||
public struct StatusHistory: Decodable, Identifiable {
|
||||
public var id: String {
|
||||
createdAt.description
|
||||
createdAt.asDate.description
|
||||
}
|
||||
|
||||
public let content: HTMLString
|
||||
|
|
|
@ -23,7 +23,7 @@ extension Array where Element == Notification {
|
|||
status: notification.status)
|
||||
}
|
||||
.sorted {
|
||||
$0.createdAt > $1.createdAt
|
||||
$0.createdAt.asDate > $1.createdAt.asDate
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -96,24 +96,7 @@ public struct StatusRowView: View {
|
|||
}
|
||||
.accessibilityElement(children: viewModel.isFocused ? .contain : .combine)
|
||||
.accessibilityActions {
|
||||
// Add the individual mentions as accessibility actions
|
||||
ForEach(viewModel.status.mentions, id: \.id) { mention in
|
||||
Button("@\(mention.username)") {
|
||||
routerPath.navigate(to: .accountDetail(id: mention.id))
|
||||
}
|
||||
}
|
||||
|
||||
Button(viewModel.displaySpoiler ? "status.show-more" : "status.show-less") {
|
||||
withAnimation {
|
||||
viewModel.displaySpoiler.toggle()
|
||||
}
|
||||
}
|
||||
|
||||
Button("@\(viewModel.status.account.username)") {
|
||||
routerPath.navigate(to: .accountDetail(id: viewModel.status.account.id))
|
||||
}
|
||||
|
||||
contextMenu
|
||||
accesibilityActions
|
||||
}
|
||||
.background {
|
||||
Color.clear
|
||||
|
@ -145,6 +128,28 @@ public struct StatusRowView: View {
|
|||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private var accesibilityActions: some View {
|
||||
// Add the individual mentions as accessibility actions
|
||||
ForEach(viewModel.status.mentions, id: \.id) { mention in
|
||||
Button("@\(mention.username)") {
|
||||
routerPath.navigate(to: .accountDetail(id: mention.id))
|
||||
}
|
||||
}
|
||||
|
||||
Button(viewModel.displaySpoiler ? "status.show-more" : "status.show-less") {
|
||||
withAnimation {
|
||||
viewModel.displaySpoiler.toggle()
|
||||
}
|
||||
}
|
||||
|
||||
Button("@\(viewModel.status.account.username)") {
|
||||
routerPath.navigate(to: .accountDetail(id: viewModel.status.account.id))
|
||||
}
|
||||
|
||||
contextMenu
|
||||
}
|
||||
|
||||
private func makeFilterView(filter: Filter) -> some View {
|
||||
HStack {
|
||||
Text("status.filter.filtered-by-\(filter.title)")
|
||||
|
|
|
@ -45,7 +45,7 @@ public actor TimelineCache {
|
|||
return try await engine
|
||||
.readAllData()
|
||||
.map { try decoder.decode(Status.self, from: $0) }
|
||||
.sorted(by: { $0.createdAt > $1.createdAt })
|
||||
.sorted(by: { $0.createdAt.asDate > $1.createdAt.asDate })
|
||||
} catch {
|
||||
return nil
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue