알림함 항목 생성 · 알림 이동 · 브리핑 항목 승인·상세 · 푸시 토픽을 가입 팬덤 전체로
This commit is contained in:
@@ -12,11 +12,9 @@ import '../../../core/i18n/app_locale.dart';
|
||||
/// *섹션 숨김, 모두 0 건이면 전체 Briefing 블록을 숨김*. 구획이 없으면 「섹션만
|
||||
/// 숨김」이라는 판정 자체가 성립하지 않는다. 블록 전체 숨김은 홈 화면이 맡는다.
|
||||
///
|
||||
/// **한 줄은 제목만 띄운다.** 출처명·게시시각·AI 요약을 어디에 배치할지는 발주사가
|
||||
/// HOME 기준으로 지정한 Figma 「REFINE v5A · HOME · Default」 확인 후 정한다.
|
||||
/// 데이터는 [BriefingItem] 에 5 요소가 전부 들어와 있으므로 배치만 남는다.
|
||||
///
|
||||
/// 줄을 누르면 원문보기로 나간다(B-75 5 요소 중 「원문보기」).
|
||||
/// **한 줄은 Figma 대로 번호 + 제목만 띄운다.** 정책 원장 B-75(확정) 「각 항목에 출처명 ·
|
||||
/// 게시/발행시각 · 원본 URL · 원문보기 · AI 요약 표시」는 Figma 에 배치가 없어, 줄을 누르면
|
||||
/// 여는 상세 시트([showBriefingDetail])에 모두 싣는다. 시트의 「원문 보기」가 원문으로 나간다.
|
||||
class BriefingList extends StatelessWidget {
|
||||
const BriefingList({super.key, required this.items, this.onOpen});
|
||||
|
||||
@@ -68,7 +66,14 @@ class BriefingList extends StatelessWidget {
|
||||
),
|
||||
for (final item in group.rows) ...[
|
||||
const SizedBox(height: AppSpace.s5),
|
||||
_Row(item: item, onOpen: () => _open(context, item)),
|
||||
_Row(
|
||||
item: item,
|
||||
onOpen: () => showBriefingDetail(
|
||||
context,
|
||||
item,
|
||||
onOriginal: () => _open(context, item),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
],
|
||||
@@ -145,3 +150,78 @@ class _Row extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 브리핑 항목 상세 — B-75 5 요소(출처명 · 게시시각 · 원본 URL · 원문보기 · AI 요약 표시)와
|
||||
/// 공식/팬덤 출처 구분을 한 시트에 싣는다.
|
||||
Future<void> showBriefingDetail(
|
||||
BuildContext context,
|
||||
BriefingItem item, {
|
||||
required Future<void> Function() onOriginal,
|
||||
}) {
|
||||
const muted = TextStyle(
|
||||
fontFamily: AppFonts.body,
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
height: 1.4,
|
||||
color: AppColors.onCosmicMuted,
|
||||
);
|
||||
final l10n = context.l10n;
|
||||
final kind = switch (item.sourceKind) {
|
||||
'official' => l10n.briefingSourceOfficial,
|
||||
'fandom' => l10n.briefingSourceFandom,
|
||||
_ => null,
|
||||
};
|
||||
String two(int v) => v.toString().padLeft(2, '0');
|
||||
final at = item.sourceAt.toLocal();
|
||||
final stamp =
|
||||
'${at.year}.${two(at.month)}.${two(at.day)} ${two(at.hour)}:${two(at.minute)}';
|
||||
return showModalBottomSheet<void>(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
backgroundColor: AppColors.cosmicSurfaceContainer,
|
||||
builder: (sheet) => SafeArea(
|
||||
child: SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(AppSpace.s5),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
Text(
|
||||
item.headline,
|
||||
style: const TextStyle(
|
||||
fontFamily: AppFonts.body,
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w600,
|
||||
height: 1.4,
|
||||
color: AppColors.onCosmic,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: AppSpace.s2),
|
||||
Text([?kind, item.sourceName, stamp].join(' · '), style: muted),
|
||||
const SizedBox(height: AppSpace.s4),
|
||||
Text(l10n.briefingAiSummary, style: muted),
|
||||
const SizedBox(height: AppSpace.s1),
|
||||
Text(
|
||||
item.aiSummary,
|
||||
style: const TextStyle(
|
||||
fontFamily: AppFonts.body,
|
||||
fontSize: 15,
|
||||
height: 1.5,
|
||||
color: AppColors.onCosmicVariant,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: AppSpace.s3),
|
||||
Text(item.sourceUrl, style: muted),
|
||||
const SizedBox(height: AppSpace.s4),
|
||||
FilledButton(
|
||||
onPressed: () async {
|
||||
Navigator.of(sheet).pop();
|
||||
await onOriginal();
|
||||
},
|
||||
child: Text(l10n.briefingViewOriginal),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user