import { LitElement, html, nothing } from "lit"; import { resolveMarkdown } from "lit-markdown"; import { customElement, state } from "lit/decorators.js"; import type { NewsItem } from "../../core/ApiSchemas"; import { getNews } from "../Api"; import { translateText } from "../Utils"; export type { NewsItem }; const DISMISSED_NEWS_KEY = "dismissedNewsItems"; const CYCLE_INTERVAL_MS = 5000; function getDismissedIds(): Set { const raw = localStorage.getItem(DISMISSED_NEWS_KEY); if (raw) return new Set(JSON.parse(raw)); return new Set(); } function saveDismissedIds(ids: Set): void { localStorage.setItem(DISMISSED_NEWS_KEY, JSON.stringify([...ids])); } export function getVisibleNewsItems(items: NewsItem[]): NewsItem[] { const dismissed = getDismissedIds(); return items.filter((item) => !dismissed.has(item.id)); } const typeLabelKeys: Record = { tournament: "news_box.tournament", tutorial: "news_box.tutorial", announcement: "news_box.news", warning: "news_box.warning", }; const typeLabelColors: Record = { tournament: "bg-amber-500/20 text-amber-300", tutorial: "bg-sky-500/20 text-sky-300", announcement: "bg-emerald-500/20 text-emerald-300", warning: "bg-red-500/20 text-red-300", }; @customElement("news-box") export class NewsBox extends LitElement { @state() private items: NewsItem[] = []; @state() private activeIndex = 0; private cycleTimer: ReturnType | null = null; createRenderRoot() { return this; } connectedCallback() { super.connectedCallback(); this.loadNews(); } private async loadNews() { try { const allItems = await getNews(); // Reset stale dismissed list when all items would be hidden const visible = getVisibleNewsItems(allItems); if (visible.length === 0 && allItems.length > 0) { localStorage.removeItem(DISMISSED_NEWS_KEY); this.items = allItems; } else { this.items = visible; } this.startCycle(); } catch (e) { console.error(e); } } disconnectedCallback() { super.disconnectedCallback(); this.stopCycle(); } private startCycle() { this.stopCycle(); if (this.items.length > 1) { this.cycleTimer = setInterval(() => { this.activeIndex = (this.activeIndex + 1) % this.items.length; }, CYCLE_INTERVAL_MS); } } private stopCycle() { if (this.cycleTimer !== null) { clearInterval(this.cycleTimer); this.cycleTimer = null; } } private dismiss(id: string) { const dismissed = getDismissedIds(); dismissed.add(id); saveDismissedIds(dismissed); this.items = this.items.filter((item) => item.id !== id); if (this.activeIndex >= this.items.length) { this.activeIndex = 0; } this.startCycle(); } private goTo(index: number) { this.activeIndex = index; this.startCycle(); } render() { if (this.items.length === 0) return nothing; const item = this.items[this.activeIndex]; return html`
${translateText( typeLabelKeys[item.type] ?? typeLabelKeys["announcement"], )}
${item.url ? html`${item.title}` : html`${item.title}`} ${resolveMarkdown( item.descriptionTranslationKey ? translateText(item.descriptionTranslationKey) : (item.description ?? ""), )}
${this.items.length > 1 ? html`
${this.items.map( (_, i) => html` `, )}
` : nothing}
`; } }