mirror of
https://github.com/block/buzz.git
synced 2026-08-18 06:50:31 +02:00
## 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>
181 lines
5.3 KiB
Dart
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'),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|