Files
sf-app-platform/modules/profile/lib/src/presentation/profile_screen.dart

258 lines
7.7 KiB
Dart

import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:navigation/navigation.dart';
import 'package:activity/activity.dart';
import 'package:payments/payments.dart';
import 'package:sf_shared/sf_shared.dart';
import 'package:sf_localizations/sf_localizations.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:profile/src/presentation/state/profile_view_model.dart';
import 'package:profile/src/presentation/state/profile_view_state.dart';
class ProfileScreen extends ConsumerWidget {
final NavigationContract navigationContract;
const ProfileScreen({super.key, required this.navigationContract});
List<num> _expensesByDay(List<WalletTransactionEntity> transactions) {
final result = List<num>.filled(7, 0);
for (final tx in transactions) {
final date = DateTime.tryParse(tx.createdDate);
if (date == null) continue;
final amount = double.tryParse(tx.amount) ?? 0;
result[date.weekday - 1] += amount.abs();
}
return result;
}
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.watch(themePortProvider);
final viewState = ref.watch(profileViewModelProvider);
return Scaffold(
backgroundColor: theme.getColorFor(ThemeCode.backgroundSecondary),
body: _buildBody(context, ref, theme, viewState),
);
}
Widget _buildBody(
BuildContext context,
WidgetRef ref,
ThemePort theme,
ProfileViewState viewState,
) {
if (viewState.isLoading) {
return const Center(child: CircularProgressIndicator());
}
if (viewState.errorMessage.isNotEmpty) {
return _buildError(context, ref, theme, viewState.errorMessage);
}
final balance = viewState.walletBalance;
final content = <Widget>[
Row(
children: [
Text(
viewState.userName,
style: TextStyle(
color: theme.getColorFor(ThemeCode.textSecondary),
fontWeight: FontWeight.bold,
fontSize: 30,
),
),
Spacer(),
TextButton(
onPressed: () =>
navigationContract.pushTo(AppRoutes.dashboardProfileSettings),
child: Text(
context.translate(I18n.profileAccountSettings),
style: TextStyle(
color: theme.getColorFor(ThemeCode.textSecondary),
),
),
),
],
),
if (balance != null)
WalletBalanceBlock(
value: balance.availableBalance,
savings: balance.allocatedBalance,
),
LineGraph(
lines: [_expensesByDay(viewState.transactions)],
lineLabels: [context.translate(I18n.profileMyAccount)],
),
if (balance != null)
DepositBlock(
max: 150 - balance.totalBalance,
onDeposit: (amount) async {
final result = await showPayinBottomSheet(
context,
amount: amount,
navigation: navigationContract,
);
if (result == true && context.mounted) {
showTopSnackbar(
context,
message: context.translate(I18n.payinSuccess),
type: MessageType.success,
);
ref.read(profileViewModelProvider.notifier).load();
}
},
),
Container(
padding: EdgeInsets.symmetric(horizontal: 24, vertical: 12),
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(10)),
color: theme.getColorFor(ThemeCode.backgroundPrimary),
),
child: TextButton(
onPressed: () =>
navigationContract.pushTo(AppRoutes.dashboardProfilePayout),
child: Row(
spacing: 10,
children: [
Icon(
Icons.output_outlined,
size: 24,
color: theme.getColorFor(ThemeCode.textPrimary),
),
Text(
context.translate(I18n.profileWithdrawMoney),
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w500,
color: theme.getColorFor(ThemeCode.textPrimary),
),
),
],
),
),
),
_buildTransactions(context, theme, viewState),
];
return Stack(
children: [
DecoratedBox(
decoration: BoxDecoration(
borderRadius: const BorderRadius.only(
bottomRight: Radius.circular(24),
bottomLeft: Radius.circular(24),
),
color: Color(0xFF4B4B4B),
),
child: SizedBox(width: double.infinity, height: 200),
),
Column(
children: [
Expanded(
child: Container(
margin: EdgeInsets.fromLTRB(20, 20, 20, 0),
child: RefreshIndicator(
onRefresh: () =>
ref.read(profileViewModelProvider.notifier).load(),
child: ListView.separated(
physics: const AlwaysScrollableScrollPhysics(),
itemBuilder: (BuildContext context, int index) {
return content[index];
},
separatorBuilder: (BuildContext context, int index) {
return Divider(color: Colors.transparent, height: 20);
},
itemCount: content.length,
),
),
),
),
],
),
],
);
}
Widget _buildTransactions(
BuildContext context,
ThemePort theme,
ProfileViewState viewState,
) {
final transactions = viewState.transactions;
if (transactions.isEmpty) {
return Center(
child: Text(
context.translate(I18n.profileNoRecentTransactions),
style: TextStyle(
fontSize: 14,
color: theme.getColorFor(ThemeCode.textPrimary),
),
),
);
}
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
context.translate(I18n.profileRecentTransactions),
style: TextStyle(
fontWeight: FontWeight.w500,
fontSize: 18,
color: theme.getColorFor(ThemeCode.textPrimary),
),
),
const SizedBox(height: 12),
...transactions.map(
(transaction) => Padding(
padding: const EdgeInsets.only(bottom: 12),
child: TransactionTile(transaction: transaction),
),
),
],
);
}
Widget _buildError(
BuildContext context,
WidgetRef ref,
ThemePort theme,
String message,
) {
final viewModel = ref.read(profileViewModelProvider.notifier);
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: () => viewModel.retry(),
child: Text(context.translate(I18n.retry)),
),
],
),
),
);
}
}