diff --git a/.github/workflows/manual-build.yml b/.github/workflows/manual-build.yml index 22e8f5a4..9347362d 100644 --- a/.github/workflows/manual-build.yml +++ b/.github/workflows/manual-build.yml @@ -111,11 +111,11 @@ jobs: - name: Build Linux packages (.deb, .rpm, and AppImage) run: | - # Add submodule Flutter to PATH first (so flutter_distributor uses it) + # Add submodule Flutter to PATH first (so fastforge uses it) export PATH="$(pwd)/submodules/flutter/bin:$PATH" - # Install flutter_distributor - dart pub global activate flutter_distributor + # Install fastforge + dart pub global activate fastforge export PATH="$HOME/.pub-cache/bin:$PATH" # Install tools for building .rpm and AppImage packages @@ -131,7 +131,7 @@ jobs: flutter --version # Build .deb, .rpm, and AppImage packages - flutter_distributor release --name=linux-release --jobs=release-linux-deb,release-linux-rpm,release-linux-appimage + fastforge release --name=linux-release --jobs=release-linux-deb,release-linux-rpm,release-linux-appimage - name: Upload .deb as artifact uses: actions/upload-artifact@v4 diff --git a/.github/workflows/on-release.yml b/.github/workflows/on-release.yml index c69b1cb9..40d6855c 100644 --- a/.github/workflows/on-release.yml +++ b/.github/workflows/on-release.yml @@ -227,11 +227,11 @@ jobs: - name: Build Linux packages (.deb, .rpm, and AppImage) run: | - # Add submodule Flutter to PATH first (so flutter_distributor uses it) + # Add submodule Flutter to PATH first (so fastforge uses it) export PATH="$(pwd)/submodules/flutter/bin:$PATH" - # Install flutter_distributor - dart pub global activate flutter_distributor + # Install fastforge + dart pub global activate fastforge export PATH="$HOME/.pub-cache/bin:$PATH" # Install tools for building .rpm and AppImage packages @@ -247,7 +247,7 @@ jobs: flutter --version # Build .deb, .rpm, and AppImage packages - flutter_distributor release --name=linux-release --jobs=release-linux-deb,release-linux-rpm,release-linux-appimage + fastforge release --name=linux-release --jobs=release-linux-deb,release-linux-rpm,release-linux-appimage - name: Upload Linux packages as Release asset uses: softprops/action-gh-release@v1 diff --git a/.gitignore b/.gitignore index 7330aa3c..b2dcb48c 100644 --- a/.gitignore +++ b/.gitignore @@ -76,7 +76,7 @@ /build-debug-file/ /tmp_build .flutter-plugins-dependencies -ios/Flutter/flutter_export_environment.sh +**/ios/Flutter/flutter_export_environment.sh macos/Flutter/ephemeral/flutter_export_environment.sh macos/Flutter/ephemeral/Flutter-Generated.xcconfig diff --git a/.metadata b/.metadata index 0505c6c9..352c9c2e 100644 --- a/.metadata +++ b/.metadata @@ -4,7 +4,7 @@ # This file should be version controlled and should not be manually edited. version: - revision: "b45fa18946ecc2d9b4009952c636ba7e2ffbb787" + revision: "8b872868494e429d94fa06dca855c306438b22c0" channel: "stable" project_type: app @@ -13,11 +13,11 @@ project_type: app migration: platforms: - platform: root - create_revision: b45fa18946ecc2d9b4009952c636ba7e2ffbb787 - base_revision: b45fa18946ecc2d9b4009952c636ba7e2ffbb787 - - platform: linux - create_revision: b45fa18946ecc2d9b4009952c636ba7e2ffbb787 - base_revision: b45fa18946ecc2d9b4009952c636ba7e2ffbb787 + create_revision: 8b872868494e429d94fa06dca855c306438b22c0 + base_revision: 8b872868494e429d94fa06dca855c306438b22c0 + - platform: macos + create_revision: 8b872868494e429d94fa06dca855c306438b22c0 + base_revision: 8b872868494e429d94fa06dca855c306438b22c0 # User provided section diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 00000000..d09a7abc --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,6 @@ +{ + "recommendations": [ + "dart-code.dart-code", + "dart-code.flutter" + ] +} diff --git a/.vscode/launch.json b/.vscode/launch.json index 3287bb67..7e461cb2 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -7,7 +7,8 @@ { "name": "Flutter", "request": "launch", - "type": "dart" + "type": "dart", + "flutterMode": "debug" } ] } \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..d1250ee5 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,20 @@ +{ + "search.exclude": { + "**/submodules": true, + }, + "terminal.integrated.defaultProfile.linux": "bash", + "terminal.integrated.profiles.linux": { + "bash": { + "path": "/usr/bin/flatpak-spawn", + "args": ["--host", "--env=TERM=xterm-256color", "bash"], + "icon": "terminal-bash", + "overrideName": true + } + }, + "dart.flutterSdkPath": "~/development/flutter", + "[dart]": { + "editor.defaultFormatter": "Dart-Code.dart-code", + "editor.formatOnSave": true, + "editor.formatOnType": true, + } +} diff --git a/build_linux.sh b/build_linux.sh index 35f10f76..c211e8de 100755 --- a/build_linux.sh +++ b/build_linux.sh @@ -93,18 +93,18 @@ if ! pkg-config --exists gtk+-3.0 2>/dev/null; then fi echo -e "${GREEN}✓ GTK3 development libraries found${NC}" -# Find flutter_distributor -DISTRIBUTOR_CMD="$HOME/.pub-cache/bin/flutter_distributor" +# Find fastforge +DISTRIBUTOR_CMD="$HOME/.pub-cache/bin/fastforge" if [ ! -f "$DISTRIBUTOR_CMD" ]; then - echo -e "${YELLOW}flutter_distributor not found, installing...${NC}" - $FLUTTER_CMD pub global activate flutter_distributor + echo -e "${YELLOW}fastforge not found, installing...${NC}" + $FLUTTER_CMD pub global activate fastforge if [ ! -f "$DISTRIBUTOR_CMD" ]; then - echo -e "${RED}Error: Failed to install flutter_distributor${NC}" + echo -e "${RED}Error: Failed to install fastforge${NC}" exit 1 fi fi -echo -e "${GREEN}✓ flutter_distributor ready${NC}" +echo -e "${GREEN}✓ fastforge ready${NC}" echo "" echo "===================================" diff --git a/ios/Flutter/ephemeral/flutter_lldb_helper.py b/ios/Flutter/ephemeral/flutter_lldb_helper.py deleted file mode 100644 index a88caf99..00000000 --- a/ios/Flutter/ephemeral/flutter_lldb_helper.py +++ /dev/null @@ -1,32 +0,0 @@ -# -# Generated file, do not edit. -# - -import lldb - -def handle_new_rx_page(frame: lldb.SBFrame, bp_loc, extra_args, intern_dict): - """Intercept NOTIFY_DEBUGGER_ABOUT_RX_PAGES and touch the pages.""" - base = frame.register["x0"].GetValueAsAddress() - page_len = frame.register["x1"].GetValueAsUnsigned() - - # Note: NOTIFY_DEBUGGER_ABOUT_RX_PAGES will check contents of the - # first page to see if handled it correctly. This makes diagnosing - # misconfiguration (e.g. missing breakpoint) easier. - data = bytearray(page_len) - data[0:8] = b'IHELPED!' - - error = lldb.SBError() - frame.GetThread().GetProcess().WriteMemory(base, data, error) - if not error.Success(): - print(f'Failed to write into {base}[+{page_len}]', error) - return - -def __lldb_init_module(debugger: lldb.SBDebugger, _): - target = debugger.GetDummyTarget() - # Caveat: must use BreakpointCreateByRegEx here and not - # BreakpointCreateByName. For some reasons callback function does not - # get carried over from dummy target for the later. - bp = target.BreakpointCreateByRegex("^NOTIFY_DEBUGGER_ABOUT_RX_PAGES$") - bp.SetScriptCallbackFunction('{}.handle_new_rx_page'.format(__name__)) - bp.SetAutoContinue(True) - print("-- LLDB integration loaded --") diff --git a/ios/Flutter/ephemeral/flutter_lldbinit b/ios/Flutter/ephemeral/flutter_lldbinit deleted file mode 100644 index e3ba6fbe..00000000 --- a/ios/Flutter/ephemeral/flutter_lldbinit +++ /dev/null @@ -1,5 +0,0 @@ -# -# Generated file, do not edit. -# - -command script import --relative-to-command-file flutter_lldb_helper.py diff --git a/ios/PLACEHOLDER b/ios/PLACEHOLDER deleted file mode 100644 index e69de29b..00000000 diff --git a/lib/categories/categories-grid.dart b/lib/categories/categories-grid.dart index 21fdfd04..9b3cd60f 100644 --- a/lib/categories/categories-grid.dart +++ b/lib/categories/categories-grid.dart @@ -22,7 +22,6 @@ class CategoriesGrid extends StatefulWidget { } class CategoriesGridState extends State { - List orderedCategories = []; bool enableManualSorting = false; late ScrollController _scrollController; @@ -76,7 +75,7 @@ class CategoriesGridState extends State { child: Container( child: Column( children: [ - CategoryIconCircle( + CategoryIconCircle( iconEmoji: category.iconEmoji, iconDataFromDefaultIconSet: category.icon, backgroundColor: category.color, diff --git a/lib/categories/categories-list.dart b/lib/categories/categories-list.dart index 2f403eaa..8adf655f 100644 --- a/lib/categories/categories-list.dart +++ b/lib/categories/categories-list.dart @@ -55,8 +55,7 @@ class CategoriesListState extends State { iconEmoji: category.iconEmoji, iconDataFromDefaultIconSet: category.icon, backgroundColor: category.color, - overlayIcon: category.isArchived ? Icons.archive : null - ), + overlayIcon: category.isArchived ? Icons.archive : null), title: Text(category.name!, style: _biggerFont), ), ), diff --git a/lib/categories/categories-tab-page-edit.dart b/lib/categories/categories-tab-page-edit.dart index e23b3bec..32420f69 100644 --- a/lib/categories/categories-tab-page-edit.dart +++ b/lib/categories/categories-tab-page-edit.dart @@ -48,7 +48,8 @@ class TabCategoriesState extends State void _handleTabChange() { // Check if the tab index has actually changed (works for both clicks and swipes) - if (_tabController!.index != _previousTabIndex && !_tabController!.indexIsChanging) { + if (_tabController!.index != _previousTabIndex && + !_tabController!.indexIsChanging) { setState(() { _fabRotation += 3.14159; // 180 degrees rotation _previousTabIndex = _tabController!.index; @@ -408,10 +409,8 @@ class TabCategoriesState extends State await Navigator.push( context, MaterialPageRoute( - builder: (context) => EditCategoryPage( - categoryType: CategoryType.expense - ) - ), + builder: (context) => EditCategoryPage( + categoryType: CategoryType.expense)), ); await refreshCategoriesAndHighlightsTab(0); } else { @@ -419,10 +418,8 @@ class TabCategoriesState extends State await Navigator.push( context, MaterialPageRoute( - builder: (context) => EditCategoryPage( - categoryType: CategoryType.income - ) - ), + builder: (context) => EditCategoryPage( + categoryType: CategoryType.income)), ); await refreshCategoriesAndHighlightsTab(1); } diff --git a/lib/categories/categories-tab-page-view.dart b/lib/categories/categories-tab-page-view.dart index e4ba31be..03deaf4d 100644 --- a/lib/categories/categories-tab-page-view.dart +++ b/lib/categories/categories-tab-page-view.dart @@ -111,7 +111,8 @@ class CategoryTabPageViewState extends State { Row( children: [ Checkbox( - value: _isDefaultOrder || _selectedSortOption == _storedDefaultOption, + value: _isDefaultOrder || + _selectedSortOption == _storedDefaultOption, onChanged: (value) { setModalState(() { _isDefaultOrder = value ?? false; @@ -161,7 +162,7 @@ class CategoryTabPageViewState extends State { ), trailing: _selectedSortOption == SortOption.alphabetical ? Icon(Icons.check, - color: Theme.of(context).colorScheme.primary) + color: Theme.of(context).colorScheme.primary) : null, onTap: () { setModalState(() { @@ -306,9 +307,7 @@ class CategoryTabPageViewState extends State { tabs: [ Semantics( identifier: 'expenses-tab', - child: Tab( - text: "Expenses".i18n.toUpperCase() - ), + child: Tab(text: "Expenses".i18n.toUpperCase()), ), Semantics( identifier: 'income-tab', diff --git a/lib/categories/edit-category-page.dart b/lib/categories/edit-category-page.dart index 7737a877..c6c7745b 100644 --- a/lib/categories/edit-category-page.dart +++ b/lib/categories/edit-category-page.dart @@ -111,7 +111,8 @@ class EditCategoryPageState extends State { Widget _getIconsGrid() { var surfaceContainer = Theme.of(context).colorScheme.surfaceContainer; var bottonActionColor = Theme.of(context).colorScheme.surfaceContainerLow; - var buttonColors = Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.6); + var buttonColors = + Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.6); return Column( children: [ Offstage( @@ -507,8 +508,8 @@ class EditCategoryPageState extends State { // Prompt confirmation AlertDialogBuilder archiveDialog = AlertDialogBuilder(dialogMessage) - .addTrueButtonName("Yes".i18n) - .addFalseButtonName("No".i18n); + .renameTrueButtonName("Yes".i18n) + .renameFalseButtonName("No".i18n); if (!isCurrentlyArchived) { archiveDialog.addSubtitle( @@ -546,8 +547,8 @@ class EditCategoryPageState extends State { .addSubtitle( "Deleting the category you will remove all the associated records" .i18n) - .addTrueButtonName("Yes".i18n) - .addFalseButtonName("No".i18n); + .renameTrueButtonName("Yes".i18n) + .renameFalseButtonName("No".i18n); var continueDelete = await showDialog( context: context, diff --git a/lib/components/category_icon_circle.dart b/lib/components/category_icon_circle.dart index d51ed438..53568d35 100644 --- a/lib/components/category_icon_circle.dart +++ b/lib/components/category_icon_circle.dart @@ -32,8 +32,7 @@ class CategoryIconCircle extends StatelessWidget { color: backgroundColor, ), child: iconEmoji != null - ? - Center( + ? Center( child: Text( iconEmoji!, // Display the emoji style: TextStyle( @@ -41,11 +40,11 @@ class CategoryIconCircle extends StatelessWidget { ), ), ) - : Icon( - iconDataFromDefaultIconSet, // Fallback to the icon - size: mainIconSize, - color: iconColor ?? Theme.of(context).colorScheme.onSurface, - ), + : Icon( + iconDataFromDefaultIconSet, // Fallback to the icon + size: mainIconSize, + color: iconColor ?? Theme.of(context).colorScheme.onSurface, + ), ); } @@ -60,7 +59,10 @@ class CategoryIconCircle extends StatelessWidget { shape: BoxShape.circle, color: iconBackground ? Theme.of(context).colorScheme.surface - : Theme.of(context).colorScheme.surfaceContainer.withValues(alpha: 0.8), + : Theme.of(context) + .colorScheme + .surfaceContainer + .withValues(alpha: 0.8), ), child: Icon( overlayIcon, @@ -83,8 +85,7 @@ class CategoryIconCircle extends StatelessWidget { backgroundColor ?? Theme.of(context).colorScheme.surface, ), if (overlayIcon != null) - _buildOverlayIcon( - context, overlayIcon, backgroundColor != null), + _buildOverlayIcon(context, overlayIcon, backgroundColor != null), ], ); } diff --git a/lib/components/year-picker.dart b/lib/components/year-picker.dart index bf1362b4..8a49ebde 100644 --- a/lib/components/year-picker.dart +++ b/lib/components/year-picker.dart @@ -393,7 +393,7 @@ class _DatePickerDialogState extends State<_DatePickerDialog> { // been updated to 4, so we will use that here for the Date Picker, but // only if there isn't one provided in the theme. shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(4.0))), + borderRadius: BorderRadius.all(Radius.circular(4.0))), clipBehavior: Clip.antiAlias, ); } diff --git a/lib/helpers/alert-dialog-builder.dart b/lib/helpers/alert-dialog-builder.dart index 134f48d2..62af9dc5 100644 --- a/lib/helpers/alert-dialog-builder.dart +++ b/lib/helpers/alert-dialog-builder.dart @@ -7,11 +7,15 @@ class AlertDialogBuilder { late String title; String? subtitle; + late bool trueButtonShow; + late bool falseButtonShow; late String trueButtonName; late String falseButtonName; AlertDialogBuilder(String title) { this.title = title; + this.trueButtonShow = true; + this.falseButtonShow = true; this.trueButtonName = "OK"; this.falseButtonName = "Cancel".i18n; this.subtitle = null; @@ -27,16 +31,26 @@ class AlertDialogBuilder { return this; } - AlertDialogBuilder addTrueButtonName(String trueButtonName) { + AlertDialogBuilder renameTrueButtonName(String trueButtonName) { this.trueButtonName = trueButtonName; return this; } - AlertDialogBuilder addFalseButtonName(String falseButtonName) { + AlertDialogBuilder renameFalseButtonName(String falseButtonName) { this.falseButtonName = falseButtonName; return this; } + AlertDialogBuilder hideTrueButton() { + this.trueButtonShow = false; + return this; + } + + AlertDialogBuilder hideFalseButton() { + this.falseButtonShow = false; + return this; + } + AlertDialog build(BuildContext context) { // set up the button Widget trueButton = TextButton( @@ -54,8 +68,8 @@ class AlertDialogBuilder { title: Text(title), content: (subtitle != null) ? Text(subtitle!) : null, actions: [ - trueButton, - falseButton, + if (trueButtonShow) trueButton, + if (falseButtonShow) falseButton, ], ); } diff --git a/lib/helpers/datetime-utility-functions.dart b/lib/helpers/datetime-utility-functions.dart index 35cc7f34..3a37c747 100644 --- a/lib/helpers/datetime-utility-functions.dart +++ b/lib/helpers/datetime-utility-functions.dart @@ -49,13 +49,17 @@ String getDateRangeStr(DateTime start, DateTime end) { } else { if (earlier.year == later.year) { // Same year: show year only once at the end - String startLocalRepr = DateFormat.MMMd(myLocale.languageCode).format(earlier); - String endLocalRepr = DateFormat.yMMMd(myLocale.languageCode).format(later); + String startLocalRepr = + DateFormat.MMMd(myLocale.languageCode).format(earlier); + String endLocalRepr = + DateFormat.yMMMd(myLocale.languageCode).format(later); return startLocalRepr + " - " + endLocalRepr; } else { // Different years: show year for both dates - String startLocalRepr = DateFormat.yMMMd(myLocale.languageCode).format(earlier); - String endLocalRepr = DateFormat.yMMMd(myLocale.languageCode).format(later); + String startLocalRepr = + DateFormat.yMMMd(myLocale.languageCode).format(earlier); + String endLocalRepr = + DateFormat.yMMMd(myLocale.languageCode).format(later); return startLocalRepr + " - " + endLocalRepr; } } @@ -86,7 +90,7 @@ String getWeekStr(DateTime dateTime) { /// - 7: Sunday /// Different locales have different week start days: /// - US, Brazil, China, Japan: Sunday (7) -/// - Arabic regions: Saturday (6) +/// - Arabic regions: Saturday (6) /// - Most European and other locales: Monday (1) int getFirstDayOfWeekIndex() { try { @@ -103,7 +107,7 @@ int getFirstDayOfWeekIndex() { // Fall back to locale-based logic String localeStr = I18n.locale.toString(); - + if (localeStr == 'en_US') return DateTime.sunday; if (localeStr.startsWith('pt_BR')) return DateTime.sunday; if (localeStr.startsWith('zh')) return DateTime.sunday; @@ -112,20 +116,23 @@ int getFirstDayOfWeekIndex() { } catch (e) { // Locale not initialized or error, use default } - + return DateTime.monday; // Default for most locales } /// Returns the last day of the week based on the first day. int _getLastDayOfWeek(int firstDayOfWeek) { - return firstDayOfWeek == DateTime.monday ? DateTime.sunday : firstDayOfWeek - 1; + return firstDayOfWeek == DateTime.monday + ? DateTime.sunday + : firstDayOfWeek - 1; } /// Calculates the number of days to offset from current day to reach target weekday. /// Handles week wraparound (e.g., going from Monday to previous Sunday). -int _calculateDaysOffset(int fromWeekday, int toWeekday, {bool forward = false}) { +int _calculateDaysOffset(int fromWeekday, int toWeekday, + {bool forward = false}) { if (forward) { - return toWeekday >= fromWeekday + return toWeekday >= fromWeekday ? toWeekday - fromWeekday : (7 - fromWeekday) + toWeekday; } else { @@ -144,11 +151,11 @@ DateTime getStartOfWeek(DateTime date) { DateTime getEndOfWeek(DateTime date) { int firstDayOfWeek = getFirstDayOfWeekIndex(); int lastDayOfWeek = _getLastDayOfWeek(firstDayOfWeek); - int daysToAdd = _calculateDaysOffset(date.weekday, lastDayOfWeek, forward: true); + int daysToAdd = + _calculateDaysOffset(date.weekday, lastDayOfWeek, forward: true); return DateTime(date.year, date.month, date.day + daysToAdd, 23, 59); } - String getDateStr(DateTime? dateTime, {AggregationMethod? aggregationMethod}) { Locale myLocale = I18n.locale; if (aggregationMethod != null) { @@ -213,10 +220,10 @@ bool isFullYear(DateTime from, DateTime to) { bool isFullWeek(DateTime intervalFrom, DateTime intervalTo) { int firstDayOfWeek = getFirstDayOfWeekIndex(); int lastDayOfWeek = _getLastDayOfWeek(firstDayOfWeek); - + return intervalTo.difference(intervalFrom).inDays == 6 && - intervalFrom.weekday == firstDayOfWeek && - intervalTo.weekday == lastDayOfWeek; + intervalFrom.weekday == firstDayOfWeek && + intervalTo.weekday == lastDayOfWeek; } tz.TZDateTime createTzDateTime(DateTime utcDateTime, String timeZoneName) { diff --git a/lib/helpers/records-utility-functions.dart b/lib/helpers/records-utility-functions.dart index fe6a8081..6cb909c9 100644 --- a/lib/helpers/records-utility-functions.dart +++ b/lib/helpers/records-utility-functions.dart @@ -362,7 +362,6 @@ Future> getRecordsByHomepageTimeInterval( return await getAllRecords(database); case HomepageTimeInterval.CurrentWeek: return await getRecordsByInterval( - database, getStartOfWeek(_now), getEndOfWeek(_now) - ); + database, getStartOfWeek(_now), getEndOfWeek(_now)); } } diff --git a/lib/i18n/i18n_helper.dart b/lib/i18n/i18n_helper.dart index 4a7b8eba..f6f35f26 100644 --- a/lib/i18n/i18n_helper.dart +++ b/lib/i18n/i18n_helper.dart @@ -49,4 +49,4 @@ class JSONImporter extends Importer { Map _load(String source) { return Map.from(json.decode(source)); } -} \ No newline at end of file +} diff --git a/lib/main.dart b/lib/main.dart index 0b050a20..78f93352 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -41,12 +41,15 @@ main() async { } logger.info('Timezone initialized: ${ServiceConfig.localTimezone}'); - PackageInfo packageInfo = await PackageInfo.fromPlatform(); ServiceConfig.packageName = packageInfo.packageName; ServiceConfig.version = packageInfo.version; - ServiceConfig.isPremium = packageInfo.packageName.endsWith("pro") || Platform.isLinux || Platform.isWindows || Platform.isMacOS; - logger.info('Package: ${ServiceConfig.packageName} v${ServiceConfig.version} (Premium: ${ServiceConfig.isPremium})'); + ServiceConfig.isPremium = packageInfo.packageName.endsWith("pro") || + Platform.isLinux || + Platform.isWindows || + Platform.isMacOS; + logger.info( + 'Package: ${ServiceConfig.packageName} v${ServiceConfig.version} (Premium: ${ServiceConfig.isPremium})'); ServiceConfig.sharedPreferences = await SharedPreferences.getInstance(); await MyI18n.loadTranslations(); @@ -60,7 +63,8 @@ main() async { final languageLocale = LocaleService.resolveLanguageLocale(); final currencyLocale = LocaleService.resolveCurrencyLocale(); LocaleService.setCurrencyLocale(currencyLocale); - logger.info('Locale configured: language=$languageLocale, currency=$currencyLocale'); + logger.info( + 'Locale configured: language=$languageLocale, currency=$currencyLocale'); final lightTheme = await MaterialThemeInstance.getLightTheme(); final darkTheme = await MaterialThemeInstance.getDarkTheme(); diff --git a/lib/models/recurrent-record-pattern.dart b/lib/models/recurrent-record-pattern.dart index fe452afe..9a5f420c 100644 --- a/lib/models/recurrent-record-pattern.dart +++ b/lib/models/recurrent-record-pattern.dart @@ -93,11 +93,11 @@ class RecurrentRecordPattern { } Set tags = map['tags'] != null - ? (map['tags'] as String) - .split(',') - .where((t) => t.trim().isNotEmpty) - .toSet() - : {}; + ? (map['tags'] as String) + .split(',') + .where((t) => t.trim().isNotEmpty) + .toSet() + : {}; return RecurrentRecordPattern( map['value'], diff --git a/lib/records/components/tag_selection_dialog.dart b/lib/records/components/tag_selection_dialog.dart index 93dfd891..51134141 100644 --- a/lib/records/components/tag_selection_dialog.dart +++ b/lib/records/components/tag_selection_dialog.dart @@ -206,7 +206,9 @@ class _TagSelectionDialogState extends State elevation: 8, icon: Icon(Icons.check), label: Text( - "Add selected tags (%s)".i18n.fill([_selectedTags.length.toString()]), + "Add selected tags (%s)" + .i18n + .fill([_selectedTags.length.toString()]), style: TextStyle( fontWeight: FontWeight.w600, fontSize: 16, diff --git a/lib/records/controllers/tab_records_controller.dart b/lib/records/controllers/tab_records_controller.dart index 715d9fce..52155dac 100644 --- a/lib/records/controllers/tab_records_controller.dart +++ b/lib/records/controllers/tab_records_controller.dart @@ -208,7 +208,8 @@ class TabRecordsController { // Check if future records should be shown final prefs = await SharedPreferences.getInstance(); final showFutureRecords = PreferencesUtils.getOrDefault( - prefs, PreferencesKeys.showFutureRecords) ?? true; + prefs, PreferencesKeys.showFutureRecords) ?? + true; // Calculate the view end date based on the current interval and preference DateTime viewEndDate; @@ -217,7 +218,8 @@ class TabRecordsController { viewEndDate = customIntervalTo!; } else { var hti = getHomepageTimeIntervalEnumSetting(); - var interval = await getTimeIntervalFromHomepageTimeInterval(_database, hti); + var interval = + await getTimeIntervalFromHomepageTimeInterval(_database, hti); viewEndDate = interval[1]; // End date of the interval } } else { @@ -229,7 +231,8 @@ class TabRecordsController { } // Update recurrent records and get future records - List futureRecords = await recurrentRecordService.updateRecurrentRecords(viewEndDate); + List futureRecords = + await recurrentRecordService.updateRecurrentRecords(viewEndDate); // Fetch records from database List newRecords; @@ -244,7 +247,7 @@ class TabRecordsController { List filteredFutureRecords = futureRecords.where((record) { return (record.utcDateTime.isAfter(intervalFromUtc) || record.utcDateTime.isAtSameMomentAs(intervalFromUtc)) && - (record.utcDateTime.isBefore(intervalToUtc) || + (record.utcDateTime.isBefore(intervalToUtc) || record.utcDateTime.isAtSameMomentAs(intervalToUtc)); }).toList(); @@ -391,7 +394,7 @@ class TabRecordsController { Future _showNoCategoryDialog(BuildContext context) async { AlertDialogBuilder noCategoryDialog = AlertDialogBuilder( "No Category is set yet.".i18n) - .addTrueButtonName("OK") + .renameTrueButtonName("OK") .addSubtitle( "You need to set a category first. Go to Category tab and add a new category." .i18n); diff --git a/lib/records/edit-record-page.dart b/lib/records/edit-record-page.dart index df1e0ad4..707a0722 100644 --- a/lib/records/edit-record-page.dart +++ b/lib/records/edit-record-page.dart @@ -945,8 +945,8 @@ class EditRecordPageState extends State { onPressed: () async { AlertDialogBuilder deleteDialog = AlertDialogBuilder("Critical action".i18n) - .addTrueButtonName("Yes".i18n) - .addFalseButtonName("No".i18n); + .renameTrueButtonName("Yes".i18n) + .renameFalseButtonName("No".i18n); if (widget.passedRecord != null) { deleteDialog = deleteDialog.addSubtitle( "Do you really want to delete this record?".i18n); diff --git a/lib/recurrent_record_patterns/patterns-page-view.dart b/lib/recurrent_record_patterns/patterns-page-view.dart index e14c8736..0d4d7059 100644 --- a/lib/recurrent_record_patterns/patterns-page-view.dart +++ b/lib/recurrent_record_patterns/patterns-page-view.dart @@ -163,24 +163,22 @@ class PatternsPageViewState extends State { mainAxisAlignment: MainAxisAlignment.center, children: [ Flexible( - child: new Column( - children: [ - Image.asset( - 'assets/images/no_entry_2.png', - width: 200, + child: new Column( + children: [ + Image.asset( + 'assets/images/no_entry_2.png', + width: 200, + ), + Container( + child: Text( + "No recurrent records yet.".i18n, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 22.0, ), - Container( - child: Text( - "No recurrent records yet.".i18n, - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 22.0, - ), - ) - ) - ], - ) - ) + )) + ], + )) ], ) : ListView.builder( diff --git a/lib/services/backup-service.dart b/lib/services/backup-service.dart index 57a99571..7bcdad87 100644 --- a/lib/services/backup-service.dart +++ b/lib/services/backup-service.dart @@ -5,6 +5,7 @@ import 'dart:typed_data'; import 'package:i18n_extension/default.i18n.dart'; import 'package:intl/intl.dart'; +import 'package:file_selector/file_selector.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:path_provider/path_provider.dart'; import 'package:piggybank/models/backup.dart'; @@ -24,14 +25,16 @@ import 'logger.dart'; /// BackupService contains the methods to create/restore backup file class BackupService { + static final _logger = Logger.withContext('BackupService'); - static final _logger = Logger.withClass(BackupService); - - static const String DEFAULT_STORAGE_DIR = "/storage/emulated/0/Documents/oinkoin"; + static String DEFAULT_STORAGE_DIR = Platform.isAndroid + ? '/storage/emulated/0/Documents/oinkoin' + : '${Platform.environment["HOME"]}/.oinkoin'; static const String MANDATORY_BACKUP_SUFFIX = "obackup.json"; - static String ERROR_MSG = "Unable to create a backup: please, delete manually the old backup".i18n; + static String ERROR_MSG = + "Unable to create a backup: please, delete manually the old backup".i18n; static const Duration AUTOMATIC_BACKUP_THRESHOLD = Duration(hours: 1); @@ -40,16 +43,18 @@ class BackupService { /// Gets the platform-appropriate default backup directory /// Android: /storage/emulated/0/Documents/oinkoin - /// Linux/Desktop: ~/Documents/oinkoin + /// Linux: ~/.oinkoin + /// macOS: /Users//Documents + /// Windows: AppData/Local/oinkoin static Future getDefaultBackupDirectory() async { - if (Platform.isLinux || Platform.isWindows || Platform.isMacOS) { - // Desktop: use Documents directory + if (Platform.isMacOS) { + // macOS final documentsDir = await getApplicationDocumentsDirectory(); return '${documentsDir.parent.path}/Documents/oinkoin'; - } else { - // Android/iOS: use the original path + } else if (Platform.isAndroid) return DEFAULT_STORAGE_DIR; - } + else + return (await getApplicationDocumentsDirectory()).path; } /// Generates a backup file name containing the app package name, version, and current time. @@ -87,7 +92,7 @@ class BackupService { }) async { try { _logger.info('Starting backup creation...'); - + // Generate backup file name if not provided backupFileName ??= await generateBackupFileName(); _logger.debug('Backup filename: $backupFileName'); @@ -114,9 +119,11 @@ class BackupService { var recurrentRecordPatterns = await database.getRecurrentRecordPatterns(); var recordTagAssociations = await database.getAllRecordTagAssociations(); - _logger.info('Backup data: ${records.length} records, ${categories.length} categories, ${recurrentRecordPatterns.length} recurrent patterns, ${recordTagAssociations.length} tags'); + _logger.info( + 'Backup data: ${records.length} records, ${categories.length} categories, ${recurrentRecordPatterns.length} recurrent patterns, ${recordTagAssociations.length} tags'); - var backup = Backup(appName, version, databaseVersion, categories, records, recurrentRecordPatterns, recordTagAssociations); + var backup = Backup(appName, version, databaseVersion, categories, + records, recurrentRecordPatterns, recordTagAssociations); var backupJsonStr = jsonEncode(backup.toMap()); // Encrypt the backup JSON string if an encryption password is provided @@ -126,11 +133,29 @@ class BackupService { } // Write on disk - var backupJsonOnDisk = File("${path.path}/$backupFileName"); - var result = await backupJsonOnDisk.writeAsString(backupJsonStr); - - _logger.info('Backup created successfully: ${backupJsonOnDisk.path} (${backupJsonStr.length} bytes)'); - return result; + if (!Platform.isAndroid) { + final FileSaveLocation? result = await getSaveLocation( + suggestedName: backupFileName, + initialDirectory: path.path, + ); + if (result == null) throw Exception("Canceled by user"); + + final Uint8List fileData = + Uint8List.fromList(utf8.encode(backupJsonStr)); + final XFile textFile = XFile.fromData( + fileData, + name: backupFileName, + ); + await textFile.saveTo(result.path); + return File(textFile.path); + } else { + var backupJsonOnDisk = File("${path.path}/$backupFileName"); + var result = await backupJsonOnDisk.writeAsString(backupJsonStr); + + _logger.info( + 'Backup created successfully: ${backupJsonOnDisk.path} (${backupJsonStr.length} bytes)'); + return result; + } } catch (e, st) { _logger.handle(e, st, 'Failed to create backup'); rethrow; @@ -162,14 +187,17 @@ class BackupService { // Check if the time since the latest backup exceeds the threshold final now = DateTime.now(); - final shouldBackup = now.difference(latestBackupDate) > AUTOMATIC_BACKUP_THRESHOLD; - + final shouldBackup = + now.difference(latestBackupDate) > AUTOMATIC_BACKUP_THRESHOLD; + if (shouldBackup) { - _logger.info("Last backup was ${now.difference(latestBackupDate).inHours}h ago, automatic backup needed"); + _logger.info( + "Last backup was ${now.difference(latestBackupDate).inHours}h ago, automatic backup needed"); } else { - _logger.debug("Last backup was ${now.difference(latestBackupDate).inMinutes}m ago, no backup needed yet"); + _logger.debug( + "Last backup was ${now.difference(latestBackupDate).inMinutes}m ago, no backup needed yet"); } - + return shouldBackup; } catch (e, st) { _logger.handle(e, st, 'Error checking if automatic backup is needed'); @@ -196,9 +224,8 @@ class BackupService { bool enableVersionAndDateInBackupName = PreferencesUtils.getOrDefault( prefs, PreferencesKeys.enableVersionAndDateInBackupName)!; - String? filename = !enableVersionAndDateInBackupName - ? await getDefaultFileName() - : null; + String? filename = + !enableVersionAndDateInBackupName ? await getDefaultFileName() : null; try { _logger.info('Creating automatic backup...'); @@ -226,8 +253,7 @@ class BackupService { prefs, PreferencesKeys.backupRetentionIntervalIndex); if (enableEncryptedBackup && backupRetentionIntervalIndex != null) { - var period = - BackupRetentionPeriod.values[backupRetentionIntervalIndex]; + var period = BackupRetentionPeriod.values[backupRetentionIntervalIndex]; if (period != BackupRetentionPeriod.ALWAYS) { final backupDir = await getDefaultBackupDirectory(); return await removeOldBackups(period, Directory(backupDir)); @@ -236,7 +262,6 @@ class BackupService { return false; } - /// Imports data from a backup file. If an encryption password is provided, /// it attempts to decrypt the file content before importing. /// @@ -256,7 +281,8 @@ class BackupService { var jsonMap = jsonDecode(fileContent); Backup backup = Backup.fromMap(jsonMap); - _logger.info('Importing: ${backup.records.length} records, ${backup.categories.length} categories'); + _logger.info( + 'Importing: ${backup.records.length} records, ${backup.categories.length} categories'); // Add categories for (var backupCategory in backup.categories) { diff --git a/lib/services/csv-service.dart b/lib/services/csv-service.dart index a9446dcd..e9e5bf67 100644 --- a/lib/services/csv-service.dart +++ b/lib/services/csv-service.dart @@ -3,7 +3,6 @@ import 'package:piggybank/models/record.dart'; import 'logger.dart'; class CSVExporter { - static final _logger = Logger.withClass(CSVExporter); static createCSVFromRecordList(List records) { @@ -21,7 +20,15 @@ class CSVExporter { } else { // Provide a default header if no records are present _logger.warning('No records to export, using default header'); - csvLines.insert(0, ['title', 'value', 'datetime', 'category_name', 'category_type', 'description', 'tags']); + csvLines.insert(0, [ + 'title', + 'value', + 'datetime', + 'category_name', + 'category_type', + 'description', + 'tags' + ]); } var csv = ListToCsvConverter().convert(csvLines); _logger.info('CSV created: ${csvLines.length} lines (including header)'); diff --git a/lib/services/database/sqlite-database.dart b/lib/services/database/sqlite-database.dart index d02eb5cd..34b7d9ed 100644 --- a/lib/services/database/sqlite-database.dart +++ b/lib/services/database/sqlite-database.dart @@ -11,9 +11,9 @@ import 'package:piggybank/models/record.dart'; import 'package:piggybank/models/recurrent-record-pattern.dart'; import 'package:piggybank/services/database/database-interface.dart'; import 'package:piggybank/services/database/sqlite-migration-service.dart'; -import 'package:sqflite/sqflite.dart'; -import 'package:sqflite_common/sqflite_logger.dart'; +import 'package:piggybank/services/backup-service.dart'; import 'package:sqflite_common_ffi/sqflite_ffi.dart'; +import 'package:sqflite_common/sqflite_logger.dart'; import 'package:timezone/timezone.dart' as tz; import 'package:uuid/uuid.dart'; @@ -51,7 +51,7 @@ class SqliteDatabase implements DatabaseInterface { _logger.info('Initializing database...'); // Initialize FFI for desktop platforms (Linux, Windows, macOS) - if (Platform.isLinux || Platform.isWindows || Platform.isMacOS) { + if (Platform.isWindows || Platform.isLinux || Platform.isMacOS) { _logger.debug('Initializing sqflite FFI for desktop platform'); sqfliteFfiInit(); databaseFactory = databaseFactoryFfi; @@ -76,7 +76,7 @@ class SqliteDatabase implements DatabaseInterface { String _path = join(databasePath, 'movements.db'); _logger.debug('Database path: $_path'); - + var factoryWithLogs = SqfliteDatabaseFactoryLogger(databaseFactory, options: SqfliteLoggerOptions(type: SqfliteDatabaseFactoryLoggerType.all)); @@ -219,14 +219,14 @@ class SqliteDatabase implements DatabaseInterface { final db = (await database)!; Batch batch = db.batch(); - for (var record in records) { - if (record == null) { - continue; - } - record.id = null; + for (var record in records) { + if (record == null) { + continue; + } + record.id = null; - // Update the INSERT statement to include the new column `time_zone_name` - batch.rawInsert(""" + // Update the INSERT statement to include the new column `time_zone_name` + batch.rawInsert(""" INSERT OR IGNORE INTO records (title, value, datetime, timezone, category_name, category_type, description, recurrence_id) SELECT ?, ?, ?, ?, ?, ?, ?, ? WHERE NOT EXISTS ( @@ -238,36 +238,36 @@ class SqliteDatabase implements DatabaseInterface { AND category_type = ? ) """, [ - record.title, - record.value, - record.utcDateTime.millisecondsSinceEpoch, // Use utcDateTime - record.timeZoneName, // Store the timezone name - record.category!.name, - record.category!.categoryType!.index, - record.description, - record.recurrencePatternId, - - // Duplicate check values - record.utcDateTime.millisecondsSinceEpoch, - record.value, - record.title, - record.category!.name, - record.category!.categoryType!.index, - ]); - } + record.title, + record.value, + record.utcDateTime.millisecondsSinceEpoch, // Use utcDateTime + record.timeZoneName, // Store the timezone name + record.category!.name, + record.category!.categoryType!.index, + record.description, + record.recurrencePatternId, + + // Duplicate check values + record.utcDateTime.millisecondsSinceEpoch, + record.value, + record.title, + record.category!.name, + record.category!.categoryType!.index, + ]); + } - await batch.commit(noResult: true); - _logger.info('Batch insert committed: ${records.length} records'); + await batch.commit(noResult: true); + _logger.info('Batch insert committed: ${records.length} records'); - // Insert tags for each record in a second batch after getting record IDs - Batch tagBatch = db.batch(); - for (var record in records) { - if (record == null || record.tags.isEmpty) { - continue; - } + // Insert tags for each record in a second batch after getting record IDs + Batch tagBatch = db.batch(); + for (var record in records) { + if (record == null || record.tags.isEmpty) { + continue; + } - // Find the record ID by querying for the record we just inserted - var recordId = await db.rawQuery(""" + // Find the record ID by querying for the record we just inserted + var recordId = await db.rawQuery(""" SELECT id FROM records WHERE datetime = ? AND value = ? @@ -276,29 +276,29 @@ class SqliteDatabase implements DatabaseInterface { AND category_type = ? LIMIT 1 """, [ - record.utcDateTime.millisecondsSinceEpoch, - record.value, - record.title, - record.category!.name, - record.category!.categoryType!.index, - ]); - - if (recordId.isNotEmpty) { - final id = recordId.first['id'] as int; - for (String tag in record.tags) { - if (tag.trim().isNotEmpty) { - tagBatch.insert( - "records_tags", - {'record_id': id, 'tag_name': tag}, - conflictAlgorithm: ConflictAlgorithm.ignore, - ); + record.utcDateTime.millisecondsSinceEpoch, + record.value, + record.title, + record.category!.name, + record.category!.categoryType!.index, + ]); + + if (recordId.isNotEmpty) { + final id = recordId.first['id'] as int; + for (String tag in record.tags) { + if (tag.trim().isNotEmpty) { + tagBatch.insert( + "records_tags", + {'record_id': id, 'tag_name': tag}, + conflictAlgorithm: ConflictAlgorithm.ignore, + ); + } } } } - } - await tagBatch.commit(noResult: true); - _logger.info('Batch complete with tags'); + await tagBatch.commit(noResult: true); + _logger.info('Batch complete with tags'); } catch (e, st) { _logger.handle(e, st, 'Failed to add records in batch'); rethrow; diff --git a/lib/services/database/sqlite-migration-service.dart b/lib/services/database/sqlite-migration-service.dart index ab9be64d..0ca200be 100644 --- a/lib/services/database/sqlite-migration-service.dart +++ b/lib/services/database/sqlite-migration-service.dart @@ -7,7 +7,6 @@ import '../../models/category.dart'; import '../logger.dart'; class SqliteMigrationService { - static final _logger = Logger.withClass(SqliteMigrationService); // SQL Queries @@ -186,7 +185,8 @@ class SqliteMigrationService { _logger.debug('Alter table succeeded'); } on DatabaseException catch (e) { // This block specifically handles DatabaseException - _logger.warning('Alter table failed (expected for existing columns): ${e.toString()}'); + _logger.warning( + 'Alter table failed (expected for existing columns): ${e.toString()}'); } catch (e, st) { // This block is a generic catch-all for any other exception types _logger.handle(e, st, 'Unexpected error in alter table'); diff --git a/lib/services/logger.dart b/lib/services/logger.dart index 89e65d0b..2a379136 100644 --- a/lib/services/logger.dart +++ b/lib/services/logger.dart @@ -131,4 +131,3 @@ class _LogScreenState extends State { ); } } - diff --git a/lib/services/platform-file-service.dart b/lib/services/platform-file-service.dart index 6f5d589c..bea46202 100644 --- a/lib/services/platform-file-service.dart +++ b/lib/services/platform-file-service.dart @@ -7,7 +7,6 @@ import 'package:share_plus/share_plus.dart'; /// On mobile: Uses share_plus /// On desktop: Uses file_selector for "Save As" dialog class PlatformFileService { - /// Check if we're running on a desktop platform static bool get isDesktop { if (kIsWeb) return false; @@ -38,7 +37,8 @@ class PlatformFileService { } /// Save file using "Save As" dialog (desktop only) - static Future _saveFileAs(File sourceFile, String? suggestedName) async { + static Future _saveFileAs( + File sourceFile, String? suggestedName) async { try { final fileName = suggestedName ?? sourceFile.path.split('/').last; @@ -77,7 +77,9 @@ class PlatformFileService { ); return result.status == ShareResultStatus.success || - result.status == ShareResultStatus.unavailable; // unavailable means it worked on some platforms + result.status == + ShareResultStatus + .unavailable; // unavailable means it worked on some platforms } catch (e) { print('Error sharing file: $e'); return false; @@ -113,4 +115,3 @@ class PlatformFileService { } } } - diff --git a/lib/services/recurrent-record-service.dart b/lib/services/recurrent-record-service.dart index d75e5018..ddf2c2a8 100644 --- a/lib/services/recurrent-record-service.dart +++ b/lib/services/recurrent-record-service.dart @@ -9,7 +9,6 @@ import 'database/database-interface.dart'; import 'logger.dart'; class RecurrentRecordService { - static final _logger = Logger.withClass(RecurrentRecordService); DatabaseInterface database = ServiceConfig.database; @@ -17,7 +16,8 @@ class RecurrentRecordService { List generateRecurrentRecordsFromDateTime( RecurrentRecordPattern recordPattern, DateTime utcEndDate) { try { - _logger.debug('Generating recurrent records for pattern: ${recordPattern.title}'); + _logger.debug( + 'Generating recurrent records for pattern: ${recordPattern.title}'); final List newRecurrentRecords = []; // 1. Get the TZLocation for the pattern's original timezone @@ -59,133 +59,135 @@ class RecurrentRecordService { lastUpdateTz = startDate; } - if (endDateTz.isBefore(lastUpdateTz)) { - return []; - } + if (endDateTz.isBefore(lastUpdateTz)) { + return []; + } - // Helper function to add records with a given interval - void addRecordsByPeriod(int periodValue, {bool isMonth = false}) { - tz.TZDateTime currentDate = lastUpdateTz!; - - // Store the original day, hour, minute, and second from the pattern's start date. - // This is crucial for maintaining consistency across DST changes and month-end rollovers. - final int originalStartDay = recordPattern.localDateTime.day; - final int originalHour = recordPattern.localDateTime.hour; - final int originalMinute = recordPattern.localDateTime.minute; - final int originalSecond = recordPattern.localDateTime.second; - - // Now, calculate and add the subsequent records. - while (true) { - tz.TZDateTime nextDate; - - // Calculate the next date. - if (isMonth) { - // Get the target year and month after adding the period value. - int targetYear = currentDate.year; - int targetMonth = currentDate.month + periodValue; - - if (targetMonth > 12) { - targetYear += (targetMonth - 1) ~/ 12; - targetMonth = (targetMonth - 1) % 12 + 1; - } + // Helper function to add records with a given interval + void addRecordsByPeriod(int periodValue, {bool isMonth = false}) { + tz.TZDateTime currentDate = lastUpdateTz!; + + // Store the original day, hour, minute, and second from the pattern's start date. + // This is crucial for maintaining consistency across DST changes and month-end rollovers. + final int originalStartDay = recordPattern.localDateTime.day; + final int originalHour = recordPattern.localDateTime.hour; + final int originalMinute = recordPattern.localDateTime.minute; + final int originalSecond = recordPattern.localDateTime.second; + + // Now, calculate and add the subsequent records. + while (true) { + tz.TZDateTime nextDate; + + // Calculate the next date. + if (isMonth) { + // Get the target year and month after adding the period value. + int targetYear = currentDate.year; + int targetMonth = currentDate.month + periodValue; + + if (targetMonth > 12) { + targetYear += (targetMonth - 1) ~/ 12; + targetMonth = (targetMonth - 1) % 12 + 1; + } + + // Create a candidate date using the original start day. + tz.TZDateTime candidateDate = tz.TZDateTime( + currentDate.location, + targetYear, + targetMonth, + originalStartDay, + originalHour, + originalMinute, + originalSecond, + ); - // Create a candidate date using the original start day. - tz.TZDateTime candidateDate = tz.TZDateTime( - currentDate.location, - targetYear, - targetMonth, - originalStartDay, - originalHour, - originalMinute, - originalSecond, - ); - - // Explicitly check for a month rollover. If the original day was invalid - // (e.g., day 30 in February), the new date will be in the next month. - if (candidateDate.month != targetMonth) { - // If a rollover occurred, set the date to the last day of the target month. + // Explicitly check for a month rollover. If the original day was invalid + // (e.g., day 30 in February), the new date will be in the next month. + if (candidateDate.month != targetMonth) { + // If a rollover occurred, set the date to the last day of the target month. + nextDate = tz.TZDateTime( + currentDate.location, + targetYear, + targetMonth + 1, + 0, + originalHour, + originalMinute, + originalSecond, + ); + } else { + // Otherwise, the candidate date is correct. + nextDate = candidateDate; + } + } else { + // Logic for non-monthly recurrence, manually incrementing the calendar day + // to avoid time drift issues caused by Daylight Saving Time (DST) changes. nextDate = tz.TZDateTime( currentDate.location, - targetYear, - targetMonth + 1, - 0, + currentDate.year, + currentDate.month, + currentDate.day + periodValue, originalHour, originalMinute, originalSecond, ); + } + + // Check if the newly calculated date is within the bounds. + if (nextDate.isBefore(endDateTz) || + nextDate.isAtSameMomentAs(endDateTz)) { + final newRecord = Record( + recordPattern.value, + recordPattern.title, + recordPattern.category, + nextDate.toUtc(), + timeZoneName: patternLocation.name, + description: recordPattern.description, + recurrencePatternId: recordPattern.id, + tags: recordPattern.tags, + ); + newRecurrentRecords.add(newRecord); + currentDate = nextDate; } else { - // Otherwise, the candidate date is correct. - nextDate = candidateDate; + // We've gone past the end date, so stop. + break; } - } else { - // Logic for non-monthly recurrence, manually incrementing the calendar day - // to avoid time drift issues caused by Daylight Saving Time (DST) changes. - nextDate = tz.TZDateTime( - currentDate.location, - currentDate.year, - currentDate.month, - currentDate.day + periodValue, - originalHour, - originalMinute, - originalSecond, - ); } + } - // Check if the newly calculated date is within the bounds. - if (nextDate.isBefore(endDateTz) || - nextDate.isAtSameMomentAs(endDateTz)) { - final newRecord = Record( - recordPattern.value, - recordPattern.title, - recordPattern.category, - nextDate.toUtc(), - timeZoneName: patternLocation.name, - description: recordPattern.description, - recurrencePatternId: recordPattern.id, - tags: recordPattern.tags, - ); - newRecurrentRecords.add(newRecord); - currentDate = nextDate; - } else { - // We've gone past the end date, so stop. + switch (recordPattern.recurrentPeriod) { + case RecurrentPeriod.EveryDay: + addRecordsByPeriod(1); + break; + case RecurrentPeriod.EveryWeek: + addRecordsByPeriod(7); + break; + case RecurrentPeriod.EveryTwoWeeks: + addRecordsByPeriod(14); + break; + case RecurrentPeriod.EveryFourWeeks: + addRecordsByPeriod(28); + break; + case RecurrentPeriod.EveryMonth: + addRecordsByPeriod(1, isMonth: true); + break; + case RecurrentPeriod.EveryThreeMonths: + addRecordsByPeriod(3, isMonth: true); + break; + case RecurrentPeriod.EveryFourMonths: + addRecordsByPeriod(4, isMonth: true); + break; + case RecurrentPeriod.EveryYear: + addRecordsByPeriod(12, isMonth: true); + break; + default: break; - } } - } - - switch (recordPattern.recurrentPeriod) { - case RecurrentPeriod.EveryDay: - addRecordsByPeriod(1); - break; - case RecurrentPeriod.EveryWeek: - addRecordsByPeriod(7); - break; - case RecurrentPeriod.EveryTwoWeeks: - addRecordsByPeriod(14); - break; - case RecurrentPeriod.EveryFourWeeks: - addRecordsByPeriod(28); - break; - case RecurrentPeriod.EveryMonth: - addRecordsByPeriod(1, isMonth: true); - break; - case RecurrentPeriod.EveryThreeMonths: - addRecordsByPeriod(3, isMonth: true); - break; - case RecurrentPeriod.EveryFourMonths: - addRecordsByPeriod(4, isMonth: true); - break; - case RecurrentPeriod.EveryYear: - addRecordsByPeriod(12, isMonth: true); - break; - default: - break; - } - _logger.info('Generated ${newRecurrentRecords.length} recurrent records for: ${recordPattern.title}'); - return newRecurrentRecords; + _logger.info( + 'Generated ${newRecurrentRecords.length} recurrent records for: ${recordPattern.title}'); + return newRecurrentRecords; } catch (e, st) { - _logger.handle(e, st, 'Failed to generate recurrent records for: ${recordPattern.title}'); + _logger.handle(e, st, + 'Failed to generate recurrent records for: ${recordPattern.title}'); rethrow; } } @@ -219,13 +221,15 @@ class RecurrentRecordService { if (allRecords.isNotEmpty) { // Split records into past (up to end of today) and future (after end of today) - final pastRecords = allRecords.where((r) => - r.utcDateTime.isBefore(endOfToday) || r.utcDateTime.isAtSameMomentAs(endOfToday) - ).toList(); + final pastRecords = allRecords + .where((r) => + r.utcDateTime.isBefore(endOfToday) || + r.utcDateTime.isAtSameMomentAs(endOfToday)) + .toList(); - final futureRecords = allRecords.where((r) => - r.utcDateTime.isAfter(endOfToday) - ).toList(); + final futureRecords = allRecords + .where((r) => r.utcDateTime.isAfter(endOfToday)) + .toList(); // Mark future records for (var record in futureRecords) { @@ -248,7 +252,8 @@ class RecurrentRecordService { } } - _logger.info('Recurrent records update completed: ${totalRecordsAdded} records added to database, ${allFutureRecords.length} future records generated from ${patterns.length} patterns'); + _logger.info( + 'Recurrent records update completed: ${totalRecordsAdded} records added to database, ${allFutureRecords.length} future records generated from ${patterns.length} patterns'); return allFutureRecords; } catch (e, st) { _logger.handle(e, st, 'Failed to update recurrent records'); diff --git a/lib/settings/backup-page.dart b/lib/settings/backup-page.dart index 7c7fd3b3..4414bd58 100644 --- a/lib/settings/backup-page.dart +++ b/lib/settings/backup-page.dart @@ -245,28 +245,31 @@ class BackupPageState extends State { return SingleChildScrollView( child: Column( children: [ - SettingsItem( - icon: Icon(Icons.backup, color: Colors.white), - iconBackgroundColor: Colors.orange.shade600, - title: 'Export Backup'.i18n, - subtitle: "Share the backup file".i18n, - onPressed: () async => - await createAndShareBackupFile()), - SettingsItem( - icon: Icon(Icons.dataset, color: Colors.white), - iconBackgroundColor: Colors.blueGrey.shade600, - title: 'Export Database'.i18n, - subtitle: "Share the database file".i18n, - onPressed: () async => await shareDatabase()), + if (Platform.isAndroid) + SettingsItem( + icon: Icon(Icons.backup, color: Colors.white), + iconBackgroundColor: Colors.orange.shade600, + title: 'Export Backup'.i18n, + subtitle: "Share the backup file".i18n, + onPressed: () async => + await createAndShareBackupFile()), + if (Platform.isAndroid) + SettingsItem( + icon: Icon(Icons.dataset, color: Colors.white), + iconBackgroundColor: Colors.blueGrey.shade600, + title: 'Export Database'.i18n, + subtitle: "Share the database file".i18n, + onPressed: () async => await shareDatabase()), SettingsItem( icon: Icon(Icons.save_alt, color: Colors.white), iconBackgroundColor: Colors.lightBlue.shade600, title: 'Store the Backup on disk'.i18n, onPressed: () async => await storeBackupFile(context)), - ClickableCustomizationItem( - title: "Destination folder".i18n, - subtitle: backupFolderPath, - enabled: false), + if (Platform.isAndroid) + ClickableCustomizationItem( + title: "Destination folder".i18n, + subtitle: backupFolderPath, + enabled: false), SwitchCustomizationItem( title: "Backup encryption".i18n, subtitle: diff --git a/lib/settings/backup-restore-dialogs.dart b/lib/settings/backup-restore-dialogs.dart index a44f7ca9..082e72aa 100644 --- a/lib/settings/backup-restore-dialogs.dart +++ b/lib/settings/backup-restore-dialogs.dart @@ -55,7 +55,9 @@ class BackupRestoreDialog { static Future importFromBackupFile(BuildContext context) async { try { - var hasDeletedCache = await FilePicker.platform.clearTemporaryFiles(); + bool hasDeletedCache = Platform.isAndroid + ? (await FilePicker.platform.clearTemporaryFiles() as bool) + : false; log("FilePicker has deleted cache: " + hasDeletedCache.toString()); } catch (e) { log("FilePicker.clearTemporaryFiles() not implemented on this platform"); @@ -109,7 +111,7 @@ class BackupRestoreDialog { BuildContext context, String title, String subtitle) async { AlertDialogBuilder resultDialog = AlertDialogBuilder(title) .addSubtitle(subtitle) - .addTrueButtonName("OK".i18n); + .renameTrueButtonName("OK".i18n); await showDialog( context: context, builder: (BuildContext context) { diff --git a/lib/settings/components/setting-separator.dart b/lib/settings/components/setting-separator.dart index ca4955b2..57caf3b6 100644 --- a/lib/settings/components/setting-separator.dart +++ b/lib/settings/components/setting-separator.dart @@ -17,9 +17,9 @@ class SettingSeparator extends StatelessWidget { child: Text( title, style: Theme.of(context).textTheme.titleMedium?.copyWith( - color: Theme.of(context).colorScheme.secondary, - fontWeight: FontWeight.bold, - ), + color: Theme.of(context).colorScheme.secondary, + fontWeight: FontWeight.bold, + ), ), ), const Divider(thickness: 1.5), @@ -27,4 +27,4 @@ class SettingSeparator extends StatelessWidget { ), ); } -} \ No newline at end of file +} diff --git a/lib/settings/constants/overview-time-interval.dart b/lib/settings/constants/overview-time-interval.dart index 8aa9d1be..2ff802ce 100644 --- a/lib/settings/constants/overview-time-interval.dart +++ b/lib/settings/constants/overview-time-interval.dart @@ -1 +1,6 @@ -enum OverviewTimeInterval { DisplayedRecords, FixCurrentMonth, FixCurrentYear, FixAllRecords } +enum OverviewTimeInterval { + DisplayedRecords, + FixCurrentMonth, + FixCurrentYear, + FixAllRecords +} diff --git a/lib/settings/constants/preferences-defaults-values.dart b/lib/settings/constants/preferences-defaults-values.dart index b5bdd513..4b9668f2 100644 --- a/lib/settings/constants/preferences-defaults-values.dart +++ b/lib/settings/constants/preferences-defaults-values.dart @@ -8,7 +8,6 @@ import '../backup-retention-period.dart'; import 'homepage-time-interval.dart'; class PreferencesDefaultValues { - static final defaultValues = { PreferencesKeys.themeColor: 0, // Default theme color index PreferencesKeys.themeMode: 0, // Default theme mode index @@ -24,7 +23,8 @@ class PreferencesDefaultValues { PreferencesKeys.enableAutomaticBackup: false, // Default to disabled PreferencesKeys.enableEncryptedBackup: false, PreferencesKeys.enableVersionAndDateInBackupName: true, - PreferencesKeys.backupRetentionIntervalIndex: BackupRetentionPeriod.ALWAYS.index, // Default retention period index + PreferencesKeys.backupRetentionIntervalIndex: + BackupRetentionPeriod.ALWAYS.index, // Default retention period index PreferencesKeys.backupPassword: '', // Default to empty password PreferencesKeys.enableAppLock: false, // Default to disabled PreferencesKeys.enableRecordNameSuggestions: true, // Default to enabled @@ -46,7 +46,7 @@ class PreferencesDefaultValues { } String existingCurrencyLocale = ServiceConfig.currencyLocale.toString(); NumberFormat currencyLocaleNumberFormat = - new NumberFormat.currency(locale: existingCurrencyLocale); + new NumberFormat.currency(locale: existingCurrencyLocale); return currencyLocaleNumberFormat.symbols.GROUP_SEP; } @@ -57,7 +57,7 @@ class PreferencesDefaultValues { } String existingCurrencyLocale = ServiceConfig.currencyLocale.toString(); NumberFormat currencyLocaleNumberFormat = - new NumberFormat.currency(locale: existingCurrencyLocale); + new NumberFormat.currency(locale: existingCurrencyLocale); return currencyLocaleNumberFormat.symbols.DECIMAL_SEP; } @@ -68,5 +68,4 @@ class PreferencesDefaultValues { static bool getOverwriteCommaValueWithDotDefaultValue() { return getDecimalSeparator() == "."; } - -} \ No newline at end of file +} diff --git a/lib/settings/constants/preferences-keys.dart b/lib/settings/constants/preferences-keys.dart index 19a50495..e7ef0b59 100644 --- a/lib/settings/constants/preferences-keys.dart +++ b/lib/settings/constants/preferences-keys.dart @@ -1,5 +1,4 @@ class PreferencesKeys { - // Theme static const themeColor = 'themeColor'; static const themeMode = 'themeMode'; @@ -24,7 +23,8 @@ class PreferencesKeys { static const enableEncryptedBackup = "enableEncryptedBackup"; static const backupRetentionIntervalIndex = 'backupRetentionIntervalIndex'; static const backupPassword = 'backupPassword'; - static const enableVersionAndDateInBackupName = 'enableVersionAndDateInBackupName'; + static const enableVersionAndDateInBackupName = + 'enableVersionAndDateInBackupName'; // Homepage static const homepageTimeInterval = 'homepageTimeInterval'; @@ -45,6 +45,8 @@ class PreferencesKeys { static const categoryListSortOption = 'defaultCategoryListSortOption'; // Statistics - static var statisticsPieChartUseCategoryColors = "statisticsPieChartUseCategoryColors"; - static var statisticsPieChartNumberOfCategoriesToDisplay = "statisticsPieChartNumberOfCategoriesToDisplay"; -} \ No newline at end of file + static var statisticsPieChartUseCategoryColors = + "statisticsPieChartUseCategoryColors"; + static var statisticsPieChartNumberOfCategoriesToDisplay = + "statisticsPieChartNumberOfCategoriesToDisplay"; +} diff --git a/lib/settings/constants/preferences-options.dart b/lib/settings/constants/preferences-options.dart index df6fb2f7..bbecd804 100644 --- a/lib/settings/constants/preferences-options.dart +++ b/lib/settings/constants/preferences-options.dart @@ -85,8 +85,7 @@ class PreferencesOptions { HomepageTimeInterval.CurrentMonth.index, "Records of the current year".i18n: HomepageTimeInterval.CurrentYear.index, "All records".i18n: HomepageTimeInterval.All.index, - "Records of the current week".i18n: - HomepageTimeInterval.CurrentWeek.index, + "Records of the current week".i18n: HomepageTimeInterval.CurrentWeek.index, }; static final Map monthDaysMap = { @@ -98,7 +97,8 @@ class PreferencesOptions { "Displayed records".i18n: OverviewTimeInterval.DisplayedRecords.index, "Records of the current month".i18n: OverviewTimeInterval.FixCurrentMonth.index, - "Records of the current year".i18n: OverviewTimeInterval.FixCurrentYear.index, + "Records of the current year".i18n: + OverviewTimeInterval.FixCurrentYear.index, "All records".i18n: OverviewTimeInterval.FixAllRecords.index, }; diff --git a/lib/settings/customization-page.dart b/lib/settings/customization-page.dart index 20834956..05a8037d 100644 --- a/lib/settings/customization-page.dart +++ b/lib/settings/customization-page.dart @@ -456,8 +456,8 @@ class CustomizationPageState extends State { SwitchCustomizationItem( title: "Show future recurrent records".i18n, subtitle: - "Generate and display upcoming recurrent records (they will be included in statistics)" - .i18n, + "Generate and display upcoming recurrent records (they will be included in statistics)" + .i18n, switchValue: PreferencesUtils.getOrDefault( prefs, PreferencesKeys.showFutureRecords)!, sharedConfigKey: PreferencesKeys.showFutureRecords, @@ -490,8 +490,7 @@ class CustomizationPageState extends State { dropdownValues: PreferencesOptions.amountInputKeyboardType, selectedDropdownKey: amountInputKeyboardTypeDropdownKey, - sharedConfigKey: - PreferencesKeys.amountInputKeyboardType, + sharedConfigKey: PreferencesKeys.amountInputKeyboardType, ), SwitchCustomizationItem( title: "Enable record's name suggestions".i18n, diff --git a/lib/settings/preferences-utils.dart b/lib/settings/preferences-utils.dart index a94cca1a..69377f0f 100644 --- a/lib/settings/preferences-utils.dart +++ b/lib/settings/preferences-utils.dart @@ -16,4 +16,4 @@ class PreferencesUtils { return defaultValueOfFunction as T?; } -} \ No newline at end of file +} diff --git a/lib/settings/settings-page.dart b/lib/settings/settings-page.dart index 91960363..82528932 100644 --- a/lib/settings/settings-page.dart +++ b/lib/settings/settings-page.dart @@ -23,7 +23,6 @@ import 'feedback-page.dart'; //https://pub.dev/packages/shared_preferences class TabSettings extends StatelessWidget { - static final _logger = Logger.withClass(TabSettings); static const double kSettingsItemsExtent = 75.0; @@ -35,8 +34,8 @@ class TabSettings extends StatelessWidget { AlertDialogBuilder premiumDialog = AlertDialogBuilder("Critical action".i18n) .addSubtitle("Do you really want to delete all the data?".i18n) - .addTrueButtonName("Yes".i18n) - .addFalseButtonName("No".i18n); + .renameTrueButtonName("Yes".i18n) + .renameFalseButtonName("No".i18n); var ok = await showDialog( context: context, builder: (BuildContext context) { @@ -47,7 +46,7 @@ class TabSettings extends StatelessWidget { AlertDialogBuilder resultDialog = AlertDialogBuilder("Data is deleted".i18n) .addSubtitle("All the data has been deleted".i18n) - .addTrueButtonName("OK"); + .hideFalseButton(); await showDialog( context: context, builder: (BuildContext context) { @@ -99,11 +98,9 @@ class TabSettings extends StatelessWidget { } goToLogs(BuildContext context) async { - Navigator.of(context).push( - MaterialPageRoute( - builder: (context) => const LogScreen(), - ) - ); + Navigator.of(context).push(MaterialPageRoute( + builder: (context) => const LogScreen(), + )); } Future _launchURL(BuildContext context, String url) async { @@ -134,7 +131,8 @@ class TabSettings extends StatelessWidget { if (context.mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( - content: Text('Could not open link. Make sure xdg-utils is installed.'), + content: Text( + 'Could not open link. Make sure xdg-utils is installed.'), duration: Duration(seconds: 5), ), ); @@ -282,8 +280,7 @@ class TabSettings extends StatelessWidget { iconBackgroundColor: Colors.tealAccent.shade700, title: 'Info'.i18n, subtitle: 'Privacy policy and credits'.i18n, - onPressed: () async => await _launchURL( - context, + onPressed: () async => await _launchURL(context, "https://github.com/emavgl/oinkoin/blob/master/privacy-policy.md"), ), SettingsItem( diff --git a/lib/tags/tags-page-view.dart b/lib/tags/tags-page-view.dart index 7b1906e1..527fa764 100644 --- a/lib/tags/tags-page-view.dart +++ b/lib/tags/tags-page-view.dart @@ -63,10 +63,9 @@ class TagsPageViewState extends State { title: Text('Delete tags'.i18n), content: Text(selectedTags.length == 1 ? 'Are you sure you want to delete this tag?'.i18n - : 'Are you sure you want to delete these %s tags?'.i18n.fill( - [selectedTags.length.toString()] - ) - ), + : 'Are you sure you want to delete these %s tags?' + .i18n + .fill([selectedTags.length.toString()])), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(), diff --git a/linux/packaging/deb/make_config.yaml b/linux/packaging/deb/make_config.yaml index 47026db0..d72a3b0b 100644 --- a/linux/packaging/deb/make_config.yaml +++ b/linux/packaging/deb/make_config.yaml @@ -1,6 +1,6 @@ display_name: Oinkoin package_name: oinkoin -version: 1.1.6 +version: 1.1.10 maintainer: name: Oinkoin Team diff --git a/linux/packaging/rpm/make_config.yaml b/linux/packaging/rpm/make_config.yaml index 48587414..80ee4b04 100644 --- a/linux/packaging/rpm/make_config.yaml +++ b/linux/packaging/rpm/make_config.yaml @@ -1,6 +1,6 @@ display_name: Oinkoin package_name: oinkoin -version: 1.1.6 +version: 1.1.10 maintainer: name: Oinkoin Team diff --git a/macos/.gitignore b/macos/.gitignore new file mode 100644 index 00000000..746adbb6 --- /dev/null +++ b/macos/.gitignore @@ -0,0 +1,7 @@ +# Flutter-related +**/Flutter/ephemeral/ +**/Pods/ + +# Xcode-related +**/dgph +**/xcuserdata/ diff --git a/macos/Flutter/Flutter-Debug.xcconfig b/macos/Flutter/Flutter-Debug.xcconfig new file mode 100644 index 00000000..4b81f9b2 --- /dev/null +++ b/macos/Flutter/Flutter-Debug.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/macos/Flutter/Flutter-Release.xcconfig b/macos/Flutter/Flutter-Release.xcconfig new file mode 100644 index 00000000..5caa9d15 --- /dev/null +++ b/macos/Flutter/Flutter-Release.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/macos/Podfile b/macos/Podfile new file mode 100644 index 00000000..ff5ddb3b --- /dev/null +++ b/macos/Podfile @@ -0,0 +1,42 @@ +platform :osx, '10.15' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\"" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_macos_podfile_setup + +target 'Runner' do + use_frameworks! + + flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) + target 'RunnerTests' do + inherit! :search_paths + end +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_macos_build_settings(target) + end +end diff --git a/macos/Podfile.lock b/macos/Podfile.lock new file mode 100644 index 00000000..c569def9 --- /dev/null +++ b/macos/Podfile.lock @@ -0,0 +1,92 @@ +PODS: + - emoji_picker_flutter (0.0.1): + - FlutterMacOS + - file_picker (0.0.1): + - FlutterMacOS + - file_selector_macos (0.0.1): + - FlutterMacOS + - flutter_timezone (0.1.0): + - FlutterMacOS + - FlutterMacOS (1.0.0) + - local_auth_darwin (0.0.1): + - Flutter + - FlutterMacOS + - package_info_plus (0.0.1): + - FlutterMacOS + - path_provider_foundation (0.0.1): + - Flutter + - FlutterMacOS + - share_plus (0.0.1): + - FlutterMacOS + - shared_preferences_foundation (0.0.1): + - Flutter + - FlutterMacOS + - sqflite_darwin (0.0.4): + - Flutter + - FlutterMacOS + - system_theme (3.2.0): + - FlutterMacOS + - url_launcher_macos (0.0.1): + - FlutterMacOS + +DEPENDENCIES: + - emoji_picker_flutter (from `Flutter/ephemeral/.symlinks/plugins/emoji_picker_flutter/macos`) + - file_picker (from `Flutter/ephemeral/.symlinks/plugins/file_picker/macos`) + - file_selector_macos (from `Flutter/ephemeral/.symlinks/plugins/file_selector_macos/macos`) + - flutter_timezone (from `Flutter/ephemeral/.symlinks/plugins/flutter_timezone/macos`) + - FlutterMacOS (from `Flutter/ephemeral`) + - local_auth_darwin (from `Flutter/ephemeral/.symlinks/plugins/local_auth_darwin/darwin`) + - package_info_plus (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos`) + - path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`) + - share_plus (from `Flutter/ephemeral/.symlinks/plugins/share_plus/macos`) + - shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`) + - sqflite_darwin (from `Flutter/ephemeral/.symlinks/plugins/sqflite_darwin/darwin`) + - system_theme (from `Flutter/ephemeral/.symlinks/plugins/system_theme/macos`) + - url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`) + +EXTERNAL SOURCES: + emoji_picker_flutter: + :path: Flutter/ephemeral/.symlinks/plugins/emoji_picker_flutter/macos + file_picker: + :path: Flutter/ephemeral/.symlinks/plugins/file_picker/macos + file_selector_macos: + :path: Flutter/ephemeral/.symlinks/plugins/file_selector_macos/macos + flutter_timezone: + :path: Flutter/ephemeral/.symlinks/plugins/flutter_timezone/macos + FlutterMacOS: + :path: Flutter/ephemeral + local_auth_darwin: + :path: Flutter/ephemeral/.symlinks/plugins/local_auth_darwin/darwin + package_info_plus: + :path: Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos + path_provider_foundation: + :path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin + share_plus: + :path: Flutter/ephemeral/.symlinks/plugins/share_plus/macos + shared_preferences_foundation: + :path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin + sqflite_darwin: + :path: Flutter/ephemeral/.symlinks/plugins/sqflite_darwin/darwin + system_theme: + :path: Flutter/ephemeral/.symlinks/plugins/system_theme/macos + url_launcher_macos: + :path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos + +SPEC CHECKSUMS: + emoji_picker_flutter: 51ca408e289d84d1e460016b2a28721ec754fcf7 + file_picker: 7584aae6fa07a041af2b36a2655122d42f578c1a + file_selector_macos: 9e9e068e90ebee155097d00e89ae91edb2374db7 + flutter_timezone: d59eea86178cbd7943cd2431cc2eaa9850f935d8 + FlutterMacOS: d0db08ddef1a9af05a5ec4b724367152bb0500b1 + local_auth_darwin: c3ee6cce0a8d56be34c8ccb66ba31f7f180aaebb + package_info_plus: f0052d280d17aa382b932f399edf32507174e870 + path_provider_foundation: bb55f6dbba17d0dccd6737fe6f7f34fbd0376880 + share_plus: 510bf0af1a42cd602274b4629920c9649c52f4cc + shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb + sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0 + system_theme: 6a419772fd834d53d9eb27accb3653b399c74426 + url_launcher_macos: f87a979182d112f911de6820aefddaf56ee9fbfd + +PODFILE CHECKSUM: 54d867c82ac51cbd61b565781b9fada492027009 + +COCOAPODS: 1.16.2 diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj new file mode 100644 index 00000000..c9bf39dc --- /dev/null +++ b/macos/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,791 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXAggregateTarget section */ + 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */; + buildPhases = ( + 33CC111E2044C6BF0003C045 /* ShellScript */, + ); + dependencies = ( + ); + name = "Flutter Assemble"; + productName = FLX; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C80D7294CF71000263BE5 /* RunnerTests.swift */; }; + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; + 782F78575047F81A429700B1 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D30ACF996EEE54058F06B69D /* Pods_RunnerTests.framework */; }; + A33A1B4A858DC4939112D5D5 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8E161C60647CADB275345FDE /* Pods_Runner.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC10EC2044A3C60003C045; + remoteInfo = Runner; + }; + 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC111A2044C6BA0003C045; + remoteInfo = FLX; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 33CC110E2044A8840003C045 /* Bundle Framework */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Bundle Framework"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; + 33CC10ED2044A3C60003C045 /* piggybank.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = piggybank.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; + 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; + 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; }; + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; }; + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; }; + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; }; + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; }; + 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; + 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; + 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; + 60940BB32018ED3B0085BB4B /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; + 7CF06CD2ED2E1B2C41215CD9 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + 8E161C60647CADB275345FDE /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; + A98FDDE8031C3A3A3FA71E36 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + B1814BB74D385F2345CDA0DB /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + C33AB5261D69BA79ADC027F5 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; + D30ACF996EEE54058F06B69D /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + FD652FC8A9098AC81BDD3A82 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 331C80D2294CF70F00263BE5 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 782F78575047F81A429700B1 /* Pods_RunnerTests.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 33CC10EA2044A3C60003C045 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + A33A1B4A858DC4939112D5D5 /* Pods_Runner.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 331C80D6294CF71000263BE5 /* RunnerTests */ = { + isa = PBXGroup; + children = ( + 331C80D7294CF71000263BE5 /* RunnerTests.swift */, + ); + path = RunnerTests; + sourceTree = ""; + }; + 33BA886A226E78AF003329D5 /* Configs */ = { + isa = PBXGroup; + children = ( + 33E5194F232828860026EE4D /* AppInfo.xcconfig */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */, + ); + path = Configs; + sourceTree = ""; + }; + 33CC10E42044A3C60003C045 = { + isa = PBXGroup; + children = ( + 33FAB671232836740065AC1E /* Runner */, + 33CEB47122A05771004F2AC0 /* Flutter */, + 331C80D6294CF71000263BE5 /* RunnerTests */, + 33CC10EE2044A3C60003C045 /* Products */, + D73912EC22F37F3D000D13A0 /* Frameworks */, + D0CB489626EFCBDF3E04EC71 /* Pods */, + ); + sourceTree = ""; + }; + 33CC10EE2044A3C60003C045 /* Products */ = { + isa = PBXGroup; + children = ( + 33CC10ED2044A3C60003C045 /* piggybank.app */, + 331C80D5294CF71000263BE5 /* RunnerTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 33CC11242044D66E0003C045 /* Resources */ = { + isa = PBXGroup; + children = ( + 33CC10F22044A3C60003C045 /* Assets.xcassets */, + 33CC10F42044A3C60003C045 /* MainMenu.xib */, + 33CC10F72044A3C60003C045 /* Info.plist */, + ); + name = Resources; + path = ..; + sourceTree = ""; + }; + 33CEB47122A05771004F2AC0 /* Flutter */ = { + isa = PBXGroup; + children = ( + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */, + ); + path = Flutter; + sourceTree = ""; + }; + 33FAB671232836740065AC1E /* Runner */ = { + isa = PBXGroup; + children = ( + 33CC10F02044A3C60003C045 /* AppDelegate.swift */, + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */, + 33E51913231747F40026EE4D /* DebugProfile.entitlements */, + 33E51914231749380026EE4D /* Release.entitlements */, + 33CC11242044D66E0003C045 /* Resources */, + 33BA886A226E78AF003329D5 /* Configs */, + ); + path = Runner; + sourceTree = ""; + }; + D0CB489626EFCBDF3E04EC71 /* Pods */ = { + isa = PBXGroup; + children = ( + B1814BB74D385F2345CDA0DB /* Pods-Runner.debug.xcconfig */, + A98FDDE8031C3A3A3FA71E36 /* Pods-Runner.release.xcconfig */, + 7CF06CD2ED2E1B2C41215CD9 /* Pods-Runner.profile.xcconfig */, + 60940BB32018ED3B0085BB4B /* Pods-RunnerTests.debug.xcconfig */, + FD652FC8A9098AC81BDD3A82 /* Pods-RunnerTests.release.xcconfig */, + C33AB5261D69BA79ADC027F5 /* Pods-RunnerTests.profile.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; + D73912EC22F37F3D000D13A0 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 8E161C60647CADB275345FDE /* Pods_Runner.framework */, + D30ACF996EEE54058F06B69D /* Pods_RunnerTests.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 331C80D4294CF70F00263BE5 /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + 19B56F26A2F39D890E3A4F35 /* [CP] Check Pods Manifest.lock */, + 331C80D1294CF70F00263BE5 /* Sources */, + 331C80D2294CF70F00263BE5 /* Frameworks */, + 331C80D3294CF70F00263BE5 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 331C80DA294CF71000263BE5 /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = RunnerTests; + productReference = 331C80D5294CF71000263BE5 /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 33CC10EC2044A3C60003C045 /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 1EE3F92A37BF4436ADDE32FF /* [CP] Check Pods Manifest.lock */, + 33CC10E92044A3C60003C045 /* Sources */, + 33CC10EA2044A3C60003C045 /* Frameworks */, + 33CC10EB2044A3C60003C045 /* Resources */, + 33CC110E2044A8840003C045 /* Bundle Framework */, + 3399D490228B24CF009A79C7 /* ShellScript */, + 5ECC50B98DD7046DD5F3B18B /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 33CC11202044C79F0003C045 /* PBXTargetDependency */, + ); + name = Runner; + productName = Runner; + productReference = 33CC10ED2044A3C60003C045 /* piggybank.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 33CC10E52044A3C60003C045 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = YES; + LastSwiftUpdateCheck = 0920; + LastUpgradeCheck = 1510; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 331C80D4294CF70F00263BE5 = { + CreatedOnToolsVersion = 14.0; + TestTargetID = 33CC10EC2044A3C60003C045; + }; + 33CC10EC2044A3C60003C045 = { + CreatedOnToolsVersion = 9.2; + LastSwiftMigration = 1100; + ProvisioningStyle = Automatic; + SystemCapabilities = { + com.apple.Sandbox = { + enabled = 1; + }; + }; + }; + 33CC111A2044C6BA0003C045 = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Manual; + }; + }; + }; + buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 33CC10E42044A3C60003C045; + preferredProjectObjectVersion = 77; + productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 33CC10EC2044A3C60003C045 /* Runner */, + 331C80D4294CF70F00263BE5 /* RunnerTests */, + 33CC111A2044C6BA0003C045 /* Flutter Assemble */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 331C80D3294CF70F00263BE5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 33CC10EB2044A3C60003C045 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 19B56F26A2F39D890E3A4F35 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 1EE3F92A37BF4436ADDE32FF /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 3399D490228B24CF009A79C7 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n"; + }; + 33CC111E2044C6BF0003C045 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + Flutter/ephemeral/FlutterInputs.xcfilelist, + ); + inputPaths = ( + Flutter/ephemeral/tripwire, + ); + outputFileListPaths = ( + Flutter/ephemeral/FlutterOutputs.xcfilelist, + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire\n"; + }; + 5ECC50B98DD7046DD5F3B18B /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 331C80D1294CF70F00263BE5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 33CC10E92044A3C60003C045 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */, + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */, + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 331C80DA294CF71000263BE5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC10EC2044A3C60003C045 /* Runner */; + targetProxy = 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */; + }; + 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; + targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 33CC10F42044A3C60003C045 /* MainMenu.xib */ = { + isa = PBXVariantGroup; + children = ( + 33CC10F52044A3C60003C045 /* Base */, + ); + name = MainMenu.xib; + path = Runner; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 331C80DB294CF71000263BE5 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 60940BB32018ED3B0085BB4B /* Pods-RunnerTests.debug.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.github.emavgl.piggybank.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/piggybank.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/piggybank"; + }; + name = Debug; + }; + 331C80DC294CF71000263BE5 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = FD652FC8A9098AC81BDD3A82 /* Pods-RunnerTests.release.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.github.emavgl.piggybank.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/piggybank.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/piggybank"; + }; + name = Release; + }; + 331C80DD294CF71000263BE5 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = C33AB5261D69BA79ADC027F5 /* Pods-RunnerTests.profile.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.github.emavgl.piggybank.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/piggybank.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/piggybank"; + }; + name = Profile; + }; + 338D0CE9231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 14.6; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Profile; + }; + 338D0CEA231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 14.6; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Profile; + }; + 338D0CEB231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Profile; + }; + 33CC10F92044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 14.6; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 33CC10FA2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 14.6; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Release; + }; + 33CC10FC2044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 14.6; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 33CC10FD2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 14.6; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + 33CC111C2044C6BA0003C045 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 33CC111D2044C6BA0003C045 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 331C80DB294CF71000263BE5 /* Debug */, + 331C80DC294CF71000263BE5 /* Release */, + 331C80DD294CF71000263BE5 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10F92044A3C60003C045 /* Debug */, + 33CC10FA2044A3C60003C045 /* Release */, + 338D0CE9231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10FC2044A3C60003C045 /* Debug */, + 33CC10FD2044A3C60003C045 /* Release */, + 338D0CEA231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC111C2044C6BA0003C045 /* Debug */, + 33CC111D2044C6BA0003C045 /* Release */, + 338D0CEB231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 33CC10E52044A3C60003C045 /* Project object */; +} diff --git a/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 00000000..b321cbf6 --- /dev/null +++ b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/macos/Runner.xcworkspace/contents.xcworkspacedata b/macos/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..21a3cc14 --- /dev/null +++ b/macos/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/macos/Runner/AppDelegate.swift b/macos/Runner/AppDelegate.swift new file mode 100644 index 00000000..b3c17614 --- /dev/null +++ b/macos/Runner/AppDelegate.swift @@ -0,0 +1,13 @@ +import Cocoa +import FlutterMacOS + +@main +class AppDelegate: FlutterAppDelegate { + override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { + return true + } + + override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { + return true + } +} diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..a2ec33f1 --- /dev/null +++ b/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "images" : [ + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_16.png", + "scale" : "1x" + }, + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "2x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "1x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_64.png", + "scale" : "2x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_128.png", + "scale" : "1x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "2x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "1x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "2x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "1x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_1024.png", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png new file mode 100644 index 00000000..82b6f9d9 Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png new file mode 100644 index 00000000..13b35eba Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png new file mode 100644 index 00000000..0a3f5fa4 Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png new file mode 100644 index 00000000..bdb57226 Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png new file mode 100644 index 00000000..f083318e Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png new file mode 100644 index 00000000..326c0e72 Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png new file mode 100644 index 00000000..2f1632cf Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png differ diff --git a/macos/Runner/Base.lproj/MainMenu.xib b/macos/Runner/Base.lproj/MainMenu.xib new file mode 100644 index 00000000..80e867a4 --- /dev/null +++ b/macos/Runner/Base.lproj/MainMenu.xib @@ -0,0 +1,343 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/macos/Runner/Configs/AppInfo.xcconfig b/macos/Runner/Configs/AppInfo.xcconfig new file mode 100644 index 00000000..66755315 --- /dev/null +++ b/macos/Runner/Configs/AppInfo.xcconfig @@ -0,0 +1,14 @@ +// Application-level settings for the Runner target. +// +// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the +// future. If not, the values below would default to using the project name when this becomes a +// 'flutter create' template. + +// The application's name. By default this is also the title of the Flutter window. +PRODUCT_NAME = piggybank + +// The application's bundle identifier +PRODUCT_BUNDLE_IDENTIFIER = com.github.emavgl.piggybank + +// The copyright displayed in application information +PRODUCT_COPYRIGHT = Copyright © 2026 com.github.emavgl. All rights reserved. diff --git a/macos/Runner/Configs/Debug.xcconfig b/macos/Runner/Configs/Debug.xcconfig new file mode 100644 index 00000000..36b0fd94 --- /dev/null +++ b/macos/Runner/Configs/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Debug.xcconfig" +#include "Warnings.xcconfig" diff --git a/macos/Runner/Configs/Release.xcconfig b/macos/Runner/Configs/Release.xcconfig new file mode 100644 index 00000000..dff4f495 --- /dev/null +++ b/macos/Runner/Configs/Release.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Release.xcconfig" +#include "Warnings.xcconfig" diff --git a/macos/Runner/Configs/Warnings.xcconfig b/macos/Runner/Configs/Warnings.xcconfig new file mode 100644 index 00000000..42bcbf47 --- /dev/null +++ b/macos/Runner/Configs/Warnings.xcconfig @@ -0,0 +1,13 @@ +WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings +GCC_WARN_UNDECLARED_SELECTOR = YES +CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES +CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE +CLANG_WARN__DUPLICATE_METHOD_MATCH = YES +CLANG_WARN_PRAGMA_PACK = YES +CLANG_WARN_STRICT_PROTOTYPES = YES +CLANG_WARN_COMMA = YES +GCC_WARN_STRICT_SELECTOR_MATCH = YES +CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES +CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES +GCC_WARN_SHADOW = YES +CLANG_WARN_UNREACHABLE_CODE = YES diff --git a/macos/Runner/DebugProfile.entitlements b/macos/Runner/DebugProfile.entitlements new file mode 100644 index 00000000..d138bd5b --- /dev/null +++ b/macos/Runner/DebugProfile.entitlements @@ -0,0 +1,14 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.cs.allow-jit + + com.apple.security.network.server + + com.apple.security.files.user-selected.read-write + + + diff --git a/macos/Runner/Info.plist b/macos/Runner/Info.plist new file mode 100644 index 00000000..4789daa6 --- /dev/null +++ b/macos/Runner/Info.plist @@ -0,0 +1,32 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIconFile + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSHumanReadableCopyright + $(PRODUCT_COPYRIGHT) + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/macos/Runner/MainFlutterWindow.swift b/macos/Runner/MainFlutterWindow.swift new file mode 100644 index 00000000..3cc05eb2 --- /dev/null +++ b/macos/Runner/MainFlutterWindow.swift @@ -0,0 +1,15 @@ +import Cocoa +import FlutterMacOS + +class MainFlutterWindow: NSWindow { + override func awakeFromNib() { + let flutterViewController = FlutterViewController() + let windowFrame = self.frame + self.contentViewController = flutterViewController + self.setFrame(windowFrame, display: true) + + RegisterGeneratedPlugins(registry: flutterViewController) + + super.awakeFromNib() + } +} diff --git a/macos/Runner/Release.entitlements b/macos/Runner/Release.entitlements new file mode 100644 index 00000000..19afff14 --- /dev/null +++ b/macos/Runner/Release.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.files.user-selected.read-write + + + diff --git a/macos/RunnerTests/RunnerTests.swift b/macos/RunnerTests/RunnerTests.swift new file mode 100644 index 00000000..61f3bd1f --- /dev/null +++ b/macos/RunnerTests/RunnerTests.swift @@ -0,0 +1,12 @@ +import Cocoa +import FlutterMacOS +import XCTest + +class RunnerTests: XCTestCase { + + func testExample() { + // If you add code to the Runner application, consider adding tests here. + // See https://developer.apple.com/documentation/xctest for more information about using XCTest. + } + +} diff --git a/pubspec.lock b/pubspec.lock index 6d695e42..6858f0f6 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -157,10 +157,10 @@ packages: dependency: transitive description: name: code_builder - sha256: "11654819532ba94c34de52ff5feb52bd81cba1de00ef2ed622fd50295f9d4243" + sha256: "6a6cab2ba4680d6423f34a9b972a4c9a94ebe1b62ecec4e1a1f2cba91fd1319d" url: "https://pub.dev" source: hosted - version: "4.11.0" + version: "4.11.1" collection: dependency: transitive description: @@ -277,10 +277,10 @@ packages: dependency: transitive description: name: ffi - sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418" + sha256: d07d37192dbf97461359c1518788f203b0c9102cfd2c35a716b823741219542c url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.1.5" file: dependency: transitive description: @@ -293,10 +293,10 @@ packages: dependency: "direct main" description: name: file_picker - sha256: "7872545770c277236fd32b022767576c562ba28366204ff1a5628853cf8f2200" + sha256: d974b6ba2606371ac71dd94254beefb6fa81185bde0b59bdc1df09885da85fde url: "https://pub.dev" source: hosted - version: "10.3.7" + version: "10.3.8" file_selector: dependency: "direct main" description: @@ -1134,10 +1134,10 @@ packages: dependency: "direct main" description: name: sqflite_common_ffi - sha256: cc78dab586034b20a84904ff5a8eb63adae587baa1b383d40428e9ee491b30f6 + sha256: c59fcdc143839a77581f7a7c4de018e53682408903a0a0800b95ef2dc4033eff url: "https://pub.dev" source: hosted - version: "2.4.0+1" + version: "2.4.0+2" sqflite_darwin: dependency: transitive description: @@ -1158,10 +1158,10 @@ packages: dependency: transitive description: name: sqlite3 - sha256: "0374add4094d34fcf9d22068b6ddb4056782353ad4a5449bc5b71c5f2175f0ec" + sha256: "1d2d2afee96acccabf315e169a4d28019fa21d5217ee014840058a7fbc2008c6" url: "https://pub.dev" source: hosted - version: "3.1.1" + version: "3.1.2" stack_trace: dependency: transitive description: @@ -1206,18 +1206,18 @@ packages: dependency: "direct main" description: name: system_theme - sha256: "5f93485401689601d4636a695f99f7c70a30873ee68c1d95025d908a3386be7e" + sha256: "827cc5f94a6c3097a4c08d4f9eb11d345322f9deea5be1cb73611ff24bdf45bb" url: "https://pub.dev" source: hosted - version: "3.1.2" + version: "3.2.0" system_theme_web: dependency: transitive description: name: system_theme_web - sha256: "900c92c5c050ce58048f241ef9a17e5cd8629808325a05b473dc62a6e99bae77" + sha256: a354b25ff0788ed802b48b632187d344a841b2034f16e4f6cdaa295e4a41a8aa url: "https://pub.dev" source: hosted - version: "0.0.3" + version: "0.0.4" talker: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 3f413014..ab81e759 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -66,7 +66,6 @@ dev_dependencies: build_runner: ^2.4.7 test: ^1.24.9 mockito: ^5.0.17 - sqflite_common_ffi: ^2.3.6 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec diff --git a/submodules/flutter b/submodules/flutter index bd7a4a6b..8b872868 160000 --- a/submodules/flutter +++ b/submodules/flutter @@ -1 +1 @@ -Subproject commit bd7a4a6b5576630823ca344e3e684c53aa1a0f46 +Subproject commit 8b872868494e429d94fa06dca855c306438b22c0 diff --git a/test/backup/database_interface.mocks.dart b/test/backup/database_interface.mocks.dart index b9f78bb0..613744d4 100644 --- a/test/backup/database_interface.mocks.dart +++ b/test/backup/database_interface.mocks.dart @@ -1 +1 @@ -// TODO Implement this library. \ No newline at end of file +// TODO Implement this library. diff --git a/test/datetime_utility_functions_locale_test.dart b/test/datetime_utility_functions_locale_test.dart index 4591236c..9c449db9 100644 --- a/test/datetime_utility_functions_locale_test.dart +++ b/test/datetime_utility_functions_locale_test.dart @@ -23,7 +23,7 @@ void main() { test('should return Sunday when given a Sunday', () { final sunday = DateTime(2025, 12, 14); // Sunday final startOfWeek = getStartOfWeek(sunday); - + expect(startOfWeek.day, 14); expect(startOfWeek.weekday, DateTime.sunday); }); @@ -31,7 +31,7 @@ void main() { test('should return Sunday when given a Monday', () { final monday = DateTime(2025, 12, 15); // Monday final startOfWeek = getStartOfWeek(monday); - + expect(startOfWeek.day, 14); // Previous Sunday expect(startOfWeek.weekday, DateTime.sunday); }); @@ -39,7 +39,7 @@ void main() { test('should return Sunday when given a Saturday', () { final saturday = DateTime(2025, 12, 20); // Saturday final startOfWeek = getStartOfWeek(saturday); - + expect(startOfWeek.day, 14); // Sunday expect(startOfWeek.weekday, DateTime.sunday); }); @@ -47,7 +47,7 @@ void main() { test('should handle week crossing month boundary', () { final friday = DateTime(2025, 1, 3); // Friday final startOfWeek = getStartOfWeek(friday); - + expect(startOfWeek.year, 2024); expect(startOfWeek.month, 12); expect(startOfWeek.day, 29); // Sunday in previous month @@ -59,7 +59,7 @@ void main() { test('should return Saturday when given a Sunday', () { final sunday = DateTime(2025, 12, 14); // Sunday final endOfWeek = getEndOfWeek(sunday); - + expect(endOfWeek.day, 20); // Saturday expect(endOfWeek.weekday, DateTime.saturday); expect(endOfWeek.hour, 23); @@ -69,7 +69,7 @@ void main() { test('should return Saturday when given a Monday', () { final monday = DateTime(2025, 12, 15); // Monday final endOfWeek = getEndOfWeek(monday); - + expect(endOfWeek.day, 20); // Saturday expect(endOfWeek.weekday, DateTime.saturday); }); @@ -77,7 +77,7 @@ void main() { test('should handle week crossing month boundary', () { final monday = DateTime(2025, 12, 29); // Monday final endOfWeek = getEndOfWeek(monday); - + expect(endOfWeek.year, 2026); expect(endOfWeek.month, 1); expect(endOfWeek.day, 3); // Saturday in next month @@ -89,14 +89,14 @@ void main() { test('should return true for Sunday-Saturday week', () { final sunday = DateTime(2025, 12, 14); final saturday = DateTime(2025, 12, 20, 23, 59); - + expect(isFullWeek(sunday, saturday), true); }); test('should return false for Monday-Sunday week', () { final monday = DateTime(2025, 12, 15); final sunday = DateTime(2025, 12, 21, 23, 59); - + expect(isFullWeek(monday, sunday), false); }); }); @@ -105,10 +105,11 @@ void main() { final testDate = DateTime(2025, 12, 17); final startOfWeek = getStartOfWeek(testDate); final endOfWeek = getEndOfWeek(testDate); - - final startDay = DateTime(startOfWeek.year, startOfWeek.month, startOfWeek.day); + + final startDay = + DateTime(startOfWeek.year, startOfWeek.month, startOfWeek.day); final endDay = DateTime(endOfWeek.year, endOfWeek.month, endOfWeek.day); - + expect(endDay.difference(startDay).inDays, 6); expect(startOfWeek.weekday, DateTime.sunday); expect(endOfWeek.weekday, DateTime.saturday); @@ -125,7 +126,7 @@ void main() { test('should return Monday when given a Monday', () { final monday = DateTime(2025, 12, 15); // Monday final startOfWeek = getStartOfWeek(monday); - + expect(startOfWeek.day, 15); expect(startOfWeek.weekday, DateTime.monday); }); @@ -133,7 +134,7 @@ void main() { test('should return Monday when given a Tuesday', () { final tuesday = DateTime(2025, 12, 16); // Tuesday final startOfWeek = getStartOfWeek(tuesday); - + expect(startOfWeek.day, 15); // Monday expect(startOfWeek.weekday, DateTime.monday); }); @@ -141,7 +142,7 @@ void main() { test('should return Monday when given a Sunday', () { final sunday = DateTime(2025, 12, 21); // Sunday final startOfWeek = getStartOfWeek(sunday); - + expect(startOfWeek.day, 15); // Monday expect(startOfWeek.weekday, DateTime.monday); }); @@ -149,7 +150,7 @@ void main() { test('should handle week crossing month boundary', () { final friday = DateTime(2025, 1, 3); // Friday final startOfWeek = getStartOfWeek(friday); - + expect(startOfWeek.year, 2024); expect(startOfWeek.month, 12); expect(startOfWeek.day, 30); // Monday in previous month @@ -159,7 +160,7 @@ void main() { test('should handle week crossing year boundary', () { final thursday = DateTime(2025, 1, 2); // Thursday final startOfWeek = getStartOfWeek(thursday); - + expect(startOfWeek.year, 2024); expect(startOfWeek.month, 12); expect(startOfWeek.day, 30); // Monday in previous year @@ -171,7 +172,7 @@ void main() { test('should return Sunday when given a Monday', () { final monday = DateTime(2025, 12, 15); // Monday final endOfWeek = getEndOfWeek(monday); - + expect(endOfWeek.day, 21); // Sunday expect(endOfWeek.weekday, DateTime.sunday); expect(endOfWeek.hour, 23); @@ -181,7 +182,7 @@ void main() { test('should return Sunday when given a Wednesday', () { final wednesday = DateTime(2025, 12, 17); // Wednesday final endOfWeek = getEndOfWeek(wednesday); - + expect(endOfWeek.day, 21); // Sunday expect(endOfWeek.weekday, DateTime.sunday); }); @@ -189,7 +190,7 @@ void main() { test('should return Sunday when given a Sunday', () { final sunday = DateTime(2025, 12, 21); // Sunday final endOfWeek = getEndOfWeek(sunday); - + expect(endOfWeek.day, 21); // Same Sunday expect(endOfWeek.weekday, DateTime.sunday); }); @@ -197,7 +198,7 @@ void main() { test('should handle week crossing month boundary', () { final monday = DateTime(2025, 12, 29); // Monday final endOfWeek = getEndOfWeek(monday); - + expect(endOfWeek.year, 2026); expect(endOfWeek.month, 1); expect(endOfWeek.day, 4); // Sunday in next month @@ -207,7 +208,7 @@ void main() { test('should handle week crossing year boundary', () { final tuesday = DateTime(2025, 12, 30); // Tuesday final endOfWeek = getEndOfWeek(tuesday); - + expect(endOfWeek.year, 2026); expect(endOfWeek.month, 1); expect(endOfWeek.day, 4); // Sunday in next year @@ -219,21 +220,21 @@ void main() { test('should return true for Monday-Sunday week', () { final monday = DateTime(2025, 12, 15); final sunday = DateTime(2025, 12, 21, 23, 59); - + expect(isFullWeek(monday, sunday), true); }); test('should return false for Sunday-Saturday week', () { final sunday = DateTime(2025, 12, 14); final saturday = DateTime(2025, 12, 20, 23, 59); - + expect(isFullWeek(sunday, saturday), false); }); test('should return false for partial week', () { final tuesday = DateTime(2025, 12, 16); final friday = DateTime(2025, 12, 19, 23, 59); - + expect(isFullWeek(tuesday, friday), false); }); }); @@ -242,10 +243,11 @@ void main() { final testDate = DateTime(2025, 12, 17); final startOfWeek = getStartOfWeek(testDate); final endOfWeek = getEndOfWeek(testDate); - - final startDay = DateTime(startOfWeek.year, startOfWeek.month, startOfWeek.day); + + final startDay = + DateTime(startOfWeek.year, startOfWeek.month, startOfWeek.day); final endDay = DateTime(endOfWeek.year, endOfWeek.month, endOfWeek.day); - + expect(endDay.difference(startDay).inDays, 6); expect(startOfWeek.weekday, DateTime.monday); expect(endOfWeek.weekday, DateTime.sunday); @@ -256,12 +258,12 @@ void main() { final wednesday = DateTime(2025, 12, 17); final friday = DateTime(2025, 12, 19); final sunday = DateTime(2025, 12, 21); - + final mondayStr = getWeekStr(monday); final wednesdayStr = getWeekStr(wednesday); final fridayStr = getWeekStr(friday); final sundayStr = getWeekStr(sunday); - + expect(mondayStr, equals(wednesdayStr)); expect(mondayStr, equals(fridayStr)); expect(mondayStr, equals(sundayStr)); @@ -277,7 +279,7 @@ void main() { test('should use Monday as start of week', () { final wednesday = DateTime(2025, 12, 17); final startOfWeek = getStartOfWeek(wednesday); - + expect(startOfWeek.day, 15); expect(startOfWeek.weekday, DateTime.monday); }); @@ -285,12 +287,63 @@ void main() { test('should use Sunday as end of week', () { final wednesday = DateTime(2025, 12, 17); final endOfWeek = getEndOfWeek(wednesday); - + expect(endOfWeek.day, 21); expect(endOfWeek.weekday, DateTime.sunday); }); }); + // ===== Tests for canShift function ===== + group('canShift for CurrentWeek (locale-aware)', () { + test('should work correctly for Monday-start weeks', () { + I18n.define(Locale('en', 'GB')); + + bool canShiftBack = + canShift(-1, null, null, HomepageTimeInterval.CurrentWeek); + expect(canShiftBack, true); + + bool canShiftForward = + canShift(1, null, null, HomepageTimeInterval.CurrentWeek); + expect(canShiftForward, false); + }); + + test('should work correctly for Sunday-start weeks', () { + I18n.define(Locale('en', 'US')); + + bool canShiftBack = + canShift(-1, null, null, HomepageTimeInterval.CurrentWeek); + expect(canShiftBack, true); + + bool canShiftForward = + canShift(1, null, null, HomepageTimeInterval.CurrentWeek); + expect(canShiftForward, false); + }); + + test('should correctly validate custom week intervals (Monday-start)', () { + I18n.define(Locale('en', 'GB')); + + // Full week: Monday to Sunday + DateTime monday = DateTime(2025, 12, 15); + DateTime sunday = DateTime(2025, 12, 21, 23, 59); + + bool canShiftBack = + canShift(-1, monday, sunday, HomepageTimeInterval.CurrentWeek); + expect(canShiftBack, true); + }); + + test('should correctly validate custom week intervals (Sunday-start)', () { + I18n.define(Locale('en', 'US')); + + // Full week: Sunday to Saturday + DateTime sunday = DateTime(2025, 12, 14); + DateTime saturday = DateTime(2025, 12, 20, 23, 59); + + bool canShiftBack = + canShift(-1, sunday, saturday, HomepageTimeInterval.CurrentWeek); + expect(canShiftBack, true); + }); + }); + // ===== Tests for getDateRangeStr ===== group('getDateRangeStr', () { setUp(() { @@ -303,18 +356,20 @@ void main() { DateTime end = DateTime(2025, 11, 30, 23, 59, 59); String result = getDateRangeStr(start, end); - + expect(result.toLowerCase(), contains('november')); expect(result, contains('2025')); }); - test('should display date range when week ends on last day of month but does not start on 1st', () { + test( + 'should display date range when week ends on last day of month but does not start on 1st', + () { // Nov 24 - Nov 30 (week ending on last day, but not full month) DateTime start = DateTime(2025, 11, 24); DateTime end = DateTime(2025, 11, 30, 23, 59); - + String result = getDateRangeStr(start, end); - + // Should show date range, not just "November 2025" expect(result, contains('24')); expect(result, contains('30')); @@ -325,9 +380,9 @@ void main() { // Dec 15 - Dec 21 (regular week) DateTime start = DateTime(2025, 12, 15); DateTime end = DateTime(2025, 12, 21, 23, 59); - + String result = getDateRangeStr(start, end); - + expect(result, contains('15')); expect(result, contains('21')); expect(result, contains('-')); @@ -337,9 +392,9 @@ void main() { // Nov 30 - Dec 6 (crosses month boundary) DateTime start = DateTime(2025, 11, 30); DateTime end = DateTime(2025, 12, 6, 23, 59); - + String result = getDateRangeStr(start, end); - + expect(result, contains('30')); expect(result, contains('6')); expect(result, contains('-')); @@ -349,9 +404,9 @@ void main() { // Dec 29, 2025 - Jan 4, 2026 (crosses year boundary) DateTime start = DateTime(2025, 12, 29); DateTime end = DateTime(2026, 1, 4, 23, 59); - + String result = getDateRangeStr(start, end); - + expect(result, contains('29')); expect(result, contains('4')); expect(result, contains('2025')); @@ -363,9 +418,9 @@ void main() { // Should still work when dates are reversed DateTime start = DateTime(2025, 12, 21, 23, 59); DateTime end = DateTime(2025, 12, 15); - + String result = getDateRangeStr(start, end); - + expect(result, contains('15')); expect(result, contains('21')); expect(result, contains('-')); @@ -377,18 +432,20 @@ void main() { DateTime end = DateTime(2024, 2, 29, 23, 59, 59); String result = getDateRangeStr(start, end); - + expect(result.toLowerCase(), contains('february')); expect(result, contains('2024')); }); - test('should display date range when ending on Feb 28 in non-leap year but not starting on 1st', () { + test( + 'should display date range when ending on Feb 28 in non-leap year but not starting on 1st', + () { // Feb 22 - Feb 28, 2025 (week ending on last day of Feb in non-leap year) DateTime start = DateTime(2025, 2, 22); DateTime end = DateTime(2025, 2, 28, 23, 59); - + String result = getDateRangeStr(start, end); - + expect(result, contains('22')); expect(result, contains('28')); expect(result, contains('-')); diff --git a/test/datetime_utility_functions_test.dart b/test/datetime_utility_functions_test.dart index 03450041..baac3f93 100644 --- a/test/datetime_utility_functions_test.dart +++ b/test/datetime_utility_functions_test.dart @@ -159,7 +159,7 @@ void main() { // Sunday, December 14, 2025 final sunday = DateTime(2025, 12, 14); final startOfWeek = getStartOfWeek(sunday); - + expect(startOfWeek.year, 2025); expect(startOfWeek.month, 12); expect(startOfWeek.day, 14); @@ -170,7 +170,7 @@ void main() { // Monday, December 15, 2025 final monday = DateTime(2025, 12, 15); final startOfWeek = getStartOfWeek(monday); - + expect(startOfWeek.year, 2025); expect(startOfWeek.month, 12); expect(startOfWeek.day, 14); // Sunday @@ -181,7 +181,7 @@ void main() { // Tuesday, December 16, 2025 final tuesday = DateTime(2025, 12, 16); final startOfWeek = getStartOfWeek(tuesday); - + expect(startOfWeek.year, 2025); expect(startOfWeek.month, 12); expect(startOfWeek.day, 14); // Sunday @@ -192,7 +192,7 @@ void main() { // Wednesday, December 17, 2025 final wednesday = DateTime(2025, 12, 17); final startOfWeek = getStartOfWeek(wednesday); - + expect(startOfWeek.year, 2025); expect(startOfWeek.month, 12); expect(startOfWeek.day, 14); // Sunday @@ -203,7 +203,7 @@ void main() { // Saturday, December 20, 2025 final saturday = DateTime(2025, 12, 20); final startOfWeek = getStartOfWeek(saturday); - + expect(startOfWeek.year, 2025); expect(startOfWeek.month, 12); expect(startOfWeek.day, 14); // Sunday @@ -214,7 +214,7 @@ void main() { // Friday, January 3, 2025 final friday = DateTime(2025, 1, 3); final startOfWeek = getStartOfWeek(friday); - + expect(startOfWeek.year, 2024); expect(startOfWeek.month, 12); expect(startOfWeek.day, 29); // Sunday in previous month @@ -225,7 +225,7 @@ void main() { // Thursday, January 2, 2025 final thursday = DateTime(2025, 1, 2); final startOfWeek = getStartOfWeek(thursday); - + expect(startOfWeek.year, 2024); expect(startOfWeek.month, 12); expect(startOfWeek.day, 29); // Sunday in previous year @@ -238,7 +238,7 @@ void main() { // Monday, December 15, 2025 final monday = DateTime(2025, 12, 15); final endOfWeek = getEndOfWeek(monday); - + expect(endOfWeek.year, 2025); expect(endOfWeek.month, 12); expect(endOfWeek.day, 20); // Saturday @@ -251,7 +251,7 @@ void main() { // Wednesday, December 17, 2025 final wednesday = DateTime(2025, 12, 17); final endOfWeek = getEndOfWeek(wednesday); - + expect(endOfWeek.year, 2025); expect(endOfWeek.month, 12); expect(endOfWeek.day, 20); // Saturday @@ -264,7 +264,7 @@ void main() { // Sunday, December 14, 2025 final sunday = DateTime(2025, 12, 14); final endOfWeek = getEndOfWeek(sunday); - + expect(endOfWeek.year, 2025); expect(endOfWeek.month, 12); expect(endOfWeek.day, 20); // Saturday @@ -277,7 +277,7 @@ void main() { // Monday, December 29, 2025 final monday = DateTime(2025, 12, 29); final endOfWeek = getEndOfWeek(monday); - + expect(endOfWeek.year, 2026); expect(endOfWeek.month, 1); expect(endOfWeek.day, 3); // Saturday in next month @@ -290,7 +290,7 @@ void main() { // Tuesday, December 30, 2025 final tuesday = DateTime(2025, 12, 30); final endOfWeek = getEndOfWeek(tuesday); - + expect(endOfWeek.year, 2026); expect(endOfWeek.month, 1); expect(endOfWeek.day, 3); // Saturday in next year @@ -305,7 +305,7 @@ void main() { // Wednesday, December 17, 2025 final wednesday = DateTime(2025, 12, 17); final weekStr = getWeekStr(wednesday); - + // Basic validation - just ensure it returns a valid string expect(weekStr, isNotNull); expect(weekStr, isNotEmpty); @@ -315,7 +315,7 @@ void main() { // Thursday, January 2, 2025 (week spans 2024-2025) final thursday = DateTime(2025, 1, 2); final weekStr = getWeekStr(thursday); - + expect(weekStr, isNotNull); expect(weekStr, isNotEmpty); }); @@ -324,7 +324,7 @@ void main() { // Tuesday, December 30, 2025 (week spans 2025-2026) final tuesday = DateTime(2025, 12, 30); final weekStr = getWeekStr(tuesday); - + expect(weekStr, isNotNull); expect(weekStr, isNotEmpty); }); @@ -335,12 +335,12 @@ void main() { final monday = DateTime(2025, 12, 15); final wednesday = DateTime(2025, 12, 17); final saturday = DateTime(2025, 12, 20); - + final sundayStr = getWeekStr(sunday); final mondayStr = getWeekStr(monday); final wednesdayStr = getWeekStr(wednesday); final saturdayStr = getWeekStr(saturday); - + expect(sundayStr, equals(mondayStr)); expect(sundayStr, equals(wednesdayStr)); expect(sundayStr, equals(saturdayStr)); @@ -352,23 +352,24 @@ void main() { final testDate = DateTime(2025, 12, 17); final startOfWeek = getStartOfWeek(testDate); final endOfWeek = getEndOfWeek(testDate); - + // Calculate days difference (ignoring hours/minutes) - final startDay = DateTime(startOfWeek.year, startOfWeek.month, startOfWeek.day); + final startDay = + DateTime(startOfWeek.year, startOfWeek.month, startOfWeek.day); final endDay = DateTime(endOfWeek.year, endOfWeek.month, endOfWeek.day); final daysDifference = endDay.difference(startDay).inDays; - + expect(daysDifference, 6); }); test('start of week should always be Sunday', () { // Test various dates throughout the year final testDates = [ - DateTime(2025, 1, 15), // Wednesday - DateTime(2025, 3, 7), // Friday - DateTime(2025, 6, 20), // Friday - DateTime(2025, 9, 14), // Sunday - DateTime(2025, 12, 1), // Monday + DateTime(2025, 1, 15), // Wednesday + DateTime(2025, 3, 7), // Friday + DateTime(2025, 6, 20), // Friday + DateTime(2025, 9, 14), // Sunday + DateTime(2025, 12, 1), // Monday ]; for (var date in testDates) { @@ -381,11 +382,11 @@ void main() { test('end of week should always be Saturday', () { // Test various dates throughout the year final testDates = [ - DateTime(2025, 1, 15), // Wednesday - DateTime(2025, 3, 7), // Friday - DateTime(2025, 6, 20), // Friday - DateTime(2025, 9, 14), // Sunday - DateTime(2025, 12, 1), // Monday + DateTime(2025, 1, 15), // Wednesday + DateTime(2025, 3, 7), // Friday + DateTime(2025, 6, 20), // Friday + DateTime(2025, 9, 14), // Sunday + DateTime(2025, 12, 1), // Monday ]; for (var date in testDates) { @@ -398,7 +399,7 @@ void main() { test('end of week should always be at 23:59', () { final testDate = DateTime(2025, 6, 15, 10, 30); // With specific time final endOfWeek = getEndOfWeek(testDate); - + expect(endOfWeek.hour, 23); expect(endOfWeek.minute, 59); }); diff --git a/test/future_records_integration_test.dart b/test/future_records_integration_test.dart index 5f7a88d6..edbbef07 100644 --- a/test/future_records_integration_test.dart +++ b/test/future_records_integration_test.dart @@ -19,8 +19,11 @@ void main() { final recurrentRecordService = RecurrentRecordService(); - test('RecordsPerDay should include future records in balance calculation when enabled', () { - final category = Category("Test Category", categoryType: CategoryType.expense); + test( + 'RecordsPerDay should include future records in balance calculation when enabled', + () { + final category = + Category("Test Category", categoryType: CategoryType.expense); final today = DateTime.now(); final dateKey = DateTime(today.year, today.month, today.day); @@ -42,7 +45,8 @@ void main() { isFutureRecord: true, ); - final recordsPerDay = RecordsPerDay(dateKey, records: [pastRecord, futureRecord]); + final recordsPerDay = + RecordsPerDay(dateKey, records: [pastRecord, futureRecord]); // When future records setting is enabled, they should be included in calculations expect(recordsPerDay.expenses, -300.0); // -100 + -200 @@ -50,8 +54,11 @@ void main() { expect(recordsPerDay.balance, -300.0); }); - test('RecordsPerDay should include future income records in calculation when enabled', () { - final incomeCategory = Category("Salary", categoryType: CategoryType.income); + test( + 'RecordsPerDay should include future income records in calculation when enabled', + () { + final incomeCategory = + Category("Salary", categoryType: CategoryType.income); final today = DateTime.now(); final dateKey = DateTime(today.year, today.month, today.day); @@ -71,7 +78,8 @@ void main() { isFutureRecord: true, ); - final recordsPerDay = RecordsPerDay(dateKey, records: [pastIncome, futureIncome]); + final recordsPerDay = + RecordsPerDay(dateKey, records: [pastIncome, futureIncome]); // Should include both past and future income when future records are enabled expect(recordsPerDay.income, 1500.0); // 500 + 1000 @@ -79,8 +87,11 @@ void main() { expect(recordsPerDay.balance, 1500.0); }); - test('RecordsPerDay with only future records should include them in balance', () { - final category = Category("Future Category", categoryType: CategoryType.expense); + test( + 'RecordsPerDay with only future records should include them in balance', + () { + final category = + Category("Future Category", categoryType: CategoryType.expense); final today = DateTime.now(); final dateKey = DateTime(today.year, today.month, today.day); @@ -101,7 +112,8 @@ void main() { isFutureRecord: true, ); - final recordsPerDay = RecordsPerDay(dateKey, records: [futureRecord1, futureRecord2]); + final recordsPerDay = + RecordsPerDay(dateKey, records: [futureRecord1, futureRecord2]); // Future records are included when the setting is enabled expect(recordsPerDay.expenses, -300.0); @@ -136,8 +148,10 @@ void main() { expect(futureRecord.isFutureRecord, false); }); - test('Monthly recurrent pattern generates correct future records count', () { - final category = Category("Monthly Bill", categoryType: CategoryType.expense); + test('Monthly recurrent pattern generates correct future records count', + () { + final category = + Category("Monthly Bill", categoryType: CategoryType.expense); final startDate = DateTime(2024, 1, 1).toUtc(); final endOfYear = DateTime(2024, 12, 31, 23, 59).toUtc(); @@ -149,7 +163,8 @@ void main() { RecurrentPeriod.EveryMonth, ); - final records = recurrentRecordService.generateRecurrentRecordsFromDateTime( + final records = + recurrentRecordService.generateRecurrentRecordsFromDateTime( pattern, endOfYear, ); @@ -165,7 +180,8 @@ void main() { }); test('Weekly pattern with future view date generates correct records', () { - final category = Category("Weekly Task", categoryType: CategoryType.expense); + final category = + Category("Weekly Task", categoryType: CategoryType.expense); final startDate = DateTime(2024, 1, 1).toUtc(); // Monday final endDate = DateTime(2024, 1, 29).toUtc(); // 4 weeks later @@ -177,7 +193,8 @@ void main() { RecurrentPeriod.EveryWeek, ); - final records = recurrentRecordService.generateRecurrentRecordsFromDateTime( + final records = + recurrentRecordService.generateRecurrentRecordsFromDateTime( pattern, endDate, ); @@ -203,14 +220,23 @@ void main() { expect(futureRecord.tags, tags); }); - test('Mixed past and future records maintain their flags independently', () { + test('Mixed past and future records maintain their flags independently', + () { final category = Category("Mixed Category"); final records = [ - Record(100.0, "Past 1", category, DateTime.now().subtract(Duration(days: 2)).toUtc(), isFutureRecord: false), - Record(200.0, "Past 2", category, DateTime.now().subtract(Duration(days: 1)).toUtc(), isFutureRecord: false), - Record(300.0, "Future 1", category, DateTime.now().add(Duration(days: 1)).toUtc(), isFutureRecord: true), - Record(400.0, "Future 2", category, DateTime.now().add(Duration(days: 2)).toUtc(), isFutureRecord: true), + Record(100.0, "Past 1", category, + DateTime.now().subtract(Duration(days: 2)).toUtc(), + isFutureRecord: false), + Record(200.0, "Past 2", category, + DateTime.now().subtract(Duration(days: 1)).toUtc(), + isFutureRecord: false), + Record(300.0, "Future 1", category, + DateTime.now().add(Duration(days: 1)).toUtc(), + isFutureRecord: true), + Record(400.0, "Future 2", category, + DateTime.now().add(Duration(days: 2)).toUtc(), + isFutureRecord: true), ]; final pastRecords = records.where((r) => !r.isFutureRecord).toList(); @@ -223,4 +249,3 @@ void main() { }); }); } - diff --git a/test/future_recurrent_records_test.dart b/test/future_recurrent_records_test.dart index 32c5138d..d2fe4c93 100644 --- a/test/future_recurrent_records_test.dart +++ b/test/future_recurrent_records_test.dart @@ -24,8 +24,8 @@ void main() { // View end date is in the future (end of month) final viewEndDate = DateTime.utc(2024, 1, 31, 23, 59); - final recordPattern = RecurrentRecordPattern( - 100.0, "Daily Pattern", category1, patternStartDate, RecurrentPeriod.EveryDay); + final recordPattern = RecurrentRecordPattern(100.0, "Daily Pattern", + category1, patternStartDate, RecurrentPeriod.EveryDay); final records = recurrentRecordService .generateRecurrentRecordsFromDateTime(recordPattern, viewEndDate); @@ -42,8 +42,8 @@ void main() { // View end date is 10 days from now final viewEndDate = today.add(Duration(days: 10)); - final recordPattern = RecurrentRecordPattern( - 50.0, "Daily Future Pattern", category1, patternStartDate, RecurrentPeriod.EveryDay); + final recordPattern = RecurrentRecordPattern(50.0, "Daily Future Pattern", + category1, patternStartDate, RecurrentPeriod.EveryDay); final records = recurrentRecordService .generateRecurrentRecordsFromDateTime(recordPattern, viewEndDate); @@ -54,20 +54,21 @@ void main() { // All records should be generated (this is just generation, marking happens in service) final lastRecord = records.last; expect( - lastRecord.utcDateTime.isBefore(viewEndDate) || - lastRecord.utcDateTime.isAtSameMomentAs(viewEndDate), - true - ); + lastRecord.utcDateTime.isBefore(viewEndDate) || + lastRecord.utcDateTime.isAtSameMomentAs(viewEndDate), + true); }); - test('should split records into past and future correctly in boundary cases', () { + test( + 'should split records into past and future correctly in boundary cases', + () { // Test edge case where today's date is exactly on a recurrent record final today = DateTime.now().toUtc(); final startOfToday = DateTime(today.year, today.month, today.day).toUtc(); // Pattern starts today - final recordPattern = RecurrentRecordPattern( - 75.0, "Boundary Test", category1, startOfToday, RecurrentPeriod.EveryDay); + final recordPattern = RecurrentRecordPattern(75.0, "Boundary Test", + category1, startOfToday, RecurrentPeriod.EveryDay); // View end date is 5 days from today final viewEndDate = startOfToday.add(Duration(days: 5)); @@ -83,8 +84,8 @@ void main() { final patternStartDate = DateTime(2024, 1, 15).toUtc(); final viewEndDate = DateTime(2024, 6, 30).toUtc(); - final recordPattern = RecurrentRecordPattern( - 200.0, "Monthly Pattern", category1, patternStartDate, RecurrentPeriod.EveryMonth); + final recordPattern = RecurrentRecordPattern(200.0, "Monthly Pattern", + category1, patternStartDate, RecurrentPeriod.EveryMonth); final records = recurrentRecordService .generateRecurrentRecordsFromDateTime(recordPattern, viewEndDate); @@ -105,8 +106,8 @@ void main() { final patternStartDate = DateTime(2024, 1, 1).toUtc(); final viewEndDate = DateTime(2024, 2, 15).toUtc(); - final recordPattern = RecurrentRecordPattern( - 30.0, "Weekly Pattern", category1, patternStartDate, RecurrentPeriod.EveryWeek); + final recordPattern = RecurrentRecordPattern(30.0, "Weekly Pattern", + category1, patternStartDate, RecurrentPeriod.EveryWeek); final records = recurrentRecordService .generateRecurrentRecordsFromDateTime(recordPattern, viewEndDate); @@ -122,7 +123,9 @@ void main() { } }); - test('future records should have isFutureRecord flag set to false by default', () { + test( + 'future records should have isFutureRecord flag set to false by default', + () { // This tests the Record model default behavior final category = Category("Test Category"); final record = Record( @@ -149,12 +152,13 @@ void main() { expect(record.isFutureRecord, true); }); - test('should not generate records when viewEndDate is before pattern start', () { + test('should not generate records when viewEndDate is before pattern start', + () { final patternStartDate = DateTime(2024, 6, 1).toUtc(); final viewEndDate = DateTime(2024, 5, 31).toUtc(); - final recordPattern = RecurrentRecordPattern( - 100.0, "Future Pattern", category1, patternStartDate, RecurrentPeriod.EveryDay); + final recordPattern = RecurrentRecordPattern(100.0, "Future Pattern", + category1, patternStartDate, RecurrentPeriod.EveryDay); final records = recurrentRecordService .generateRecurrentRecordsFromDateTime(recordPattern, viewEndDate); @@ -167,7 +171,11 @@ void main() { final viewEndDate = DateTime(2024, 1, 31).toUtc(); final recordPattern = RecurrentRecordPattern( - 150.0, "Year Boundary Pattern", category1, patternStartDate, RecurrentPeriod.EveryWeek); + 150.0, + "Year Boundary Pattern", + category1, + patternStartDate, + RecurrentPeriod.EveryWeek); final records = recurrentRecordService .generateRecurrentRecordsFromDateTime(recordPattern, viewEndDate); @@ -275,4 +283,3 @@ void main() { }); }); } - diff --git a/test/helpers/test_database.dart b/test/helpers/test_database.dart index d5174db0..86e0b890 100644 --- a/test/helpers/test_database.dart +++ b/test/helpers/test_database.dart @@ -13,7 +13,7 @@ class TestDatabaseHelper { var factoryWithLogs = SqfliteDatabaseFactoryLogger(databaseFactory, options: SqfliteLoggerOptions(type: SqfliteDatabaseFactoryLoggerType.all)); - + final db = await factoryWithLogs.openDatabase( inMemoryDatabasePath, // Each call creates a new isolated in-memory database options: OpenDatabaseOptions( @@ -22,10 +22,10 @@ class TestDatabaseHelper { onUpgrade: SqliteMigrationService.onUpgrade, onDowngrade: SqliteMigrationService.onUpgrade), ); - + // Set the database for the singleton instance to use SqliteDatabase.setDatabaseForTesting(db); - + return db; } } diff --git a/test/recurrent_pattern_tags_integration_test.dart b/test/recurrent_pattern_tags_integration_test.dart index 33e22e2c..0eea9d38 100644 --- a/test/recurrent_pattern_tags_integration_test.dart +++ b/test/recurrent_pattern_tags_integration_test.dart @@ -72,19 +72,12 @@ void main() { DateTime.utc(2023, 1, 1), DateTime.now().toUtc()); expect( - taggedRecords - .any((element) => element['key'] == 'streaming'), - true, + taggedRecords.any((element) => element['key'] == 'streaming'), true, reason: 'Should be able to find records by streaming tag'); - expect( - taggedRecords - .any((element) => element['key'] == 'entertainment'), + expect(taggedRecords.any((element) => element['key'] == 'entertainment'), true, reason: 'Should be able to find records by entertainment tag'); - expect( - taggedRecords - .any((element) => element['key'] == 'monthly'), - true, + expect(taggedRecords.any((element) => element['key'] == 'monthly'), true, reason: 'Should be able to find records by monthly tag'); }); }); diff --git a/test/show_future_records_preference_test.dart b/test/show_future_records_preference_test.dart index 19e96f4c..a099e21b 100644 --- a/test/show_future_records_preference_test.dart +++ b/test/show_future_records_preference_test.dart @@ -12,8 +12,8 @@ void main() { }); test('Default value for showFutureRecords should be true', () { - final defaultValue = PreferencesDefaultValues.defaultValues[ - PreferencesKeys.showFutureRecords]; + final defaultValue = PreferencesDefaultValues + .defaultValues[PreferencesKeys.showFutureRecords]; expect(defaultValue, true); }); @@ -69,4 +69,3 @@ void main() { }); }); } - diff --git a/test/tab_records_controller_test.dart b/test/tab_records_controller_test.dart index 6ff8e2b5..2d28e913 100644 --- a/test/tab_records_controller_test.dart +++ b/test/tab_records_controller_test.dart @@ -31,11 +31,11 @@ void main() { setUpAll(() async { TestWidgetsFlutterBinding.ensureInitialized(); await initializeDateFormatting('en_US', null); - + // Initialize FFI for sqflite sqfliteFfiInit(); databaseFactory = databaseFactoryFfi; - + // Initialize timezone data tz.initializeTimeZones(); ServiceConfig.localTimezone = "Europe/Vienna"; @@ -53,7 +53,7 @@ void main() { // Create a new isolated in-memory database for each test await TestDatabaseHelper.setupTestDatabase(); database = ServiceConfig.database; - + // Add test category await database.addCategory(testCategory); @@ -61,7 +61,9 @@ void main() { }); group('shiftMonthWeekYear', () { - test('should shift month forward by 1 with custom interval set to a full month', () async { + test( + 'should shift month forward by 1 with custom interval set to a full month', + () async { // Setup: Custom interval is January 2025 controller.customIntervalFrom = DateTime(2025, 1, 1); controller.customIntervalTo = getEndOfMonth(2025, 1); @@ -73,10 +75,13 @@ void main() { expect(controller.customIntervalFrom, DateTime(2025, 2, 1)); expect(controller.customIntervalTo!.year, 2025); expect(controller.customIntervalTo!.month, 2); - expect(controller.customIntervalTo!.day, 28); // February has 28 days in 2025 + expect( + controller.customIntervalTo!.day, 28); // February has 28 days in 2025 }); - test('should shift month backward by 1 with custom interval set to a full month', () async { + test( + 'should shift month backward by 1 with custom interval set to a full month', + () async { // Setup: Custom interval is March 2025 controller.customIntervalFrom = DateTime(2025, 3, 1); controller.customIntervalTo = getEndOfMonth(2025, 3); @@ -91,7 +96,9 @@ void main() { expect(controller.customIntervalTo!.day, 28); }); - test('should shift year forward by 1 with custom interval set to a full year', () async { + test( + 'should shift year forward by 1 with custom interval set to a full year', + () async { // Setup: Custom interval is full year 2024 controller.customIntervalFrom = DateTime(2024, 1, 1); controller.customIntervalTo = DateTime(2024, 12, 31, 23, 59); @@ -109,7 +116,9 @@ void main() { expect(controller.customIntervalTo, DateTime(2025, 12, 31).add(DateTimeConstants.END_OF_DAY)); }); - test('should shift year backward by 1 with custom interval set to a full year', () async { + test( + 'should shift year backward by 1 with custom interval set to a full year', + () async { // Setup: Custom interval is full year 2025 controller.customIntervalFrom = DateTime(2025, 1, 1); controller.customIntervalTo = DateTime(2025, 12, 31, 23, 59); @@ -127,11 +136,13 @@ void main() { expect(controller.customIntervalTo, DateTime(2024, 12, 31).add(DateTimeConstants.END_OF_DAY)); }); - test('should shift week forward by 1 when HomepageTimeInterval is CurrentWeek', () async { + test( + 'should shift week forward by 1 when HomepageTimeInterval is CurrentWeek', + () async { // Setup: No custom interval, use CurrentWeek setting controller.customIntervalFrom = null; controller.customIntervalTo = null; - + await sharedPreferences.setInt( PreferencesKeys.homepageTimeInterval, HomepageTimeInterval.CurrentWeek.index, @@ -140,14 +151,14 @@ void main() { // Get the current week's start DateTime now = DateTime.now(); DateTime currentWeekStart = getStartOfWeek(now); - + // Act: Shift forward by 1 week await controller.shiftInterval(1); // Assert: Should be next week DateTime expectedStart = currentWeekStart.add(Duration(days: 7)); DateTime expectedEnd = expectedStart.add(Duration(days: 6)); - + expect(controller.customIntervalFrom!.year, expectedStart.year); expect(controller.customIntervalFrom!.month, expectedStart.month); expect(controller.customIntervalFrom!.day, expectedStart.day); @@ -156,11 +167,13 @@ void main() { expect(controller.customIntervalTo!.day, expectedEnd.day); }); - test('should shift week backward by 1 when HomepageTimeInterval is CurrentWeek', () async { + test( + 'should shift week backward by 1 when HomepageTimeInterval is CurrentWeek', + () async { // Setup: No custom interval, use CurrentWeek setting controller.customIntervalFrom = null; controller.customIntervalTo = null; - + await sharedPreferences.setInt( PreferencesKeys.homepageTimeInterval, HomepageTimeInterval.CurrentWeek.index, @@ -169,14 +182,14 @@ void main() { // Get the current week's start DateTime now = DateTime.now(); DateTime currentWeekStart = getStartOfWeek(now); - + // Act: Shift backward by 1 week await controller.shiftInterval(-1); // Assert: Should be previous week DateTime expectedStart = currentWeekStart.subtract(Duration(days: 7)); DateTime expectedEnd = expectedStart.add(Duration(days: 6)); - + expect(controller.customIntervalFrom!.year, expectedStart.year); expect(controller.customIntervalFrom!.month, expectedStart.month); expect(controller.customIntervalFrom!.day, expectedStart.day); @@ -185,18 +198,19 @@ void main() { expect(controller.customIntervalTo!.day, expectedEnd.day); }); - test('should shift month forward when HomepageTimeInterval is CurrentMonth', () async { + test('should shift month forward when HomepageTimeInterval is CurrentMonth', + () async { // Setup: No custom interval, use CurrentMonth setting controller.customIntervalFrom = null; controller.customIntervalTo = null; - + await sharedPreferences.setInt( PreferencesKeys.homepageTimeInterval, HomepageTimeInterval.CurrentMonth.index, ); DateTime now = DateTime.now(); - + // Act: Shift forward by 1 month await controller.shiftInterval(1); @@ -207,18 +221,19 @@ void main() { expect(controller.customIntervalTo, expectedDateTo); }); - test('should shift year forward when HomepageTimeInterval is CurrentYear', () async { + test('should shift year forward when HomepageTimeInterval is CurrentYear', + () async { // Setup: No custom interval, use CurrentYear setting controller.customIntervalFrom = null; controller.customIntervalTo = null; - + await sharedPreferences.setInt( PreferencesKeys.homepageTimeInterval, HomepageTimeInterval.CurrentYear.index, ); DateTime now = DateTime.now(); - + // Act: Shift forward by 1 year await controller.shiftInterval(1); diff --git a/test/tag_management_test.dart b/test/tag_management_test.dart index dbc09b5f..cb8faa9d 100644 --- a/test/tag_management_test.dart +++ b/test/tag_management_test.dart @@ -8,7 +8,6 @@ import 'package:timezone/data/latest_all.dart' as tz; import 'helpers/test_database.dart'; void main() { - // Setup sqflite_common_ffi for flutter test setUpAll(() { TestWidgetsFlutterBinding.ensureInitialized(); diff --git a/test/test_database.dart b/test/test_database.dart index 7a53e314..d70d24eb 100644 --- a/test/test_database.dart +++ b/test/test_database.dart @@ -647,8 +647,8 @@ Future main() async { for (var record in allRecords) { expect(record?.tags, isNotEmpty, reason: 'Record should have tags from pattern'); - expect(record?.tags, - containsAll(['subscription', 'recurring', 'digital']), + expect( + record?.tags, containsAll(['subscription', 'recurring', 'digital']), reason: 'Record should contain all tags from the recurrent pattern'); }