번역 평가 버튼 · 내 퀘스트 검토 버튼 · MY 질문 상세 · MY 완료 퀘스트 수 · 행성/성운 칸 · 라운지 빈 상태 수정
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
import 'dart:math' as math;
|
||||
|
||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||
import 'package:firebase_storage/firebase_storage.dart';
|
||||
|
||||
@@ -14,7 +16,8 @@ import '../../core/i18n/app_locale.dart';
|
||||
/// 쓰면 어느 것이 진짜인지 화면에서 알 수 없다.
|
||||
///
|
||||
/// 읽는 곳 — **서버가 실제로 쓰는 필드**를 읽는다.
|
||||
/// - 프로필 `users/{uid}` (nickname · photoUrl · originNo · steps · completedQuests)
|
||||
/// - 프로필 `users/{uid}` (nickname · photoUrl · originNo · steps)
|
||||
/// - 완료 퀘스트 `users/{uid}/verifiedCompletions` 표식 수(무효 제외)
|
||||
/// - 누적 기여 POP `users/{uid}.totalContributedPop`(`fn-pop-spend` 가 갱신, 설계서 §3.6)
|
||||
/// - 레벨 `users/{uid}.{userExp, userLevel}` · Passport `users/{uid}.passportTier`
|
||||
/// (C-18 · B-65). 필드가 없는 기존 사용자는 Lv.1 · SILVER 로 둔다.
|
||||
@@ -46,6 +49,18 @@ class FirestoreMyRepository extends SampleMyRepository {
|
||||
|
||||
final userF = db.doc('users/$uid').get();
|
||||
final walletF = db.doc('users/$uid/wallet/main').get();
|
||||
// 완료 퀘스트 = 검증 활동 표식 수 − 무효 표식 수. 서버 `verifiedQuestCountOf`(fpi.ts)와 같은 식.
|
||||
// `users.completedQuests` 는 쓰는 서버 코드가 없어 늘 0 이었다.
|
||||
final done = db.collection('users/$uid/verifiedCompletions');
|
||||
// 규칙 배포 전 서버에서는 이 읽기가 거부된다 — MY 전체를 떨어뜨리지 않고 0 으로 둔다.
|
||||
final completedF =
|
||||
Future.wait([
|
||||
done.count().get(),
|
||||
done.where('voided', isEqualTo: true).count().get(),
|
||||
]).then(
|
||||
(c) => math.max(0, (c[0].count ?? 0) - (c[1].count ?? 0)),
|
||||
onError: (Object _) => 0,
|
||||
);
|
||||
final user = (await userF).data() ?? const <String, dynamic>{};
|
||||
final wallet = (await walletF).data() ?? const <String, dynamic>{};
|
||||
|
||||
@@ -83,7 +98,7 @@ class FirestoreMyRepository extends SampleMyRepository {
|
||||
passportTier: PassportTier.parse(user.strOr('passportTier')),
|
||||
balancePop: wallet.intOr('activityBalance'),
|
||||
steps: user.intOr('steps'),
|
||||
completedQuests: user.intOr('completedQuests'),
|
||||
completedQuests: await completedF,
|
||||
planetTiers: tiers,
|
||||
photoUrl: photoUrl.isEmpty ? null : photoUrl,
|
||||
originNo: user.containsKey('originNo') ? user.intOr('originNo') : null,
|
||||
@@ -127,10 +142,9 @@ class FirestoreMyRepository extends SampleMyRepository {
|
||||
@override
|
||||
Future<void> updateNickname(String nickname) async {
|
||||
final uid = _backend.requireUid;
|
||||
await _backend.db.doc('users/$uid').set(
|
||||
{'nickname': nickname},
|
||||
SetOptions(merge: true),
|
||||
);
|
||||
await _backend.db.doc('users/$uid').set({
|
||||
'nickname': nickname,
|
||||
}, SetOptions(merge: true));
|
||||
}
|
||||
|
||||
/// POP 원장 — S-M-WALLET · 명세 `554:23732`.
|
||||
@@ -174,22 +188,24 @@ class FirestoreMyRepository extends SampleMyRepository {
|
||||
final reasonCode = data.strOr('reasonCode');
|
||||
final label = popReasonLabel(reasonCode);
|
||||
final at = data.dateOr('createdAt', _epoch);
|
||||
rows.add(_LedgerRow(
|
||||
reasonCode: reasonCode,
|
||||
// Zap 한 행이 몇 번분인가. `fn-eclipse-defend` 가 `count` 를 묶어 받으므로
|
||||
// 행 하나가 여러 번일 수 있다. 없는 문서는 한 번으로 본다.
|
||||
count: data.intOr('count', 1).clamp(1, 1 << 30),
|
||||
entry: PopLedgerEntry(
|
||||
at: at,
|
||||
title: label.title,
|
||||
type: label.type,
|
||||
kind: PopKind.parse(data.strOr('popType')),
|
||||
txType: PopTxType.parse(data.strOr('type', 'grant')),
|
||||
delta: data.intOr('amount'),
|
||||
balanceAfter: data.intOr('balanceAfter'),
|
||||
cursor: PopCursor(at: at, id: doc.id),
|
||||
rows.add(
|
||||
_LedgerRow(
|
||||
reasonCode: reasonCode,
|
||||
// Zap 한 행이 몇 번분인가. `fn-eclipse-defend` 가 `count` 를 묶어 받으므로
|
||||
// 행 하나가 여러 번일 수 있다. 없는 문서는 한 번으로 본다.
|
||||
count: data.intOr('count', 1).clamp(1, 1 << 30),
|
||||
entry: PopLedgerEntry(
|
||||
at: at,
|
||||
title: label.title,
|
||||
type: label.type,
|
||||
kind: PopKind.parse(data.strOr('popType')),
|
||||
txType: PopTxType.parse(data.strOr('type', 'grant')),
|
||||
delta: data.intOr('amount'),
|
||||
balanceAfter: data.intOr('balanceAfter'),
|
||||
cursor: PopCursor(at: at, id: doc.id),
|
||||
),
|
||||
),
|
||||
));
|
||||
);
|
||||
}
|
||||
return _foldZap(rows);
|
||||
}
|
||||
@@ -262,7 +278,10 @@ class FirestoreMyRepository extends SampleMyRepository {
|
||||
PaymentEntry _payment(String id, Map<String, dynamic> data) => PaymentEntry(
|
||||
id: id,
|
||||
at: data.dateOr('purchasedAt', _epoch),
|
||||
itemName: data.strOr('itemName', data.strOr('productId', appL10n.payItemFallback)),
|
||||
itemName: data.strOr(
|
||||
'itemName',
|
||||
data.strOr('productId', appL10n.payItemFallback),
|
||||
),
|
||||
price: data.intOr('priceAmount'),
|
||||
method: data.strOr('method', appL10n.payMethodInApp),
|
||||
state: paymentStateOf(data.strOr('state')),
|
||||
@@ -416,10 +435,9 @@ class FirestoreMyRepository extends SampleMyRepository {
|
||||
);
|
||||
}
|
||||
final url = await ref.getDownloadURL();
|
||||
await _backend.db.doc('users/$uid').set(
|
||||
{'photoUrl': url},
|
||||
SetOptions(merge: true),
|
||||
);
|
||||
await _backend.db.doc('users/$uid').set({
|
||||
'photoUrl': url,
|
||||
}, SetOptions(merge: true));
|
||||
return url;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user