From 74f42c4b023955b62f3cbcac855bdfcec09666d2 Mon Sep 17 00:00:00 2001 From: Akshay Kolli Date: Tue, 30 Jun 2026 10:22:09 -0700 Subject: [PATCH] WIP: use readable card age text --- docs/SMOKE_TEST.md | 1 + .../clipbored/views/ClipboardPanelView.swift | 18 +++++++++++++---- .../ClipboardPanelViewTests.swift | 20 +++++++++++++++++++ 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/docs/SMOKE_TEST.md b/docs/SMOKE_TEST.md index 0a32606..31958c0 100644 --- a/docs/SMOKE_TEST.md +++ b/docs/SMOKE_TEST.md @@ -56,6 +56,7 @@ Use this checklist before a release or after changes to panel, pasteboard, setti 23. Resize or test on a narrow display and confirm the bottom shelf switches to compact cards that still show two recent clips cleanly. 24. Select a file, rich text, or URL card and confirm the selected-card rail exposes `Paste Plain Text`, the corner source/kind badge remains visible, and on a narrow shelf secondary actions collapse behind `More` instead of overflowing the card. 25. Confirm card footers do not show `Unknown` for clips without a source app, and confirm used clips show their usage count beside the source app. +26. Confirm card headers use readable relative ages such as `3 minutes ago` or `2 hours ago`, including when viewing a named collection. ## Copy And Paste diff --git a/sources/clipbored/views/ClipboardPanelView.swift b/sources/clipbored/views/ClipboardPanelView.swift index 70b2094..7e3b5e4 100644 --- a/sources/clipbored/views/ClipboardPanelView.swift +++ b/sources/clipbored/views/ClipboardPanelView.swift @@ -4268,9 +4268,19 @@ private final class ClipboardItemCardView: NSView, NSDraggingSource { private static func relativeDateText(for date: Date) -> String { let seconds = max(0, Int(Date().timeIntervalSince(date))) if seconds < 60 { return "Just now" } - if seconds < 3600 { return "\(seconds / 60)m ago" } - if seconds < 86400 { return "\(seconds / 3600)h ago" } - if seconds < 604800 { return "\(seconds / 86400)d ago" } - return "\(seconds / 604800)w ago" + if seconds < 3600 { + let minutes = seconds / 60 + return "\(minutes) \(minutes == 1 ? "minute" : "minutes") ago" + } + if seconds < 86400 { + let hours = seconds / 3600 + return "\(hours) \(hours == 1 ? "hour" : "hours") ago" + } + if seconds < 604800 { + let days = seconds / 86400 + return "\(days) \(days == 1 ? "day" : "days") ago" + } + let weeks = seconds / 604800 + return "\(weeks) \(weeks == 1 ? "week" : "weeks") ago" } } diff --git a/tests/clipboredtests/ClipboardPanelViewTests.swift b/tests/clipboredtests/ClipboardPanelViewTests.swift index 04c2053..c0aea82 100644 --- a/tests/clipboredtests/ClipboardPanelViewTests.swift +++ b/tests/clipboredtests/ClipboardPanelViewTests.swift @@ -349,6 +349,26 @@ final class ClipboardPanelViewTests: XCTestCase { XCTAssertEqual(fixture.view.debugFirstCardFooterDetailText, "17 characters") } + func testCardHeaderUsesReadableRelativeAgeText() { + let fixture = makePanelFixture() + var item = makeTextItem("Readable age", store: fixture.store) + item.createdAt = Date().addingTimeInterval(-3 * 60) + + fixture.store.upsert(item) + drainMainQueue() + fixture.window.contentView?.layoutSubtreeIfNeeded() + + XCTAssertEqual(fixture.view.debugFirstCardHeaderSubtitle, "3 minutes ago") + + fixture.viewModel.createCollection(named: "Age Stack", colorHex: "#FF3B30", selectAfterCreate: false) + fixture.viewModel.assignSelected(to: "Age Stack") + fixture.viewModel.selectCollection(named: "Age Stack") + drainMainQueue() + fixture.window.contentView?.layoutSubtreeIfNeeded() + + XCTAssertEqual(fixture.view.debugFirstCardHeaderSubtitle, "Text - 3 minutes ago") + } + func testCollectionChipsExposeManagementMenuActions() { let fixture = makePanelFixture() fixture.viewModel.createCollection(named: "Research Stack", colorHex: "#0A9EB8")