Passport Tier 팬덤 단위 · 활동 Badge · 진화 Emblem · Eclipse 참여 기록

This commit is contained in:
Sn-Kinos
2026-10-05 22:09:10 +09:00
parent 71ec17dc3c
commit cc0f2fb292
36 changed files with 877 additions and 81 deletions
+44 -4
View File
@@ -238,7 +238,7 @@ enum PassportTier {
final String label;
/// 서버 문자열(`users.passportTier`). 없거나 모르는 값은 기본 SILVER.
/// 서버 문자열(`users/{uid}/passports/{fandomId}.tier`). 없거나 모르는 값은 기본 SILVER.
static PassportTier parse(String raw) => PassportTier.values.firstWhere(
(t) => t.label == raw.toUpperCase(),
orElse: () => PassportTier.silver,
@@ -250,13 +250,16 @@ enum PassportTier {
/// *갤럭시에서 선택된 기본 행성의 티어를 메인으로 상단에 위치 ·*
/// *나머지 행성 티어는 티어 높은순으로 소팅*
///
/// 칭호는 개인 `userLevel` 에서만 나온다(C-18). 행성별로는 **기여 실적(POP)만**
/// 보인다 — 행성 기여로 칭호를 매기면 MY 카드와 칭호가 갈린다(인계 결함).
/// 칭호는 개인 `userLevel` 에서만 나온다(C-18). 행성별로는 **그 팬덤 Passport Tier 와 기여 실적**이
/// 보인다 — Tier 는 사용자 × 팬덤 단위다(10/3 통합회신 #10 · 9/21 회신 5절).
class PlanetTier {
const PlanetTier({
required this.fandomName,
required this.contributedPop,
this.isPrimary = false,
this.fandomId = '',
this.passportTier = PassportTier.silver,
this.joined = true,
});
final String fandomName;
@@ -264,6 +267,39 @@ class PlanetTier {
/// 갤럭시에서 고른 기본 행성인가.
final bool isPrimary;
final String fandomId;
/// 그 팬덤의 Passport Tier — `users/{uid}/passports/{fandomId}.tier`.
final PassportTier passportTier;
/// 지금 가입 중인가. false 면 탈퇴한 팬덤의 보존 기록(FX §7.1 ARCHIVED).
final bool joined;
}
/// 보유 Badge 한 건 — `users/{uid}/badges/{badgeId}`(서버 판정). 상세정의 4장 · 22장 · 별첨 B.
class MyBadge {
const MyBadge({required this.id, required this.kind, required this.stage, this.axis = '',
this.memberNo, this.toStage});
final String id;
/// `activity`(활동 카테고리 7단계) · `founder100`(팬덤/성운 최초 100명) · `evolution`(진화 기념).
final String kind;
final int stage;
/// 활동 Badge 카테고리(FPI 축 키).
final String axis;
final int? memberNo;
final int? toStage;
/// 화면 표기. 축 이름은 답변서 v1.1 Q3 확정 표기(영문)라 번역하지 않는다.
String get label => switch (kind) {
'activity' => '${axis.toUpperCase()} · $stage/7',
'founder100' => 'FIRST 100 · #${(memberNo ?? 0).toString().padLeft(3, '0')}',
'evolution' => 'EVOLUTION · STAGE ${toStage ?? stage}',
_ => id,
};
}
/// MY 첫 화면이 쓰는 값.
@@ -279,6 +315,7 @@ class MyProfile {
required this.steps,
required this.completedQuests,
required this.planetTiers,
this.badges = const [],
this.photoLabel,
this.photoUrl,
this.originNo,
@@ -296,7 +333,7 @@ class MyProfile {
/// 레벨 — `users.userLevel`. **서버가 준 값**이며 앱이 다시 매기지 않는다.
final int userLevel;
/// Passport Tier — `users.passportTier`(B-65).
/// 대표 팬덤의 Passport Tier(B-65 · 10/3 #10 사용자 × 팬덤). 팬덤별 값은 [planetTiers].
final PassportTier passportTier;
/// 보유 POP(활동).
@@ -308,6 +345,9 @@ class MyProfile {
final List<PlanetTier> planetTiers;
/// 보유 Badge — 별첨 1 ②-5 「등급·배지 부여·프로필 표시」.
final List<MyBadge> badges;
/// 프로필 사진. 자산이 아직 없어 라벨로 둔다.
final String? photoLabel;