feat: reduce resume cooldown to 30s; add pull-to-refresh to News, Calendar; refresh chat on resume

This commit is contained in:
2026-04-07 17:43:46 -04:00
parent 36350d35b1
commit d2582f9111
4 changed files with 34 additions and 8 deletions
+1 -1
View File
@@ -193,7 +193,7 @@ class _ShellState extends ConsumerState<_Shell> with WidgetsBindingObserver {
]; ];
// Minimum gap between app-resume refreshes to avoid hammering the server. // Minimum gap between app-resume refreshes to avoid hammering the server.
static const _resumeCooldown = Duration(minutes: 5); static const _resumeCooldown = Duration(seconds: 30);
DateTime? _lastResumeRefresh; DateTime? _lastResumeRefresh;
int? _prevTabIndex; int? _prevTabIndex;
+13 -5
View File
@@ -67,10 +67,13 @@ class CalendarScreen extends ConsumerWidget {
), ),
const Divider(height: 1), const Divider(height: 1),
Expanded( Expanded(
child: _AgendaList( child: RefreshIndicator(
events: onRefresh: () async => ref.invalidate(calendarProvider),
cal.eventsByDay[dateOnly(cal.selectedDay)] ?? [], child: _AgendaList(
notifier: notifier, events:
cal.eventsByDay[dateOnly(cal.selectedDay)] ?? [],
notifier: notifier,
),
), ),
), ),
], ],
@@ -106,7 +109,12 @@ class _AgendaList extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (events.isEmpty) { if (events.isEmpty) {
return const Center(child: Text('No events')); return ListView(
children: const [
SizedBox(height: 80),
Center(child: Text('No events')),
],
);
} }
return ListView.builder( return ListView.builder(
padding: const EdgeInsets.symmetric(vertical: 8), padding: const EdgeInsets.symmetric(vertical: 8),
+16 -1
View File
@@ -16,12 +16,27 @@ class ChatScreen extends ConsumerStatefulWidget {
ConsumerState<ChatScreen> createState() => _ChatScreenState(); ConsumerState<ChatScreen> createState() => _ChatScreenState();
} }
class _ChatScreenState extends ConsumerState<ChatScreen> { class _ChatScreenState extends ConsumerState<ChatScreen>
with WidgetsBindingObserver {
final _controller = TextEditingController(); final _controller = TextEditingController();
final _scrollController = ScrollController(); final _scrollController = ScrollController();
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed) {
ref.invalidate(messagesProvider(widget.conversationId));
}
}
@override @override
void dispose() { void dispose() {
WidgetsBinding.instance.removeObserver(this);
_controller.dispose(); _controller.dispose();
_scrollController.dispose(); _scrollController.dispose();
// Exit voice mode if the user navigates away mid-session. // Exit voice mode if the user navigates away mid-session.
+4 -1
View File
@@ -96,7 +96,9 @@ class _NewsScreenState extends ConsumerState<NewsScreen> {
), ),
const Divider(height: 1), const Divider(height: 1),
Expanded( Expanded(
child: ListView.builder( child: RefreshIndicator(
onRefresh: () async => ref.invalidate(newsProvider),
child: ListView.builder(
padding: padding:
const EdgeInsets.symmetric(horizontal: 8, vertical: 8), const EdgeInsets.symmetric(horizontal: 8, vertical: 8),
itemCount: news.items.length + 1, itemCount: news.items.length + 1,
@@ -128,6 +130,7 @@ class _NewsScreenState extends ConsumerState<NewsScreen> {
); );
}, },
), ),
),
), ),
], ],
), ),