FPI 랭킹 내 행성·내 국가 판정 · 콜드 스타트 약관 깜빡임 · 로그아웃 상한
This commit is contained in:
@@ -151,11 +151,13 @@ class PushMessaging {
|
||||
/// 토픽 해지를 먼저 하는 이유는 실패 순서다. 토큰 삭제가 실패해도 구독은 끊겨
|
||||
/// 알림이 멈춘다. 반대로 두면 토큰만 지워진 채 토픽 알림이 계속 온다.
|
||||
Future<void> clearOnSignOut() async {
|
||||
for (final fandomId in await _resolveFandomIds()) {
|
||||
for (final topic in PushTopic.values) {
|
||||
await _transport.unsubscribeFromTopic(topic.topicFor(fandomId));
|
||||
}
|
||||
}
|
||||
// 해제는 한꺼번에 보낸다. 가입 행성 × 구분 수만큼 차례로 기다리면 그것만으로
|
||||
// 로그아웃이 수 초 걸린다.
|
||||
await Future.wait([
|
||||
for (final fandomId in await _resolveFandomIds())
|
||||
for (final topic in PushTopic.values)
|
||||
_transport.unsubscribeFromTopic(topic.topicFor(fandomId)),
|
||||
]);
|
||||
final uid = _backend.uid;
|
||||
final token = await _transport.getToken();
|
||||
if (uid != null && token != null && token.isNotEmpty) {
|
||||
|
||||
@@ -136,7 +136,13 @@ GoRouter buildRouter(AuthRepository auth, {Repositories? repositories}) {
|
||||
path: onboardingPath,
|
||||
builder: (_, _) => ListenableBuilder(
|
||||
listenable: auth,
|
||||
builder: (_, _) => auth.onboarding == OnboardingGate.checking
|
||||
// 약관 동의가 **필요하다고 확인된 뒤에만** 온보딩을 그린다. 판정 중이거나
|
||||
// 이미 동의한 계정이면 셸로 넘어가는 동안 스피너(스플래시)만 보인다 —
|
||||
// `!= checking` 으로 가르면 판정이 done 으로 바뀌는 순간 약관 화면이 한
|
||||
// 프레임 그려진 채 페이드로 빠졌다(10/6 실기기 콜드 스타트 깜빡임).
|
||||
// 온보딩을 막 마친 경우(justSignedUp)는 가입 완료로 넘어갈 때까지 그대로 둔다.
|
||||
builder: (_, _) =>
|
||||
auth.onboarding != OnboardingGate.required && !auth.justSignedUp
|
||||
? const Scaffold(body: Center(child: CircularProgressIndicator()))
|
||||
: OnboardingFlow(
|
||||
auth: auth,
|
||||
|
||||
@@ -10,7 +10,7 @@ import '../theme/tokens.dart';
|
||||
/// 단일 버튼 알림(Figma `511:86714` · `554:23103`)은 실행 버튼 하나가 폭을 다 쓴다.
|
||||
///
|
||||
/// [content] 는 입력 칸이 있는 창(반려 사유 · 인증 제출 등)에서 문구 대신 넣는다.
|
||||
class AppDialog extends StatelessWidget {
|
||||
class AppDialog extends StatefulWidget {
|
||||
const AppDialog({
|
||||
super.key,
|
||||
required this.title,
|
||||
@@ -18,6 +18,7 @@ class AppDialog extends StatelessWidget {
|
||||
this.content,
|
||||
this.cancelLabel,
|
||||
required this.confirmLabel,
|
||||
this.onConfirm,
|
||||
});
|
||||
|
||||
final String title;
|
||||
@@ -28,6 +29,17 @@ class AppDialog extends StatelessWidget {
|
||||
final String? cancelLabel;
|
||||
final String confirmLabel;
|
||||
|
||||
/// 실행 버튼을 누르면 창을 닫기 **전에** 돈다. 도는 동안 실행 버튼은 진행 중
|
||||
/// 표시로 바뀌고 두 버튼 모두 눌리지 않는다(로그아웃처럼 정리가 걸리는 실행).
|
||||
final Future<void> Function()? onConfirm;
|
||||
|
||||
@override
|
||||
State<AppDialog> createState() => _AppDialogState();
|
||||
}
|
||||
|
||||
class _AppDialogState extends State<AppDialog> {
|
||||
bool _busy = false;
|
||||
|
||||
static const _shape = RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(AppRadius.sm)),
|
||||
);
|
||||
@@ -55,21 +67,42 @@ class AppDialog extends StatelessWidget {
|
||||
filled ? AppColors.onCosmicAccent : AppColors.onCosmic,
|
||||
).copyWith(height: 1),
|
||||
);
|
||||
void pop() => Navigator.of(context).pop(value);
|
||||
Future<void> pop() async {
|
||||
final run = widget.onConfirm;
|
||||
if (value && run != null) {
|
||||
setState(() => _busy = true);
|
||||
try {
|
||||
await run();
|
||||
} finally {
|
||||
if (mounted) setState(() => _busy = false);
|
||||
}
|
||||
}
|
||||
if (context.mounted) Navigator.of(context).pop(value);
|
||||
}
|
||||
|
||||
return Expanded(
|
||||
child: filled
|
||||
? FilledButton(
|
||||
onPressed: pop,
|
||||
onPressed: _busy ? null : pop,
|
||||
style: FilledButton.styleFrom(
|
||||
minimumSize: const Size.fromHeight(48),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||||
backgroundColor: AppColors.cosmicAccent,
|
||||
disabledBackgroundColor: AppColors.cosmicAccent,
|
||||
shape: _shape,
|
||||
),
|
||||
child: text,
|
||||
child: _busy
|
||||
? const SizedBox.square(
|
||||
dimension: 20,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2,
|
||||
color: AppColors.onCosmicAccent,
|
||||
),
|
||||
)
|
||||
: text,
|
||||
)
|
||||
: OutlinedButton(
|
||||
onPressed: pop,
|
||||
onPressed: _busy ? null : pop,
|
||||
style: OutlinedButton.styleFrom(
|
||||
minimumSize: const Size.fromHeight(48),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||||
@@ -98,11 +131,11 @@ class AppDialog extends StatelessWidget {
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
widget.title,
|
||||
textAlign: TextAlign.center,
|
||||
style: _text(16, FontWeight.w600, AppColors.onCosmic),
|
||||
),
|
||||
if (body case final b?) ...[
|
||||
if (widget.body case final b?) ...[
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
b,
|
||||
@@ -111,7 +144,7 @@ class AppDialog extends StatelessWidget {
|
||||
.copyWith(height: 1.4),
|
||||
),
|
||||
],
|
||||
if (content case final c?) ...[
|
||||
if (widget.content case final c?) ...[
|
||||
const SizedBox(height: 16),
|
||||
c,
|
||||
],
|
||||
@@ -122,11 +155,11 @@ class AppDialog extends StatelessWidget {
|
||||
const SizedBox(height: 16),
|
||||
Row(
|
||||
children: [
|
||||
if (cancelLabel case final c?) ...[
|
||||
if (widget.cancelLabel case final c?) ...[
|
||||
button(c, false, filled: false),
|
||||
const SizedBox(width: 8),
|
||||
],
|
||||
button(confirmLabel, true, filled: true),
|
||||
button(widget.confirmLabel, true, filled: true),
|
||||
],
|
||||
),
|
||||
],
|
||||
@@ -146,16 +179,20 @@ Future<bool> showConfirmDialog(
|
||||
Widget? content,
|
||||
String? cancelLabel,
|
||||
String? confirmLabel,
|
||||
Future<void> Function()? onConfirm,
|
||||
}) async {
|
||||
final r = await showDialog<bool>(
|
||||
context: context,
|
||||
barrierColor: _barrier,
|
||||
// 실행이 도는 동안 바깥을 눌러 닫히면 진행 중 표시가 사라진 채 실행만 남는다.
|
||||
barrierDismissible: onConfirm == null,
|
||||
builder: (context) => AppDialog(
|
||||
title: title,
|
||||
body: body,
|
||||
content: content,
|
||||
cancelLabel: cancelLabel ?? context.l10n.commonCancel,
|
||||
confirmLabel: confirmLabel ?? context.l10n.commonChange,
|
||||
onConfirm: onConfirm,
|
||||
),
|
||||
);
|
||||
return r ?? false;
|
||||
|
||||
Reference in New Issue
Block a user