diff --git a/lib/api/recurring_entry.dart b/lib/api/recurring_entry.dart new file mode 100644 index 0000000..46a1eb3 --- /dev/null +++ b/lib/api/recurring_entry.dart @@ -0,0 +1,32 @@ +import 'package:json_annotation/json_annotation.dart'; +import 'package:prasule/api/walletentry.dart'; +import 'package:prasule/api/category.dart'; +import 'package:prasule/api/entry_data.dart'; +part 'recurring_entry.g.dart'; + +enum RecurringType { byDay, byMonth, byYear } + +@JsonSerializable() + +/// Used to store data about recurring entries +class RecurringEntry extends WalletSingleEntry { + /// Sets recurring by day, month or year + RecurringType recurringType; + + /// Indicates by how many days/months/years should we recur + int recurEvery; + RecurringEntry( + {required super.data, + required super.type, + required super.date, + required super.category, + required super.id, + required this.recurringType, + required this.recurEvery}); + + factory RecurringEntry.fromJson(Map json) => + _$RecurringEntryFromJson(json); + + @override + Map toJson() => _$RecurringEntryToJson(this); +} diff --git a/lib/api/recurring_entry.g.dart b/lib/api/recurring_entry.g.dart new file mode 100644 index 0000000..354c4d0 --- /dev/null +++ b/lib/api/recurring_entry.g.dart @@ -0,0 +1,41 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'recurring_entry.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +RecurringEntry _$RecurringEntryFromJson(Map json) => + RecurringEntry( + data: EntryData.fromJson(json['data'] as Map), + type: $enumDecode(_$EntryTypeEnumMap, json['type']), + date: DateTime.parse(json['date'] as String), + category: + WalletCategory.fromJson(json['category'] as Map), + id: json['id'] as int, + recurringType: $enumDecode(_$RecurringTypeEnumMap, json['recurringType']), + recurEvery: json['recurEvery'] as int, + ); + +Map _$RecurringEntryToJson(RecurringEntry instance) => + { + 'type': _$EntryTypeEnumMap[instance.type]!, + 'data': instance.data, + 'date': instance.date.toIso8601String(), + 'category': instance.category, + 'id': instance.id, + 'recurringType': _$RecurringTypeEnumMap[instance.recurringType]!, + 'recurEvery': instance.recurEvery, + }; + +const _$EntryTypeEnumMap = { + EntryType.expense: 'expense', + EntryType.income: 'income', +}; + +const _$RecurringTypeEnumMap = { + RecurringType.byDay: 'byDay', + RecurringType.byMonth: 'byMonth', + RecurringType.byYear: 'byYear', +}; diff --git a/lib/api/wallet.dart b/lib/api/wallet.dart index 05e2d0a..3837f98 100644 --- a/lib/api/wallet.dart +++ b/lib/api/wallet.dart @@ -1,6 +1,7 @@ import 'package:currency_picker/currency_picker.dart'; import 'package:json_annotation/json_annotation.dart'; import 'package:prasule/api/category.dart'; +import 'package:prasule/api/recurring_entry.dart'; import 'package:prasule/api/walletentry.dart'; part 'wallet.g.dart'; @@ -12,6 +13,7 @@ class Wallet { final String name; final List categories; final List entries; + final List recurringEntries; double starterBalance; @JsonKey(fromJson: _currencyFromJson) final Currency currency; @@ -21,7 +23,8 @@ class Wallet { required this.currency, this.categories = const [], this.entries = const [], - this.starterBalance = 0}); + this.starterBalance = 0, + this.recurringEntries = const []}); /// Connect the generated [_$WalletEntry] function to the `fromJson` /// factory. diff --git a/lib/api/wallet.g.dart b/lib/api/wallet.g.dart index d2affe5..4e9bac3 100644 --- a/lib/api/wallet.g.dart +++ b/lib/api/wallet.g.dart @@ -19,12 +19,17 @@ Wallet _$WalletFromJson(Map json) => Wallet( .toList() ?? const [], starterBalance: (json['starterBalance'] as num?)?.toDouble() ?? 0, + recurringEntries: (json['recurringEntries'] as List?) + ?.map((e) => RecurringEntry.fromJson(e as Map)) + .toList() ?? + const [], ); Map _$WalletToJson(Wallet instance) => { 'name': instance.name, 'categories': instance.categories, 'entries': instance.entries, + 'recurringEntries': instance.recurringEntries, 'starterBalance': instance.starterBalance, 'currency': instance.currency, }; diff --git a/lib/l10n/app_cs.arb b/lib/l10n/app_cs.arb index 3f32913..5f71b92 100644 --- a/lib/l10n/app_cs.arb +++ b/lib/l10n/app_cs.arb @@ -61,6 +61,8 @@ "createTestData":"Vytvořit vzorková data", "spendingStats":"Statistiky utrácení", "yearly":"Roční", - "monthly":"Měsíční" + "monthly":"Měsíční", + "sourceCode":"Zdrojový kód", + "recurringEntries":"Opakující se záznamy" } \ No newline at end of file diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 3fd1003..09c2f6f 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -97,5 +97,7 @@ "createTestData":"Create test data", "spendingStats":"Spending statistics", "yearly":"Yearly", - "monthly":"Monthly" + "monthly":"Monthly", + "sourceCode":"Source code", + "recurringEntries":"Recurring entries" } \ No newline at end of file diff --git a/lib/util/drawer.dart b/lib/util/drawer.dart index fbc3a20..63eab28 100644 --- a/lib/util/drawer.dart +++ b/lib/util/drawer.dart @@ -3,6 +3,7 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:prasule/pw/platformroute.dart'; import 'package:prasule/views/graph_view.dart'; import 'package:prasule/views/home.dart'; +import 'package:prasule/views/recurring_view.dart'; /// Makes the drawer because I won't enter the same code in every view Drawer makeDrawer(BuildContext context, int page) => Drawer( @@ -39,6 +40,21 @@ Drawer makeDrawer(BuildContext context, int page) => Drawer( .pushReplacement(platformRoute((p0) => const GraphView())); }, ), + ListTile( + leading: const Icon(Icons.loop), + title: Text( + AppLocalizations.of(context)!.recurringEntries, + ), + selected: page == 3, + onTap: () { + if (page == 3) { + Navigator.of(context).pop(); + return; + } + Navigator.of(context).pushReplacement( + platformRoute((p0) => const RecurringEntriesView())); + }, + ), ], ), ); diff --git a/lib/views/graph_view.dart b/lib/views/graph_view.dart index e03c7c6..8bf588c 100644 --- a/lib/views/graph_view.dart +++ b/lib/views/graph_view.dart @@ -1,4 +1,3 @@ -import 'package:fl_chart/fl_chart.dart'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'package:prasule/api/wallet.dart'; diff --git a/lib/views/home.dart b/lib/views/home.dart index 6d6e5ef..86af247 100644 --- a/lib/views/home.dart +++ b/lib/views/home.dart @@ -1,6 +1,5 @@ import 'dart:math'; -import 'package:currency_picker/currency_picker.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_slidable/flutter_slidable.dart'; @@ -26,6 +25,7 @@ import 'package:prasule/views/settings/settings.dart'; import 'package:prasule/views/settings/tessdata_list.dart'; import 'package:prasule/views/setup.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:url_launcher/url_launcher_string.dart'; class HomeView extends StatefulWidget { const HomeView({super.key}); @@ -197,6 +197,14 @@ class _HomeViewState extends State { ); } else if (value == AppLocalizations.of(context)!.about) { showAboutDialog( + children: [ + PlatformButton( + text: AppLocalizations.of(context)!.sourceCode, + onPressed: () => launchUrlString( + "https://git.mnau.xyz/hernik/prasule", + mode: LaunchMode.externalApplication), + ), + ], context: context, applicationLegalese: AppLocalizations.of(context)!.license, applicationName: "Prašule"); diff --git a/lib/views/recurring_view.dart b/lib/views/recurring_view.dart new file mode 100644 index 0000000..561c716 --- /dev/null +++ b/lib/views/recurring_view.dart @@ -0,0 +1,27 @@ +import 'package:flutter/material.dart'; +import 'package:prasule/api/wallet.dart'; +import 'package:prasule/util/drawer.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; + +class RecurringEntriesView extends StatefulWidget { + const RecurringEntriesView({super.key}); + + @override + State createState() => _RecurringEntriesViewState(); +} + +class _RecurringEntriesViewState extends State { + Wallet? selectedWallet; + List wallets = []; + String? locale; + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text(AppLocalizations.of(context)!.recurringEntries), + ), + drawer: makeDrawer(context, 3), + ); + } +} diff --git a/pubspec.lock b/pubspec.lock index e4109b8..d3f93b1 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1041,6 +1041,70 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.2" + url_launcher: + dependency: "direct main" + description: + name: url_launcher + sha256: b1c9e98774adf8820c96fbc7ae3601231d324a7d5ebd8babe27b6dfac91357ba + url: "https://pub.dev" + source: hosted + version: "6.2.1" + url_launcher_android: + dependency: transitive + description: + name: url_launcher_android + sha256: "31222ffb0063171b526d3e569079cf1f8b294075ba323443fdc690842bfd4def" + url: "https://pub.dev" + source: hosted + version: "6.2.0" + url_launcher_ios: + dependency: transitive + description: + name: url_launcher_ios + sha256: bba3373219b7abb6b5e0d071b0fe66dfbe005d07517a68e38d4fc3638f35c6d3 + url: "https://pub.dev" + source: hosted + version: "6.2.1" + url_launcher_linux: + dependency: transitive + description: + name: url_launcher_linux + sha256: "9f2d390e096fdbe1e6e6256f97851e51afc2d9c423d3432f1d6a02a8a9a8b9fd" + url: "https://pub.dev" + source: hosted + version: "3.1.0" + url_launcher_macos: + dependency: transitive + description: + name: url_launcher_macos + sha256: b7244901ea3cf489c5335bdacda07264a6e960b1c1b1a9f91e4bc371d9e68234 + url: "https://pub.dev" + source: hosted + version: "3.1.0" + url_launcher_platform_interface: + dependency: transitive + description: + name: url_launcher_platform_interface + sha256: "980e8d9af422f477be6948bdfb68df8433be71f5743a188968b0c1b887807e50" + url: "https://pub.dev" + source: hosted + version: "2.2.0" + url_launcher_web: + dependency: transitive + description: + name: url_launcher_web + sha256: "138bd45b3a456dcfafc46d1a146787424f8d2edfbf2809c9324361e58f851cf7" + url: "https://pub.dev" + source: hosted + version: "2.2.1" + url_launcher_windows: + dependency: transitive + description: + name: url_launcher_windows + sha256: "7754a1ad30ee896b265f8d14078b0513a4dba28d358eabb9d5f339886f4a1adc" + url: "https://pub.dev" + source: hosted + version: "3.1.0" vector_math: dependency: transitive description: @@ -1131,4 +1195,4 @@ packages: version: "3.1.2" sdks: dart: ">=3.2.0 <4.0.0" - flutter: ">=3.7.0" + flutter: ">=3.16.0" diff --git a/pubspec.yaml b/pubspec.yaml index 8f0235b..29927a6 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,7 +16,6 @@ dependencies: flutter: sdk: flutter - # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.2 @@ -39,6 +38,7 @@ dependencies: flutter_localizations: sdk: flutter fl_chart: ^0.65.0 + url_launcher: ^6.2.1 dev_dependencies: flutter_test: @@ -76,7 +76,6 @@ flutter_launcher_icons: # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec - # The following section is specific to Flutter packages. flutter: generate: true @@ -87,18 +86,14 @@ flutter: assets: - assets/ - assets/tessdata/ - # To add assets to your application, add an assets section, like this: # assets: # - images/a_dot_burr.jpeg # - images/a_dot_ham.jpeg - # An image asset can refer to one or more resolution-specific "variants", see # https://flutter.dev/assets-and-images/#resolution-aware - # For details regarding adding assets from package dependencies, see # https://flutter.dev/assets-and-images/#from-packages - # To add custom fonts to your application, add a fonts section here, # in this "flutter" section. Each entry in this list should have a # "family" key with the font family name, and a "fonts" key with a