diff --git a/renderer/app.js b/renderer/app.js index e31b893..abc5777 100644 --- a/renderer/app.js +++ b/renderer/app.js @@ -350,7 +350,7 @@ function startDateLabel (dateStr) { // "Dec 17" for a SALES day (a date-only string parses as UTC midnight, so print it in UTC: // in the Americas the local date would be the day before). The year shows unless it is this year's. function saleDateLabel (d, now) { - return d.toLocaleDateString('en-US', { month: 'short', day: 'numeric', timeZone: 'UTC', year: d.getUTCFullYear() === now.getFullYear() ? undefined : 'numeric' }) + return d.toLocaleDateString('en-US', { month: 'short', day: 'numeric', timeZone: 'UTC', year: d.getUTCFullYear() === now.getUTCFullYear() ? undefined : 'numeric' }) } function fmtChecked (ts) { diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 9494281..13d143a 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -743,7 +743,11 @@ fn wishlist_notice(fresh: &[&WishItem], proton: &HashMap) -> Opt let p = w.price_info.as_ref().unwrap(); Some(Notice { title: format!("🏷️ {} is on sale!", w.name), - body: format!("{}% off — now {} on {}{}", p.discount, p.formatted.clone().unwrap_or_default(), store_label(&w.store), proton_suffix(&w.app_id, proton)), + // No formatted price (Epic may not send one): name the store, never an empty "now on" (no $ either: not every price is USD) + body: match &p.formatted { + Some(formatted) => format!("{}% off — now {formatted} on {}{}", p.discount, store_label(&w.store), proton_suffix(&w.app_id, proton)), + None => format!("{}% off on {}{}", p.discount, store_label(&w.store), proton_suffix(&w.app_id, proton)), + }, }) } many => { @@ -1547,6 +1551,19 @@ mod tests { } } + #[test] + fn a_wishlist_notice_without_a_formatted_price_names_the_store() { + // Epic may send no formatted price: no doubled-space "now on", and no invented $ amount (not every price is USD) + let bare = WishItem { + store: "epic".into(), id: "e1".into(), name: "Hades".into(), + price_info: Some(Price { discount: 50, formatted: None, ..Default::default() }), + ..Default::default() + }; + assert_eq!(wishlist_notice(&[&bare], &HashMap::new()).unwrap().body, "50% off on Epic Games"); + let priced = WishItem { price_info: Some(Price { discount: 50, formatted: Some("$4.99".into()), ..Default::default() }), ..bare }; + assert_eq!(wishlist_notice(&[&priced], &HashMap::new()).unwrap().body, "50% off — now $4.99 on Epic Games"); + } + #[test] fn keep_prices_restores_last_good_cheapshark_data() { let cheap = |price| Some(super::Cheapest { store: "GMG".into(), price, url: String::new() }); diff --git a/tools/preview/selfcheck.mjs b/tools/preview/selfcheck.mjs index 80394f0..5894df7 100644 --- a/tools/preview/selfcheck.mjs +++ b/tools/preview/selfcheck.mjs @@ -246,6 +246,8 @@ for (const zone of ['America/Los_Angeles', 'Pacific/Auckland', 'UTC']) { process.env.TZ = zone assert.equal(h.saleDateLabel(new Date('2026-12-17'), new Date('2026-10-18T12:00:00Z')), 'Dec 17', zone) assert.equal(h.saleDateLabel(new Date('2027-01-04'), new Date('2026-10-18T12:00:00Z')), 'Jan 4, 2027', zone) + // the year compares with now's UTC year, not the local one (Auckland is already in 2027 while UTC is still 2026) + assert.equal(h.saleDateLabel(new Date('2027-01-01'), new Date('2026-12-31T23:30:00Z')), 'Jan 1, 2027', zone) } if (tz === undefined) delete process.env.TZ; else process.env.TZ = tz assert.equal(h.fmtChecked(Date.now()), 'updated just now')