mirror of
https://github.com/koloideal/Casha.git
synced 2026-06-10 18:35:28 +03:00
update
This commit is contained in:
@@ -10,6 +10,8 @@ class AddTransactionState {
|
||||
final String note;
|
||||
final bool isSubmitting;
|
||||
final String? editingId;
|
||||
final String overrideCurrency;
|
||||
final String overrideCurrencyCode;
|
||||
|
||||
const AddTransactionState({
|
||||
this.amount,
|
||||
@@ -19,6 +21,8 @@ class AddTransactionState {
|
||||
this.note = '',
|
||||
this.isSubmitting = false,
|
||||
this.editingId,
|
||||
this.overrideCurrency = '\$',
|
||||
this.overrideCurrencyCode = 'USD',
|
||||
});
|
||||
|
||||
factory AddTransactionState.fromTransaction(Transaction tx) {
|
||||
@@ -29,6 +33,8 @@ class AddTransactionState {
|
||||
date: tx.date,
|
||||
note: tx.note ?? '',
|
||||
editingId: tx.id,
|
||||
overrideCurrency: tx.currency,
|
||||
overrideCurrencyCode: tx.currencyCode,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -44,6 +50,8 @@ class AddTransactionState {
|
||||
String? note,
|
||||
bool? isSubmitting,
|
||||
String? editingId,
|
||||
String? overrideCurrency,
|
||||
String? overrideCurrencyCode,
|
||||
}) =>
|
||||
AddTransactionState(
|
||||
amount: amount ?? this.amount,
|
||||
@@ -53,6 +61,8 @@ class AddTransactionState {
|
||||
note: note ?? this.note,
|
||||
isSubmitting: isSubmitting ?? this.isSubmitting,
|
||||
editingId: editingId ?? this.editingId,
|
||||
overrideCurrency: overrideCurrency ?? this.overrideCurrency,
|
||||
overrideCurrencyCode: overrideCurrencyCode ?? this.overrideCurrencyCode,
|
||||
);
|
||||
|
||||
bool get isEditing => editingId != null;
|
||||
@@ -80,6 +90,10 @@ class AddTransactionNotifier extends StateNotifier<AddTransactionState> {
|
||||
|
||||
void setSubmitting(bool v) => state = state.copyWith(isSubmitting: v);
|
||||
|
||||
void setCurrency(String symbol, String code) {
|
||||
state = state.copyWith(overrideCurrency: symbol, overrideCurrencyCode: code);
|
||||
}
|
||||
|
||||
void reset() => state = AddTransactionState.empty();
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,12 @@ class _AddTransactionScreenState extends ConsumerState<AddTransactionScreen> {
|
||||
if (widget.initial != null) {
|
||||
_amountController.text = widget.initial!.amount.toString();
|
||||
_noteController.text = widget.initial!.note ?? '';
|
||||
} else {
|
||||
// Set default currency from global provider after first frame
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
final curr = ref.read(currencyProvider);
|
||||
ref.read(addTransactionProvider(null).notifier).setCurrency(curr.symbol, curr.code);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +59,6 @@ class _AddTransactionScreenState extends ConsumerState<AddTransactionScreen> {
|
||||
Future<void> _submit() async {
|
||||
if (!_formKey.currentState!.validate()) return;
|
||||
final state = ref.read(addTransactionProvider(widget.initial));
|
||||
final currencyInfo = ref.read(currencyProvider);
|
||||
ref.read(addTransactionProvider(widget.initial).notifier).setSubmitting(true);
|
||||
|
||||
final note = _noteController.text.trim();
|
||||
@@ -65,8 +70,8 @@ class _AddTransactionScreenState extends ConsumerState<AddTransactionScreen> {
|
||||
type: state.type,
|
||||
date: state.date,
|
||||
note: note.isEmpty ? null : note,
|
||||
currency: currencyInfo.symbol,
|
||||
currencyCode: currencyInfo.code,
|
||||
currency: state.overrideCurrency,
|
||||
currencyCode: state.overrideCurrencyCode,
|
||||
);
|
||||
|
||||
if (state.isEditing) {
|
||||
@@ -105,7 +110,7 @@ class _AddTransactionScreenState extends ConsumerState<AddTransactionScreen> {
|
||||
Widget build(BuildContext context) {
|
||||
final state = ref.watch(addTransactionProvider(widget.initial));
|
||||
final categories = ref.watch(availableCategoriesProvider(widget.initial));
|
||||
final currencyInfo = ref.watch(currencyProvider);
|
||||
final overrideCurrency = state.overrideCurrency;
|
||||
final isDark = Theme.of(context).brightness == Brightness.dark;
|
||||
|
||||
return Scaffold(
|
||||
@@ -181,7 +186,7 @@ class _AddTransactionScreenState extends ConsumerState<AddTransactionScreen> {
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||||
child: Text(
|
||||
currencyInfo.symbol,
|
||||
overrideCurrency,
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w600,
|
||||
@@ -226,6 +231,22 @@ class _AddTransactionScreenState extends ConsumerState<AddTransactionScreen> {
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
|
||||
// Currency
|
||||
Text(
|
||||
'Currency',
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
color: Theme.of(context).colorScheme.onSurface.withOpacity(0.6),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
_CurrencyPicker(
|
||||
selected: state.overrideCurrencyCode,
|
||||
onChanged: (symbol, code) =>
|
||||
ref.read(addTransactionProvider(widget.initial).notifier).setCurrency(symbol, code),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
|
||||
// Category
|
||||
_SectionLabel('Category'),
|
||||
const SizedBox(height: 8),
|
||||
@@ -484,3 +505,65 @@ class _CategoryPicker extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _CurrencyPicker extends StatelessWidget {
|
||||
final String selected;
|
||||
final void Function(String symbol, String code) onChanged;
|
||||
const _CurrencyPicker({
|
||||
required this.selected,
|
||||
required this.onChanged,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final currencies = [
|
||||
('USD', '\$'),
|
||||
('EUR', '€'),
|
||||
('BYN', 'Br'),
|
||||
('RUB', '₽'),
|
||||
];
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
return Row(
|
||||
children: currencies.map((c) {
|
||||
final isSelected = c.$1 == selected;
|
||||
return Expanded(
|
||||
child: GestureDetector(
|
||||
onTap: () => onChanged(c.$2, c.$1),
|
||||
child: Container(
|
||||
margin: EdgeInsets.only(right: c.$1 == currencies.last.$1 ? 0 : 8),
|
||||
padding: const EdgeInsets.symmetric(vertical: 10),
|
||||
decoration: BoxDecoration(
|
||||
color: isSelected ? const Color(0xFF7C6DED).withOpacity(0.15) : Theme.of(context).cardColor,
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
border: Border.all(
|
||||
color: isSelected ? const Color(0xFF7C6DED) : Colors.transparent,
|
||||
width: 1.5,
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Text(
|
||||
c.$2,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: isSelected ? const Color(0xFF7C6DED) : colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
c.$1,
|
||||
style: TextStyle(
|
||||
fontSize: 10,
|
||||
color: colorScheme.onSurface.withOpacity(0.5),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,6 +195,7 @@ class _SearchBar extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final isDark = Theme.of(context).brightness == Brightness.dark;
|
||||
return TextField(
|
||||
controller: controller,
|
||||
focusNode: focusNode,
|
||||
@@ -212,6 +213,23 @@ class _SearchBar extends StatelessWidget {
|
||||
},
|
||||
)
|
||||
: null,
|
||||
filled: true,
|
||||
fillColor: Theme.of(context).inputDecorationTheme.fillColor,
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderSide: BorderSide(
|
||||
color: isDark ? Colors.transparent : const Color(0xFFCCCCDD),
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderSide: const BorderSide(color: Color(0xFF7C6DED), width: 1.5),
|
||||
),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderSide: BorderSide.none,
|
||||
),
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||
),
|
||||
onChanged: (v) => ref.read(searchQueryProvider.notifier).state = v,
|
||||
|
||||
Reference in New Issue
Block a user