팬덤 탈퇴 · 성운 창립자 권한 승계 · 대표 팬덤 자동 지정
This commit is contained in:
@@ -85,8 +85,6 @@ class Backend {
|
||||
static String _message(String code, FirebaseFunctionsException error) {
|
||||
final details = error.details;
|
||||
return switch (code) {
|
||||
// 성운 창립자 탄생 전 탈퇴(Q-PN-03).
|
||||
'E-FDM-05' => appL10n.fandomFounderLeaveBlocked,
|
||||
// 검증 활동(Quest 완료) 미달 — 서버가 `done` · `need` 를 싣는다(Campaign 개설자격 A안).
|
||||
'E-CMP-04'
|
||||
when details is Map &&
|
||||
|
||||
@@ -329,6 +329,9 @@ abstract interface class PlanetRepository {
|
||||
/// 이 행성/성운에 가입 — `fn-fandom-join`. 이미 멤버면 서버가 아무것도 하지 않는다.
|
||||
Future<void> join();
|
||||
|
||||
/// 탈퇴 — `fn-fandom-leave`. 대표 팬덤 자동 지정(C-101) · 성운 창립자 승계(C-100)는 서버가 한다.
|
||||
Future<void> leave();
|
||||
|
||||
/// 명세 — *최초 5개, 스크롤 시 5개씩 호출*.
|
||||
Future<List<LoungePost>> morePosts({required int offset, int limit = 5});
|
||||
|
||||
@@ -391,6 +394,9 @@ class SamplePlanetRepository implements PlanetRepository {
|
||||
@override
|
||||
Future<void> join() async {}
|
||||
|
||||
@override
|
||||
Future<void> leave() async {}
|
||||
|
||||
@override
|
||||
Future<PlanetHistoryPage> history({String? cursor, bool personal = false}) async => const PlanetHistoryPage(items: []);
|
||||
@override
|
||||
|
||||
@@ -6,6 +6,7 @@ import '../../core/data/backend.dart' show BackendException;
|
||||
import '../../core/data/planet_visual_state.dart';
|
||||
|
||||
import '../../core/theme/tokens.dart';
|
||||
import '../../core/ui/app_dialog.dart';
|
||||
import '../../core/ui/app_icons.dart';
|
||||
import '../eclipse/eclipse_data.dart';
|
||||
import '../eclipse/widgets/eclipse_banner.dart';
|
||||
@@ -74,6 +75,30 @@ class _PlanetDetailScreenState extends State<PlanetDetailScreen> {
|
||||
}
|
||||
}
|
||||
|
||||
/// 「가입중」 알약 → 탈퇴 시트 → 확인 창 — Figma `343:213428`(탈퇴하기 · 취소) ·
|
||||
/// `343:213582`(행성 탈퇴를 할까요?). 대표 팬덤 자동 지정 · 성운 창립자 승계는 서버 몫이다(C-100 · C-101).
|
||||
void _leave() => showActionSheet(context, [
|
||||
ActionSheetItem(context.l10n.planetLeave, () async {
|
||||
final ok = await showConfirmDialog(
|
||||
context,
|
||||
context.l10n.planetLeaveConfirmTitle,
|
||||
body: context.l10n.planetLeaveConfirmBody,
|
||||
confirmLabel: context.l10n.commonConfirm,
|
||||
);
|
||||
if (!ok) return;
|
||||
try {
|
||||
await widget.repository.leave();
|
||||
if (mounted) {
|
||||
setState(() { _detail = widget.repository.load(nebula: widget.nebula); });
|
||||
}
|
||||
} on BackendException catch (e) {
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(e.message)));
|
||||
}
|
||||
}
|
||||
}),
|
||||
]);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => FutureBuilder<PlanetDetail>(
|
||||
future: _detail,
|
||||
@@ -127,7 +152,7 @@ class _PlanetDetailScreenState extends State<PlanetDetailScreen> {
|
||||
ownerName: d.ownerName,
|
||||
ownerPhoto: d.ownerPhoto,
|
||||
joined: d.joined,
|
||||
onJoin: d.joined ? null : _join,
|
||||
onJoin: d.joined ? _leave : _join,
|
||||
onOpenHistory: () => Navigator.of(context).push(
|
||||
MaterialPageRoute<void>(
|
||||
builder: (_) => PlanetHistoryScreen(
|
||||
|
||||
@@ -103,6 +103,9 @@ class FirestorePlanetRepository implements PlanetRepository {
|
||||
@override
|
||||
Future<void> join() => _backend.call('fnFandomJoin', {'fandomId': fandomId});
|
||||
|
||||
@override
|
||||
Future<void> leave() => _backend.call('fnFandomLeave', {'fandomId': fandomId});
|
||||
|
||||
@override
|
||||
Future<PlanetDetail> load({required bool nebula}) async {
|
||||
final db = _backend.db;
|
||||
|
||||
@@ -43,7 +43,7 @@ class PlanetDetailHero extends StatelessWidget {
|
||||
final bool joined;
|
||||
final VoidCallback? onHeroParticipate;
|
||||
|
||||
/// 「가입하기」 알약을 눌렀을 때. 이미 가입했으면 null 이고 알약은 상태만 보인다.
|
||||
/// 알약을 눌렀을 때 — 「가입하기」면 가입, 「가입중」이면 탈퇴 시트. null 이면 상태만 보인다.
|
||||
final VoidCallback? onJoin;
|
||||
|
||||
/// 성운 상세(`373:442708`)는 규모 칩과 FPI 줄이 없다.
|
||||
|
||||
@@ -630,7 +630,9 @@
|
||||
"nebulaCreateFailed": "Couldn't create the nebula. Please try again later.",
|
||||
"nebulaAlreadyToday": "You've already created a nebula today.",
|
||||
"nebulaCreatedKeepPrimary": "Nebula created. Your main planet stays the same because it can only be changed 30 days after the last change.",
|
||||
"fandomFounderLeaveBlocked": "You can leave after the planet is born.",
|
||||
"planetLeave": "Leave",
|
||||
"planetLeaveConfirmTitle": "Leave this planet?",
|
||||
"planetLeaveConfirmBody": "Contributed POP and activity\ncannot be undone.",
|
||||
"sortPopular": "Popular",
|
||||
"campaignSortContributing": "Contributing",
|
||||
"campaignStatusReview": "This campaign is under review.",
|
||||
|
||||
@@ -630,7 +630,9 @@
|
||||
"nebulaCreateFailed": "No se pudo crear la nebulosa. Inténtalo más tarde.",
|
||||
"nebulaAlreadyToday": "Ya creaste una nebulosa hoy.",
|
||||
"nebulaCreatedKeepPrimary": "Nebulosa creada. Tu planeta principal no cambia: solo puede cambiarse 30 días después del último cambio.",
|
||||
"fandomFounderLeaveBlocked": "Podrás salir cuando nazca el planeta.",
|
||||
"planetLeave": "Salir",
|
||||
"planetLeaveConfirmTitle": "¿Salir de este planeta?",
|
||||
"planetLeaveConfirmBody": "El POP aportado y la actividad\nno se pueden deshacer.",
|
||||
"sortPopular": "Populares",
|
||||
"campaignSortContributing": "Contribuyendo",
|
||||
"campaignStatusReview": "Esta campaña está en revisión.",
|
||||
|
||||
@@ -630,7 +630,9 @@
|
||||
"nebulaCreateFailed": "星雲を作成できませんでした。しばらくしてから再度お試しください。",
|
||||
"nebulaAlreadyToday": "今日はすでに星雲を作成しました。",
|
||||
"nebulaCreatedKeepPrimary": "星雲を作成しました。メイン惑星は前回の変更から30日経過後に変更できるため、そのままです。",
|
||||
"fandomFounderLeaveBlocked": "惑星が誕生した後に脱退できます。",
|
||||
"planetLeave": "脱退する",
|
||||
"planetLeaveConfirmTitle": "惑星から脱退しますか?",
|
||||
"planetLeaveConfirmBody": "貢献したPOPと活動は\n元に戻せません。",
|
||||
"sortPopular": "人気順",
|
||||
"campaignSortContributing": "貢献中",
|
||||
"campaignStatusReview": "運営が審査中のキャンペーンです。",
|
||||
|
||||
@@ -1248,7 +1248,9 @@
|
||||
"nebulaCreateFailed": "성운을 만들지 못했습니다. 잠시 후 다시 시도해 주세요.",
|
||||
"nebulaAlreadyToday": "이미 오늘 성운을 생성하셨습니다.",
|
||||
"nebulaCreatedKeepPrimary": "성운을 만들었습니다. 기본 행성은 최근 변경 후 30일이 지나야 바꿀 수 있어 그대로 유지됩니다.",
|
||||
"fandomFounderLeaveBlocked": "행성이 탄생한 뒤에 탈퇴할 수 있어요.",
|
||||
"planetLeave": "탈퇴하기",
|
||||
"planetLeaveConfirmTitle": "행성 탈퇴를 할까요?",
|
||||
"planetLeaveConfirmBody": "기여한 POP와 활동은 모두\n되돌릴 수 없어요.",
|
||||
"sortPopular": "인기순",
|
||||
"campaignSortContributing": "기여중",
|
||||
"campaignStatusReview": "운영 검수 중인 캠페인입니다.",
|
||||
|
||||
@@ -3884,11 +3884,23 @@ abstract class AppLocalizations {
|
||||
/// **'성운을 만들었습니다. 기본 행성은 최근 변경 후 30일이 지나야 바꿀 수 있어 그대로 유지됩니다.'**
|
||||
String get nebulaCreatedKeepPrimary;
|
||||
|
||||
/// No description provided for @fandomFounderLeaveBlocked.
|
||||
/// No description provided for @planetLeave.
|
||||
///
|
||||
/// In ko, this message translates to:
|
||||
/// **'행성이 탄생한 뒤에 탈퇴할 수 있어요.'**
|
||||
String get fandomFounderLeaveBlocked;
|
||||
/// **'탈퇴하기'**
|
||||
String get planetLeave;
|
||||
|
||||
/// No description provided for @planetLeaveConfirmTitle.
|
||||
///
|
||||
/// In ko, this message translates to:
|
||||
/// **'행성 탈퇴를 할까요?'**
|
||||
String get planetLeaveConfirmTitle;
|
||||
|
||||
/// No description provided for @planetLeaveConfirmBody.
|
||||
///
|
||||
/// In ko, this message translates to:
|
||||
/// **'기여한 POP와 활동은 모두\n되돌릴 수 없어요.'**
|
||||
String get planetLeaveConfirmBody;
|
||||
|
||||
/// No description provided for @sortPopular.
|
||||
///
|
||||
|
||||
@@ -2190,8 +2190,14 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||
'Nebula created. Your main planet stays the same because it can only be changed 30 days after the last change.';
|
||||
|
||||
@override
|
||||
String get fandomFounderLeaveBlocked =>
|
||||
'You can leave after the planet is born.';
|
||||
String get planetLeave => 'Leave';
|
||||
|
||||
@override
|
||||
String get planetLeaveConfirmTitle => 'Leave this planet?';
|
||||
|
||||
@override
|
||||
String get planetLeaveConfirmBody =>
|
||||
'Contributed POP and activity\ncannot be undone.';
|
||||
|
||||
@override
|
||||
String get sortPopular => 'Popular';
|
||||
|
||||
@@ -2200,8 +2200,14 @@ class AppLocalizationsEs extends AppLocalizations {
|
||||
'Nebulosa creada. Tu planeta principal no cambia: solo puede cambiarse 30 días después del último cambio.';
|
||||
|
||||
@override
|
||||
String get fandomFounderLeaveBlocked =>
|
||||
'Podrás salir cuando nazca el planeta.';
|
||||
String get planetLeave => 'Salir';
|
||||
|
||||
@override
|
||||
String get planetLeaveConfirmTitle => '¿Salir de este planeta?';
|
||||
|
||||
@override
|
||||
String get planetLeaveConfirmBody =>
|
||||
'El POP aportado y la actividad\nno se pueden deshacer.';
|
||||
|
||||
@override
|
||||
String get sortPopular => 'Populares';
|
||||
|
||||
@@ -2118,7 +2118,13 @@ class AppLocalizationsJa extends AppLocalizations {
|
||||
'星雲を作成しました。メイン惑星は前回の変更から30日経過後に変更できるため、そのままです。';
|
||||
|
||||
@override
|
||||
String get fandomFounderLeaveBlocked => '惑星が誕生した後に脱退できます。';
|
||||
String get planetLeave => '脱退する';
|
||||
|
||||
@override
|
||||
String get planetLeaveConfirmTitle => '惑星から脱退しますか?';
|
||||
|
||||
@override
|
||||
String get planetLeaveConfirmBody => '貢献したPOPと活動は\n元に戻せません。';
|
||||
|
||||
@override
|
||||
String get sortPopular => '人気順';
|
||||
|
||||
@@ -2119,7 +2119,13 @@ class AppLocalizationsKo extends AppLocalizations {
|
||||
'성운을 만들었습니다. 기본 행성은 최근 변경 후 30일이 지나야 바꿀 수 있어 그대로 유지됩니다.';
|
||||
|
||||
@override
|
||||
String get fandomFounderLeaveBlocked => '행성이 탄생한 뒤에 탈퇴할 수 있어요.';
|
||||
String get planetLeave => '탈퇴하기';
|
||||
|
||||
@override
|
||||
String get planetLeaveConfirmTitle => '행성 탈퇴를 할까요?';
|
||||
|
||||
@override
|
||||
String get planetLeaveConfirmBody => '기여한 POP와 활동은 모두\n되돌릴 수 없어요.';
|
||||
|
||||
@override
|
||||
String get sortPopular => '인기순';
|
||||
|
||||
@@ -2115,7 +2115,13 @@ class AppLocalizationsZh extends AppLocalizations {
|
||||
String get nebulaCreatedKeepPrimary => '星云已创建。默认星球需在上次更改 30 天后才能更换,因此保持不变。';
|
||||
|
||||
@override
|
||||
String get fandomFounderLeaveBlocked => '行星诞生后才能退出。';
|
||||
String get planetLeave => '退出';
|
||||
|
||||
@override
|
||||
String get planetLeaveConfirmTitle => '要退出这个行星吗?';
|
||||
|
||||
@override
|
||||
String get planetLeaveConfirmBody => '已贡献的POP和活动\n都无法撤回。';
|
||||
|
||||
@override
|
||||
String get sortPopular => '热门';
|
||||
|
||||
@@ -630,7 +630,9 @@
|
||||
"nebulaCreateFailed": "无法创建星云,请稍后再试。",
|
||||
"nebulaAlreadyToday": "你今天已创建过星云。",
|
||||
"nebulaCreatedKeepPrimary": "星云已创建。默认星球需在上次更改 30 天后才能更换,因此保持不变。",
|
||||
"fandomFounderLeaveBlocked": "行星诞生后才能退出。",
|
||||
"planetLeave": "退出",
|
||||
"planetLeaveConfirmTitle": "要退出这个行星吗?",
|
||||
"planetLeaveConfirmBody": "已贡献的POP和活动\n都无法撤回。",
|
||||
"sortPopular": "热门",
|
||||
"campaignSortContributing": "贡献中",
|
||||
"campaignStatusReview": "该活动正在接受运营审核。",
|
||||
|
||||
@@ -658,9 +658,6 @@ class RemoteAppLocalizations extends AppLocalizations {
|
||||
@override
|
||||
String get evoTransition => _remote['evoTransition'] ?? _base.evoTransition;
|
||||
|
||||
@override
|
||||
String get fandomFounderLeaveBlocked => _remote['fandomFounderLeaveBlocked'] ?? _base.fandomFounderLeaveBlocked;
|
||||
|
||||
@override
|
||||
String get focusActive => _remote['focusActive'] ?? _base.focusActive;
|
||||
|
||||
@@ -1426,6 +1423,15 @@ class RemoteAppLocalizations extends AppLocalizations {
|
||||
@override
|
||||
String planetLastUpdated(String time) => _fill('planetLastUpdated', {'time': time}) ?? _base.planetLastUpdated(time);
|
||||
|
||||
@override
|
||||
String get planetLeave => _remote['planetLeave'] ?? _base.planetLeave;
|
||||
|
||||
@override
|
||||
String get planetLeaveConfirmBody => _remote['planetLeaveConfirmBody'] ?? _base.planetLeaveConfirmBody;
|
||||
|
||||
@override
|
||||
String get planetLeaveConfirmTitle => _remote['planetLeaveConfirmTitle'] ?? _base.planetLeaveConfirmTitle;
|
||||
|
||||
@override
|
||||
String get planetLiveRanking => _remote['planetLiveRanking'] ?? _base.planetLiveRanking;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user