diff --git a/Mail/Components/ThreadCell/ThreadCell.swift b/Mail/Components/ThreadCell/ThreadCell.swift index 5f71c2d17..9ac491bdf 100644 --- a/Mail/Components/ThreadCell/ThreadCell.swift +++ b/Mail/Components/ThreadCell/ThreadCell.swift @@ -49,7 +49,7 @@ struct ThreadCellDataHolder { init(thread: Thread) { let lastMessageNotFromSent = thread.messages.last { $0.folder?.role != .sent } ?? thread.messages.last - date = thread.date.customRelativeFormatted + date = thread.date.formatted(.thread(.list)) subject = thread.formattedSubject diff --git a/Mail/Views/Thread/Message/MessageHeader/MessageHeaderDateView.swift b/Mail/Views/Thread/Message/MessageHeader/MessageHeaderDateView.swift index fb948fdef..c5ac21773 100644 --- a/Mail/Views/Thread/Message/MessageHeader/MessageHeaderDateView.swift +++ b/Mail/Views/Thread/Message/MessageHeader/MessageHeaderDateView.swift @@ -22,7 +22,7 @@ struct MessageHeaderDateView: View { let date: Date var body: some View { - Text(date.messageHeaderRelativeFormatted) + Text(date, format: .thread(.header)) .lineLimit(1) .layoutPriority(1) .textStyle(.labelSecondary) diff --git a/MailCore/Utils/Formatters.swift b/MailCore/Utils/Formatters.swift index 56cc8c0ff..d4426d489 100644 --- a/MailCore/Utils/Formatters.swift +++ b/MailCore/Utils/Formatters.swift @@ -18,47 +18,66 @@ import Foundation +// MARK: Date + public extension Date { - var customRelativeFormatted: String { - if self > .now { - return formatted(date: .numeric, time: .omitted) - } else if Calendar.current.isDateInToday(self) { - return formatted(date: .omitted, time: .shortened) - } else if Calendar.current.isDateInYesterday(self) { - let dateMidnight = Calendar.current.date(bySettingHour: 0, minute: 0, second: 0, of: self)! - return dateMidnight.formatted(.relative(presentation: .named)) - } else if let lastWeek = Calendar.current.date(byAdding: .day, value: -7, to: Date()), self > lastWeek { - return formatted(.dateTime.weekday(.wide)) - } else if Calendar.current.isDate(self, equalTo: .now, toGranularity: .year) { - return formatted(.dateTime.day().month()) - } else { - return formatted(date: .numeric, time: .omitted) + struct ThreadFormatStyle: Foundation.FormatStyle { + // swiftlint:disable:next nesting + public enum Style: Codable, Equatable, Hashable { + case list + case header } - } - var messageHeaderRelativeFormatted: String { - if self > .now { - return formatted(date: .numeric, time: .shortened) - } else if Calendar.current.isDateInToday(self) { - return formatted(date: .omitted, time: .shortened) - } else if Calendar.current.isDateInYesterday(self) { - let dateFormatter = DateFormatter() - dateFormatter.dateStyle = .long - dateFormatter.timeStyle = .short - dateFormatter.formattingContext = .middleOfSentence - dateFormatter.doesRelativeDateFormatting = true - return dateFormatter.string(from: self) - } else if Calendar.current.isDate(self, equalTo: .now, toGranularity: .year) { - return formatted(.dateTime.day().month().hour().minute()) - } else { - return formatted(.dateTime.year().day().month().hour().minute()) + private let style: Style + + init(style: Style) { + self.style = style } - } -} -public extension FormatStyle where Self == ByteCountFormatStyle { - static var defaultByteCount: ByteCountFormatStyle { - return .byteCount(style: .binary) + public func format(_ value: Date) -> String { + switch style { + case .list: + return formatToCustomRelative(value) + case .header: + return formatToMessageHeaderRelative(value) + } + } + + private func formatToCustomRelative(_ date: Date) -> String { + if date > .now { + return date.formatted(date: .numeric, time: .omitted) + } else if Calendar.current.isDateInToday(date) { + return date.formatted(date: .omitted, time: .shortened) + } else if Calendar.current.isDateInYesterday(date) { + let dateMidnight = Calendar.current.date(bySettingHour: 0, minute: 0, second: 0, of: date)! + return dateMidnight.formatted(.relative(presentation: .named)) + } else if let lastWeek = Calendar.current.date(byAdding: .day, value: -7, to: .now), date > lastWeek { + return date.formatted(.dateTime.weekday(.wide)) + } else if Calendar.current.isDate(date, equalTo: .now, toGranularity: .year) { + return date.formatted(.dateTime.day().month()) + } else { + return date.formatted(date: .numeric, time: .omitted) + } + } + + private func formatToMessageHeaderRelative(_ date: Date) -> String { + if date > .now { + return date.formatted(date: .numeric, time: .shortened) + } else if Calendar.current.isDateInToday(date) { + return date.formatted(date: .omitted, time: .shortened) + } else if Calendar.current.isDateInYesterday(date) { + let dateFormatter = DateFormatter() + dateFormatter.dateStyle = .long + dateFormatter.timeStyle = .short + dateFormatter.formattingContext = .middleOfSentence + dateFormatter.doesRelativeDateFormatting = true + return dateFormatter.string(from: date) + } else if Calendar.current.isDate(date, equalTo: .now, toGranularity: .year) { + return date.formatted(.dateTime.day().month().hour().minute()) + } else { + return date.formatted(.dateTime.year().day().month().hour().minute()) + } + } } } @@ -78,4 +97,16 @@ public extension FormatStyle where Self == Date.FormatStyle { static var calendarDateTime: Date.FormatStyle { return .dateTime.day().month().year().hour().minute() } + + static func thread(_ style: Date.ThreadFormatStyle.Style) -> Date.ThreadFormatStyle { + return .init(style: style) + } +} + +// MARK: ByteCount + +public extension FormatStyle where Self == ByteCountFormatStyle { + static var defaultByteCount: ByteCountFormatStyle { + return .byteCount(style: .binary) + } }