diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 62cb12ec..b124104e 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -140,4 +140,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: a62623f56f2d1d0e85a4a3c73509cd2832d5c86f -COCOAPODS: 1.14.3 +COCOAPODS: 1.15.0 diff --git a/packages/features/landing_page_screen/test/widget/landing_page_test.dart b/packages/features/landing_page_screen/test/widget/landing_page_test.dart index 492f9fa7..32e72f1c 100644 --- a/packages/features/landing_page_screen/test/widget/landing_page_test.dart +++ b/packages/features/landing_page_screen/test/widget/landing_page_test.dart @@ -415,6 +415,7 @@ void main() { expect(saveProjectButton, findsOneWidget); await tester.tap(saveProjectButton); + when(fileService.getNextProjectNumber()).thenAnswer((_) async => 1); await tester.pumpAndSettle(); final textFormField = find.widgetWithText(TextFormField, 'Project name'); diff --git a/packages/features/landing_page_screen/test/widget/landing_page_test.mocks.dart b/packages/features/landing_page_screen/test/widget/landing_page_test.mocks.dart index 3f8b937c..950dbe36 100644 --- a/packages/features/landing_page_screen/test/widget/landing_page_test.mocks.dart +++ b/packages/features/landing_page_screen/test/widget/landing_page_test.mocks.dart @@ -309,6 +309,13 @@ class MockIFileService extends _i1.Mock implements _i9.IFileService { _i1.throwOnMissingStub(this); } + @override + Future getNextProjectNumber() => super.noSuchMethod( + Invocation.method(#getNextProjectNumber, []), + returnValue: Future.value(1), + returnValueForMissingStub: Future.value(-1), + ); + @override _i3.Future<_i5.Result<_i11.File, _i9.Failure>> save( String? filename, diff --git a/packages/io_library/lib/src/service/file_service.dart b/packages/io_library/lib/src/service/file_service.dart index 2d72e9b9..963c4396 100644 --- a/packages/io_library/lib/src/service/file_service.dart +++ b/packages/io_library/lib/src/service/file_service.dart @@ -7,6 +7,7 @@ import 'package:io_library/io_library.dart'; import 'package:oxidized/oxidized.dart'; import 'package:path_provider/path_provider.dart'; +import 'package:shared_preferences/shared_preferences.dart'; abstract class IFileService { Future> save(String filename, Uint8List data); @@ -18,6 +19,10 @@ abstract class IFileService { Result getFile(String path); + Future getNextImageNumber(); + + Future getNextProjectNumber(); + static final provider = Provider((ref) => FileService()); Future checkIfFileExistsInApplicationDirectory(String fileName); @@ -107,4 +112,42 @@ class FileService with LoggableMixin implements IFileService { return const Result.err(LoadImageFailure.unidentified); } } + + @override + Future getNextImageNumber() async { + final prefs = await SharedPreferences.getInstance(); + int lastNumber = prefs.getInt('lastImageNumber') ?? 0; + int nextNumber = lastNumber + 1; + await prefs.setInt('lastImageNumber', nextNumber); + return nextNumber; + } + + @override + Future getNextProjectNumber() async { + final directory = await getApplicationDocumentsDirectory(); + final files = await directory.list().toList(); + int maxNum = 0; + + for (var file in files) { + if (file is File) { + final fileName = file.path.split('/').last; + + if (fileName.startsWith('project')) { + final match = RegExp(r'project(\d+)').firstMatch(fileName); + if (match != null) { + final numString = match.group(1); + if (numString != null) { + final num = int.tryParse(numString) ?? 0; + + if (num > maxNum) { + maxNum = num; + } + } + } + } + } + } + + return maxNum + 1; + } } diff --git a/packages/io_library/lib/src/ui/save_image_dialog.dart b/packages/io_library/lib/src/ui/save_image_dialog.dart index 104f4bb5..1109acd3 100644 --- a/packages/io_library/lib/src/ui/save_image_dialog.dart +++ b/packages/io_library/lib/src/ui/save_image_dialog.dart @@ -1,6 +1,7 @@ import 'package:component_library/component_library.dart'; import 'package:flutter/material.dart'; import 'package:io_library/io_library.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; /// Returns [null] if user dismissed the dialog by tapping outside Future showSaveImageDialog( @@ -12,17 +13,17 @@ Future showSaveImageDialog( barrierDismissible: true, barrierLabel: 'Dismiss save image dialog box'); -class SaveImageDialog extends StatefulWidget { +class SaveImageDialog extends ConsumerStatefulWidget { final bool savingProject; const SaveImageDialog({Key? key, required this.savingProject}) : super(key: key); @override - State createState() => _SaveImageDialogState(); + SaveImageDialogState createState() => SaveImageDialogState(); } -class _SaveImageDialogState extends State { +class SaveImageDialogState extends ConsumerState { final TextEditingController nameFieldController = TextEditingController(); final formKey = GlobalKey(debugLabel: 'SaveImageDialog Form'); var selectedFormat = ImageFormat.jpg; @@ -31,10 +32,29 @@ class _SaveImageDialogState extends State { @override void initState() { super.initState(); - if (widget.savingProject) { selectedFormat = ImageFormat.catrobatImage; } + + WidgetsBinding.instance.addPostFrameCallback((_) { + _setDefaultFileName(); + }); + } + + Future _setDefaultFileName() async { + final fileService = ref.read(IFileService.provider); + String defaultName; + if (widget.savingProject) { + final nextNumber = await fileService.getNextProjectNumber(); + defaultName = 'project$nextNumber'; + } else { + final nextNumber = await fileService.getNextImageNumber(); + defaultName = 'image$nextNumber'; + } + + setState(() { + nameFieldController.text = defaultName; + }); } void _dismissDialogWithData() { diff --git a/packages/io_library/pubspec.yaml b/packages/io_library/pubspec.yaml index 33e1fe2b..19c513db 100644 --- a/packages/io_library/pubspec.yaml +++ b/packages/io_library/pubspec.yaml @@ -27,6 +27,7 @@ dependencies: oxidized: ^5.2.0 intl: ^0.18.0 json_annotation: ^4.8.1 + shared_preferences: ^2.0.15 # Internal packages: component_library: