이클립스 흐름 — 배너 진입 · 이벤트 버튼 · 오버레이 · 방어 진행 표시 · 흔들기 감지 통일
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -48,8 +48,12 @@ class HomeHero extends StatefulWidget {
|
||||
this.onItem,
|
||||
this.miniMap,
|
||||
this.zapButtonKey,
|
||||
this.eclipseButton,
|
||||
});
|
||||
|
||||
/// 이클립스 이벤트 버튼(`516:88124`). 경보 단계부터 행성 아래 가운데에 뜬다.
|
||||
final Widget? eclipseButton;
|
||||
|
||||
/// 화면 하단에 떠 있는 ZAP 버튼([HomeZap])의 키 — 기여 연출이 이 자리에서 행성으로 날아간다.
|
||||
final GlobalKey? zapButtonKey;
|
||||
|
||||
@@ -181,35 +185,57 @@ class _HomeHeroState extends State<HomeHero> {
|
||||
builder: (context, box) => SizedBox(
|
||||
width: double.infinity,
|
||||
height: math.min(planet, box.maxHeight),
|
||||
child: widget.unjoined
|
||||
? _EmptyPlanet(
|
||||
onCreateNebula: widget.onCreateNebula,
|
||||
onJoin: widget.onJoin,
|
||||
)
|
||||
: growth.isNebula
|
||||
? _Nebula(
|
||||
key: _planetKey,
|
||||
// 탄생은 창립자만(Q-PN-01 A안) — 다른 멤버에게는 안내 · 탭이 없다.
|
||||
ready: growth.nebulaReady && growth.isFounder,
|
||||
onBirth: widget.onBirth,
|
||||
)
|
||||
: Stack(
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
Positioned.fill(
|
||||
child: LivePlanet25D(
|
||||
onOpenHistory: widget.onOpenHistory,
|
||||
child: Stack(
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
Positioned.fill(
|
||||
child: widget.unjoined
|
||||
? _EmptyPlanet(
|
||||
onCreateNebula: widget.onCreateNebula,
|
||||
onJoin: widget.onJoin,
|
||||
)
|
||||
: growth.isNebula
|
||||
? _Nebula(
|
||||
key: _planetKey,
|
||||
fandomId: fandomId,
|
||||
watch: watchPlanet,
|
||||
stage: growth.stage,
|
||||
image: 'assets/figma/planet-home.png',
|
||||
size: math.min(planet, box.maxHeight),
|
||||
glow: AppColors.neonViolet,
|
||||
// 탄생은 창립자만(Q-PN-01 A안) — 다른 멤버에게는 안내 · 탭이 없다.
|
||||
ready:
|
||||
growth.nebulaReady &&
|
||||
growth.isFounder,
|
||||
onBirth: widget.onBirth,
|
||||
)
|
||||
: Stack(
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
Positioned.fill(
|
||||
child: LivePlanet25D(
|
||||
onOpenHistory: widget.onOpenHistory,
|
||||
key: _planetKey,
|
||||
fandomId: fandomId,
|
||||
watch: watchPlanet,
|
||||
stage: growth.stage,
|
||||
image:
|
||||
'assets/figma/planet-home.png',
|
||||
size: math.min(
|
||||
planet,
|
||||
box.maxHeight,
|
||||
),
|
||||
glow: AppColors.neonViolet,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
// 이클립스 이벤트 버튼 — 명세 `516:88125` 「성운(행성) 아래 중앙 정렬」.
|
||||
// 사본 `Group 563` 은 행성 상자(212) 안 아래쪽에 겹쳐 놓인다.
|
||||
if (widget.eclipseButton case final button?)
|
||||
Positioned(
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
child: Center(child: button),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -7,27 +7,45 @@ import '../../../core/theme/tokens.dart';
|
||||
/// 사본 `Frame 1984081132`(「홈」 `2097:16428`) — 헤더 바로 아래 전폭 띠(높이 22 · 패딩 4/0 ·
|
||||
/// 바탕 없음), 가운데 한 줄(42dot Medium 12 · 행간 14 흰색). 실시간 소셜 프루프(견적서 4주차)의
|
||||
/// 표면이다.
|
||||
///
|
||||
/// 명세 `516:87664` *동작: 이벤트 발생 시 텍스트 우측에서 좌측으로 1회 슬라이드* — 문구가
|
||||
/// 바뀔 때마다 오른쪽 밖에서 한 번 미끄러져 들어와 멈춘다.
|
||||
class LiveTicker extends StatelessWidget {
|
||||
const LiveTicker({super.key, required this.text});
|
||||
|
||||
final String text;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => Container(
|
||||
height: 22,
|
||||
padding: const EdgeInsets.symmetric(horizontal: AppSpace.s4),
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
text,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(
|
||||
fontFamily: AppFonts.body,
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
height: 14 / 12,
|
||||
letterSpacing: -0.3,
|
||||
color: AppColors.onCosmic,
|
||||
Widget build(BuildContext context) => ClipRect(
|
||||
child: Container(
|
||||
height: 22,
|
||||
padding: const EdgeInsets.symmetric(horizontal: AppSpace.s4),
|
||||
alignment: Alignment.center,
|
||||
child: TweenAnimationBuilder<double>(
|
||||
key: ValueKey(text),
|
||||
tween: Tween(begin: 1, end: 0),
|
||||
duration: const Duration(milliseconds: 600),
|
||||
curve: Curves.easeOutCubic,
|
||||
// 띠 너비만큼 오른쪽 밖에서 출발한다.
|
||||
builder: (context, dx, child) => LayoutBuilder(
|
||||
builder: (context, box) => Transform.translate(
|
||||
offset: Offset(dx * box.maxWidth, 0),
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
child: Text(
|
||||
text,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(
|
||||
fontFamily: AppFonts.body,
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
height: 14 / 12,
|
||||
letterSpacing: -0.3,
|
||||
color: AppColors.onCosmic,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user