fix: update loop, ghost queue item, briefing card scroll trap

- Update dialog no longer re-appears on every shell re-mount; check()
  is skipped if status is already available/upToDate/downloading
- Offline queue dequeue now happens before the mounted check, preventing
  ghost items when the widget disposes mid-drain
- Briefing digest card and messages now share a CustomScrollView so
  expanding the card doesn't trap the user without scroll access

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-12 08:23:24 -04:00
parent baba5c3462
commit 844f68d376
2 changed files with 56 additions and 37 deletions
+44 -31
View File
@@ -154,39 +154,52 @@ class _BriefingScreenState extends ConsumerState<BriefingScreen> {
return Column(
children: [
// Digest card header
BriefingDigestCard(
message: firstAssistant,
onGenerateNow: _refresh,
),
// Divider + label
if (conv.messages.isNotEmpty) ...[
const SizedBox(height: 4),
Row(children: [
const Expanded(child: Divider()),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: Text(
'Conversation',
style: Theme.of(context).textTheme.labelSmall?.copyWith(
color: scheme.onSurfaceVariant,
),
),
),
const Expanded(child: Divider()),
]),
],
// Message list
// Digest card + messages share one scroll view so expanding the
// card doesn't trap the user — the whole page just becomes taller.
Expanded(
child: ListView.builder(
child: CustomScrollView(
controller: _scrollController,
padding: const EdgeInsets.symmetric(
horizontal: 8, vertical: 8),
itemCount: conv.messages.length,
itemBuilder: (_, i) =>
ChatMessageBubble(message: conv.messages[i]),
slivers: [
SliverToBoxAdapter(
child: BriefingDigestCard(
message: firstAssistant,
onGenerateNow: _refresh,
),
),
if (conv.messages.isNotEmpty)
SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: Row(children: [
const Expanded(child: Divider()),
Padding(
padding:
const EdgeInsets.symmetric(horizontal: 10),
child: Text(
'Conversation',
style: Theme.of(context)
.textTheme
.labelSmall
?.copyWith(
color: scheme.onSurfaceVariant),
),
),
const Expanded(child: Divider()),
]),
),
),
SliverPadding(
padding: const EdgeInsets.symmetric(
horizontal: 8, vertical: 8),
sliver: SliverList.builder(
itemCount: conv.messages.length,
itemBuilder: (_, i) =>
ChatMessageBubble(message: conv.messages[i]),
),
),
],
),
),