257 lines
7.9 KiB
Dart
257 lines
7.9 KiB
Dart
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/transaction_tile.dart';
|
|
|
|
class ActivityScreen extends ConsumerStatefulWidget {
|
|
const ActivityScreen({super.key});
|
|
|
|
@override
|
|
ConsumerState<ActivityScreen> createState() => _ActivityScreenState();
|
|
}
|
|
|
|
class _ActivityScreenState extends ConsumerState<ActivityScreen> {
|
|
@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.transactions.isEmpty) {
|
|
return Center(
|
|
child: Text(
|
|
context.translate(I18n.activityNoTransactions),
|
|
style: TextStyle(
|
|
fontSize: 16,
|
|
color: theme.getColorFor(ThemeCode.textPrimary),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
final walletId = viewState.selectedTab!.walletId;
|
|
final balanceAsync = ref.watch(walletBalanceProvider(walletId));
|
|
|
|
return RefreshIndicator(
|
|
onRefresh: () =>
|
|
ref.read(activityViewModelProvider.notifier).loadTabs(),
|
|
child: ListView.builder(
|
|
physics: const AlwaysScrollableScrollPhysics(),
|
|
padding: const EdgeInsets.symmetric(horizontal: 24),
|
|
itemCount: viewState.transactions.length + 1,
|
|
itemBuilder: (context, index) {
|
|
if (index == 0) {
|
|
return 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,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
final transaction = viewState.transactions[index - 1];
|
|
return Padding(
|
|
padding: const EdgeInsets.only(bottom: 12),
|
|
child: TransactionTile(transaction: transaction),
|
|
);
|
|
},
|
|
),
|
|
);
|
|
}
|
|
|
|
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)),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|