Skip to content

feat: add NewsBox component and integrate news items into PlayPage#3545

Open
AlexBesios wants to merge 2 commits intoopenfrontio:mainfrom
AlexBesios:feat/advertise-section
Open

feat: add NewsBox component and integrate news items into PlayPage#3545
AlexBesios wants to merge 2 commits intoopenfrontio:mainfrom
AlexBesios:feat/advertise-section

Conversation

@AlexBesios
Copy link
Copy Markdown

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.

screenshot-2026-03-31_00-48-33 screenshot-2026-03-31_00-48-24 screenshot-2026-03-31_00-48-04

Please complete the following:

  • I have added screenshots for all UI updates
  • I process any text displayed to the user through translateText() and I've added it to the en.json file
  • I have added relevant tests to the test directory
  • I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced

Please put your Discord username so you can be contacted if a bug or regression is found:

deathllotus

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 30, 2026

Walkthrough

Adds a new NewsBox LitElement component that loads news from resources/news.json, filters dismissed items via localStorage, auto-cycles visible items, supports dismissing items persistently, and is embedded into the PlayPage layout.

Changes

Cohort / File(s) Summary
News Data Resource
resources/news.json
New JSON array with three news items; each item includes id, title, description, url, and type.
NewsBox Component
src/client/components/NewsBox.ts
New exported NewsItem interface, getVisibleNewsItems() helper, and NewsBox LitElement. Loads news.json, filters dismissed IDs from localStorage, manages active index and 5s auto-rotation, supports dismissing items (persisting to localStorage), and renders type label, optional external link, dot navigation, and dismiss control.
PlayPage Integration
src/client/components/PlayPage.ts
Imported ./NewsBox.js and inserted <news-box class="lg:col-span-2"></news-box> into the PlayPage template.
Component Tests
tests/client/components/NewsBox.test.ts
New test suite that mocks localStorage, validates getVisibleNewsItems() behavior with dismissed IDs and corrupted storage, checks item schema and uniqueness, verifies full dismissal leads to empty result, and asserts at least one tournament item exists.

Sequence Diagram

sequenceDiagram
    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
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

📰 A box that cycles news for play,
Dismiss the old, let new ones stay,
Tournament flags and tips that guide,
Quiet when all have waved goodbye —
Small bits of joy on the homepage ride. 🚀

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main changes: adding a NewsBox component and integrating it into PlayPage.
Description check ✅ Passed The description is directly related to the changeset, explaining the purpose of the news box feature, its placement, functionality, and includes screenshots demonstrating the UI.
Linked Issues check ✅ Passed The PR implements all core requirements from issue #2998: a news box on the homepage advertising tournaments and tutorials with auto-cycling functionality and persistence for dismissed items.
Out of Scope Changes check ✅ Passed All changes directly support the news box feature: the JSON data file, NewsBox component, integration into PlayPage, and comprehensive tests are all scoped to the stated objectives.

✏️ 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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 expiresAt field 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

📥 Commits

Reviewing files that changed from the base of the PR and between 6fa9bf6 and 787196f.

📒 Files selected for processing (4)
  • resources/news.json
  • src/client/components/NewsBox.ts
  • src/client/components/PlayPage.ts
  • tests/client/components/NewsBox.test.ts

@github-project-automation github-project-automation bot moved this from Triage to Development in OpenFront Release Management Mar 30, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.json by 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

📥 Commits

Reviewing files that changed from the base of the PR and between 787196f and da5c3f4.

📒 Files selected for processing (1)
  • src/client/components/NewsBox.ts

Comment on lines +26 to +28
function saveDismissedIds(ids: Set<string>): void {
localStorage.setItem(DISMISSED_NEWS_KEY, JSON.stringify([...ids]));
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

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.

Comment on lines +35 to +45
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",
};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

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 evanpelle added this to the v30 milestone Mar 31, 2026
Copy link
Copy Markdown
Collaborator

@evanpelle evanpelle left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you also remove the large margin below the navigation menu

Comment on lines +5 to +12
export interface NewsItem {
id: string;
title: string;
description: string;
url?: string;
type: "tournament" | "tutorial" | "announcement";
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make it a zod schema and move it to ApiSchema.ts

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The client will fetch news from api.openfront.io

localStorage.setItem(DISMISSED_NEWS_KEY, JSON.stringify([...ids]));
}

export function getVisibleNewsItems(): NewsItem[] {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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";
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can do:

import ./NewsBox

for consistency

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Development

Development

Successfully merging this pull request may close these issues.

Add a news/advertise section to the homepage

2 participants