import 'package:design_system/design_system.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:sf_shared/sf_shared.dart'; import 'package:sf_localizations/sf_localizations.dart'; import 'package:activity/src/presentation/state/activity_view_model.dart'; import 'package:activity/src/presentation/state/activity_view_state.dart'; import 'package:activity/src/widgets/pagination_bar.dart'; import 'package:activity/src/widgets/transaction_tile.dart'; class ActivityScreen extends ConsumerStatefulWidget { const ActivityScreen({super.key}); @override ConsumerState createState() => _ActivityScreenState(); } class _ActivityScreenState extends ConsumerState { @override Widget build(BuildContext context) { final theme = ref.watch(themePortProvider); final viewState = ref.watch(activityViewModelProvider); return Scaffold( backgroundColor: theme.getColorFor(ThemeCode.backgroundSecondary), body: _buildBody(context, theme, viewState), ); } Widget _buildBody( BuildContext context, ThemePort theme, ActivityViewState viewState, ) { if (viewState.isLoading) { return const Center(child: AppLoadingIndicator()); } if (viewState.errorMessage.isNotEmpty && viewState.tabs.isEmpty) { return _buildError(context, theme, viewState.errorMessage); } if (viewState.tabs.isEmpty) { return Center( child: Text( context.translate(I18n.activityNoWallets), style: TextStyle( fontSize: 16, color: theme.getColorFor(ThemeCode.textPrimary), ), ), ); } return Column( children: [ _buildHeader(context, theme, viewState), Expanded(child: _buildTransactions(context, theme, viewState)), ], ); } Widget _buildHeader( BuildContext context, ThemePort theme, ActivityViewState viewState, ) { return SafeArea( child: Container( margin: const EdgeInsets.symmetric(horizontal: 24, vertical: 16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( context.translate(I18n.activityRecentTransactions), style: TextStyle(fontWeight: FontWeight.w500, fontSize: 24), ), const SizedBox(height: 16), _buildDateFilter(context, theme, viewState), const SizedBox(height: 12), _buildWalletSelector(context, theme, viewState), ], ), ), ); } Widget _buildDateFilter( BuildContext context, ThemePort theme, ActivityViewState viewState, ) { final viewModel = ref.read(activityViewModelProvider.notifier); return Row( children: DateFilter.values.map((filter) { final isSelected = filter == viewState.selectedDateFilter; return Padding( padding: const EdgeInsets.only(right: 8), child: isSelected ? FilledButton( onPressed: () {}, style: FilledButton.styleFrom( backgroundColor: theme.getColorFor(ThemeCode.buttonPrimary), ), child: Text(context.translate(filter.i18nKey)), ) : TextButton( onPressed: () => viewModel.selectDateFilter(filter), child: Text(context.translate(filter.i18nKey)), ), ); }).toList(), ); } Widget _buildWalletSelector( BuildContext context, ThemePort theme, ActivityViewState viewState, ) { final viewModel = ref.read(activityViewModelProvider.notifier); return Row( children: [ Text( context.translate(I18n.activityViewTransactionsFrom), style: TextStyle( fontSize: 14, fontWeight: FontWeight.w500, color: theme.getColorFor(ThemeCode.textPrimary), ), ), const SizedBox(width: 12), Expanded( child: SingleChildScrollView( scrollDirection: Axis.horizontal, child: Row( children: List.generate(viewState.tabs.length, (index) { final isSelected = index == viewState.selectedWalletIndex; return Padding( padding: const EdgeInsets.only(right: 8), child: ChoiceChip( label: Text(viewState.tabs[index].label), selected: isSelected, onSelected: (_) => viewModel.selectWallet(index), selectedColor: theme.getColorFor(ThemeCode.buttonPrimary), labelStyle: TextStyle( color: isSelected ? Colors.white : theme.getColorFor(ThemeCode.textPrimary), fontWeight: FontWeight.w500, ), ), ); }), ), ), ), ], ); } Widget _buildTransactions( BuildContext context, ThemePort theme, ActivityViewState viewState, ) { if (viewState.isLoadingTransactions) { return const Center(child: AppLoadingIndicator()); } if (viewState.errorMessage.isNotEmpty) { return _buildError(context, theme, viewState.errorMessage); } if (viewState.transactionPages.isEmpty) { return Center( child: Text( context.translate(I18n.activityNoTransactions), style: TextStyle( fontSize: 16, color: theme.getColorFor(ThemeCode.textPrimary), ), ), ); } final currentTransactions = viewState.transactionPages[viewState.currentPage]; return RefreshIndicator( onRefresh: () => ref.read(activityViewModelProvider.notifier).loadTabs(), child: ListView( physics: const AlwaysScrollableScrollPhysics(), padding: const EdgeInsets.symmetric(horizontal: 24), children: [ // TODO: WalletBalanceBlock temporarily hidden // final walletId = viewState.selectedTab!.walletId; // final balanceAsync = ref.watch(walletBalanceProvider(walletId)); // Padding( // padding: const EdgeInsets.only(bottom: 16), // child: balanceAsync.when( // loading: () => // const Center(child: AppLoadingIndicator(size: 48)), // error: (_, __) => const SizedBox.shrink(), // data: (balance) => WalletBalanceBlock( // availableBalance: balance.availableBalance, // allocatedBalance: balance.allocatedBalance, // totalBalance: balance.totalBalance, // ), // ), // ), ...currentTransactions.map( (tx) => Padding( padding: const EdgeInsets.only(bottom: 12), child: TransactionTile(transaction: tx), ), ), PaginationBar( currentPage: viewState.currentPage, totalPages: viewState.transactionPages.length, hasMore: viewState.nextCursor != null, isLoadingMore: viewState.isLoadingMore, onPageChanged: (page) => ref.read(activityViewModelProvider.notifier).setPage(page), onLoadMore: () => ref.read(activityViewModelProvider.notifier).loadMore(), ), ], ), ); } Widget _buildError(BuildContext context, ThemePort theme, String message) { return Center( child: Padding( padding: const EdgeInsets.all(24), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( context.translate(I18n.errorLoadingData), style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, color: theme.getColorFor(ThemeCode.textPrimary), ), ), const SizedBox(height: 8), Text( message, textAlign: TextAlign.center, style: TextStyle(color: theme.getColorFor(ThemeCode.textPrimary)), ), const SizedBox(height: 16), TextButton( onPressed: () => ref.read(activityViewModelProvider.notifier).retry(), child: Text(context.translate(I18n.retry)), ), ], ), ), ); } }