jjsos_autosos_flutter/lib/pages/me/messenger_page.dart

167 lines
4.9 KiB
Dart
Raw Permalink Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import 'package:autosos_flutter/model/misc.dart';
import 'package:autosos_flutter/provider/providers.dart';
import 'package:autosos_flutter/router/app_router.dart';
import 'package:autosos_flutter/widget/multi_state_view.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:intl/intl.dart';
/// 消息中心(对应 Android MessengerActivity
class MessengerPage extends ConsumerStatefulWidget {
const MessengerPage({super.key});
@override
ConsumerState<MessengerPage> createState() => _MessengerPageState();
}
class _MessengerPageState extends ConsumerState<MessengerPage> {
MultiState _state = MultiState.loading;
List<MessengerItem> _items = [];
int _page = 1;
bool _hasMore = true;
bool _loadingMore = false;
final _scroll = ScrollController();
@override
void initState() {
super.initState();
_load(reset: true);
_scroll.addListener(() {
if (_scroll.position.pixels >=
_scroll.position.maxScrollExtent - 200) {
_load();
}
});
}
@override
void dispose() {
_scroll.dispose();
super.dispose();
}
Future<void> _load({bool reset = false}) async {
if (reset) {
_page = 1;
_hasMore = true;
_items = [];
setState(() => _state = MultiState.loading);
}
if (!_hasMore || _loadingMore) return;
_loadingMore = true;
try {
final resp = await ref.read(userApiProvider).messages(
page: _page,
size: 20,
);
if (!mounted) return;
if (!resp.isSuccess) {
if (reset) setState(() => _state = MultiState.error);
return;
}
final list = resp.data?.list ?? [];
_items.addAll(list);
_hasMore = (resp.data?.hasMore ?? false) && list.length >= 20;
_page++;
setState(() {
_state = _items.isEmpty ? MultiState.empty : MultiState.success;
});
} catch (e) {
if (mounted && reset) setState(() => _state = MultiState.error);
} finally {
_loadingMore = false;
}
}
Future<void> _onTap(MessengerItem item) async {
if (!item.read) {
try {
await ref.read(userApiProvider).readMessage(item.id);
} catch (_) {}
}
if (!mounted) return;
setState(() {
final i = _items.indexWhere((e) => e.id == item.id);
if (i >= 0) {
_items[i] = MessengerItem(
id: item.id,
title: item.title,
content: item.content,
createdAt: item.createdAt,
read: true,
link: item.link,
);
}
});
if (item.link != null && item.link!.isNotEmpty) {
context.push(
'${Routes.webview}?url=${Uri.encodeComponent(item.link!)}&title=${Uri.encodeComponent(item.title)}',
);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('消息中心')),
body: RefreshIndicator(
onRefresh: () => _load(reset: true),
child: MultiStateView(
state: _state,
emptyText: '暂无消息',
onRetry: () => _load(reset: true),
child: ListView.separated(
controller: _scroll,
itemCount: _items.length + (_hasMore ? 1 : 0),
separatorBuilder: (_, __) => Divider(
height: 1,
color: Colors.grey.shade200,
indent: 16,
),
itemBuilder: (ctx, i) {
if (i >= _items.length) {
return const Padding(
padding: EdgeInsets.all(16),
child: Center(
child: CircularProgressIndicator(strokeWidth: 2),
),
);
}
final m = _items[i];
return ListTile(
leading: CircleAvatar(
backgroundColor: m.read
? Colors.grey.shade300
: Theme.of(context).primaryColor,
child: const Icon(Icons.notifications,
color: Colors.white, size: 20),
),
title: Text(
m.title,
style: TextStyle(
fontWeight: m.read
? FontWeight.normal
: FontWeight.bold,
),
),
subtitle: Text(
m.content,
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: const TextStyle(fontSize: 13),
),
trailing: Text(
DateFormat('MM-dd HH:mm').format(m.createdAt),
style: const TextStyle(fontSize: 11, color: Colors.grey),
),
onTap: () => _onTap(m),
);
},
),
),
),
);
}
}