diff --git a/Mastodon/In Progress New Layout and Datamodel/GroupedNotificationFeedLoader.swift b/Mastodon/In Progress New Layout and Datamodel/GroupedNotificationFeedLoader.swift index 53398f56e..bf566d0b6 100644 --- a/Mastodon/In Progress New Layout and Datamodel/GroupedNotificationFeedLoader.swift +++ b/Mastodon/In Progress New Layout and Datamodel/GroupedNotificationFeedLoader.swift @@ -51,6 +51,8 @@ final public class GroupedNotificationFeedLoader { return cacheManager?.currentLastReadMarker } + private let timestampUpdater = TimestampUpdater(TimeInterval(30)) + private var isFetching: Bool = false public let useGroupedNotificationsApi: Bool @@ -319,7 +321,7 @@ extension GroupedNotificationFeedLoader { if let ungrouped = results as? [Mastodon.Entity.Notification] { return NotificationRowViewModel.viewModelsFromUngroupedNotifications( - ungrouped, myAccountID: authenticationBox.userID, + ungrouped, timestamper: timestampUpdater, myAccountID: authenticationBox.userID, myAccountDomain: authenticationBox.domain, navigateToScene: navigateToScene ?? { _, _ in }, presentError: presentError ?? { _ in } @@ -328,6 +330,7 @@ extension GroupedNotificationFeedLoader { return NotificationRowViewModel .viewModelsFromGroupedNotificationResults( grouped, + timestamper: timestampUpdater, myAccountID: authenticationBox.userID, myAccountDomain: authenticationBox.domain, navigateToScene: navigateToScene ?? { _, _ in }, @@ -363,3 +366,16 @@ extension NotificationRowViewModel: Hashable { hasher.combine(identifier) } } + +class TimestampUpdater: ObservableObject { + @Published var timestamp: Date = .now + private var timer: Timer? + + init(_ interval: TimeInterval) { + timer = Timer.scheduledTimer(withTimeInterval: interval, repeats: true, block: { [weak self] _ in + Task { @MainActor in + self?.timestamp = .now + } + }) + } +} diff --git a/Mastodon/In Progress New Layout and Datamodel/NotificationRowView.swift b/Mastodon/In Progress New Layout and Datamodel/NotificationRowView.swift index 34a74e12e..2ff9babd4 100644 --- a/Mastodon/In Progress New Layout and Datamodel/NotificationRowView.swift +++ b/Mastodon/In Progress New Layout and Datamodel/NotificationRowView.swift @@ -532,6 +532,12 @@ let actionSuperheaderHeight: CGFloat = 20 struct NotificationRowView: View { @ObservedObject var viewModel: NotificationRowViewModel + @ObservedObject var timestamper: TimestampUpdater + + init(viewModel: NotificationRowViewModel) { + self.viewModel = viewModel + self.timestamper = viewModel.timestampUpdater + } var body: some View { HStack(alignment: .top, spacing: avatarSpacing) { @@ -602,7 +608,7 @@ struct NotificationRowView: View { Text(string) .frame(maxWidth: .infinity, alignment: .leading) case .timeSinceLabel(let date): - Text(date.localizedExtremelyAbbreviatedTimeElapsedUntilNow) + Text(date.localizedExtremelyAbbreviatedTimeElapsedUntil(now: timestamper.timestamp)) .font(.subheadline) .frame(height: actionSuperheaderHeight) .fixedSize(horizontal: true, vertical: false) @@ -628,7 +634,7 @@ struct NotificationRowView: View { HStack(alignment: .top, spacing: 2) { Text(string) .frame(maxWidth: .infinity, alignment: .leading) - Text(date.localizedExtremelyAbbreviatedTimeElapsedUntilNow) + Text(date.localizedExtremelyAbbreviatedTimeElapsedUntil(now: timestamper.timestamp)) .font(.subheadline) .frame(height: actionSuperheaderHeight) .fixedSize(horizontal: true, vertical: false) diff --git a/Mastodon/In Progress New Layout and Datamodel/NotificationRowViewModel.swift b/Mastodon/In Progress New Layout and Datamodel/NotificationRowViewModel.swift index ec89cb374..8e231c6f3 100644 --- a/Mastodon/In Progress New Layout and Datamodel/NotificationRowViewModel.swift +++ b/Mastodon/In Progress New Layout and Datamodel/NotificationRowViewModel.swift @@ -11,6 +11,7 @@ import SwiftUICore class NotificationRowViewModel: ObservableObject { let identifier: MastodonFeedItemIdentifier let timestamp: Date? + let timestampUpdater: TimestampUpdater let oldestID: String? let newestID: String? let type: GroupedNotificationType @@ -45,6 +46,7 @@ class NotificationRowViewModel: ObservableObject { init( _ notificationInfo: GroupedNotificationInfo, + timestamper: TimestampUpdater, myAccountDomain: String, navigateToScene: @escaping ( SceneCoordinator.Scene, SceneCoordinator.Transition @@ -53,6 +55,7 @@ class NotificationRowViewModel: ObservableObject { self.identifier = .notificationGroup(id: notificationInfo.id) self.timestamp = notificationInfo.timestamp + self.timestampUpdater = timestamper self.oldestID = notificationInfo.oldestNotificationID self.newestID = notificationInfo.newestNotificationID self.type = notificationInfo.groupedNotificationType @@ -526,6 +529,7 @@ extension NotificationRowViewModel { extension NotificationRowViewModel { static func viewModelsFromGroupedNotificationResults( _ results: Mastodon.Entity.GroupedNotificationsResults, + timestamper: TimestampUpdater, myAccountID: String, myAccountDomain: String, navigateToScene: @escaping ( @@ -607,7 +611,7 @@ extension NotificationRowViewModel { ) return NotificationRowViewModel( - info, myAccountDomain: myAccountDomain, + info, timestamper: timestamper, myAccountDomain: myAccountDomain, navigateToScene: navigateToScene, presentError: presentError) } @@ -615,6 +619,7 @@ extension NotificationRowViewModel { static func viewModelsFromUngroupedNotifications( _ notifications: [Mastodon.Entity.Notification], + timestamper: TimestampUpdater, myAccountID: String, myAccountDomain: String, navigateToScene: @escaping ( @@ -675,7 +680,7 @@ extension NotificationRowViewModel { ) return NotificationRowViewModel( - info, myAccountDomain: myAccountDomain, + info, timestamper: timestamper, myAccountDomain: myAccountDomain, navigateToScene: navigateToScene, presentError: presentError) } diff --git a/MastodonSDK/Sources/MastodonUI/Extension/Date.swift b/MastodonSDK/Sources/MastodonUI/Extension/Date.swift index d8e321429..fa8403ff9 100644 --- a/MastodonSDK/Sources/MastodonUI/Extension/Date.swift +++ b/MastodonSDK/Sources/MastodonUI/Extension/Date.swift @@ -44,8 +44,8 @@ extension Date { return Date.relativeTimestampFormatter.localizedString(for: self, relativeTo: Date()) } - public var localizedExtremelyAbbreviatedTimeElapsedUntilNow: String { - let interval = Date.now.timeIntervalSince(self) + public func localizedExtremelyAbbreviatedTimeElapsedUntil(now: Date) -> String { + let interval = now.timeIntervalSince(self) guard interval > TimeInterval(integerLiteral: 60) else { return "now" } return extremeDateAbbreviatingFormatter.string(from: interval) ?? "" }