e_receipt_mobile/lib/presentation/home/home_screen.dart

215 lines
8.1 KiB
Dart
Raw Normal View History

2026-04-01 17:58:59 +00:00
import 'package:e_receipt_mobile/domain/entities/login_user.dart';
import 'package:e_receipt_mobile/presentation/auth/logout_view_model.dart';
import 'package:e_receipt_mobile/presentation/home/home_pagination_providers.dart';
import 'package:e_receipt_mobile/presentation/home/merchant_paging_view_model.dart';
import 'package:e_receipt_mobile/presentation/home/widgets/home_drawer.dart';
import 'package:e_receipt_mobile/presentation/home/widgets/merchant_header.dart';
import 'package:e_receipt_mobile/presentation/home/widgets/merchant_list_view.dart';
import 'package:e_receipt_mobile/presentation/terminal/terminal_selection_screen.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class HomeScreen extends ConsumerWidget {
const HomeScreen({required this.user, super.key});
final LoginUser user;
@override
Widget build(BuildContext context, WidgetRef ref) {
final pagingState = ref.watch(merchantPagingViewModelProvider);
final pagingViewModel = ref.read(merchantPagingViewModelProvider.notifier);
final logoutState = ref.watch(logoutViewModelProvider);
final colorScheme = Theme.of(context).colorScheme;
final query = ref.watch(merchantSearchQueryProvider);
return Scaffold(
appBar: AppBar(
title: const Text('Merchants'),
actions: [
IconButton(
tooltip: 'Refresh',
onPressed: pagingState.isLoading
? null
: () => pagingViewModel.refresh(),
icon: const Icon(Icons.refresh),
),
],
),
drawer: HomeDrawer(
user: user,
onLogout: () async {
if (logoutState.isLoading) {
return;
}
final shouldLogout = await showDialog<bool>(
context: context,
builder: (context) {
return AlertDialog(
title: const Text('Logout'),
content: const Text('Are you sure you want to logout?'),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(false),
child: const Text('Cancel'),
),
TextButton(
onPressed: () => Navigator.of(context).pop(true),
child: const Text('Logout'),
),
],
);
},
);
if (shouldLogout != true || !context.mounted) {
return;
}
Navigator.of(context).pop(); // close drawer
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Signed out'),
behavior: SnackBarBehavior.floating,
),
);
await ref.read(logoutViewModelProvider.notifier).logout();
},
),
body: pagingState.isLoading && pagingState.items.isEmpty
? const Center(child: CircularProgressIndicator())
: pagingState.errorMessage != null && pagingState.items.isEmpty
? Center(
child: Padding(
padding: EdgeInsets.fromLTRB(
24,
24,
24,
MediaQuery.viewPaddingOf(context).bottom + 24,
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.wifi_off_outlined,
size: 56,
color: colorScheme.onSurfaceVariant,
),
const SizedBox(height: 12),
Text(
'Failed to load merchants',
style: Theme.of(context).textTheme.titleMedium,
textAlign: TextAlign.center,
),
const SizedBox(height: 6),
Text(
pagingState.errorMessage!,
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: colorScheme.onSurfaceVariant,
),
textAlign: TextAlign.center,
),
const SizedBox(height: 14),
FilledButton.tonal(
onPressed: pagingViewModel.refresh,
child: const Text('Try again'),
),
],
),
),
)
: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
MerchantHeader(
loadedCount: pagingState.items.length,
query: query,
onQueryChanged: (value) {
ref.read(merchantSearchQueryProvider.notifier).state =
value;
pagingViewModel.setSearchTerm(value);
},
onClear: () {
ref.read(merchantSearchQueryProvider.notifier).state = '';
pagingViewModel.setSearchTerm('');
},
),
Expanded(
child: MerchantListView(
merchants: pagingState.items,
hasMore: pagingState.hasMore,
isLoadingMore: pagingState.isLoadingMore,
onRefresh: pagingViewModel.refresh,
onMerchantTap: (merchant) {
final id = merchant.id;
if (id == null) {
return;
}
_openTerminalSelection(context, id, merchant.name ?? '-');
},
onLoadMore: pagingViewModel.loadMore,
onEndReached: pagingViewModel.loadMore,
),
),
if (pagingState.errorMessage != null &&
pagingState.items.isNotEmpty)
Padding(
padding: EdgeInsets.fromLTRB(
16,
8,
16,
MediaQuery.viewPaddingOf(context).bottom + 8,
),
child: Material(
color: colorScheme.errorContainer,
borderRadius: BorderRadius.circular(16),
child: Padding(
padding: const EdgeInsets.all(12),
child: Row(
children: [
Icon(
Icons.error_outline,
color: colorScheme.onErrorContainer,
),
const SizedBox(width: 10),
Expanded(
child: Text(
pagingState.errorMessage!,
style: Theme.of(context).textTheme.bodyMedium
?.copyWith(
color: colorScheme.onErrorContainer,
),
),
),
const SizedBox(width: 8),
TextButton(
onPressed: pagingViewModel.loadMore,
child: const Text('Retry'),
),
],
),
),
),
),
],
),
);
}
void _openTerminalSelection(
BuildContext context,
String merchantId,
String merchantName,
) {
Navigator.of(context).push(
MaterialPageRoute<void>(
builder: (_) => TerminalSelectionScreen(
merchantId: merchantId,
merchantName: merchantName,
),
),
);
}
}