mirror of
https://github.com/koloideal/Casha.git
synced 2026-06-10 10:25:28 +03:00
update
This commit is contained in:
@@ -33,11 +33,17 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> {
|
|||||||
Color tempPrimary = CardColorService.defaultPrimary;
|
Color tempPrimary = CardColorService.defaultPrimary;
|
||||||
Color tempSecondary = CardColorService.defaultSecondary;
|
Color tempSecondary = CardColorService.defaultSecondary;
|
||||||
HSVColor tempPrimaryHSV = HSVColor.fromColor(CardColorService.defaultPrimary);
|
HSVColor tempPrimaryHSV = HSVColor.fromColor(CardColorService.defaultPrimary);
|
||||||
HSVColor tempSecondaryHSV = HSVColor.fromColor(CardColorService.defaultSecondary);
|
HSVColor tempSecondaryHSV = HSVColor.fromColor(
|
||||||
|
CardColorService.defaultSecondary,
|
||||||
|
);
|
||||||
Color savedPrimary = CardColorService.defaultPrimary;
|
Color savedPrimary = CardColorService.defaultPrimary;
|
||||||
Color savedSecondary = CardColorService.defaultSecondary;
|
Color savedSecondary = CardColorService.defaultSecondary;
|
||||||
HSVColor savedPrimaryHSV = HSVColor.fromColor(CardColorService.defaultPrimary);
|
HSVColor savedPrimaryHSV = HSVColor.fromColor(
|
||||||
HSVColor savedSecondaryHSV = HSVColor.fromColor(CardColorService.defaultSecondary);
|
CardColorService.defaultPrimary,
|
||||||
|
);
|
||||||
|
HSVColor savedSecondaryHSV = HSVColor.fromColor(
|
||||||
|
CardColorService.defaultSecondary,
|
||||||
|
);
|
||||||
GradientType tempGradientType = CardColorService.defaultGradient;
|
GradientType tempGradientType = CardColorService.defaultGradient;
|
||||||
GradientType savedGradientType = CardColorService.defaultGradient;
|
GradientType savedGradientType = CardColorService.defaultGradient;
|
||||||
OverlayEntry? overlayEntry;
|
OverlayEntry? overlayEntry;
|
||||||
@@ -69,10 +75,8 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> {
|
|||||||
|
|
||||||
void _showOverlay() {
|
void _showOverlay() {
|
||||||
overlayEntry = OverlayEntry(
|
overlayEntry = OverlayEntry(
|
||||||
builder: (overlayContext) => FullScreenBlurOverlay(
|
builder: (overlayContext) =>
|
||||||
dashboardState: this,
|
FullScreenBlurOverlay(dashboardState: this, context: context),
|
||||||
context: context,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
Overlay.of(context, rootOverlay: true).insert(overlayEntry!);
|
Overlay.of(context, rootOverlay: true).insert(overlayEntry!);
|
||||||
}
|
}
|
||||||
@@ -80,7 +84,9 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> {
|
|||||||
void closeOverlay({required bool apply}) {
|
void closeOverlay({required bool apply}) {
|
||||||
if (apply) {
|
if (apply) {
|
||||||
HapticService.medium();
|
HapticService.medium();
|
||||||
ref.read(cardColorsProvider.notifier).save(tempPrimary, tempSecondary, tempGradientType);
|
ref
|
||||||
|
.read(cardColorsProvider.notifier)
|
||||||
|
.save(tempPrimary, tempSecondary, tempGradientType);
|
||||||
} else {
|
} else {
|
||||||
setState(() {
|
setState(() {
|
||||||
tempPrimary = savedPrimary;
|
tempPrimary = savedPrimary;
|
||||||
@@ -118,10 +124,8 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> {
|
|||||||
|
|
||||||
void _showAccountOverlay() {
|
void _showAccountOverlay() {
|
||||||
overlayEntry = OverlayEntry(
|
overlayEntry = OverlayEntry(
|
||||||
builder: (overlayContext) => AccountEditorOverlay(
|
builder: (overlayContext) =>
|
||||||
dashboardState: this,
|
AccountEditorOverlay(dashboardState: this, context: context),
|
||||||
context: context,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
Overlay.of(context, rootOverlay: true).insert(overlayEntry!);
|
Overlay.of(context, rootOverlay: true).insert(overlayEntry!);
|
||||||
}
|
}
|
||||||
@@ -131,11 +135,9 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> {
|
|||||||
HapticService.medium();
|
HapticService.medium();
|
||||||
|
|
||||||
// Save colors
|
// Save colors
|
||||||
await ref.read(accountCardColorsProvider(editingAccount!.id).notifier).save(
|
await ref
|
||||||
tempPrimary,
|
.read(accountCardColorsProvider(editingAccount!.id).notifier)
|
||||||
tempSecondary,
|
.save(tempPrimary, tempSecondary, tempGradientType);
|
||||||
tempGradientType,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Update account name and currency
|
// Update account name and currency
|
||||||
final updatedAccount = Account(
|
final updatedAccount = Account(
|
||||||
@@ -149,10 +151,15 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> {
|
|||||||
|
|
||||||
await ref.read(accountRepositoryProvider).update(updatedAccount);
|
await ref.read(accountRepositoryProvider).update(updatedAccount);
|
||||||
} else {
|
} else {
|
||||||
|
// Restore original values on cancel
|
||||||
setState(() {
|
setState(() {
|
||||||
tempPrimary = savedPrimary;
|
tempPrimary = savedPrimary;
|
||||||
tempSecondary = savedSecondary;
|
tempSecondary = savedSecondary;
|
||||||
tempGradientType = savedGradientType;
|
tempGradientType = savedGradientType;
|
||||||
|
if (editingAccount != null) {
|
||||||
|
tempAccountName = editingAccount!.name;
|
||||||
|
tempAccountCurrency = editingAccount!.currency;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,12 +231,19 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> {
|
|||||||
child: Center(
|
child: Center(
|
||||||
child: Builder(
|
child: Builder(
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
final raw = DateFormat('LLLL, yyyy', s.dateLocale).format(DateTime.now());
|
final raw = DateFormat(
|
||||||
final capitalized = raw.isNotEmpty ? '${raw[0].toUpperCase()}${raw.substring(1)}' : raw;
|
'LLLL, yyyy',
|
||||||
|
s.dateLocale,
|
||||||
|
).format(DateTime.now());
|
||||||
|
final capitalized = raw.isNotEmpty
|
||||||
|
? '${raw[0].toUpperCase()}${raw.substring(1)}'
|
||||||
|
: raw;
|
||||||
return Text(
|
return Text(
|
||||||
capitalized,
|
capitalized,
|
||||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||||
color: Theme.of(context).colorScheme.onSurface.withOpacity(0.5),
|
color: Theme.of(
|
||||||
|
context,
|
||||||
|
).colorScheme.onSurface.withOpacity(0.5),
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -268,7 +282,9 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> {
|
|||||||
onAccountLongPress: _onAccountCardLongPress,
|
onAccountLongPress: _onAccountCardLongPress,
|
||||||
previewPrimary: editingCard ? tempPrimary : null,
|
previewPrimary: editingCard ? tempPrimary : null,
|
||||||
previewSecondary: editingCard ? tempSecondary : null,
|
previewSecondary: editingCard ? tempSecondary : null,
|
||||||
previewGradientType: editingCard ? tempGradientType : null,
|
previewGradientType: editingCard
|
||||||
|
? tempGradientType
|
||||||
|
: null,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
SummaryRow(
|
SummaryRow(
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|||||||
import '../../../core/l10n/app_strings.dart';
|
import '../../../core/l10n/app_strings.dart';
|
||||||
import '../../../core/l10n/locale_provider.dart';
|
import '../../../core/l10n/locale_provider.dart';
|
||||||
import '../../../core/services/card_color_service.dart';
|
import '../../../core/services/card_color_service.dart';
|
||||||
|
import '../../../shared/models/transaction.dart';
|
||||||
import '../../settings/provider.dart';
|
import '../../settings/provider.dart';
|
||||||
import '../provider.dart';
|
import '../provider.dart';
|
||||||
import 'balance_card.dart';
|
import 'balance_card.dart';
|
||||||
@@ -32,16 +33,36 @@ class _AccountEditorOverlayState extends State<AccountEditorOverlay> {
|
|||||||
dynamic get dash => widget.dashboardState;
|
dynamic get dash => widget.dashboardState;
|
||||||
late TextEditingController _nameController;
|
late TextEditingController _nameController;
|
||||||
late String _selectedCurrency;
|
late String _selectedCurrency;
|
||||||
|
late String _originalCurrency;
|
||||||
bool _showCurrencyDropdown = false;
|
bool _showCurrencyDropdown = false;
|
||||||
|
bool _showLimitError = false;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_nameController = TextEditingController(text: dash.tempAccountName);
|
_nameController = TextEditingController(text: dash.tempAccountName);
|
||||||
_selectedCurrency = dash.tempAccountCurrency;
|
_selectedCurrency = dash.tempAccountCurrency;
|
||||||
|
_originalCurrency = dash.tempAccountCurrency;
|
||||||
_nameController.addListener(() {
|
_nameController.addListener(() {
|
||||||
|
final text = _nameController.text;
|
||||||
|
if (text.length > 17) {
|
||||||
|
_nameController.text = text.substring(0, 17);
|
||||||
|
_nameController.selection = TextSelection.fromPosition(
|
||||||
|
const TextPosition(offset: 17),
|
||||||
|
);
|
||||||
|
setState(() => _showLimitError = true);
|
||||||
|
Future.delayed(const Duration(seconds: 2), () {
|
||||||
|
if (mounted) setState(() => _showLimitError = false);
|
||||||
|
});
|
||||||
|
return; // Skip updating dash to avoid out-of-bounds
|
||||||
|
}
|
||||||
|
|
||||||
|
// Real-time update on card
|
||||||
|
dash.setState(() {
|
||||||
dash.tempAccountName = _nameController.text;
|
dash.tempAccountName = _nameController.text;
|
||||||
});
|
});
|
||||||
|
dash.overlayEntry?.markNeedsBuild();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -60,6 +81,29 @@ class _AccountEditorOverlayState extends State<AccountEditorOverlay> {
|
|||||||
final colorPanelTop = editorPanelTop + editorPanelHeight + 12;
|
final colorPanelTop = editorPanelTop + editorPanelHeight + 12;
|
||||||
const colorPanelHeight = 410.0;
|
const colorPanelHeight = 410.0;
|
||||||
|
|
||||||
|
return Consumer(
|
||||||
|
builder: (context, ref, _) {
|
||||||
|
final exchangeService = ref.watch(exchangeRateServiceProvider);
|
||||||
|
|
||||||
|
// Get the original balance from the editing account
|
||||||
|
double originalBalance = ref.read(totalBalanceProvider);
|
||||||
|
if (dash.editingAccount != null) {
|
||||||
|
// Get the account's actual balance
|
||||||
|
final txs = ref.watch(accountFilteredTransactionsProvider);
|
||||||
|
final accountTxs = txs.where((t) => t.accountId == dash.editingAccount!.id);
|
||||||
|
originalBalance = accountTxs.fold<double>(
|
||||||
|
0.0,
|
||||||
|
(sum, t) => sum + (t.type == TransactionType.income ? t.amount : -t.amount),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert to the preview currency
|
||||||
|
final previewBalance = exchangeService.convert(
|
||||||
|
originalBalance,
|
||||||
|
_originalCurrency,
|
||||||
|
dash.tempAccountCurrency,
|
||||||
|
);
|
||||||
|
|
||||||
return Material(
|
return Material(
|
||||||
color: Colors.transparent,
|
color: Colors.transparent,
|
||||||
child: Stack(
|
child: Stack(
|
||||||
@@ -90,9 +134,8 @@ class _AccountEditorOverlayState extends State<AccountEditorOverlay> {
|
|||||||
top: cardTop,
|
top: cardTop,
|
||||||
left: 20,
|
left: 20,
|
||||||
right: 20,
|
right: 20,
|
||||||
child: Consumer(
|
child: BalanceCard(
|
||||||
builder: (ctx, ref, _) => BalanceCard(
|
balance: previewBalance,
|
||||||
balance: ref.read(totalBalanceProvider),
|
|
||||||
currencyInfo: CurrencyInfo(
|
currencyInfo: CurrencyInfo(
|
||||||
currencyMap[dash.tempAccountCurrency]?.symbol ?? '\$',
|
currencyMap[dash.tempAccountCurrency]?.symbol ?? '\$',
|
||||||
dash.tempAccountCurrency,
|
dash.tempAccountCurrency,
|
||||||
@@ -104,7 +147,6 @@ class _AccountEditorOverlayState extends State<AccountEditorOverlay> {
|
|||||||
previewGradientType: dash.tempGradientType,
|
previewGradientType: dash.tempGradientType,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
// Account Editor Panel
|
// Account Editor Panel
|
||||||
Positioned(
|
Positioned(
|
||||||
top: editorPanelTop,
|
top: editorPanelTop,
|
||||||
@@ -136,14 +178,10 @@ class _AccountEditorOverlayState extends State<AccountEditorOverlay> {
|
|||||||
// Currency Dropdown - Above everything
|
// Currency Dropdown - Above everything
|
||||||
if (_showCurrencyDropdown)
|
if (_showCurrencyDropdown)
|
||||||
Positioned(
|
Positioned(
|
||||||
top: editorPanelTop + 58, // Position below the currency button
|
top: editorPanelTop + 62,
|
||||||
left: 20 + 14 + (MediaQuery.of(context).size.width - 40 - 28) * 0.75 + 8, // Align with currency button
|
right: 34, // 20 (panel padding) + 14 (inner padding)
|
||||||
width: (MediaQuery.of(context).size.width - 40 - 28) * 0.25, // Same width as currency button
|
width: (MediaQuery.of(context).size.width - 68) * 0.25, // 25% of the inner row width
|
||||||
child: Consumer(
|
child: Material(
|
||||||
builder: (context, ref, _) {
|
|
||||||
final exchangeService = ref.read(exchangeRateServiceProvider);
|
|
||||||
|
|
||||||
return Material(
|
|
||||||
elevation: 12,
|
elevation: 12,
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
child: Container(
|
child: Container(
|
||||||
@@ -166,18 +204,15 @@ class _AccountEditorOverlayState extends State<AccountEditorOverlay> {
|
|||||||
final isSelected = entry.$1 == _selectedCurrency;
|
final isSelected = entry.$1 == _selectedCurrency;
|
||||||
return InkWell(
|
return InkWell(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
final oldCurrency = _selectedCurrency;
|
|
||||||
final newCurrency = entry.$1;
|
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_selectedCurrency = newCurrency;
|
_selectedCurrency = entry.$1;
|
||||||
dash.tempAccountCurrency = newCurrency;
|
// Update temp currency for preview
|
||||||
|
dash.setState(() {
|
||||||
|
dash.tempAccountCurrency = entry.$1;
|
||||||
|
});
|
||||||
|
dash.overlayEntry?.markNeedsBuild();
|
||||||
_showCurrencyDropdown = false;
|
_showCurrencyDropdown = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Note: Currency conversion will happen automatically
|
|
||||||
// when the account is saved, as the exchangeRateServiceProvider
|
|
||||||
// will handle the conversion in the balance calculations
|
|
||||||
},
|
},
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
@@ -217,8 +252,6 @@ class _AccountEditorOverlayState extends State<AccountEditorOverlay> {
|
|||||||
}).toList(),
|
}).toList(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
// Close Button - Top Right
|
// Close Button - Top Right
|
||||||
@@ -254,6 +287,8 @@ class _AccountEditorOverlayState extends State<AccountEditorOverlay> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildEditorPanel(double panelHeight) {
|
Widget _buildEditorPanel(double panelHeight) {
|
||||||
@@ -289,14 +324,14 @@ class _AccountEditorOverlayState extends State<AccountEditorOverlay> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
Row(
|
IntrinsicHeight(
|
||||||
|
child: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
flex: 3,
|
flex: 3,
|
||||||
child: TextField(
|
child: TextField(
|
||||||
controller: _nameController,
|
controller: _nameController,
|
||||||
maxLength: 17,
|
|
||||||
buildCounter: (context, {required currentLength, required isFocused, maxLength}) => null,
|
buildCounter: (context, {required currentLength, required isFocused, maxLength}) => null,
|
||||||
style: const TextStyle(fontSize: 13),
|
style: const TextStyle(fontSize: 13),
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
@@ -307,25 +342,31 @@ class _AccountEditorOverlayState extends State<AccountEditorOverlay> {
|
|||||||
border: OutlineInputBorder(
|
border: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
borderSide: BorderSide(
|
borderSide: BorderSide(
|
||||||
color: Theme.of(widget.context).colorScheme.onSurface.withOpacity(0.15),
|
color: _showLimitError
|
||||||
|
? Colors.red
|
||||||
|
: Theme.of(widget.context).colorScheme.onSurface.withOpacity(0.15),
|
||||||
width: 1.5,
|
width: 1.5,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
enabledBorder: OutlineInputBorder(
|
enabledBorder: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
borderSide: BorderSide(
|
borderSide: BorderSide(
|
||||||
color: Theme.of(widget.context).colorScheme.onSurface.withOpacity(0.15),
|
color: _showLimitError
|
||||||
|
? Colors.red
|
||||||
|
: Theme.of(widget.context).colorScheme.onSurface.withOpacity(0.15),
|
||||||
width: 1.5,
|
width: 1.5,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
focusedBorder: OutlineInputBorder(
|
focusedBorder: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
borderSide: const BorderSide(
|
borderSide: BorderSide(
|
||||||
color: Color(0xFF7C6DED),
|
color: _showLimitError
|
||||||
|
? Colors.red
|
||||||
|
: const Color(0xFF7C6DED),
|
||||||
width: 1.5,
|
width: 1.5,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
contentPadding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10),
|
contentPadding: const EdgeInsets.symmetric(horizontal: 10, vertical: 12),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -338,7 +379,7 @@ class _AccountEditorOverlayState extends State<AccountEditorOverlay> {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
child: Container(
|
child: Container(
|
||||||
height: 44, // Increased to match TextField height
|
height: double.infinity,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Theme.of(widget.context).colorScheme.onSurface.withOpacity(0.05),
|
color: Theme.of(widget.context).colorScheme.onSurface.withOpacity(0.05),
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
@@ -371,6 +412,7 @@ class _AccountEditorOverlayState extends State<AccountEditorOverlay> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
+4
-4
@@ -213,18 +213,18 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: drift
|
name: drift
|
||||||
sha256: "61f876c0291b194980bafd203f48e85d5fb04e4a7334367d1a89f44004dbcb83"
|
sha256: "055c249d1f91be5a47fe447f88afc24c4ca6f4cd6c5ed66767b4797d48acc2e5"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.32.0"
|
version: "2.32.1"
|
||||||
drift_dev:
|
drift_dev:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: drift_dev
|
name: drift_dev
|
||||||
sha256: d687e955cc4b1706ad49b3860fcc1045c09bbf1d84c3c7383615f7f9c3320aa2
|
sha256: "88a9de3af8571518148a6d8a513b57779fd1e60a026d3ab8a481a878fba01d91"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.32.0"
|
version: "2.32.1"
|
||||||
equatable:
|
equatable:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|||||||
Reference in New Issue
Block a user