feat(client): notices api + provider(未读计数/markAllRead/延迟首拉)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01P9G7E3wmAYL9KeYCVZVsqu
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
// notices_api.dart — 系统通知代理端点封装(JWT 经 ApiClient 自动注入)。
|
||||
import '../l10n/app_text.dart';
|
||||
import 'api_client.dart';
|
||||
|
||||
class NoticeItem {
|
||||
const NoticeItem({
|
||||
required this.id,
|
||||
required this.type,
|
||||
required this.titleZh,
|
||||
required this.titleEn,
|
||||
required this.bodyZh,
|
||||
required this.bodyEn,
|
||||
required this.link,
|
||||
required this.publishedAt,
|
||||
required this.unread,
|
||||
});
|
||||
|
||||
final int id;
|
||||
final String type, titleZh, titleEn, bodyZh, bodyEn, link;
|
||||
final DateTime? publishedAt;
|
||||
final bool unread;
|
||||
|
||||
/// 单显文案:zh 取中文,非 zh(en/ja/ko/ru/es)一律取英文兜底。
|
||||
String title(AppLang lang) => lang == AppLang.zh ? titleZh : titleEn;
|
||||
|
||||
factory NoticeItem.fromJson(Map<String, dynamic> j) {
|
||||
DateTime? published;
|
||||
final raw = j['published_at'] as String?;
|
||||
if (raw != null && raw.isNotEmpty) {
|
||||
try {
|
||||
published = DateTime.parse(raw);
|
||||
} catch (_) {
|
||||
published = null;
|
||||
}
|
||||
}
|
||||
return NoticeItem(
|
||||
id: (j['id'] as num?)?.toInt() ?? 0,
|
||||
type: j['type'] as String? ?? '',
|
||||
titleZh: j['title_zh'] as String? ?? '',
|
||||
titleEn: j['title_en'] as String? ?? '',
|
||||
bodyZh: j['body_zh'] as String? ?? '',
|
||||
bodyEn: j['body_en'] as String? ?? '',
|
||||
link: j['link'] as String? ?? '',
|
||||
publishedAt: published,
|
||||
unread: j['unread'] as bool? ?? false,
|
||||
);
|
||||
}
|
||||
|
||||
NoticeItem copyWith({bool? unread}) => NoticeItem(
|
||||
id: id,
|
||||
type: type,
|
||||
titleZh: titleZh,
|
||||
titleEn: titleEn,
|
||||
bodyZh: bodyZh,
|
||||
bodyEn: bodyEn,
|
||||
link: link,
|
||||
publishedAt: publishedAt,
|
||||
unread: unread ?? this.unread,
|
||||
);
|
||||
}
|
||||
|
||||
class NoticesData {
|
||||
const NoticesData({required this.items, required this.unreadCount});
|
||||
|
||||
final List<NoticeItem> items;
|
||||
final int unreadCount;
|
||||
|
||||
factory NoticesData.fromJson(Map<String, dynamic> j) {
|
||||
final rawItems = j['notices'] as List<dynamic>? ?? const [];
|
||||
return NoticesData(
|
||||
items: rawItems
|
||||
.whereType<Map<String, dynamic>>()
|
||||
.map(NoticeItem.fromJson)
|
||||
.toList(),
|
||||
unreadCount: (j['unread_count'] as num?)?.toInt() ?? 0,
|
||||
);
|
||||
}
|
||||
|
||||
NoticesData copyWith({List<NoticeItem>? items, int? unreadCount}) =>
|
||||
NoticesData(items: items ?? this.items, unreadCount: unreadCount ?? this.unreadCount);
|
||||
}
|
||||
|
||||
class NoticesApi {
|
||||
NoticesApi(this._c);
|
||||
final ApiClient _c;
|
||||
|
||||
Future<NoticesData> fetch() async => NoticesData.fromJson(await _c.getJson('/v1/notices'));
|
||||
|
||||
Future<void> markRead() async {
|
||||
await _c.postJson('/v1/notices/read');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user