Skip to content

Commit ec60d14

Browse files
committed
Release v0.2.0
- bump extension version to 0.2.0 - add changelog / release notes for v0.2.0 - treat empty Free to Keep results as a successful check without warnings - update badge behavior: hide badge when there are no active promotions - update popup empty/status text for no new free promotions
1 parent c5a57ee commit ec60d14

12 files changed

Lines changed: 77 additions & 19 deletions

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Changelog
2+
3+
## 0.2.0
4+
5+
### What's new
6+
7+
- Empty `Free to Keep` results are now treated as a normal successful check instead of a source warning.
8+
- The popup now clearly shows `No new free promotions found` when the check succeeds but there are no active giveaways.
9+
- The popup list now keeps only active promotions visible, which removes stale non-active entries from the main view.
10+
- The toolbar badge is now hidden when there are no active `Free to Keep` promotions.
11+
- When active promotions exist, the badge still shows the active count: red for unread items, green when everything is already seen.

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ Steam Promo Watch is a lightweight Chrome / Edge extension that watches Steam fo
55
> Early alpha: the extension is still in active development.
66
> Behavior may change, some features may be incomplete, and bugs are still expected.
77
8+
Release notes are tracked in [CHANGELOG.md](CHANGELOG.md).
9+
810
## What it is
911

1012
Steam Promo Watch helps you catch Steam games that become free to keep for a limited time.

manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"manifest_version": 3,
33
"name": "Steam Promo Watch",
44
"description": "Tracks new Steam free-to-keep promotions and notifies you about new giveaways.",
5-
"version": "0.1.0",
5+
"version": "0.2.0",
66
"minimum_chrome_version": "120",
77
"permissions": [
88
"alarms",

src/lib/badge.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ export async function updateBadge(runtimeState, settings) {
1515

1616
const unreadCount = Number(runtimeState?.unreadCount) || 0;
1717
const activeFreeToKeepCount = Number(runtimeState?.activeFreeToKeepCount) || 0;
18+
if (activeFreeToKeepCount <= 0) {
19+
await chrome.action.setBadgeText({ text: "" });
20+
return;
21+
}
1822

1923
await chrome.action.setBadgeBackgroundColor({
2024
color: unreadCount > 0 ? BADGE_COLORS.unread : BADGE_COLORS.read

src/lib/providers/fallbackProvider.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export const fallbackProvider = {
2727

2828
return {
2929
promotions,
30-
warnings: promotions.length === 0 ? ["Featured categories did not contain free giveaways."] : [],
30+
warnings: [],
3131
success: true
3232
};
3333
}

src/lib/providers/index.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ export async function fetchPromotionsFromProviders(settings) {
99
let hadSuccess = false;
1010

1111
if (settings.trackFreeToKeep) {
12+
let primaryFailedMessage = "";
13+
1214
try {
1315
const primary = await steamStoreSearchProvider.fetchPromotions();
1416
providerIds.push(steamStoreSearchProvider.id);
@@ -24,14 +26,17 @@ export async function fetchPromotionsFromProviders(settings) {
2426
hadSuccess = hadSuccess || fallback.success;
2527
}
2628
} catch (error) {
27-
warnings.push(`Free to Keep provider failed: ${error instanceof Error ? error.message : String(error)}`);
29+
primaryFailedMessage = `Free to Keep provider failed: ${error instanceof Error ? error.message : String(error)}`;
2830
try {
2931
const fallback = await fallbackProvider.fetchPromotions();
3032
providerIds.push(fallbackProvider.id);
3133
warnings.push(...(fallback.warnings || []));
3234
promotions.push(...(fallback.promotions || []));
3335
hadSuccess = hadSuccess || fallback.success;
3436
} catch (fallbackError) {
37+
if (primaryFailedMessage) {
38+
warnings.push(primaryFailedMessage);
39+
}
3540
warnings.push(`Fallback provider failed: ${fallbackError instanceof Error ? fallbackError.message : String(fallbackError)}`);
3641
}
3742
}

src/lib/providers/steamStoreSearchProvider.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ export const steamStoreSearchProvider = {
6969

7070
return {
7171
promotions,
72-
warnings: promotions.length === 0 ? ["Steam search returned no free special rows."] : [],
72+
warnings: [],
7373
success: true
7474
};
7575
}

src/ui/popup-state.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
export function getVisiblePromotions(entries, maxItems = 10) {
2+
return (Array.isArray(entries) ? entries : [])
3+
.filter((entry) => entry?.status === "active")
4+
.slice(0, maxItems);
5+
}
6+
7+
export function getPopupStatusText(runtimeState = {}) {
8+
if (runtimeState.checkInProgress) {
9+
return "Checking now...";
10+
}
11+
if (runtimeState.lastCheckOutcome === "error") {
12+
return "Source error";
13+
}
14+
if (runtimeState.lastCheckOutcome === "success") {
15+
return runtimeState.lastResultCount > 0
16+
? `${runtimeState.lastResultCount} promotion(s) tracked`
17+
: "No new free promotions found";
18+
}
19+
return "Idle";
20+
}

src/ui/popup.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ <h1>Free to Keep</h1>
3838
<h2 id="promotions-heading">Latest promotions</h2>
3939
<span id="unread-pill" class="pill hidden"></span>
4040
</div>
41-
<div id="empty-state" class="empty-state hidden">No promotions found yet.</div>
41+
<div id="empty-state" class="empty-state hidden">No new free promotions found.</div>
4242
<ul id="promotion-list" class="promotion-list"></ul>
4343
</section>
4444

src/ui/popup.js

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { getContentTypeLabel, getPromotionTypeLabel } from "../lib/filters.js";
22
import { formatDateTime, formatRelativeTime, getPromotionImageUrl } from "../lib/utils.js";
3+
import { getPopupStatusText, getVisiblePromotions } from "./popup-state.js";
34

45
const checkNowButton = document.querySelector("#check-now");
56
const statusText = document.querySelector("#status-text");
@@ -26,7 +27,7 @@ function renderWarning(text) {
2627
}
2728

2829
function renderPromotions(entries) {
29-
const visibleEntries = entries.slice(0, 10);
30+
const visibleEntries = getVisiblePromotions(entries);
3031

3132
promotionList.textContent = "";
3233
emptyState.classList.toggle("hidden", visibleEntries.length > 0);
@@ -113,16 +114,7 @@ function renderPromotions(entries) {
113114
}
114115

115116
function renderStatus(runtimeState) {
116-
if (runtimeState.checkInProgress) {
117-
statusText.textContent = "Checking now...";
118-
} else if (runtimeState.lastCheckOutcome === "error") {
119-
statusText.textContent = "Source error";
120-
} else if (runtimeState.lastCheckOutcome === "success") {
121-
statusText.textContent = runtimeState.lastResultCount > 0 ? `${runtimeState.lastResultCount} promotion(s) tracked` : "No active promotions";
122-
} else {
123-
statusText.textContent = "Idle";
124-
}
125-
117+
statusText.textContent = getPopupStatusText(runtimeState);
126118
lastSuccess.textContent = formatDateTime(runtimeState.lastSuccessAt);
127119
nextCheck.textContent = runtimeState.nextCheckAt ? `${formatDateTime(runtimeState.nextCheckAt)} (${formatRelativeTime(runtimeState.nextCheckAt)})` : "Not scheduled";
128120
checkNowButton.disabled = runtimeState.checkInProgress;

0 commit comments

Comments
 (0)