Figma 설명 프레임 반영 — 로그인·온보딩·스플래시 · HOME · GALAXY · 퀘스트·이클립스·알림
This commit is contained in:
@@ -26,6 +26,7 @@ import 'quest_reward_screen.dart';
|
||||
import 'widgets/quest_card_tile.dart';
|
||||
import 'widgets/time_attack_countdown.dart';
|
||||
import '../../core/i18n/app_locale.dart';
|
||||
import '../../core/ui/sheet_route.dart';
|
||||
|
||||
/// 퀘스트 상세 — 화면 대조 원장 F4.
|
||||
///
|
||||
@@ -118,6 +119,9 @@ class _QuestDetailScreenState extends State<QuestDetailScreen> {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
actions: [
|
||||
// 명세 `548:19275` — 더보기는 *사용자 퀘스트 중 타인 퀘스트에서만 노출*. 내 퀘스트의
|
||||
// 수정 · 취소는 확정 정책(C-07 · B-30)이 더한 항목이라 생성자에게도 둔다. 운영 Quest 는 없음.
|
||||
if (ugc)
|
||||
PopupMenuButton<String>(
|
||||
tooltip: context.l10n.commonMore,
|
||||
icon: const ImageIcon(
|
||||
@@ -128,7 +132,7 @@ class _QuestDetailScreenState extends State<QuestDetailScreen> {
|
||||
onSelected: (value) => switch (value) {
|
||||
'edit' => _edit(detail),
|
||||
'cancel' => _cancel(detail),
|
||||
_ => _report(card.title),
|
||||
_ => _report(card),
|
||||
},
|
||||
itemBuilder: (_) => [
|
||||
// 공개 후 수정은 생성자의 제목 · 설명뿐이다(C-07).
|
||||
@@ -146,10 +150,11 @@ class _QuestDetailScreenState extends State<QuestDetailScreen> {
|
||||
value: 'cancel',
|
||||
child: Text(context.l10n.questCancel),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: 'report',
|
||||
child: Text(context.l10n.reportTitle),
|
||||
),
|
||||
if (stage != QuestStage.mine)
|
||||
PopupMenuItem(
|
||||
value: 'report',
|
||||
child: Text(context.l10n.reportTitle),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
@@ -406,9 +411,11 @@ class _QuestDetailScreenState extends State<QuestDetailScreen> {
|
||||
detail,
|
||||
stage,
|
||||
blocked,
|
||||
// 정원이 찬 퀘스트도 종료다(명세 `523:18176` 종료 시점 1) — 시작 전인 사람만 막힌다.
|
||||
closed:
|
||||
detail.endsAt != null &&
|
||||
!ServerClock.now().isBefore(detail.endsAt!),
|
||||
(detail.endsAt != null &&
|
||||
!ServerClock.now().isBefore(detail.endsAt!)) ||
|
||||
(questFull(card) && stage == QuestStage.notStarted),
|
||||
)
|
||||
: TimeAttackCountdown(
|
||||
endsAt: endsAt,
|
||||
@@ -959,9 +966,13 @@ class _QuestDetailScreenState extends State<QuestDetailScreen> {
|
||||
|
||||
// 기본 보상은 이미 원장에 있다. 광고는 그 위의 선택적 증분이다(원장 B-03 · C-122) —
|
||||
// 광고가 실패해도 기본 보상은 그대로다.
|
||||
final bonus =
|
||||
await showQuestAdBonus(context, bonusPop: detail.card.adBonusPop) ??
|
||||
false;
|
||||
// 일 한도를 다 쓴 사람에게는 모달을 띄우지 않는다(별첨 1 ⑥ 「일일 한도 도달 시 노출 제한」) —
|
||||
// 띄우면 광고를 끝까지 보고도 증분 0 이다.
|
||||
final adOpen = await widget.repository.adAvailable();
|
||||
if (!mounted) return;
|
||||
final bonus = adOpen &&
|
||||
(await showQuestAdBonus(context, bonusPop: detail.card.adBonusPop) ??
|
||||
false);
|
||||
if (!mounted) return;
|
||||
int? adPop;
|
||||
if (bonus) {
|
||||
@@ -979,6 +990,13 @@ class _QuestDetailScreenState extends State<QuestDetailScreen> {
|
||||
if (mounted) _toast(error.message);
|
||||
}
|
||||
if (!mounted) return;
|
||||
} else if (_adShown) {
|
||||
// 명세 `518:88645` — 광고 종료(100% 시청 또는 skip) 후 *광고 시청 후 애니메이션*. 끝까지
|
||||
// 보지 않았으니 증분은 없다(B-03) — 애니메이션만 거쳐 완료 화면으로 간다.
|
||||
await _warp(QuestAdWarpScreen.after(
|
||||
until: Future<void>.delayed(const Duration(milliseconds: 1200)),
|
||||
));
|
||||
if (!mounted) return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1003,7 +1021,11 @@ class _QuestDetailScreenState extends State<QuestDetailScreen> {
|
||||
/// 끝까지 봤으면 노출 ID, 아니면 null 이다. 불러오지 못했거나 중간에 닫았으면
|
||||
/// 그 사실을 알리고 **기본 보상으로 넘어간다**(원장 B-03 — 실패 시 기본 보상 지급 ·
|
||||
/// 횟수 미차감). 대기 중 「건너뛰기」를 누르면 늦게 온 광고는 띄우지 않고 버린다.
|
||||
/// 이번 보상에서 광고가 실제로 화면에 떴는가 — skip 으로 닫았을 때도 시청 후 애니메이션을 거친다.
|
||||
bool _adShown = false;
|
||||
|
||||
Future<String?> _watchAd(QuestReward reward) async {
|
||||
_adShown = false;
|
||||
RewardedAdHandle? ad;
|
||||
BackendException? failure;
|
||||
final loading = widget.repository
|
||||
@@ -1030,6 +1052,7 @@ class _QuestDetailScreenState extends State<QuestDetailScreen> {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
_adShown = true;
|
||||
final impression = await loaded.show();
|
||||
if (impression == null && mounted) {
|
||||
_toast(context.l10n.adNotFinished);
|
||||
@@ -1065,14 +1088,16 @@ class _QuestDetailScreenState extends State<QuestDetailScreen> {
|
||||
rootNavigator: true,
|
||||
).push<void>(MaterialPageRoute(builder: (_) => screen));
|
||||
|
||||
Future<void> _report(String title) async {
|
||||
Future<void> _report(QuestCard card) async {
|
||||
final reason = await Navigator.of(context, rootNavigator: true)
|
||||
.push<String>(
|
||||
MaterialPageRoute(
|
||||
// 명세 `548:21389` — 신고 화면은 *페이지 아래 move out* 으로 닫힌다.
|
||||
SheetRoute(
|
||||
builder: (_) => ReportScreen(
|
||||
// 신고 정보 영역 — *작성자 닉네임 · 게시물 제목*. 작성자 칸에 제목이 들어가던 것을 바로잡음.
|
||||
target: ModerationTarget(
|
||||
authorName: title,
|
||||
title: context.l10n.questNamed(title),
|
||||
authorName: card.authorName,
|
||||
title: card.title,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user