feat: add NewsBox component and integrate news items into PlayPage#3545
feat: add NewsBox component and integrate news items into PlayPage#3545AlexBesios wants to merge 2 commits intoopenfrontio:mainfrom
Conversation
WalkthroughAdds a new NewsBox LitElement component that loads news from Changes
Sequence DiagramsequenceDiagram
participant PlayPage
participant NewsBox as NewsBox<br/>(Component)
participant JSON as news.json
participant Storage as localStorage
PlayPage->>NewsBox: mount component
activate NewsBox
NewsBox->>JSON: import/load newsItems
JSON-->>NewsBox: return items array
NewsBox->>Storage: read "dismissedNewsItems"
Storage-->>NewsBox: return stored IDs (or null)
NewsBox->>NewsBox: filter items -> visibleItems
NewsBox->>NewsBox: set activeIndex, start 5s timer if needed
NewsBox-->>PlayPage: render active visible item
deactivate NewsBox
PlayPage->>NewsBox: user clicks dismiss
activate NewsBox
NewsBox->>Storage: write updated dismissed IDs
NewsBox->>NewsBox: remove dismissed item, adjust activeIndex
NewsBox->>NewsBox: restart/stop timer based on remaining items
NewsBox-->>PlayPage: render updated state
deactivate NewsBox
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
resources/news.json (1)
3-8: Consider adding an expiration field for time-sensitive items.This item has a specific deadline ("April 12"). Once the date passes, the item becomes stale. Consider adding an optional
expiresAtfield to auto-hide outdated announcements, or plan manual updates.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@resources/news.json` around lines 3 - 8, Add an optional expiresAt ISO-8601 timestamp to the news item with id "clan-tournament-spring-2026" (e.g. "expiresAt": "2026-04-12T23:59:59Z") so time-sensitive announcements can be auto-hidden; ensure any code that renders or loads news checks for expiresAt and treats items with now > expiresAt as expired/hidden (or falls back to existing behavior when the field is missing).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/client/components/NewsBox.ts`:
- Line 3: Replace the nonstandard JSON import in NewsBox.ts: change the import
that uses "with { type: 'json' }" to the standard JSON import form so it matches
other modules (e.g., use the same pattern as imports in Privilege.ts and
LangSelector.ts); update the import for newsItems in the NewsBox component to
the plain import syntax and ensure the newsItems symbol is used unchanged
elsewhere in the file (TypeScript is already configured with resolveJsonModule).
---
Nitpick comments:
In `@resources/news.json`:
- Around line 3-8: Add an optional expiresAt ISO-8601 timestamp to the news item
with id "clan-tournament-spring-2026" (e.g. "expiresAt": "2026-04-12T23:59:59Z")
so time-sensitive announcements can be auto-hidden; ensure any code that renders
or loads news checks for expiresAt and treats items with now > expiresAt as
expired/hidden (or falls back to existing behavior when the field is missing).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: fa5a2181-096c-4e1f-a2a5-55c49ac2d247
📒 Files selected for processing (4)
resources/news.jsonsrc/client/components/NewsBox.tssrc/client/components/PlayPage.tstests/client/components/NewsBox.test.ts
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
src/client/components/NewsBox.ts (1)
5-10: Add an expiry window for dated promos.The current data already includes time-based copy like “before April 12”. After April 12, 2026, that item will still rotate until someone edits
resources/news.jsonby hand. Adding optional publish/expiry fields here would keep old tournament banners from going stale.Also applies to: 30-32
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/client/components/NewsBox.ts` around lines 5 - 10, The NewsItem interface lacks optional publish/expiry fields so time-limited promos remain valid; update the NewsItem interface to include optional publishDate and expiryDate (e.g., publishDate?: string; expiryDate?: string) using ISO-8601 date strings, and propagate these optional properties to any code that reads resources/news.json (references to NewsItem usage) so consumers parse/compare dates before rendering rotation logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/client/components/NewsBox.ts`:
- Around line 26-28: The saveDismissedIds function currently calls
localStorage.setItem(DISMISSED_NEWS_KEY, JSON.stringify([...ids])) which can
throw if storage is unavailable or full; wrap that write in a try/catch around
the setItem/JSON.stringify call (or the whole body of saveDismissedIds) and
silently handle failures (e.g., log a warning via console.warn or
processLogger.warn) so UI dismiss still succeeds even when persistence fails;
reference saveDismissedIds and, optionally, mirror the fallback behavior used in
getDismissedIds.
- Around line 35-45: The badge and banner copy are hard-coded in typeLabels and
elsewhere in NewsBox.ts; change these to call translateText() so UI strings
(badge text, title/description shown from resources/news.json, and any ARIA
labels used in the component) are localized. Replace direct uses of typeLabels
and other raw English strings in the NewsBox component with
translateText(typeLabelKey) or translateText(rawString) as appropriate, and
ensure ARIA attributes (e.g., aria-label/aria-labelledby values) also pass
through translateText(); update any places referenced by the identifiers
typeLabels, typeLabelColors (keep colors intact) and the title/description
render paths to use translateText().
---
Nitpick comments:
In `@src/client/components/NewsBox.ts`:
- Around line 5-10: The NewsItem interface lacks optional publish/expiry fields
so time-limited promos remain valid; update the NewsItem interface to include
optional publishDate and expiryDate (e.g., publishDate?: string; expiryDate?:
string) using ISO-8601 date strings, and propagate these optional properties to
any code that reads resources/news.json (references to NewsItem usage) so
consumers parse/compare dates before rendering rotation logic.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 290cbe58-64a7-4ece-84e0-2ce2db1da44e
📒 Files selected for processing (1)
src/client/components/NewsBox.ts
| function saveDismissedIds(ids: Set<string>): void { | ||
| localStorage.setItem(DISMISSED_NEWS_KEY, JSON.stringify([...ids])); | ||
| } |
There was a problem hiding this comment.
Guard the localStorage write path.
getDismissedIds() already falls back cleanly, but saveDismissedIds() can still throw when storage is blocked or full. In that case, clicking Dismiss fails instead of just skipping persistence.
Suggested fix
function saveDismissedIds(ids: Set<string>): void {
- localStorage.setItem(DISMISSED_NEWS_KEY, JSON.stringify([...ids]));
+ try {
+ localStorage.setItem(DISMISSED_NEWS_KEY, JSON.stringify([...ids]));
+ } catch {
+ // Ignore storage errors and keep dismiss working for this session.
+ }
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/client/components/NewsBox.ts` around lines 26 - 28, The saveDismissedIds
function currently calls localStorage.setItem(DISMISSED_NEWS_KEY,
JSON.stringify([...ids])) which can throw if storage is unavailable or full;
wrap that write in a try/catch around the setItem/JSON.stringify call (or the
whole body of saveDismissedIds) and silently handle failures (e.g., log a
warning via console.warn or processLogger.warn) so UI dismiss still succeeds
even when persistence fails; reference saveDismissedIds and, optionally, mirror
the fallback behavior used in getDismissedIds.
| const typeLabels: Record<NewsItem["type"], string> = { | ||
| tournament: "TOURNAMENT", | ||
| tutorial: "TUTORIAL", | ||
| announcement: "NEWS", | ||
| }; | ||
|
|
||
| const typeLabelColors: Record<NewsItem["type"], string> = { | ||
| tournament: "bg-amber-500/20 text-amber-300", | ||
| tutorial: "bg-sky-500/20 text-sky-300", | ||
| announcement: "bg-emerald-500/20 text-emerald-300", | ||
| }; |
There was a problem hiding this comment.
Localize the banner copy before shipping.
resources/news.json:1-23 contains English title and description, and Lines 35-45 plus Lines 143-153 add more English labels in code. This makes the new homepage box untranslated for non-English players. Please route the badge text, banner text, and ARIA labels through translateText() instead of rendering raw English.
Also applies to: 117-153
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/client/components/NewsBox.ts` around lines 35 - 45, The badge and banner
copy are hard-coded in typeLabels and elsewhere in NewsBox.ts; change these to
call translateText() so UI strings (badge text, title/description shown from
resources/news.json, and any ARIA labels used in the component) are localized.
Replace direct uses of typeLabels and other raw English strings in the NewsBox
component with translateText(typeLabelKey) or translateText(rawString) as
appropriate, and ensure ARIA attributes (e.g., aria-label/aria-labelledby
values) also pass through translateText(); update any places referenced by the
identifiers typeLabels, typeLabelColors (keep colors intact) and the
title/description render paths to use translateText().
evanpelle
left a comment
There was a problem hiding this comment.
Can you also remove the large margin below the navigation menu
| export interface NewsItem { | ||
| id: string; | ||
| title: string; | ||
| description: string; | ||
| url?: string; | ||
| type: "tournament" | "tutorial" | "announcement"; | ||
| } | ||
|
|
There was a problem hiding this comment.
Make it a zod schema and move it to ApiSchema.ts
There was a problem hiding this comment.
The client will fetch news from api.openfront.io
| localStorage.setItem(DISMISSED_NEWS_KEY, JSON.stringify([...ids])); | ||
| } | ||
|
|
||
| export function getVisibleNewsItems(): NewsItem[] { |
There was a problem hiding this comment.
can you add a getNews() function to Api.ts which calls getApiBase()/news, and then fallback to the resources/news.json if that fails.
| @@ -1,5 +1,6 @@ | |||
| import { LitElement, html } from "lit"; | |||
| import { customElement } from "lit/decorators.js"; | |||
| import "./NewsBox.js"; | |||
There was a problem hiding this comment.
I think you can do:
import ./NewsBox
for consistency
Resolves #2998
Description:
Adds a news box to the lobby homepage that advertises upcoming clan tournaments, weekly tournaments, and new player tutorials. The component sits above the username input and cycles through items automatically.
Please complete the following:
Please put your Discord username so you can be contacted if a bug or regression is found:
deathllotus