mirror of
https://github.com/Dimillian/IceCubesApp.git
synced 2025-02-08 23:48:48 +01:00
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")!,
|
header: URL(string: "https://files.mastodon.social/media_attachments/files/003/134/405/original/04060b07ddf7bb0b.png")!,
|
||||||
acct: "johnm@example.com",
|
acct: "johnm@example.com",
|
||||||
note: .init(stringValue: "Some content"),
|
note: .init(stringValue: "Some content"),
|
||||||
createdAt: "2022-12-16T10:20:54.000Z",
|
createdAt: ServerDate(),
|
||||||
followersCount: 10,
|
followersCount: 10,
|
||||||
followingCount: 10,
|
followingCount: 10,
|
||||||
statusesCount: 10,
|
statusesCount: 10,
|
||||||
|
@ -1,13 +1,16 @@
|
|||||||
import Foundation
|
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 static var sampleDate: ServerDate {
|
public let relativeFormatted: String
|
||||||
createdAtDateFormatter.string(from: Date()-100)
|
public let shortDateFormatted: String
|
||||||
}
|
|
||||||
|
|
||||||
|
private static let calendar = Calendar(identifier: .gregorian)
|
||||||
|
|
||||||
private static var createdAtDateFormatter: DateFormatter = {
|
private static var createdAtDateFormatter: DateFormatter = {
|
||||||
let dateFormatter = DateFormatter()
|
let dateFormatter = DateFormatter()
|
||||||
dateFormatter.calendar = .init(identifier: .iso8601)
|
dateFormatter.calendar = .init(identifier: .iso8601)
|
||||||
@ -15,31 +18,47 @@ extension ServerDate {
|
|||||||
dateFormatter.timeZone = .init(abbreviation: "UTC")
|
dateFormatter.timeZone = .init(abbreviation: "UTC")
|
||||||
return dateFormatter
|
return dateFormatter
|
||||||
}()
|
}()
|
||||||
|
|
||||||
private static var createdAtRelativeFormatter: RelativeDateTimeFormatter = {
|
private static var createdAtRelativeFormatter: RelativeDateTimeFormatter = {
|
||||||
let dateFormatter = RelativeDateTimeFormatter()
|
let dateFormatter = RelativeDateTimeFormatter()
|
||||||
dateFormatter.unitsStyle = .short
|
dateFormatter.unitsStyle = .short
|
||||||
return dateFormatter
|
return dateFormatter
|
||||||
}()
|
}()
|
||||||
|
|
||||||
private static var createdAtShortDateFormatted: DateFormatter = {
|
private static var createdAtShortDateFormatted: DateFormatter = {
|
||||||
let dateFormatter = DateFormatter()
|
let dateFormatter = DateFormatter()
|
||||||
dateFormatter.dateStyle = .short
|
dateFormatter.dateStyle = .short
|
||||||
dateFormatter.timeStyle = .none
|
dateFormatter.timeStyle = .none
|
||||||
return dateFormatter
|
return dateFormatter
|
||||||
}()
|
}()
|
||||||
|
|
||||||
private static let calendar = Calendar(identifier: .gregorian)
|
public init() {
|
||||||
|
asDate = Date()-100
|
||||||
public var asDate: Date {
|
relativeFormatted = Self.createdAtRelativeFormatter.localizedString(for: asDate, relativeTo: Date())
|
||||||
Self.createdAtDateFormatter.date(from: self)!
|
shortDateFormatted = Self.createdAtShortDateFormatted.string(from: asDate)
|
||||||
}
|
}
|
||||||
|
|
||||||
public var relativeFormatted: String {
|
public init(from decoder: Decoder) throws {
|
||||||
return Self.createdAtRelativeFormatter.localizedString(for: asDate, relativeTo: Date())
|
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 {
|
public func encode(to encoder: Encoder) throws {
|
||||||
return Self.createdAtShortDateFormatted.string(from: asDate)
|
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 {
|
public static func placeholder() -> ConsolidatedNotification {
|
||||||
.init(notifications: [Notification.placeholder()],
|
.init(notifications: [Notification.placeholder()],
|
||||||
type: .favourite,
|
type: .favourite,
|
||||||
createdAt: "2022-12-16T10:20:54.000Z",
|
createdAt: ServerDate(),
|
||||||
accounts: [.placeholder()],
|
accounts: [.placeholder()],
|
||||||
status: .placeholder())
|
status: .placeholder())
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ public struct Notification: Decodable, Identifiable {
|
|||||||
public static func placeholder() -> Notification {
|
public static func placeholder() -> Notification {
|
||||||
.init(id: UUID().uuidString,
|
.init(id: UUID().uuidString,
|
||||||
type: NotificationType.favourite.rawValue,
|
type: NotificationType.favourite.rawValue,
|
||||||
createdAt: "2022-12-16T10:20:54.000Z",
|
createdAt: ServerDate(),
|
||||||
account: .placeholder(),
|
account: .placeholder(),
|
||||||
status: .placeholder())
|
status: .placeholder())
|
||||||
}
|
}
|
||||||
|
@ -30,12 +30,12 @@ public struct Poll: Codable, Equatable, Hashable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public struct NullableString: Codable, Equatable, Hashable {
|
public struct NullableString: Codable, Equatable, Hashable {
|
||||||
public let value: String?
|
public let value: ServerDate?
|
||||||
|
|
||||||
public init(from decoder: Decoder) throws {
|
public init(from decoder: Decoder) throws {
|
||||||
do {
|
do {
|
||||||
let container = try decoder.singleValueContainer()
|
let container = try decoder.singleValueContainer()
|
||||||
value = try container.decode(String.self)
|
value = try container.decode(ServerDate.self)
|
||||||
} catch {
|
} catch {
|
||||||
value = nil
|
value = nil
|
||||||
}
|
}
|
||||||
|
@ -54,15 +54,19 @@ public protocol AnyStatus {
|
|||||||
var language: String? { get }
|
var language: String? { get }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension AnyStatus {
|
||||||
|
public var viewId: String {
|
||||||
|
get {
|
||||||
|
"\(id)\(createdAt.asDate.description)\(editedAt?.asDate.description ?? "")"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protocol StatusUI {
|
protocol StatusUI {
|
||||||
var userMentioned: Bool? { get set }
|
var userMentioned: Bool? { get set }
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct Status: AnyStatus, Codable, Identifiable, Equatable, Hashable, StatusUI {
|
public struct Status: AnyStatus, Codable, Identifiable, Equatable, Hashable, StatusUI {
|
||||||
public var viewId: String {
|
|
||||||
id + createdAt + (editedAt ?? "")
|
|
||||||
}
|
|
||||||
|
|
||||||
public var userMentioned: Bool?
|
public var userMentioned: Bool?
|
||||||
|
|
||||||
public static func == (lhs: Status, rhs: Status) -> 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),
|
content: .init(stringValue: "Lorem ipsum [#dolor](#) sit amet\nconsectetur [@adipiscing](#) elit\nAsed do eiusmod tempor incididunt ut labore.", parseMarkdown: forSettings),
|
||||||
|
|
||||||
account: .placeholder(),
|
account: .placeholder(),
|
||||||
createdAt: ServerDate.sampleDate,
|
createdAt: ServerDate(),
|
||||||
editedAt: nil,
|
editedAt: nil,
|
||||||
reblog: nil,
|
reblog: nil,
|
||||||
mediaAttachments: [],
|
mediaAttachments: [],
|
||||||
@ -137,10 +141,6 @@ public struct Status: AnyStatus, Codable, Identifiable, Equatable, Hashable, Sta
|
|||||||
}
|
}
|
||||||
|
|
||||||
public struct ReblogStatus: AnyStatus, Codable, Identifiable, Equatable, Hashable {
|
public struct ReblogStatus: AnyStatus, Codable, Identifiable, Equatable, Hashable {
|
||||||
public var viewId: String {
|
|
||||||
id + createdAt + (editedAt ?? "")
|
|
||||||
}
|
|
||||||
|
|
||||||
public static func == (lhs: ReblogStatus, rhs: ReblogStatus) -> Bool {
|
public static func == (lhs: ReblogStatus, rhs: ReblogStatus) -> Bool {
|
||||||
lhs.id == rhs.id
|
lhs.id == rhs.id
|
||||||
}
|
}
|
||||||
@ -152,7 +152,7 @@ public struct ReblogStatus: AnyStatus, Codable, Identifiable, Equatable, Hashabl
|
|||||||
public let id: String
|
public let id: String
|
||||||
public let content: HTMLString
|
public let content: HTMLString
|
||||||
public let account: Account
|
public let account: Account
|
||||||
public let createdAt: String
|
public let createdAt: ServerDate
|
||||||
public let editedAt: ServerDate?
|
public let editedAt: ServerDate?
|
||||||
public let mediaAttachments: [MediaAttachment]
|
public let mediaAttachments: [MediaAttachment]
|
||||||
public let mentions: [Mention]
|
public let mentions: [Mention]
|
||||||
|
@ -2,7 +2,7 @@ import Foundation
|
|||||||
|
|
||||||
public struct StatusHistory: Decodable, Identifiable {
|
public struct StatusHistory: Decodable, Identifiable {
|
||||||
public var id: String {
|
public var id: String {
|
||||||
createdAt.description
|
createdAt.asDate.description
|
||||||
}
|
}
|
||||||
|
|
||||||
public let content: HTMLString
|
public let content: HTMLString
|
||||||
|
@ -23,7 +23,7 @@ extension Array where Element == Notification {
|
|||||||
status: notification.status)
|
status: notification.status)
|
||||||
}
|
}
|
||||||
.sorted {
|
.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)
|
.accessibilityElement(children: viewModel.isFocused ? .contain : .combine)
|
||||||
.accessibilityActions {
|
.accessibilityActions {
|
||||||
// Add the individual mentions as accessibility actions
|
accesibilityActions
|
||||||
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
|
|
||||||
}
|
}
|
||||||
.background {
|
.background {
|
||||||
Color.clear
|
Color.clear
|
||||||
@ -144,6 +127,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 {
|
private func makeFilterView(filter: Filter) -> some View {
|
||||||
HStack {
|
HStack {
|
||||||
|
@ -45,7 +45,7 @@ public actor TimelineCache {
|
|||||||
return try await engine
|
return try await engine
|
||||||
.readAllData()
|
.readAllData()
|
||||||
.map { try decoder.decode(Status.self, from: $0) }
|
.map { try decoder.decode(Status.self, from: $0) }
|
||||||
.sorted(by: { $0.createdAt > $1.createdAt })
|
.sorted(by: { $0.createdAt.asDate > $1.createdAt.asDate })
|
||||||
} catch {
|
} catch {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user