82 lines
3.9 KiB
Dart
82 lines
3.9 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 참여 진입이 된다.
|
|
/// [push] 는 같은 사건을 알리는 Push 의 `kind` — 띠가 떠 있으면 앱 안 Push 를 겹쳐 띄우지 않는다(v1.5 §1.1).
|
|
typedef TickerLine = ({String text, bool hero, String? push});
|
|
|
|
/// 지금 화면에 보이는 HOME 라이브 띠의 사건 — `liveTickerKey(kind, fandomId)`. 띠가 그릴 때 적는다.
|
|
/// ponytail: 전역 하나. HOME 이 띠를 하나만 그리므로 충분하다 — 띠가 여러 곳에 생기면 집합으로.
|
|
String? liveTickerShowing;
|
|
|
|
/// 띠 한 줄 고르기 — 원장 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,
|
|
push: 'eclipse',
|
|
);
|
|
}
|
|
if (eclipse.canShake(now) && eclipse.defense >= 2) {
|
|
return (
|
|
text: appL10n.liveEclipseDefending('${eclipse.defense}'),
|
|
hero: false,
|
|
push: 'eclipse',
|
|
);
|
|
}
|
|
}
|
|
if (heroTime) {
|
|
return (
|
|
text: heroNicknames.isEmpty
|
|
? appL10n.liveHeroTime(fandomName)
|
|
: appL10n.liveHeroTimeBy(fandomName, heroNicknames.join(' · ')),
|
|
hero: true,
|
|
// HERO TIME 발동 알림(영웅의 선물)은 알림함 `item_used` 로 온다.
|
|
push: 'item_used',
|
|
);
|
|
}
|
|
if (event != null) {
|
|
return (text: liveEventMessage(event), hero: false, push: null);
|
|
}
|
|
return fallback == null ? null : (text: fallback, hero: false, push: null);
|
|
}
|