Discover request surface — taste-aware, rotating, snoozable, tag-targeted (milestone #268) #116

Merged
bvandeusen merged 14 commits from dev into main 2026-08-03 08:38:25 -04:00
4 changed files with 45 additions and 2 deletions
Showing only changes of commit f17356560d - Show all commits
@@ -119,8 +119,15 @@ data class SuggestionSnoozeRef(
private companion object {
const val MILLIS_PER_DAY = 86_400_000.0
// Below this, days read more naturally than a rounded month count.
const val DAYS_BEFORE_MONTHS = 45
//
// Must be <= DAYS_PER_MONTH, or the singular "in about a month" is
// unreachable: a rounded month count of 1 needs 15..44 days, and any
// threshold above 30 sends all of those down the days branch instead.
// This was 45 and the singular branch was dead code — the unit test
// for it is what surfaced that.
const val DAYS_BEFORE_MONTHS = 30
const val DAYS_PER_MONTH = 30.0
}
}
@@ -35,6 +35,22 @@ class SuggestionSnoozeRefTest {
assertEquals("in 14 days", snoozeIn(14.0).returnsIn(now))
}
// Pins the days→months boundary. The singular branch was originally dead
// code because the threshold (45) sat above the divisor (30), so no day
// count could ever round to one month without being caught by the days
// branch first. Asserting both sides of the seam keeps that from
// regressing silently.
@Test
fun `the days-to-months boundary is exactly at 30 days`() {
assertEquals("in 29 days", snoozeIn(29.0).returnsIn(now))
assertEquals("in about a month", snoozeIn(30.0).returnsIn(now))
}
@Test
fun `well past a month still reads in the singular rather than jumping to two`() {
assertEquals("in about a month", snoozeIn(40.0).returnsIn(now))
}
@Test
fun `tomorrow is named, not rendered as 1 days`() {
assertEquals("tomorrow", snoozeIn(1.0).returnsIn(now))
+6 -1
View File
@@ -66,7 +66,12 @@
const days = Math.round(ms / 86_400_000);
if (days < 1) return 'today';
if (days === 1) return 'tomorrow';
if (days < 45) return `in ${days} days`;
// The 30 must not exceed the divisor below, or the singular "in about a
// month" is unreachable — a rounded month count of 1 needs 15..44 days,
// and any higher threshold sends all of those down the days branch. This
// read 45 and the singular case was dead code (caught by the Android
// unit test for the same logic).
if (days < 30) return `in ${days} days`;
const months = Math.round(days / 30);
return months === 1 ? 'in about a month' : `in about ${months} months`;
}
@@ -210,6 +210,21 @@ describe('SuggestionFeed snooze (#2375)', () => {
expect(screen.getByText(/back in 14 days/i)).toBeInTheDocument();
});
// Pins the days→months seam. The singular branch was originally dead code
// here too: the threshold (45) sat above the divisor (30), so no day count
// could round to one month without hitting the days branch first. Kept in
// lockstep with SuggestionSnoozeRefTest on Android.
test('the days-to-months boundary sits at 30 days, so "a month" is reachable', () => {
setSuggestions([]);
setSnoozes([
{ mbid: 'a', name: 'JustUnder', snoozed_until: inDays(29), created_at: inDays(0) },
{ mbid: 'b', name: 'JustOver', snoozed_until: inDays(30), created_at: inDays(0) }
]);
render(SuggestionFeed);
expect(screen.getByText(/back in 29 days/i)).toBeInTheDocument();
expect(screen.getByText(/back in about a month/i)).toBeInTheDocument();
});
test('no snoozed section when nothing is parked', () => {
setSuggestions([oneSeed]);
setSnoozes([]);