이클립스 흐름 — 배너 진입 · 이벤트 버튼 · 오버레이 · 방어 진행 표시 · 흔들기 감지 통일

This commit is contained in:
Sn-Kinos
2026-10-05 19:05:21 +09:00
parent be623d2e46
commit 576dcfbdfd
27 changed files with 1369 additions and 369 deletions
+38 -27
View File
@@ -19,7 +19,7 @@ import '../../core/ui/app_icons.dart';
import '../../core/data/planet_visual_state.dart';
import '../../core/ui/live_stream.dart';
import '../eclipse/eclipse_data.dart';
import '../eclipse/widgets/eclipse_banner.dart';
import '../eclipse/widgets/eclipse_event_button.dart';
import '../eclipse/widgets/live_proof.dart';
import 'home_data.dart';
import 'nebula_create_screen.dart';
@@ -221,19 +221,36 @@ class _LiveTickerSlot extends StatelessWidget {
final String? fallback;
@override
Widget build(BuildContext context) => LiveStreamBuilder<List<LiveEvent>>(
key: ValueKey('live-$fandomId'),
// 띠는 한 줄만 그리므로 최신 몇 건만 받는다.
Widget build(BuildContext context) => LiveStreamBuilder<EclipseState?>(
key: ValueKey('live-eclipse-$fandomId'),
// 명세 `516:87664` 이벤트2 「이클립스 이벤트 — 행성/성운명」(사본 `373:443187`
// 「BTS 행성에 이클립스가 발생했어요!」). 발생한 회차만 본다.
create: () => fandomId.isEmpty
? Stream.value(const <LiveEvent>[])
: eclipse.liveEvents(fandomId: fandomId, limit: 5),
builder: (context, events) {
final latest = (events == null || events.isEmpty)
? fallback
: liveEventMessage(events.first);
if (latest == null) return const SizedBox.shrink();
return LiveTicker(text: latest);
},
? Stream<EclipseState?>.value(null)
: eclipse.watch(fandomId: fandomId),
builder: (context, eclipseState) => LiveStreamBuilder<List<LiveEvent>>(
key: ValueKey('live-$fandomId'),
// 띠는 한 줄만 그리므로 최신 몇 건만 받는다.
create: () => fandomId.isEmpty
? Stream.value(const <LiveEvent>[])
: eclipse.liveEvents(fandomId: fandomId, limit: 5),
builder: (context, events) {
final event = (events == null || events.isEmpty) ? null : events.first;
final started = eclipseState?.startAt;
// 발생 소식은 그 뒤에 온 이벤트(방어 참여 등)가 없을 때 띠에 오른다.
final eclipseNews =
eclipseState != null &&
eclipseState.result == null &&
(event == null || started == null || event.at.isBefore(started));
final latest = eclipseNews
? context.l10n.eclipseLiveOccurred(eclipseState.fandomName)
: event == null
? fallback
: liveEventMessage(event);
if (latest == null) return const SizedBox.shrink();
return LiveTicker(text: latest);
},
),
);
}
@@ -540,6 +557,12 @@ class _HomeBody extends StatelessWidget {
GoRouter.maybeOf(context)?.go('/galaxy'),
zapButtonKey: zapKey,
onHeroParticipate: _heroParticipate(context),
eclipseButton: data.fandomId.isEmpty
? null
: EclipseEventButton(
repository: eclipse,
fandomId: data.fandomId,
),
),
),
),
@@ -564,20 +587,8 @@ class _HomeBody extends StatelessWidget {
),
],
),
// 이클립스 인앱 경보 — 별첨 1 ① 예외 처리 「푸시 미수신 단말 — 인앱
// 워닝 배너 병행」. 활성 이클립스가 없으면 자리도 차지하지 않는다.
if (data.fandomId.isNotEmpty)
EclipseBanner(
repository: eclipse,
fandomId: data.fandomId,
// 히어로 아래 24 = 히어로 상자 12 + 여기 12. 구획 사이는 36(`Frame 4241`).
margin: const EdgeInsets.fromLTRB(
AppSpace.s4,
HomeSectionHeader.slack,
AppSpace.s4,
_sectionGap,
),
),
// 이클립스 인앱 경보(별첨 1 ① 「푸시 미수신 단말 — 인앱 워닝 배너 병행」)는
// Figma 대로 히어로 행성 아래의 이클립스 이벤트 버튼(`516:88124`)이 맡는다.
// 이하 구획은 Figma 「홈」 `2097:16428` `Frame 4241`(패딩 24/16 · 구획 사이 36 · 머리와
// 본문 16) 순서 — TODAY'S QUEST · 팬덤행성 인큐베이터 · 아티스트 소식 · HOT ARCHIVE ·
// 인기 K-INSIDE.