Files
buzz/mobile/lib/features/activity/activity_page/status_views.dart
ff0b7982f1 Polish mobile top navigation (#4778)
## Summary

- Polish mobile Home, Activity, Search, and Settings navigation chrome.
- Add progressive Buzz gradients/frost, aligned theme colors, dividers,
typography, and section spacing.
- Refine Search and Settings motion, including automatic keyboard focus
on search activation.

<img width="630" height="1368"
alt="C78FA3CE-F2B3-45F2-B9F5-7EA7500778CC"
src="https://github.com/user-attachments/assets/5935514b-d894-4010-80dd-a938363fee93"
/>
<img width="630" height="1368"
alt="5C058A73-1879-476A-881C-531ACC256D84"
src="https://github.com/user-attachments/assets/5266c841-17ee-49e8-9841-b06d84f4195f"
/>
<img width="630" height="1368"
alt="3F50ADB7-9BDA-4A8D-A81E-20560C3B9EA6"
src="https://github.com/user-attachments/assets/f615f61e-9e96-4bce-b261-ae5ec54db872"
/>
<img width="630" height="1368"
alt="35ECE741-01F3-4B79-80C5-1DDD447121A7"
src="https://github.com/user-attachments/assets/b5145186-9884-44eb-8ebc-f3303831c0a4"
/>

## Validation


- `flutter analyze`
- Focused Home, Activity, Channels, Search, theme, and footer widget
tests
- Full pre-push checks, including mobile tests, desktop checks, and
Tauri checks
- On-device iPhone review during the visual polish pass

---------

Signed-off-by: kenny lopez <klopez4212@gmail.com>
Signed-off-by: npub1glqcqfjxdens59scl477pmejh8lht4hqkhx0y4w38jxr6e6w6y2sm29y4e <47c18026466e670a1618fd7de0ef32b9ff75d6e0b5ccf255d13c8c3d674ed115@buzz.block.builderlab.xyz>
Signed-off-by: Code Reviewer <037593536284cf40e221c96c931e9877d4166d54f6bb84e5341a86d7fd5d05a4@buzz.block.builderlab.xyz>
Signed-off-by: Kenny Lopez <klopez4212@gmail.com>
Co-authored-by: npub1glqcqfjxdens59scl477pmejh8lht4hqkhx0y4w38jxr6e6w6y2sm29y4e <47c18026466e670a1618fd7de0ef32b9ff75d6e0b5ccf255d13c8c3d674ed115@buzz.block.builderlab.xyz>
Co-authored-by: Code Reviewer <037593536284cf40e221c96c931e9877d4166d54f6bb84e5341a86d7fd5d05a4@buzz.block.builderlab.xyz>
2026-08-05 19:01:20 +01:00

181 lines
5.3 KiB
Dart

part of '../activity_page.dart';
class _LoadingSkeleton extends StatelessWidget {
final ScrollController scrollController;
const _LoadingSkeleton({required this.scrollController});
@override
Widget build(BuildContext context) {
return ListView.separated(
controller: scrollController,
padding: _activityScrollPadding(
context,
horizontal: Grid.gutter,
top: Grid.gutter,
bottom: Grid.gutter,
),
itemCount: 8,
separatorBuilder: (_, _) => const SizedBox(height: Grid.xs),
itemBuilder: (context, _) => Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: 36,
height: 36,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: context.colors.outlineVariant.withValues(alpha: 0.4),
),
),
const SizedBox(width: Grid.twelve),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: 140,
height: 12,
decoration: BoxDecoration(
color: context.colors.outlineVariant.withValues(alpha: 0.4),
borderRadius: BorderRadius.circular(4),
),
),
const SizedBox(height: Grid.xxs),
Container(
width: double.infinity,
height: 10,
decoration: BoxDecoration(
color: context.colors.outlineVariant.withValues(alpha: 0.3),
borderRadius: BorderRadius.circular(4),
),
),
const SizedBox(height: Grid.half),
Container(
width: 220,
height: 10,
decoration: BoxDecoration(
color: context.colors.outlineVariant.withValues(alpha: 0.2),
borderRadius: BorderRadius.circular(4),
),
),
],
),
),
],
),
);
}
}
class _EmptySurface extends StatelessWidget {
final IconData icon;
final String message;
final String? detail;
const _EmptySurface({required this.icon, required this.message, this.detail});
@override
Widget build(BuildContext context) {
return Center(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: Grid.gutter),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(icon, size: Grid.lg, color: context.colors.onSurfaceVariant),
const SizedBox(height: Grid.xxs),
Text(
message,
style: context.textTheme.bodyMedium?.copyWith(
color: context.colors.onSurfaceVariant,
),
),
if (detail != null) ...[
const SizedBox(height: Grid.half),
Text(
detail!,
style: context.textTheme.bodySmall?.copyWith(
color: context.colors.onSurfaceVariant,
),
textAlign: TextAlign.center,
),
],
],
),
),
);
}
}
class _EmptyFilterState extends StatelessWidget {
final InboxFilter filter;
final bool unreadOnly;
const _EmptyFilterState({required this.filter, required this.unreadOnly});
@override
Widget build(BuildContext context) {
if (unreadOnly) {
return const _EmptySurface(
icon: LucideIcons.mailOpen,
message: 'No unread messages',
detail: 'Turn off the unread filter to see read messages.',
);
}
final (icon, message) = switch (filter) {
InboxFilter.mention => (LucideIcons.atSign, 'No mentions yet'),
InboxFilter.thread => (
LucideIcons.messageSquare,
'No thread replies yet',
),
InboxFilter.needsAction => (
LucideIcons.circleAlert,
'Nothing needs your action',
),
InboxFilter.activity => (
LucideIcons.activity,
'No recent channel activity',
),
InboxFilter.agentActivity => (LucideIcons.bot, 'No agent updates'),
_ => (LucideIcons.bell, 'No activity yet'),
};
return _EmptySurface(icon: icon, message: message);
}
}
class _ErrorView extends StatelessWidget {
final Future<void> Function() onRetry;
const _ErrorView({required this.onRetry});
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
LucideIcons.triangleAlert,
size: Grid.lg,
color: context.colors.error,
),
const SizedBox(height: Grid.xxs),
Text(
'Failed to load activity',
style: context.textTheme.bodyMedium?.copyWith(
color: context.colors.onSurfaceVariant,
),
),
const SizedBox(height: Grid.xs),
FilledButton.icon(
onPressed: () => unawaited(onRetry()),
icon: const Icon(LucideIcons.refreshCcw, size: 16),
label: const Text('Retry'),
),
],
),
);
}
}