Zap·후원 FPI 귀속 · 이클립스 방어 Shake 전용 · 가입 팬덤만 방어
This commit is contained in:
@@ -11,7 +11,7 @@ import '../../core/theme/tokens.dart';
|
||||
import '../../core/ui/app_icons.dart';
|
||||
import '../../core/ui/live_stream.dart';
|
||||
import '../home/home_screen.dart' show formatPop;
|
||||
import '../quest/quest_evidence.dart' show deviceShakes;
|
||||
import '../quest/quest_evidence.dart' show ShakeProbe, deviceShakes;
|
||||
import 'eclipse_data.dart';
|
||||
import '../../core/i18n/app_locale.dart';
|
||||
|
||||
@@ -34,10 +34,14 @@ class EclipseDefendScreen extends StatefulWidget {
|
||||
super.key,
|
||||
required this.repository,
|
||||
required this.fandomId,
|
||||
this.shakes = deviceShakes,
|
||||
});
|
||||
|
||||
final EclipseRepository repository;
|
||||
|
||||
/// 흔들기 입력. 테스트는 가짜 스트림을 넣는다 — 방어 수단은 **Shake 하나**다(FX §5).
|
||||
final ShakeProbe shakes;
|
||||
|
||||
/// 어느 팬덤의 이클립스인가. 화면은 구독으로 현재 상태를 따라간다.
|
||||
final String fandomId;
|
||||
|
||||
@@ -67,6 +71,9 @@ class _EclipseDefendScreenState extends State<EclipseDefendScreen>
|
||||
|
||||
Timer? _burst;
|
||||
bool _sending = false;
|
||||
|
||||
/// 가속도 센서가 없는 단말. 탭 대체 참여는 두지 않는다(FX §5 「수동 방어 Shake only」).
|
||||
bool _noSensor = false;
|
||||
({String fandomId, String eclipseId, String seq, int shakes})? _batch;
|
||||
|
||||
/// 묶어 보내는 시간창. 흔들 때마다 부르면 호출이 초당 여러 번 나간다.
|
||||
@@ -77,11 +84,12 @@ class _EclipseDefendScreenState extends State<EclipseDefendScreen>
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addObserver(this);
|
||||
// 흔들기 판정 규칙(문턱 · 표본 주기)은 Music Shake 와 같다 — [ShakeDetector].
|
||||
_accel = deviceShakes().listen(
|
||||
_accel = widget.shakes().listen(
|
||||
(_) => _shake(),
|
||||
onError: (_) {
|
||||
// 센서가 없는 단말(에뮬레이터·일부 태블릿)이다. 화면은 그대로 두고
|
||||
// 아래의 「직접 누르기」로 참여할 수 있게 한다.
|
||||
// 센서가 없는 단말(에뮬레이터·일부 태블릿)이다. 방어는 Shake only(FX §5)라
|
||||
// 대체 입력을 두지 않고 지원하지 않는다고만 알린다.
|
||||
if (mounted) setState(() => _noSensor = true);
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -396,7 +404,6 @@ class _EclipseDefendScreenState extends State<EclipseDefendScreen>
|
||||
final remaining = window.inMilliseconds <= 0
|
||||
? 0.0
|
||||
: (left.inMilliseconds / window.inMilliseconds).clamp(0.0, 1.0);
|
||||
final canShake = state.canShake(now);
|
||||
return Column(
|
||||
children: [
|
||||
const _CloseBar(),
|
||||
@@ -467,26 +474,8 @@ class _EclipseDefendScreenState extends State<EclipseDefendScreen>
|
||||
// 소모가 없다는 것을 화면이 직접 말한다 — 「POP 이 빠지나?」를 묻게 두지 않는다(설계서 v1.1 §9-#5).
|
||||
_Note(context.l10n.eclipseNoPopNote('${state.qualifyShakes}')),
|
||||
if (_batch != null) _retryButton(),
|
||||
// 센서가 없는 단말에서도 참여할 수 있게 둔다. 판정은 서버가 하므로 이 버튼이
|
||||
// 흔들기보다 유리하지 않다(상한·속도 제한이 같다). 사본에 없는 보조 입력이라
|
||||
// 채움 없는 낮은 강조로 둔다.
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(
|
||||
AppSpace.s4,
|
||||
AppSpace.s2,
|
||||
AppSpace.s4,
|
||||
AppSpace.s4,
|
||||
),
|
||||
child: FilledButton(
|
||||
onPressed: canShake && (_batch == null || _sending) ? _shake : null,
|
||||
style: FilledButton.styleFrom(
|
||||
backgroundColor: AppColors.cosmicSurfaceContainer,
|
||||
foregroundColor: AppColors.onCosmicVariant,
|
||||
minimumSize: const Size.fromHeight(AppSizing.touchMinApp),
|
||||
),
|
||||
child: Text(context.l10n.eclipseTapToJoin),
|
||||
),
|
||||
),
|
||||
if (_noSensor) _Note(context.l10n.eclipseNoSensor),
|
||||
const SizedBox(height: AppSpace.s4),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user