알림함 항목 생성 · 알림 이동 · 브리핑 항목 승인·상세 · 푸시 토픽을 가입 팬덤 전체로
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../core/theme/tokens.dart';
|
||||
@@ -9,6 +11,25 @@ import '../../core/i18n/app_locale.dart';
|
||||
/// 이 화면을 임포트한 곳이 모델까지 함께 쓰므로 그대로 내보낸다.
|
||||
export 'notification_data.dart';
|
||||
|
||||
/// 알림이 가리키는 문서 경로 → 열 화면. 앱 루트(`app.dart`)가 저장소를 쥐고 준다.
|
||||
/// 화면을 만들지 못하는 경로(아직 이동 화면이 없는 종류)는 null — 누르면 읽음만 남는다.
|
||||
class NotificationTargets extends InheritedWidget {
|
||||
const NotificationTargets({
|
||||
super.key,
|
||||
required this.screenFor,
|
||||
required super.child,
|
||||
});
|
||||
|
||||
final Widget? Function(String targetPath) screenFor;
|
||||
|
||||
static NotificationTargets? maybeOf(BuildContext context) =>
|
||||
context.dependOnInheritedWidgetOfExactType<NotificationTargets>();
|
||||
|
||||
@override
|
||||
bool updateShouldNotify(NotificationTargets old) =>
|
||||
old.screenFor != screenFor;
|
||||
}
|
||||
|
||||
/// 알림 — 화면 대조 원장 F0 · Figma `229:11265`.
|
||||
class NotificationScreen extends StatefulWidget {
|
||||
const NotificationScreen({super.key, required this.repository});
|
||||
@@ -90,6 +111,13 @@ class _NotificationScreenState extends State<NotificationScreen> {
|
||||
);
|
||||
}
|
||||
|
||||
/// 명세 `518:88353` 종류별 이동 규칙 — 좋아요 · 댓글 → 게시물, 캠페인 달성 → 캠페인,
|
||||
/// 퀘스트 완료 → 퀘스트 상세. 이클립스 · 아이템 사용은 이동하지 않는다.
|
||||
Widget? _screenFor(AppNotification item) => switch (item.target) {
|
||||
final path? => NotificationTargets.maybeOf(context)?.screenFor(path),
|
||||
null => null,
|
||||
};
|
||||
|
||||
Widget _tile(AppNotification item) {
|
||||
final read = _read.contains(item.id);
|
||||
final (fill, icon) = _badge(item.kind);
|
||||
@@ -134,8 +162,8 @@ class _NotificationScreenState extends State<NotificationScreen> {
|
||||
],
|
||||
),
|
||||
),
|
||||
// 이동할 곳이 없는 종류에는 화살표를 그리지 않는다.
|
||||
if (item.kind.navigable) ...[
|
||||
// 이동할 곳이 없는 알림에는 화살표를 그리지 않는다.
|
||||
if (_screenFor(item) != null) ...[
|
||||
const SizedBox(width: 13),
|
||||
const ImageIcon(
|
||||
AppIcons.chevronRight,
|
||||
@@ -152,23 +180,15 @@ class _NotificationScreenState extends State<NotificationScreen> {
|
||||
// 처음 읽은 것만 서버에 알린다. 이미 읽은 알림을 다시 눌러도 쓰기가 없다.
|
||||
final first = _read.add(item.id);
|
||||
setState(() {});
|
||||
if (item.kind.navigable && mounted) {
|
||||
ScaffoldMessenger.of(context)
|
||||
..hideCurrentSnackBar()
|
||||
..showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(context.l10n.notifGoTo('${item.kind.target}')),
|
||||
behavior: SnackBarBehavior.floating,
|
||||
),
|
||||
);
|
||||
}
|
||||
if (!first) return;
|
||||
try {
|
||||
await widget.repository.markRead(item.id);
|
||||
} on Exception {
|
||||
// 읽음 표시가 실패해도 화면을 막지 않는다. 다음 진입에서 서버 값으로
|
||||
if (first) {
|
||||
// 읽음 표시가 실패해도 이동을 막지 않는다. 다음 진입에서 서버 값으로
|
||||
// 되돌아오므로 잃는 것이 없고, 여기서 막으면 알림 자체를 못 읽는다.
|
||||
unawaited(widget.repository.markRead(item.id).catchError((_) {}));
|
||||
}
|
||||
final screen = _screenFor(item);
|
||||
if (screen == null || !mounted) return;
|
||||
await Navigator.of(context)
|
||||
.push<void>(MaterialPageRoute(builder: (_) => screen));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user