Files
fanuzu/app/lib/features/eclipse/widgets/live_proof.dart
T

71 lines
3.3 KiB
Dart

import '../eclipse_data.dart';
import '../../../core/i18n/app_locale.dart';
/// 티커 한 줄의 문구.
///
/// Figma 홈 상단 라이브 띠의 문장 형식을 그대로 쓴다 —
/// *「[LIVE] …님의 아이템 사용으로 버프가 발동되었습니다!」*.
/// 이클립스 방어는 **합류 1회**만 싣고 Shake 횟수는 싣지 않는다 — UX·FX v1.5 §5.1 「다른 팬의 행동은
/// 개별 Shake 횟수를 모두 노출하지 않고 참여자 수 변화 중심」. 집약 수는 방어 화면의 유효 참여자 표시다.
///
/// **문구를 만드는 곳을 한 곳에 둔다.** 홈 티커와 갤럭시 섬광 배너가 각자 문장을
/// 조립하면 같은 이벤트가 두 화면에서 다르게 읽힌다.
///
/// 별첨 B 「Global Ticker」로 Eclipse 외의 종류가 뒤에 들어오므로 종류마다 한 줄씩
/// 늘려 간다. 모르는 종류는 공지로 읽어 티커가 멈추지 않게 한다.
String liveEventMessage(LiveEvent event) => switch (event.kind) {
LiveEventKind.eclipseDefense => appL10n.liveEclipseDefense(event.nickname),
LiveEventKind.artistHotTime => appL10n.liveHotTime(event.nickname),
LiveEventKind.heroGift => appL10n.liveHeroGift(event.nickname),
LiveEventKind.campaign => appL10n.liveCampaign(event.nickname),
LiveEventKind.notice => '[LIVE] ${event.nickname}',
};
/// 홈 라이브 띠(전광판)에 올릴 한 줄. [hero] 면 띠가 HERO TIME 참여 진입이 된다.
typedef TickerLine = ({String text, bool hero});
/// 띠 한 줄 고르기 — 원장 C-78 순서(위기 → 성취)대로 핵심 이벤트가 먼저 오른다.
///
/// 1. 이클립스 발생 소식 — 그 뒤 이벤트(방어 참여 등)가 아직 없을 때.
/// 2. 방어 중 집약 — UX·FX v1.5 §1.1 「개인 이벤트를 연속 티커로 쌓기보다 집단 상태로 집약」 ·
/// §5.1 「참여자 수 변화 중심」 · 기능명세 §2.10~§2.12 「지금 N 명이 함께 방어 중」. N 은 서버가
/// 인정한 고유 참여자 수([EclipseState.defense])이며 2명 이상일 때 집약한다(「다수」 해석 — 해석 확인 항목).
/// 3. HERO TIME — v1.5 §6 전광판 「공개 닉네임 + 대상 팬덤 + 진행 중 CTA」.
/// 4. 최신 이벤트 한 건, 없으면 [fallback].
TickerLine? tickerLine({
required EclipseState? eclipse,
required bool heroTime,
required List<String> heroNicknames,
required List<LiveEvent> events,
required String fandomName,
required DateTime now,
String? fallback,
}) {
final event = events.isEmpty ? null : events.first;
if (eclipse != null && eclipse.result == null) {
final started = eclipse.startAt;
if (event == null || started == null || event.at.isBefore(started)) {
return (
text: appL10n.eclipseLiveOccurred(eclipse.fandomName),
hero: false,
);
}
if (eclipse.canShake(now) && eclipse.defense >= 2) {
return (
text: appL10n.liveEclipseDefending('${eclipse.defense}'),
hero: false,
);
}
}
if (heroTime) {
return (
text: heroNicknames.isEmpty
? appL10n.liveHeroTime(fandomName)
: appL10n.liveHeroTimeBy(fandomName, heroNicknames.join(' · ')),
hero: true,
);
}
if (event != null) return (text: liveEventMessage(event), hero: false);
return fallback == null ? null : (text: fallback, hero: false);
}