diff --git a/android/app/src/main/res/xml/act_counter_info.xml b/android/app/src/main/res/xml/act_counter_info.xml index 7b65bdcc9..98f3d4fbf 100644 --- a/android/app/src/main/res/xml/act_counter_info.xml +++ b/android/app/src/main/res/xml/act_counter_info.xml @@ -1,8 +1,4 @@ - { - _TestObjectRepository() : super(_defDbTest, _defTableTestObject); +class SavedTestEntity extends TestEntity with SavedDatabaseTupleMixin {} + +class TestRepository + extends DatabaseTupleRepository { + TestRepository(super.tableDefinition); + + @override + SavedTestEntity pack(Map tuple) { + // TODO: implement pack + throw UnimplementedError(); + } @override - TestObjectDatabaseTupleEntity pack(Map map) => - TestObjectDatabaseTupleEntity.fromMap(map); + Map unpack(TestEntity entity) { + // TODO: implement unpack + throw UnimplementedError(); + } } void main() => group( _name, () { - late String databasePath; - setUpAll( - () async { - databasePath = - await DatabaseFactory.buildDatabasePath(_defDbTest.name); - + setUpAll(() async { + DatabaseFactory.onTest = true; + for (final testDefDatabase in [ + sampleDefDb, + sampleDefDBAddedTable, + sampleDefDBAddedColumn, + ]) { await DatabaseFactory // ignore: deprecated_member_use_from_same_package .nativeFactory - .deleteDatabase(databasePath); - }, - ); + .deleteDatabase( + await DatabaseFactory.buildDatabasePath(testDefDatabase.name), + ); + } + + DatabaseTupleRepository.databaseAccessor = + await DatabaseRepository().receive(sampleDefDBAddedColumn); + }); + tearDownAll(() { + DatabaseFactory.onTest = false; + DatabaseTupleRepository.databaseAccessor = null; + }); test( - '#new', + ": count.", () async { - _TestObjectRepository(); + final repository = TestRepository(sampleDefTable); + + final count = await repository.count( + condition: Equals(sampleDefPk.name, 1), + ); - expect( - await DatabaseFactory - // ignore: deprecated_member_use_from_same_package - .nativeFactory - .databaseExists(databasePath), - false); + expect(count, equals(0)); }, ); group( - 'operations', + 'ship', () { - final repository = _TestObjectRepository(); - - group( - '#count', - () { - setUpAll( - () async { - final falseSample = TestObjectEntity(false); - final trueSample = TestObjectEntity(true); - final now = DateTime.now(); - - final dbA = await DatabaseFactory.open(_defDbTest); - - await dbA.delete(_defTableTestObject); - await dbA.insert(_defTableTestObject, - falseSample.toMap..addAll({defColCreatedAt.name: now})); - await dbA.insert(_defTableTestObject, - falseSample.toMap..addAll({defColCreatedAt.name: now})); - await dbA.insert(_defTableTestObject, - trueSample.toMap..addAll({defColCreatedAt.name: now})); - }, - ); + test( + 'group by', + () async { + final repository = TestRepository(sampleDefTable); - test( - ': all.', - () async { - final count = await repository.count(); - - expect(count, 3); - }, + final shipped = await repository.ship( + groupBy: GroupBy( + [sampleDefColBoolean], + extraColumns: [Max(sampleDefPk)], + ), ); - test( - ': condition.', - () async { - final count = await repository.count( - condition: Equals(_defColA, false)); - expect(count, 2); - }, - ); + expect(shipped, hasLength(0)); }, ); - group( - '#receive', - () { - setUpAll( - () async { - final dbA = await DatabaseFactory.open(_defDbTest); + test( + 'order by', + () async { + final repository = TestRepository(sampleDefTable); - await dbA.delete(_defTableTestObject); - }, + final shipped = await repository.ship( + orderBy: [ + Ascending(sampleDefPk), + Descending(sampleDefColInteger), + ], + offset: 1, + limit: 1, ); - test( - ': received.', - () async { - final now = DateTime.now(); - final entity = TestObjectEntity(false); - - final received = - await repository.receive(entity, createdAt: now); - - expect( - received, - equals(TestObjectDatabaseTupleEntity(entity.a).withMap( - {defPkId.name: 1, defColCreatedAt.name: now}))); - }, - ); - }, - ); - - group( - '#ship', - () { - final falseSample = TestObjectEntity(false); - final trueSample = TestObjectEntity(true); - final now = DateTime.now(); - final later = now.add(const Duration(seconds: 1)); - - late int sampleId1; - late int sampleId2; - late int sampleId3; - setUpAll( - () async { - final dbA = await DatabaseFactory.open(_defDbTest); - - await dbA.delete(_defTableTestObject); - sampleId1 = await dbA.insert(_defTableTestObject, - falseSample.toMap..addAll({defColCreatedAt.name: now})); - sampleId2 = await dbA.insert(_defTableTestObject, - trueSample.toMap..addAll({defColCreatedAt.name: now})); - sampleId3 = await dbA.insert( - _defTableTestObject, - falseSample.toMap - ..addAll({defColCreatedAt.name: later})); - }, - ); - - test( - ': all.', - () async { - final shipped = await repository.ship(); - - expect(shipped, hasLength(3)); - expect(shipped[0].id, equals(sampleId1)); - expect(shipped[0].createdAt, equals(now)); - }, - ); - test( - ': condition.', - () async { - final shipped = await repository.ship( - condition: Equals(_defColA, false), - ); - - expect(shipped, hasLength(2)); - }, - ); - test( - ': groupBy.', - () async { - final shipped = await repository.ship( - groupBy: GroupBy([_defColA], - extraColumns: [Max(defColCreatedAt)])); - - expect(shipped, hasLength(2)); - expect(shipped[0].id, equals(sampleId3)); - expect(shipped[0].createdAt, equals(later)); - }, - ); - test( - ': orderBy.', - () async { - final shipped = - await repository.ship(orderBy: [Descending(defPkId)]); - - expect(shipped, hasLength(3)); - expect(shipped[0].id, sampleId3); - }, - ); - test( - ': offset.', - () async { - final shipped = await repository.ship(offset: 1); - - expect(shipped, hasLength(2)); - expect(shipped[0].id, sampleId2); - }, - ); - test( - ': limit.', - () async { - final shipped = await repository.ship(limit: 2); - - expect(shipped, hasLength(2)); - expect(shipped[1].id, sampleId2); - }, - ); - }, - ); - - group( - '#replace', - () { - late TestObjectDatabaseTupleEntity savedFalseSample; - setUpAll( - () async { - final falseSample = TestObjectEntity(false); - final now = DateTime.now(); - - final dbA = await DatabaseFactory.open(_defDbTest); - - await dbA.delete(_defTableTestObject); - savedFalseSample = - TestObjectDatabaseTupleEntity.fromMap(falseSample.toMap - ..addAll({ - defPkId.name: await dbA.insert( - _defTableTestObject, - falseSample.toMap - ..addAll({defColCreatedAt.name: now})), - defColCreatedAt.name: now - })); - }, - ); - - test(': updated.', () async { - final updatedAt = DateTime.now(); - final updating = TestObjectDatabaseTupleEntity.fromMap( - savedFalseSample.toMap - ..update( - TestObjectEntity.fieldNames[0], (value) => false)); - - final updated = - await repository.replace(updating, updatedAt: updatedAt); - - expect(updated.a, equals(updating.a)); - expect(updated.updatedAt, equals(updatedAt)); - }); - }, - ); - - group( - '#archive', - () { - late TestObjectDatabaseTupleEntity savedFalseSample; - setUpAll( - () async { - final falseSample = TestObjectEntity(false); - final now = DateTime.now(); - - final dbA = await DatabaseFactory.open(_defDbTest); - - await dbA.delete(_defTableTestObject); - savedFalseSample = - TestObjectDatabaseTupleEntity.fromMap(falseSample.toMap - ..addAll({ - defPkId.name: await dbA.insert( - _defTableTestObject, - falseSample.toMap - ..addAll({defColCreatedAt.name: now})), - defColCreatedAt.name: now - })); - }, - ); - - test( - ': archived.', - () async { - final archivedAt = DateTime.now(); - - final archived = await repository.archive(savedFalseSample, - archivedAt: archivedAt); - - expect(archived.archivedAt, equals(archivedAt)); - }, - ); - }, - ); - - group( - '#unarchive', - () { - late TestObjectDatabaseTupleEntity savedArchivedSample; - setUpAll( - () async { - final falseSample = TestObjectEntity(false); - final now = DateTime.now(); - - final dbA = await DatabaseFactory.open(_defDbTest); - - await dbA.delete(_defTableTestObject); - savedArchivedSample = - TestObjectDatabaseTupleEntity.fromMap(falseSample.toMap - ..addAll({ - defPkId.name: await dbA.insert( - _defTableTestObject, - falseSample.toMap - ..addAll({ - defColCreatedAt.name: now, - defColArchivedAt.name: now - })), - defColCreatedAt.name: now, - defColArchivedAt.name: now - })); - }, - ); - - test( - ': unarchived.', - () async { - final updatedAt = DateTime.now(); - - final archived = await repository - .unarchive(savedArchivedSample, updatedAt: updatedAt); - - expect(archived.updatedAt, equals(updatedAt)); - expect(archived.archivedAt, isNull); - }, - ); - }, - ); - - group( - '#waste', - () { - late TestObjectDatabaseTupleEntity savedFalseSample; - setUpAll( - () async { - final falseSample = TestObjectEntity(false); - final now = DateTime.now(); - - final dbA = await DatabaseFactory.open(_defDbTest); - - await dbA.delete(_defTableTestObject); - savedFalseSample = - TestObjectDatabaseTupleEntity.fromMap(falseSample.toMap - ..addAll({ - defPkId.name: await dbA.insert( - _defTableTestObject, - falseSample.toMap - ..addAll({defColCreatedAt.name: now})), - defColCreatedAt.name: now - })); - }, - ); - - test( - ': wasted.', - () async { - final wastedList = await repository.waste( - condition: Equals(defPkId, savedFalseSample.id)); - - expect(wastedList, hasLength(equals(1))); - expect(wastedList[0], equals(savedFalseSample)); - }, - ); + expect(shipped, hasLength(0)); }, ); }, diff --git a/integration_test/scenarios/habit/act_counter_scenario.dart b/integration_test/scenarios/habit/act_counter_scenario.dart index 23e612594..098d9cf7d 100644 --- a/integration_test/scenarios/habit/act_counter_scenario.dart +++ b/integration_test/scenarios/habit/act_counter_scenario.dart @@ -2,7 +2,8 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:mem/act_counter/act_counter_entity.dart'; + +import 'package:mem/act_counter/act_counter_repository.dart'; import 'package:mem/databases/definition.dart'; import 'package:mem/databases/table_definitions/acts.dart'; import 'package:mem/databases/table_definitions/base.dart'; @@ -21,7 +22,7 @@ void main() => group( const insertedMemName2 = '$_name: inserted - mem name - 2'; late int insertedMemId; late int insertedMemId2; - late DateTime actStart; + late DateTime actPeriod; late final DatabaseAccessor dbA; @@ -58,7 +59,7 @@ void main() => group( defTableActs, { defFkActsMemId.name: insertedMemId, - defColActsStart.name: actStart = DateTime.now(), + defColActsStart.name: actPeriod = DateTime.now(), defColActsStartIsAllDay.name: 0, defColCreatedAt.name: zeroDate, }, @@ -72,14 +73,11 @@ void main() => group( var saveWidgetDataCount = 0; var updateWidgetCount = 0; final homeWidgetId = randomInt(); - final actCounter = - ActCounterEntity(insertedMemId, insertedMemName, 1, actStart); - widgetTester.binding.defaultBinaryMessenger .setMockMethodCallHandler( - MethodChannel(actCounter.methodChannelName), + const MethodChannel(methodChannelName), (message) { - expect(message.method, actCounter.initializeMethodName); + expect(message.method, initializeMethodName); expect(message.arguments, null); initializeCount++; @@ -98,7 +96,7 @@ void main() => group( }, 2: { 'id': "lastUpdatedAtSeconds-$insertedMemId", - 'data': actStart.millisecondsSinceEpoch.toDouble(), + 'data': actPeriod.millisecondsSinceEpoch.toDouble(), }, 3: { 'id': "memId-$homeWidgetId", diff --git a/integration_test/scenarios/habit/act_list_page_scenario.dart b/integration_test/scenarios/habit/act_list_page_scenario.dart index 062da87cc..14cb07168 100644 --- a/integration_test/scenarios/habit/act_list_page_scenario.dart +++ b/integration_test/scenarios/habit/act_list_page_scenario.dart @@ -14,367 +14,439 @@ import '../helpers.dart'; const _name = "ActListPage scenario"; -void main() => group(_name, () { - const oneMin = Duration(minutes: 1); - const insertedMemName = '$_name: inserted mem - name'; - - final oneMinDate = zeroDate.add(oneMin); - - late final DatabaseAccessor dbA; - late final int insertedMemId; - - setUpAll(() async { - dbA = await openTestDatabase(databaseDefinition); - await clearAllTestDatabaseRows(databaseDefinition); - - insertedMemId = await dbA.insert(defTableMems, { - defColMemsName.name: insertedMemName, - defColCreatedAt.name: zeroDate +void main() => group( + _name, + () { + const oneMin = Duration(minutes: 1); + const insertedMemName = '$_name: inserted mem - name'; + + final oneMinDate = zeroDate.add(oneMin); + + late final DatabaseAccessor dbA; + late final int insertedMemId; + + setUpAll(() async { + dbA = await openTestDatabase(databaseDefinition); + await clearAllTestDatabaseRows(databaseDefinition); + + insertedMemId = await dbA.insert( + defTableMems, + { + defColMemsName.name: insertedMemName, + defColCreatedAt.name: zeroDate, + }, + ); }); - }); - setUp(() async { - await dbA.delete(defTableActs); - - await dbA.insert(defTableActs, { - defFkActsMemId.name: insertedMemId, - defColActsStart.name: zeroDate, - defColActsStartIsAllDay.name: 0, - defColActsEnd.name: oneMinDate, - defColActsEndIsAllDay.name: 0, - defColCreatedAt.name: zeroDate - }); - }); - - group(": show inserted acts", () { - testWidgets(": by Mem.", (widgetTester) async { - await runApplication(); - await widgetTester.pumpAndSettle(); - await widgetTester.tap(find.text(insertedMemName)); - await widgetTester.pumpAndSettle(); - await widgetTester.tap(startIconFinder); - await widgetTester.pumpAndSettle(); - await widgetTester.tap(find.byIcon(Icons.numbers)); - await widgetTester.pumpAndSettle(); - - expect(widgetTester.textAt(0).data, equals(insertedMemName)); - expect(widgetTester.textAt(1).data, equals(dateText(zeroDate))); - expect(widgetTester.textAt(2).data, equals("1")); - expect(widgetTester.textAt(3).data, equals(oneMin.format())); - expect(widgetTester.textAt(4).data, equals(timeText(zeroDate))); - expect(widgetTester.textAt(5).data, equals("~")); - expect(widgetTester.textAt(6).data, equals(timeText(oneMinDate))); - - expect(startIconFinder, findsOneWidget); - expect(stopIconFinder, findsNothing); + setUp(() async { + await dbA.delete(defTableActs); + + await dbA.insert( + defTableActs, + { + defFkActsMemId.name: insertedMemId, + defColActsStart.name: zeroDate, + defColActsStartIsAllDay.name: 0, + defColActsEnd.name: oneMinDate, + defColActsEndIsAllDay.name: 0, + defColCreatedAt.name: zeroDate, + }, + ); }); group( - ": All", + ": show inserted acts", () { - testWidgets(': time.', (widgetTester) async { - await runApplication(); - await widgetTester.pumpAndSettle(); - - await widgetTester.tap(find.byIcon(Icons.playlist_play)); - await widgetTester.pumpAndSettle(); - - expect(startIconFinder, findsNothing); - expect(stopIconFinder, findsNothing); - expect(widgetTester.textAt(0).data, equals("All")); - expect(widgetTester.textAt(1).data, equals(dateText(zeroDate))); - expect(widgetTester.textAt(2).data, equals("1")); - expect(widgetTester.textAt(3).data, equals(oneMin.format())); - expect(widgetTester.textAt(4).data, equals(oneMin.format())); - expect(widgetTester.textAt(5).data, equals("1")); - expect(widgetTester.textAt(6).data, equals(insertedMemName)); - }); - - testWidgets(': count.', (widgetTester) async { - await runApplication(); - await widgetTester.pumpAndSettle(); - await widgetTester.tap(find.byIcon(Icons.playlist_play)); - await widgetTester.pumpAndSettle(); - - await widgetTester.tap(find.byIcon(Icons.numbers)); - await widgetTester.pumpAndSettle(); - - expect(startIconFinder, findsNothing); - expect(stopIconFinder, findsNothing); - expect(find.byIcon(Icons.access_time), findsOneWidget); - expect(widgetTester.textAt(0).data, equals("All")); - expect(widgetTester.textAt(1).data, equals(dateText(zeroDate))); - expect(widgetTester.textAt(2).data, equals("1")); - expect(widgetTester.textAt(3).data, equals(oneMin.format())); - expect(widgetTester.textAt(4).data, equals(timeText(zeroDate))); - expect(widgetTester.textAt(5).data, equals("~")); - expect(widgetTester.textAt(6).data, equals(timeText(oneMinDate))); - expect(widgetTester.textAt(7).data, equals(insertedMemName)); - }); - - testWidgets(': month view.', (widgetTester) async { - await runApplication(); - await widgetTester.pumpAndSettle(); - await widgetTester.tap(find.byIcon(Icons.playlist_play)); - await widgetTester.pumpAndSettle(); - - await widgetTester.tap(find.byIcon(Icons.calendar_view_month)); - await widgetTester.pumpAndSettle(); + testWidgets( + ": by Mem.", + (widgetTester) async { + await runApplication(); + await widgetTester.pumpAndSettle(); + await widgetTester.tap(find.text(insertedMemName)); + await widgetTester.pumpAndSettle(); + await widgetTester.tap(startIconFinder); + await widgetTester.pumpAndSettle(); + await widgetTester.tap(find.byIcon(Icons.numbers)); + await widgetTester.pumpAndSettle(); + + expect(widgetTester.textAt(0).data, equals(insertedMemName)); + expect(widgetTester.textAt(1).data, equals(dateText(zeroDate))); + expect(widgetTester.textAt(2).data, equals("1")); + expect(widgetTester.textAt(3).data, equals(oneMin.format())); + expect(widgetTester.textAt(4).data, equals(timeText(zeroDate))); + expect(widgetTester.textAt(5).data, equals("~")); + expect( + widgetTester.textAt(6).data, equals(timeText(oneMinDate))); + + expect(startIconFinder, findsOneWidget); + expect(stopIconFinder, findsNothing); + }, + ); + + group( + ": All", + () { + testWidgets( + ': time.', + (widgetTester) async { + await runApplication(); + await widgetTester.pumpAndSettle(); + + await widgetTester.tap(find.byIcon(Icons.playlist_play)); + await widgetTester.pumpAndSettle(); + + expect(startIconFinder, findsNothing); + expect(stopIconFinder, findsNothing); + expect(widgetTester.textAt(0).data, equals("All")); + expect(widgetTester.textAt(1).data, + equals(dateText(zeroDate))); + expect(widgetTester.textAt(2).data, equals("1")); + expect( + widgetTester.textAt(3).data, equals(oneMin.format())); + expect( + widgetTester.textAt(4).data, equals(oneMin.format())); + expect(widgetTester.textAt(5).data, equals("1")); + expect( + widgetTester.textAt(6).data, equals(insertedMemName)); + }, + ); + + testWidgets( + ': count.', + (widgetTester) async { + await runApplication(); + await widgetTester.pumpAndSettle(); + await widgetTester.tap(find.byIcon(Icons.playlist_play)); + await widgetTester.pumpAndSettle(); + + await widgetTester.tap(find.byIcon(Icons.numbers)); + await widgetTester.pumpAndSettle(); + + expect(startIconFinder, findsNothing); + expect(stopIconFinder, findsNothing); + expect(find.byIcon(Icons.access_time), findsOneWidget); + expect(widgetTester.textAt(0).data, equals("All")); + expect(widgetTester.textAt(1).data, + equals(dateText(zeroDate))); + expect(widgetTester.textAt(2).data, equals("1")); + expect( + widgetTester.textAt(3).data, equals(oneMin.format())); + expect(widgetTester.textAt(4).data, + equals(timeText(zeroDate))); + expect(widgetTester.textAt(5).data, equals("~")); + expect(widgetTester.textAt(6).data, + equals(timeText(oneMinDate))); + expect( + widgetTester.textAt(7).data, equals(insertedMemName)); + }, + ); + + testWidgets( + ': month view.', + (widgetTester) async { + await runApplication(); + await widgetTester.pumpAndSettle(); + await widgetTester.tap(find.byIcon(Icons.playlist_play)); + await widgetTester.pumpAndSettle(); + + await widgetTester + .tap(find.byIcon(Icons.calendar_view_month)); + await widgetTester.pumpAndSettle(); + + expect(widgetTester.textAt(0).data, equals("All")); + expect(widgetTester.textAt(1).data, equals("January 0")); + expect(widgetTester.textAt(2).data, equals("1")); + expect( + widgetTester.textAt(3).data, equals(oneMin.format())); + expect( + widgetTester.textAt(4).data, equals(oneMin.format())); + expect(widgetTester.textAt(5).data, equals("1")); + expect( + widgetTester.textAt(6).data, equals(insertedMemName)); + }, + ); + }, + ); + + group( + ": many acts", + () { + const days = 30; + const numberOfActsByDate = 3; + + setUp(() async { + await dbA.delete(defTableActs); + + for (var j = 0; j < days; j++) { + for (var i = 0; i < numberOfActsByDate; i++) { + final start = zeroDate + .add(Duration(days: j)) + .add(Duration(minutes: i)); + final end = start.add(oneMin); + + await dbA.insert( + defTableActs, + { + defFkActsMemId.name: insertedMemId, + defColActsStart.name: start, + defColActsStartIsAllDay.name: 0, + defColActsEnd.name: end, + defColActsEndIsAllDay.name: 0, + defColCreatedAt.name: zeroDate, + }, + ); + } + } + }); - expect(widgetTester.textAt(0).data, equals("All")); - expect(widgetTester.textAt(1).data, equals("January 0")); - expect(widgetTester.textAt(2).data, equals("1")); - expect(widgetTester.textAt(3).data, equals(oneMin.format())); - expect(widgetTester.textAt(4).data, equals(oneMin.format())); - expect(widgetTester.textAt(5).data, equals("1")); - expect(widgetTester.textAt(6).data, equals(insertedMemName)); - }); + testWidgets( + ': infinite scroll.', + (widgetTester) async { + await runApplication(); + await widgetTester.pumpAndSettle(); + await widgetTester.tap(find.byIcon(Icons.playlist_play)); + await widgetTester.pumpAndSettle(); + + final noOnInitialItemFinder = find.text("1/15/0"); + final earliestItemFinder = find.text("1/1/0"); + + expect(noOnInitialItemFinder, findsNothing); + expect(earliestItemFinder, findsNothing); + + final listFinder = find.byType(Scrollable); + + await widgetTester.scrollUntilVisible( + noOnInitialItemFinder, + 500.0, + scrollable: listFinder, + ); + + expect(noOnInitialItemFinder, findsOneWidget); + expect(earliestItemFinder, findsNothing); + + await widgetTester.scrollUntilVisible( + earliestItemFinder, + 500.0, + scrollable: listFinder, + ); + + expect(noOnInitialItemFinder, findsNothing); + expect(earliestItemFinder, findsOneWidget); + }, + ); + }, + ); }, ); - group(": many acts", () { - const days = 30; - const numberOfActsByDate = 3; - - setUp(() async { - await dbA.delete(defTableActs); - - for (var j = 0; j < days; j++) { - for (var i = 0; i < numberOfActsByDate; i++) { - final start = - zeroDate.add(Duration(days: j)).add(Duration(minutes: i)); - final end = start.add(oneMin); - - await dbA.insert(defTableActs, { - defFkActsMemId.name: insertedMemId, - defColActsStart.name: start, - defColActsStartIsAllDay.name: 0, - defColActsEnd.name: end, - defColActsEndIsAllDay.name: 0, - defColCreatedAt.name: zeroDate - }); - } - } - }); - - testWidgets(': infinite scroll.', (widgetTester) async { + group(": by Mem", () { + Future showMemListPage(WidgetTester widgetTester) async { await runApplication(); await widgetTester.pumpAndSettle(); - await widgetTester.tap(find.byIcon(Icons.playlist_play)); - await widgetTester.pumpAndSettle(); - - final noOnInitialItemFinder = find.text("1/15/0"); - final earliestItemFinder = find.text("1/1/0"); - - expect(noOnInitialItemFinder, findsNothing); - expect(earliestItemFinder, findsNothing); - - final listFinder = find.byType(Scrollable); - - await widgetTester.scrollUntilVisible(noOnInitialItemFinder, 500.0, - scrollable: listFinder); - - expect(noOnInitialItemFinder, findsOneWidget); - expect(earliestItemFinder, findsNothing); + } - await widgetTester.scrollUntilVisible(earliestItemFinder, 500.0, - scrollable: listFinder); + Future showActListPage(WidgetTester widgetTester) async { + await showMemListPage(widgetTester); - expect(noOnInitialItemFinder, findsNothing); - expect(earliestItemFinder, findsOneWidget); - }); - }); - }); - - group(": by Mem", () { - Future showMemListPage(WidgetTester widgetTester) async { - await runApplication(); - await widgetTester.pumpAndSettle(); - } - - Future showActListPage(WidgetTester widgetTester) async { - await showMemListPage(widgetTester); - - await widgetTester.tap(find.text(insertedMemName)); - await widgetTester.pumpAndSettle(); - - await widgetTester.tap(startIconFinder); - await widgetTester.pumpAndSettle(); - - await widgetTester.tap(find.byIcon(Icons.numbers)); - await widgetTester.pumpAndSettle(); - } - - testWidgets(': start & finish act.', - // 時間に関するテストなのでリトライ可能とする - retry: maxRetryCount, (widgetTester) async { - widgetTester - .ignoreMockMethodCallHandler(MethodChannelMock.permissionHandler); - widgetTester.ignoreMockMethodCallHandler( - MethodChannelMock.flutterLocalNotifications); - - await showActListPage(widgetTester); - - expect(stopIconFinder, findsNothing); - final startTime = DateTime.now(); - await widgetTester.tap(startIconFinder); - await Future.delayed(defaultTransitionDuration); - await widgetTester.pumpAndSettle(waitSideEffectDuration); - - expect(startIconFinder, findsNothing); - expect(widgetTester.textAt(0).data, equals(insertedMemName)); - expect(widgetTester.textAt(1).data, equals(dateText(startTime))); - expect(widgetTester.textAt(2).data, equals("1")); - expect(widgetTester.textAt(3).data, equals(Duration.zero.format())); - expect(widgetTester.textAt(4).data, equals(timeText(startTime))); - expect(widgetTester.textAt(5).data, equals("~")); - expect(widgetTester.textAt(6).data, equals(dateText(zeroDate))); - expect(widgetTester.textAt(7).data, equals("1")); - expect(widgetTester.textAt(8).data, equals(oneMin.format())); - expect(widgetTester.textAt(9).data, equals(timeText(zeroDate))); - expect(widgetTester.textAt(10).data, equals("~")); - expect(widgetTester.textAt(11).data, equals(timeText(oneMinDate))); - - final stopTime = DateTime.now(); - await widgetTester.tap(stopIconFinder); - await Future.delayed(defaultTransitionDuration); - await widgetTester.pumpAndSettle(waitSideEffectDuration); - - expect(widgetTester.textAt(0).data, equals(insertedMemName)); - expect(widgetTester.textAt(1).data, equals(dateText(startTime))); - expect(widgetTester.textAt(2).data, equals("1")); - expect(widgetTester.textAt(3).data, isNotNull); - expect(widgetTester.textAt(4).data, equals(timeText(startTime))); - expect(widgetTester.textAt(5).data, equals("~")); - expect(widgetTester.textAt(6).data, equals(timeText(stopTime))); - expect(widgetTester.textAt(7).data, equals(dateText(zeroDate))); - expect(widgetTester.textAt(8).data, equals("1")); - expect(widgetTester.textAt(9).data, equals(oneMin.format())); - expect(widgetTester.textAt(10).data, equals(timeText(zeroDate))); - expect(widgetTester.textAt(11).data, equals("~")); - expect(widgetTester.textAt(12).data, equals(timeText(oneMinDate))); - expect(stopIconFinder, findsNothing); - - final startTime2 = DateTime.now(); - await widgetTester.tap(startIconFinder); - await Future.delayed(defaultTransitionDuration); - await widgetTester.pumpAndSettle(waitSideEffectDuration); - - expect(widgetTester.textAt(0).data, equals(insertedMemName)); - expect(widgetTester.textAt(1).data, equals(dateText(startTime2))); - expect(widgetTester.textAt(2).data, equals("2")); - expect(widgetTester.textAt(3).data, isNotNull); - expect(widgetTester.textAt(4).data, equals(timeText(startTime2))); - expect(widgetTester.textAt(5).data, equals("~")); - expect(widgetTester.textAt(6).data, equals(timeText(startTime))); - expect(widgetTester.textAt(7).data, equals("~")); - expect(widgetTester.textAt(8).data, equals(timeText(stopTime))); - expect(widgetTester.textAt(9).data, equals(dateText(zeroDate))); - expect(widgetTester.textAt(10).data, equals("1")); - expect(widgetTester.textAt(11).data, equals(oneMin.format())); - expect(widgetTester.textAt(12).data, equals(timeText(zeroDate))); - expect(widgetTester.textAt(13).data, equals("~")); - expect(widgetTester.textAt(14).data, equals(timeText(oneMinDate))); - }); + await widgetTester.tap(find.text(insertedMemName)); + await widgetTester.pumpAndSettle(); - group(': Edit act', () { - setUp(() async { - await dbA.insert(defTableActs, { - defFkActsMemId.name: insertedMemId, - defColActsStart.name: zeroDate, - defColActsStartIsAllDay.name: 0, - defColCreatedAt.name: zeroDate - }); - }); + await widgetTester.tap(startIconFinder); + await widgetTester.pumpAndSettle(); - testWidgets(': save.', (widgetTester) async { - widgetTester.ignoreMockMethodCallHandler( - MethodChannelMock.flutterLocalNotifications); + await widgetTester.tap(find.byIcon(Icons.numbers)); + await widgetTester.pumpAndSettle(); + } - await showActListPage(widgetTester); + testWidgets( + ': start & finish act.', + (widgetTester) async { + widgetTester.ignoreMockMethodCallHandler( + MethodChannelMock.flutterLocalNotifications); - await widgetTester.longPress(find.text(timeText(zeroDate)).at(0)); - await widgetTester.pumpAndSettle(); + await showActListPage(widgetTester); - await widgetTester.tap(find.byType(Switch).at(1)); - await widgetTester.pump(); + expect(stopIconFinder, findsNothing); + final startTime = DateTime.now(); + await widgetTester.tap(startIconFinder); + await Future.delayed(defaultTransitionDuration); + await widgetTester.pumpAndSettle(waitSideEffectDuration); - final pickedDate = DateTime.now(); - await widgetTester.tap(find.text('OK')); - await widgetTester.pump(); + expect(startIconFinder, findsNothing); + expect(widgetTester.textAt(0).data, equals(insertedMemName)); + expect(widgetTester.textAt(1).data, equals(dateText(startTime))); + expect(widgetTester.textAt(2).data, equals("1")); + expect( + widgetTester.textAt(3).data, equals(Duration.zero.format())); + expect(widgetTester.textAt(4).data, equals(timeText(startTime))); + expect(widgetTester.textAt(5).data, equals("~")); + expect(widgetTester.textAt(6).data, equals(dateText(zeroDate))); + expect(widgetTester.textAt(7).data, equals("1")); + expect(widgetTester.textAt(8).data, equals(oneMin.format())); + expect(widgetTester.textAt(9).data, equals(timeText(zeroDate))); + expect(widgetTester.textAt(10).data, equals("~")); + expect( + widgetTester.textAt(11).data, equals(timeText(oneMinDate))); + + final stopTime = DateTime.now(); + await widgetTester.tap(stopIconFinder); + await Future.delayed(defaultTransitionDuration); + await widgetTester.pumpAndSettle(waitSideEffectDuration); + + expect(widgetTester.textAt(0).data, equals(insertedMemName)); + expect(widgetTester.textAt(1).data, equals(dateText(startTime))); + expect(widgetTester.textAt(2).data, equals("1")); + expect(widgetTester.textAt(3).data, isNotNull); + expect(widgetTester.textAt(4).data, equals(timeText(startTime))); + expect(widgetTester.textAt(5).data, equals("~")); + expect(widgetTester.textAt(6).data, equals(timeText(stopTime))); + expect(widgetTester.textAt(7).data, equals(dateText(zeroDate))); + expect(widgetTester.textAt(8).data, equals("1")); + expect(widgetTester.textAt(9).data, equals(oneMin.format())); + expect(widgetTester.textAt(10).data, equals(timeText(zeroDate))); + expect(widgetTester.textAt(11).data, equals("~")); + expect( + widgetTester.textAt(12).data, equals(timeText(oneMinDate))); + expect(stopIconFinder, findsNothing); - await widgetTester.tap(find.byIcon(Icons.save_alt)); - await widgetTester.pumpAndSettle(waitSideEffectDuration); + final startTime2 = DateTime.now(); + await widgetTester.tap(startIconFinder); + await Future.delayed(defaultTransitionDuration); + await widgetTester.pumpAndSettle(waitSideEffectDuration); - expect(widgetTester.textAt(0).data, equals(insertedMemName)); - expect(widgetTester.textAt(1).data, equals(dateText(zeroDate))); - expect(widgetTester.textAt(2).data, equals("2")); - expect(widgetTester.textAt(3).data, isNotNull); - expect(widgetTester.textAt(4).data, equals(timeText(zeroDate))); - expect(widgetTester.textAt(5).data, equals("~")); - expect(widgetTester.textAt(6).data, equals(timeText(pickedDate))); - expect(widgetTester.textAt(7).data, equals(timeText(zeroDate))); - expect(widgetTester.textAt(8).data, equals("~")); - expect(widgetTester.textAt(9).data, equals(timeText(oneMinDate))); + expect(widgetTester.textAt(0).data, equals(insertedMemName)); + expect(widgetTester.textAt(1).data, equals(dateText(startTime2))); + expect(widgetTester.textAt(2).data, equals("2")); + expect(widgetTester.textAt(3).data, isNotNull); + expect(widgetTester.textAt(4).data, equals(timeText(startTime2))); + expect(widgetTester.textAt(5).data, equals("~")); + expect(widgetTester.textAt(6).data, equals(timeText(startTime))); + expect(widgetTester.textAt(7).data, equals("~")); + expect(widgetTester.textAt(8).data, equals(timeText(stopTime))); + expect(widgetTester.textAt(9).data, equals(dateText(zeroDate))); + expect(widgetTester.textAt(10).data, equals("1")); + expect(widgetTester.textAt(11).data, equals(oneMin.format())); + expect(widgetTester.textAt(12).data, equals(timeText(zeroDate))); + expect(widgetTester.textAt(13).data, equals("~")); + expect( + widgetTester.textAt(14).data, equals(timeText(oneMinDate))); + }, + ); + + group(': Edit act', () { + setUp(() async { + await dbA.insert( + defTableActs, + { + defFkActsMemId.name: insertedMemId, + defColActsStart.name: zeroDate, + defColActsStartIsAllDay.name: 0, + defColCreatedAt.name: zeroDate, + }, + ); + }); - await widgetTester.longPress(find.text(timeText(zeroDate)).at(1)); - await widgetTester.pumpAndSettle(defaultTransitionDuration); + testWidgets( + ': save.', + (widgetTester) async { + widgetTester.ignoreMockMethodCallHandler( + MethodChannelMock.flutterLocalNotifications); + + await showActListPage(widgetTester); + + await widgetTester + .longPress(find.text(timeText(zeroDate)).at(0)); + await widgetTester.pumpAndSettle(); + + await widgetTester.tap(find.byType(Switch).at(1)); + await widgetTester.pump(); + + final pickedDate = DateTime.now(); + await widgetTester.tap(find.text('OK')); + await widgetTester.pump(); + + await widgetTester.tap(find.byIcon(Icons.save_alt)); + await widgetTester.pumpAndSettle(waitSideEffectDuration); + + expect(widgetTester.textAt(0).data, equals(insertedMemName)); + expect(widgetTester.textAt(1).data, equals(dateText(zeroDate))); + expect(widgetTester.textAt(2).data, equals("2")); + expect(widgetTester.textAt(3).data, isNotNull); + expect(widgetTester.textAt(4).data, equals(timeText(zeroDate))); + expect(widgetTester.textAt(5).data, equals("~")); + expect( + widgetTester.textAt(6).data, equals(timeText(pickedDate))); + expect(widgetTester.textAt(7).data, equals(timeText(zeroDate))); + expect(widgetTester.textAt(8).data, equals("~")); + expect( + widgetTester.textAt(9).data, equals(timeText(oneMinDate))); + + await widgetTester + .longPress(find.text(timeText(zeroDate)).at(1)); + await widgetTester.pumpAndSettle(defaultTransitionDuration); + + await widgetTester.tap(find.byIcon(Icons.clear).at(1)); + await widgetTester.pumpAndSettle(); + + await widgetTester.tap(find.byIcon(Icons.save_alt)); + await widgetTester.pumpAndSettle(waitSideEffectDuration); + + expect(widgetTester.textAt(0).data, equals(insertedMemName)); + expect(widgetTester.textAt(1).data, equals(dateText(zeroDate))); + expect(widgetTester.textAt(2).data, equals("2")); + expect(widgetTester.textAt(3).data, isNotNull); + expect(widgetTester.textAt(4).data, equals(timeText(zeroDate))); + expect(widgetTester.textAt(5).data, equals("~")); + expect(widgetTester.textAt(6).data, equals(timeText(zeroDate))); + expect(widgetTester.textAt(7).data, equals("~")); + expect( + widgetTester.textAt(8).data, equals(timeText(pickedDate))); + }, + ); + + testWidgets(': delete.', (widgetTester) async { + widgetTester.ignoreMockMethodCallHandler( + MethodChannelMock.flutterLocalNotifications); + + await showActListPage(widgetTester); + + await widgetTester.longPress(find.text(timeText(zeroDate)).at(0)); + await widgetTester.pumpAndSettle(); - await widgetTester.tap(find.byIcon(Icons.clear).at(1)); - await widgetTester.pumpAndSettle(); + await widgetTester.tap(find.byIcon(Icons.delete)); + await widgetTester.pumpAndSettle(); - await widgetTester.tap(find.byIcon(Icons.save_alt)); - await widgetTester.pumpAndSettle(waitSideEffectDuration); - - expect(widgetTester.textAt(0).data, equals(insertedMemName)); - expect(widgetTester.textAt(1).data, equals(dateText(zeroDate))); - expect(widgetTester.textAt(2).data, equals("2")); - expect(widgetTester.textAt(3).data, isNotNull); - expect(widgetTester.textAt(4).data, equals(timeText(zeroDate))); - expect(widgetTester.textAt(5).data, equals("~")); - expect(widgetTester.textAt(6).data, equals(timeText(zeroDate))); - expect(widgetTester.textAt(7).data, equals("~")); - expect(widgetTester.textAt(8).data, equals(timeText(pickedDate))); + expect(widgetTester.textAt(0).data, equals(insertedMemName)); + expect(widgetTester.textAt(1).data, equals(dateText(zeroDate))); + expect(widgetTester.textAt(2).data, equals("1")); + expect(widgetTester.textAt(3).data, equals(oneMin.format())); + expect(widgetTester.textAt(4).data, equals(timeText(zeroDate))); + expect(widgetTester.textAt(5).data, equals("~")); + expect(widgetTester.textAt(6).data, equals(timeText(oneMinDate))); + }); }); + }); - testWidgets(': delete.', (widgetTester) async { + testWidgets( + ": show MemDetailPage.", + (widgetTester) async { widgetTester.ignoreMockMethodCallHandler( MethodChannelMock.flutterLocalNotifications); - await showActListPage(widgetTester); - - await widgetTester.longPress(find.text(timeText(zeroDate)).at(0)); + await runApplication(); + await widgetTester.pumpAndSettle(); + await widgetTester.tap(find.byIcon(Icons.playlist_play)); await widgetTester.pumpAndSettle(); - await widgetTester.tap(find.byIcon(Icons.delete)); + await widgetTester.tap(find.byIcon(Icons.arrow_forward)); await widgetTester.pumpAndSettle(); - expect(widgetTester.textAt(0).data, equals(insertedMemName)); - expect(widgetTester.textAt(1).data, equals(dateText(zeroDate))); - expect(widgetTester.textAt(2).data, equals("1")); - expect(widgetTester.textAt(3).data, equals(oneMin.format())); - expect(widgetTester.textAt(4).data, equals(timeText(zeroDate))); - expect(widgetTester.textAt(5).data, equals("~")); - expect(widgetTester.textAt(6).data, equals(timeText(oneMinDate))); - }); - }); - }); - - testWidgets(": show MemDetailPage.", (widgetTester) async { - widgetTester.ignoreMockMethodCallHandler( - MethodChannelMock.flutterLocalNotifications); - - await runApplication(); - await widgetTester.pumpAndSettle(); - await widgetTester.tap(find.byIcon(Icons.playlist_play)); - await widgetTester.pumpAndSettle(); - - await widgetTester.tap(find.byIcon(Icons.arrow_forward)); - await widgetTester.pumpAndSettle(); - - expect( - widgetTester - .widget(find.byKey(keyMemName)) - .initialValue, - insertedMemName); - }); - }); + expect( + widgetTester + .widget(find.byKey(keyMemName)) + .initialValue, + insertedMemName, + ); + }, + ); + }, + ); diff --git a/integration_test/scenarios/habit/after_act_started_habit_scenario.dart b/integration_test/scenarios/habit/after_act_started_habit_scenario.dart index ad5502b58..93fd76445 100644 --- a/integration_test/scenarios/habit/after_act_started_habit_scenario.dart +++ b/integration_test/scenarios/habit/after_act_started_habit_scenario.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mem/components/mem/mem_name.dart'; import 'package:mem/components/time_text_form_field.dart'; -import 'package:mem/mems/mem_notification.dart'; +import 'package:mem/core/mem_notification.dart'; import 'package:mem/databases/definition.dart'; import 'package:mem/databases/table_definitions/base.dart'; import 'package:mem/databases/table_definitions/mem_notifications.dart'; diff --git a/integration_test/scenarios/habit/mem_list_page_scenario.dart b/integration_test/scenarios/habit/mem_list_page_scenario.dart index bcde6c7ef..70db5680e 100644 --- a/integration_test/scenarios/habit/mem_list_page_scenario.dart +++ b/integration_test/scenarios/habit/mem_list_page_scenario.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mem/components/timer.dart'; -import 'package:mem/mems/mem_notification.dart'; +import 'package:mem/core/mem_notification.dart'; import 'package:mem/databases/definition.dart'; import 'package:mem/databases/table_definitions/acts.dart'; import 'package:mem/databases/table_definitions/base.dart'; @@ -175,8 +175,8 @@ void main() => group( () { testWidgets( 'start.', - // 時間に関するテストなのでリトライ可能とする - retry: maxRetryCount, + // 時間に関するテストなので3回までリトライ可能とする + retry: 3, (widgetTester) async { widgetTester.ignoreMockMethodCallHandler( MethodChannelMock.flutterLocalNotifications); diff --git a/integration_test/scenarios/habit/repeat_by_day_of_week_scenario.dart b/integration_test/scenarios/habit/repeat_by_day_of_week_scenario.dart index ef2c177cf..2b41267fe 100644 --- a/integration_test/scenarios/habit/repeat_by_day_of_week_scenario.dart +++ b/integration_test/scenarios/habit/repeat_by_day_of_week_scenario.dart @@ -4,7 +4,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:intl/intl.dart'; -import 'package:mem/mems/mem_notification.dart'; +import 'package:mem/core/mem_notification.dart'; import 'package:mem/databases/definition.dart'; import 'package:mem/databases/table_definitions/base.dart'; import 'package:mem/databases/table_definitions/mem_notifications.dart'; diff --git a/integration_test/scenarios/habit/repeat_by_n_day_habit_scenario.dart b/integration_test/scenarios/habit/repeat_by_n_day_habit_scenario.dart index ae5a4dd53..d22dad4b3 100644 --- a/integration_test/scenarios/habit/repeat_by_n_day_habit_scenario.dart +++ b/integration_test/scenarios/habit/repeat_by_n_day_habit_scenario.dart @@ -3,7 +3,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mem/components/mem/mem_name.dart'; -import 'package:mem/mems/mem_notification.dart'; +import 'package:mem/core/mem_notification.dart'; import 'package:mem/databases/definition.dart'; import 'package:mem/databases/table_definitions/acts.dart'; import 'package:mem/databases/table_definitions/base.dart'; @@ -25,138 +25,146 @@ import '../helpers.dart'; const _name = 'Repeat by n day habit scenario'; -void main() => group(': $_name', () { - const insertedMemName = "$_name - mem name - inserted"; - const insertedMemRepeatByNDay = 2; - const withoutActMemName = "$insertedMemName - without act"; - const withOldActMemName = "$insertedMemName - with old act"; - const withCurrentActMemName = "$insertedMemName - with current act"; - const insertedRepeatNotificationMessage = - "$_name - inserted - mem notification message - repeat"; - - late final DatabaseAccessor dbA; - - int insertedMemId = 0; - int withoutActMemId = 0; - int withOldActMemId = 0; - int withCurrentActMemId = 0; - - setUpAll(() async { - dbA = await openTestDatabase(databaseDefinition); - - await clearAllTestDatabaseRows(databaseDefinition); +void main() => group( + _name, + () { + const insertedMemName = "$_name - mem name - inserted"; + const insertedMemRepeatByNDay = 2; + const withoutActMemName = "$insertedMemName - without act"; + const withOldActMemName = "$insertedMemName - with old act"; + const withCurrentActMemName = "$insertedMemName - with current act"; + const insertedRepeatNotificationMessage = + "$_name - inserted - mem notification message - repeat"; + + late final DatabaseAccessor dbA; + + int insertedMemId = 0; + int withoutActMemId = 0; + int withOldActMemId = 0; + int withCurrentActMemId = 0; + + setUpAll(() async { + dbA = await openTestDatabase(databaseDefinition); + + await clearAllTestDatabaseRows(databaseDefinition); + + insertedMemId = await dbA.insert( + defTableMems, + { + defColMemsName.name: insertedMemName, + defColCreatedAt.name: zeroDate, + }, + ); + await dbA.insert( + defTableMemNotifications, + { + defFkMemNotificationsMemId.name: insertedMemId, + defColMemNotificationsType.name: MemNotificationType.repeat.name, + defColMemNotificationsTime.name: 1, + defColMemNotificationsMessage.name: "never", + defColCreatedAt.name: zeroDate, + }, + ); + await dbA.insert( + defTableMemNotifications, + { + defFkMemNotificationsMemId.name: insertedMemId, + defColMemNotificationsType.name: + MemNotificationType.repeatByNDay.name, + defColMemNotificationsTime.name: insertedMemRepeatByNDay, + defColMemNotificationsMessage.name: "never", + defColCreatedAt.name: zeroDate, + }, + ); - insertedMemId = await dbA.insert( - defTableMems, - { - defColMemsName.name: insertedMemName, + withoutActMemId = await dbA.insert(defTableMems, { + defColMemsName.name: withoutActMemName, + defColMemsDoneAt.name: null, defColCreatedAt.name: zeroDate, - }, - ); - await dbA.insert( - defTableMemNotifications, - { - defFkMemNotificationsMemId.name: insertedMemId, - defColMemNotificationsType.name: MemNotificationType.repeat.name, - defColMemNotificationsTime.name: 1, - defColMemNotificationsMessage.name: "never", + }); + withOldActMemId = await dbA.insert(defTableMems, { + defColMemsName.name: withOldActMemName, + defColMemsDoneAt.name: null, defColCreatedAt.name: zeroDate, - }, - ); - await dbA.insert(defTableMemNotifications, { - defFkMemNotificationsMemId.name: insertedMemId, - defColMemNotificationsType.name: - MemNotificationType.repeatByNDay.name, - defColMemNotificationsTime.name: insertedMemRepeatByNDay, - defColMemNotificationsMessage.name: "never", - defColCreatedAt.name: zeroDate - }); + }); + withCurrentActMemId = await dbA.insert(defTableMems, { + defColMemsName.name: withCurrentActMemName, + defColMemsDoneAt.name: null, + defColCreatedAt.name: zeroDate, + }); - withoutActMemId = await dbA.insert(defTableMems, { - defColMemsName.name: withoutActMemName, - defColMemsDoneAt.name: null, - defColCreatedAt.name: zeroDate, - }); - withOldActMemId = await dbA.insert(defTableMems, { - defColMemsName.name: withOldActMemName, - defColMemsDoneAt.name: null, - defColCreatedAt.name: zeroDate - }); - withCurrentActMemId = await dbA.insert(defTableMems, { - defColMemsName.name: withCurrentActMemName, - defColMemsDoneAt.name: null, - defColCreatedAt.name: zeroDate - }); + [withoutActMemId, withOldActMemId, withCurrentActMemId] + .forEachIndexed((index, insertedMemId) async { + await dbA.insert(defTableMemNotifications, { + defFkMemNotificationsMemId.name: insertedMemId, + defColMemNotificationsTime.name: 0, + defColMemNotificationsType.name: MemNotificationType.repeat.name, + defColMemNotificationsMessage.name: + insertedRepeatNotificationMessage, + defColCreatedAt.name: zeroDate, + }); + await dbA.insert(defTableMemNotifications, { + defFkMemNotificationsMemId.name: insertedMemId, + defColMemNotificationsTime.name: + index + insertedMemRepeatByNDay + 1, + defColMemNotificationsType.name: + MemNotificationType.repeatByNDay.name, + defColMemNotificationsMessage.name: + "$_name - inserted - mem notification message - after act started", + defColCreatedAt.name: zeroDate, + }); + }); - [withoutActMemId, withOldActMemId, withCurrentActMemId] - .forEachIndexed((index, insertedMemId) async { - await dbA.insert(defTableMemNotifications, { - defFkMemNotificationsMemId.name: insertedMemId, - defColMemNotificationsTime.name: 0, - defColMemNotificationsType.name: MemNotificationType.repeat.name, - defColMemNotificationsMessage.name: - insertedRepeatNotificationMessage, - defColCreatedAt.name: zeroDate + await dbA.insert(defTableActs, { + defFkActsMemId.name: withOldActMemId, + defColActsStart.name: zeroDate.toIso8601String(), + defColActsStartIsAllDay.name: 0, + defColCreatedAt.name: zeroDate, }); - await dbA.insert(defTableMemNotifications, { - defFkMemNotificationsMemId.name: insertedMemId, - defColMemNotificationsTime.name: - index + insertedMemRepeatByNDay + 1, - defColMemNotificationsType.name: - MemNotificationType.repeatByNDay.name, - defColMemNotificationsMessage.name: - "$_name - inserted - mem notification message - after act started", - defColCreatedAt.name: zeroDate + await dbA.insert(defTableActs, { + defFkActsMemId.name: withCurrentActMemId, + defColActsStart.name: DateTime.now().toIso8601String(), + defColActsStartIsAllDay.name: 0, + defColCreatedAt.name: zeroDate, }); }); - await dbA.insert(defTableActs, { - defFkActsMemId.name: withOldActMemId, - defColActsStart.name: zeroDate.toIso8601String(), - defColActsStartIsAllDay.name: 0, - defColCreatedAt.name: zeroDate + setUp(() async { + NotificationClient.resetSingleton(); }); - await dbA.insert(defTableActs, { - defFkActsMemId.name: withCurrentActMemId, - defColActsStart.name: DateTime.now().toIso8601String(), - defColActsStartIsAllDay.name: 0, - defColCreatedAt.name: zeroDate - }); - }); - setUp(() async { - NotificationClient.resetSingleton(); - }); + testWidgets( + 'show saved.', + (widgetTester) async { + widgetTester.ignoreMockMethodCallHandler( + MethodChannelMock.flutterLocalNotifications); - testWidgets( - 'show saved.', - (widgetTester) async { - widgetTester.ignoreMockMethodCallHandler( - MethodChannelMock.flutterLocalNotifications); + const repeatText = "12:00 AM every $insertedMemRepeatByNDay days"; - const repeatText = "12:00 AM every $insertedMemRepeatByNDay days"; + await runApplication(); + await widgetTester.pumpAndSettle(defaultTransitionDuration); - await runApplication(); - await widgetTester.pumpAndSettle(defaultTransitionDuration); + expect(find.text(repeatText), findsOneWidget); + await widgetTester.tap(find.text(insertedMemName)); + await widgetTester.pumpAndSettle(defaultTransitionDuration); - expect(find.text(repeatText), findsOneWidget); - await widgetTester.tap(find.text(insertedMemName)); - await widgetTester.pumpAndSettle(defaultTransitionDuration); + expect( + widgetTester + .widget(find.descendant( + of: find.byKey(keyMemNotificationsView), + matching: find.byType(Text))) + .data, + repeatText); + + await widgetTester.tap( + find.descendant( + of: find.byKey(keyMemNotificationsView), + matching: find.byIcon(Icons.edit), + ), + ); + await widgetTester.pumpAndSettle(defaultTransitionDuration); - expect( - widgetTester - .widget(find.descendant( - of: find.byKey(keyMemNotificationsView), - matching: find.byType(Text))) - .data, - repeatText); - - await widgetTester.tap(find.descendant( - of: find.byKey(keyMemNotificationsView), - matching: find.byIcon(Icons.edit))); - await widgetTester.pumpAndSettle(defaultTransitionDuration); - - expect( + expect( widgetTester .widget( find.descendant( @@ -165,311 +173,349 @@ void main() => group(': $_name', () { ), ) .initialValue, - insertedMemRepeatByNDay.toString()); - }, - ); - - testWidgets('save.', (widgetTester) async { - widgetTester.ignoreMockMethodCallHandler( - MethodChannelMock.flutterLocalNotifications); - - final testStart = DateTime.now(); - var expectedSavedMemId = - ((await dbA.select(defTableMems, orderBy: "id DESC", limit: 1)) - .single[defPkId.name] as int) + - 1; - - int checkPermissionStatusCount = 0; - widgetTester.setMockMethodCallHandler( - MethodChannelMock.permissionHandler, - List.generate( - 3, - (i) => (m) async { - expect(m.method, 'checkPermissionStatus'); - checkPermissionStatusCount++; - return 1; - })); - int alarmServiceStartCount = 0; - int alarmCancelCount = 0; - int alarmPeriodicCount = 0; - widgetTester - .setMockMethodCallHandler(MethodChannelMock.androidAlarmManager, [ - (message) async { - expect(message.method, equals('AlarmService.start')); - expect(message.arguments, orderedEquals([isNotNull])); - alarmServiceStartCount++; - return true; + insertedMemRepeatByNDay.toString(), + ); }, - (message) async { - expect(message.method, equals('Alarm.cancel')); - expect( - message.arguments, - orderedEquals( - [equals(memStartNotificationId(expectedSavedMemId))])); - alarmCancelCount++; - return false; - }, - (message) async { - expect(message.method, equals('Alarm.cancel')); - expect( - message.arguments, - orderedEquals( - [equals(memEndNotificationId(expectedSavedMemId))])); - alarmCancelCount++; - return false; - }, - (message) async { - expect(message.method, equals('Alarm.periodic')); - expect(message.arguments[0], - equals(memRepeatedNotificationId(expectedSavedMemId))); - expect(message.arguments[1], isFalse); - expect(message.arguments[2], isFalse); - expect(message.arguments[3], isFalse); - expect( - DateTime.fromMillisecondsSinceEpoch(message.arguments[4]), - equals(testStart - .copyWith( - hour: defaultStartOfDay.hour, - minute: defaultStartOfDay.minute, - second: 0, - millisecond: 0, - microsecond: 0) - .add(const Duration(days: 1)))); - expect( - message.arguments[5], const Duration(days: 1).inMilliseconds); - expect(message.arguments[6], isFalse); - expect(message.arguments[7], isNotNull); - expect( - message.arguments[8], - equals({ - memIdKey: expectedSavedMemId, - notificationTypeKey: NotificationType.repeat.name - })); - alarmPeriodicCount++; - return false; - }, - ]); - - await runApplication(); - await widgetTester.pumpAndSettle(); - await widgetTester.tap(newMemFabFinder); - await widgetTester.pumpAndSettle(); - const enteringMemName = "$_name: Save - entering - mem name"; - await widgetTester.enterText(find.byKey(keyMemName), enteringMemName); - - final notificationAddFinder = find.descendant( - of: find.byKey(keyMemNotificationsView), - matching: find.byIcon(Icons.notification_add)); - await widgetTester.tap(notificationAddFinder); - await widgetTester.pumpAndSettle(defaultTransitionDuration); - - await widgetTester.tap(timeIconFinder); - await widgetTester.pump(); - await widgetTester.tap(okFinder); - await widgetTester.pump(); - - const enteringNDay = 3; - await widgetTester.enterText( - find.descendant( - of: find.byKey(keyMemRepeatByNDayNotification), - matching: find.byType(TextFormField)), - enteringNDay.toString()); - - await widgetTester.pageBack(); - await widgetTester.pumpAndSettle(); - await widgetTester.tap(find.byKey(keySaveMemFab)); - await widgetTester.pumpAndSettle(); - - final savedMem = (await dbA.select( - defTableMems, - where: "${defColMemsName.name} = ?", - whereArgs: [enteringMemName], - )) - .single; - final savedMemNotification = (await dbA.select(defTableMemNotifications, - where: "${defFkMemNotificationsMemId.name} = ?" - " AND ${defColMemNotificationsType.name} = ?" - " AND ${defColMemNotificationsTime.name} = ?", - whereArgs: [ - savedMem[defPkId.name], - MemNotificationType.repeatByNDay.name, - enteringNDay - ])) - .single; - expect(savedMemNotification[defColMemNotificationsTime.name], - equals(enteringNDay), - reason: 'enteringNDay'); - - if (defaultTargetPlatform == TargetPlatform.android) { - expect(checkPermissionStatusCount, equals(3), - reason: 'checkPermissionStatusCount'); - expect(alarmServiceStartCount, equals(1), - reason: 'alarmServiceStartCount'); - expect(alarmCancelCount, equals(2), reason: 'alarmCancelCount'); - expect(alarmPeriodicCount, equals(1), reason: 'alarmPeriodicCount'); - } else { - expect(checkPermissionStatusCount, equals(3), - reason: 'checkPermissionStatusCount'); - expect(alarmServiceStartCount, equals(0), - reason: 'alarmServiceStartCount'); - expect(alarmCancelCount, equals(0), reason: 'alarmCancelCount'); - expect(alarmPeriodicCount, equals(0), reason: 'alarmPeriodicCount'); - } - - widgetTester.clearAllMockMethodCallHandler(); - }); - - group('notify repeatByNDay', () { - testWidgets('withoutAct.', (widgetTester) async { - int checkPermissionStatusCount = 0; - widgetTester - .setMockMethodCallHandler(MethodChannelMock.permissionHandler, [ - (m) async { - expect(m.method, 'checkPermissionStatus'); - checkPermissionStatusCount++; - return 1; - } - ]); - int initializeCount = 0; - int cancelCount = 0; - int showCount = 0; - widgetTester.setMockMethodCallHandler( - MethodChannelMock.flutterLocalNotifications, [ - (message) async { - expect(message.method, equals('initialize')); - initializeCount++; - return true; - }, - ...[ - memStartNotificationId(withoutActMemId), - memEndNotificationId(withoutActMemId), - pausedActNotificationId(withoutActMemId), - afterActStartedNotificationId(withoutActMemId) - ].map((e) => (message) async { - expect(message.method, equals('cancel')); - expect(message.arguments['id'], equals(e)); - cancelCount++; - return false; - }), - (message) async { - expect(message.method, equals('show')); - expect(message.arguments['id'], - equals(memRepeatedNotificationId(withoutActMemId))); - expect(message.arguments['title'], equals(withoutActMemName)); - expect(message.arguments['body'], - equals(insertedRepeatNotificationMessage)); - expect(message.arguments['payload'], - equals("{\"$memIdKey\":$withoutActMemId}")); - showCount++; - return false; - } - ]); - - final params = { - memIdKey: withoutActMemId, - notificationTypeKey: NotificationType.repeat.name, - }; - - await scheduleCallback(0, params); - - if (defaultTargetPlatform == TargetPlatform.android) { - expect(checkPermissionStatusCount, equals(1), - reason: 'checkPermissionStatusCount'); - expect(initializeCount, equals(1), reason: 'initializeCount'); - expect(cancelCount, equals(4), reason: 'cancelCount'); - expect(showCount, equals(1), reason: 'showCount'); - } else { - expect(checkPermissionStatusCount, equals(1), - reason: 'checkPermissionStatusCount'); - expect(initializeCount, equals(0), reason: 'initializeCount'); - expect(cancelCount, equals(0), reason: 'cancelCount'); - expect(showCount, equals(0), reason: 'showCount'); - } - - widgetTester.clearAllMockMethodCallHandler(); - }); + ); - testWidgets('withOldAct', (widgetTester) async { - int checkPermissionStatusCount = 0; - widgetTester - .setMockMethodCallHandler(MethodChannelMock.permissionHandler, [ - (m) async { - expect(m.method, 'checkPermissionStatus'); - checkPermissionStatusCount++; - return 1; - } - ]); - int initializeCount = 0; - int cancelCount = 0; - int showCount = 0; - widgetTester.setMockMethodCallHandler( - MethodChannelMock.flutterLocalNotifications, [ - (message) async { - expect(message.method, equals('initialize')); - initializeCount++; - return true; - }, - ...[ - memStartNotificationId(withOldActMemId), - memEndNotificationId(withOldActMemId), - pausedActNotificationId(withOldActMemId), - afterActStartedNotificationId(withOldActMemId), - ].map((e) => (message) async { - expect(message.method, equals('cancel')); - expect(message.arguments['id'], equals(e)); - cancelCount++; - return false; - }), - (message) async { - expect(message.method, equals('show')); - expect(message.arguments['id'], - equals(memRepeatedNotificationId(withOldActMemId))); - expect(message.arguments['title'], equals(withOldActMemName)); - expect(message.arguments['body'], - equals(insertedRepeatNotificationMessage)); - expect(message.arguments['payload'], - equals("{\"$memIdKey\":$withOldActMemId}")); - showCount++; - return false; + testWidgets( + 'save.', + (widgetTester) async { + widgetTester.ignoreMockMethodCallHandler( + MethodChannelMock.flutterLocalNotifications); + + final testStart = DateTime.now(); + var expectedSavedMemId = + ((await dbA.select(defTableMems, orderBy: "id DESC", limit: 1)) + .single[defPkId.name] as int) + + 1; + + int checkPermissionStatusCount = 0; + widgetTester.setMockMethodCallHandler( + MethodChannelMock.permissionHandler, + List.generate( + 3, + (i) => (m) async { + expect(m.method, 'checkPermissionStatus'); + checkPermissionStatusCount++; + return 1; + })); + int alarmServiceStartCount = 0; + int alarmCancelCount = 0; + int alarmPeriodicCount = 0; + widgetTester.setMockMethodCallHandler( + MethodChannelMock.androidAlarmManager, [ + (message) async { + expect(message.method, equals('AlarmService.start')); + expect( + message.arguments, + orderedEquals([ + isNotNull, + ])); + alarmServiceStartCount++; + return true; + }, + (message) async { + expect(message.method, equals('Alarm.cancel')); + expect( + message.arguments, + orderedEquals( + [equals(memStartNotificationId(expectedSavedMemId))])); + alarmCancelCount++; + return false; + }, + (message) async { + expect(message.method, equals('Alarm.cancel')); + expect( + message.arguments, + orderedEquals( + [equals(memEndNotificationId(expectedSavedMemId))])); + alarmCancelCount++; + return false; + }, + (message) async { + expect(message.method, equals('Alarm.periodic')); + expect(message.arguments[0], + equals(memRepeatedNotificationId(expectedSavedMemId))); + expect(message.arguments[1], isFalse); + expect(message.arguments[2], isFalse); + expect(message.arguments[3], isFalse); + expect( + message.arguments[4], + equals(testStart + .copyWith( + hour: defaultStartOfDay.hour, + minute: defaultStartOfDay.minute, + second: 0, + millisecond: 0, + microsecond: 0) + .add(const Duration(days: 1)) + .millisecondsSinceEpoch)); + expect(message.arguments[5], + const Duration(days: 1).inMilliseconds); + expect(message.arguments[6], isFalse); + expect(message.arguments[7], isNotNull); + expect( + message.arguments[8], + equals({ + memIdKey: expectedSavedMemId, + notificationTypeKey: NotificationType.repeat.name + })); + alarmPeriodicCount++; + return false; + }, + ]); + + await runApplication(); + await widgetTester.pumpAndSettle(); + await widgetTester.tap(newMemFabFinder); + await widgetTester.pumpAndSettle(); + const enteringMemName = "$_name: Save - entering - mem name"; + await widgetTester.enterText( + find.byKey(keyMemName), enteringMemName); + + final notificationAddFinder = find.descendant( + of: find.byKey(keyMemNotificationsView), + matching: find.byIcon(Icons.notification_add)); + await widgetTester.tap(notificationAddFinder); + await widgetTester.pumpAndSettle(defaultTransitionDuration); + + await widgetTester.tap(timeIconFinder); + await widgetTester.pump(); + await widgetTester.tap(okFinder); + await widgetTester.pump(); + + const enteringNDay = 3; + await widgetTester.enterText( + find.descendant( + of: find.byKey(keyMemRepeatByNDayNotification), + matching: find.byType(TextFormField)), + enteringNDay.toString(), + ); + + await widgetTester.pageBack(); + await widgetTester.pumpAndSettle(); + await widgetTester.tap(find.byKey(keySaveMemFab)); + await widgetTester.pumpAndSettle(); + + final savedMem = (await dbA.select( + defTableMems, + where: "${defColMemsName.name} = ?", + whereArgs: [enteringMemName], + )) + .single; + final savedMemNotification = (await dbA.select( + defTableMemNotifications, + where: "${defFkMemNotificationsMemId.name} = ?" + " AND ${defColMemNotificationsType.name} = ?" + " AND ${defColMemNotificationsTime.name} = ?", + whereArgs: [ + savedMem[defPkId.name], + MemNotificationType.repeatByNDay.name, + enteringNDay, + ], + )) + .single; + expect(savedMemNotification[defColMemNotificationsTime.name], + equals(enteringNDay), + reason: 'enteringNDay'); + + if (defaultTargetPlatform == TargetPlatform.android) { + expect(checkPermissionStatusCount, equals(3), + reason: 'checkPermissionStatusCount'); + expect(alarmServiceStartCount, equals(1), + reason: 'alarmServiceStartCount'); + expect(alarmCancelCount, equals(2), reason: 'alarmCancelCount'); + expect(alarmPeriodicCount, equals(1), + reason: 'alarmPeriodicCount'); + } else { + expect(checkPermissionStatusCount, equals(3), + reason: 'checkPermissionStatusCount'); + expect(alarmServiceStartCount, equals(0), + reason: 'alarmServiceStartCount'); + expect(alarmCancelCount, equals(0), reason: 'alarmCancelCount'); + expect(alarmPeriodicCount, equals(0), + reason: 'alarmPeriodicCount'); } - ]); - - final params = { - memIdKey: withOldActMemId, - notificationTypeKey: NotificationType.repeat.name - }; - - await scheduleCallback(0, params); - - if (defaultTargetPlatform == TargetPlatform.android) { - expect(checkPermissionStatusCount, equals(1), - reason: 'checkPermissionStatusCount'); - expect(initializeCount, equals(1), reason: 'initializeCount'); - expect(cancelCount, equals(4), reason: 'cancelCount'); - expect(showCount, equals(1), reason: 'showCount'); - } else { - expect(checkPermissionStatusCount, equals(1), - reason: 'checkPermissionStatusCount'); - expect(initializeCount, equals(0), reason: 'initializeCount'); - expect(cancelCount, equals(0), reason: 'cancelCount'); - expect(showCount, equals(0), reason: 'showCount'); - } - - widgetTester.clearAllMockMethodCallHandler(); - }); - testWidgets('withCurrentAct', (widgetTester) async { - widgetTester.setMockMethodCallHandler( - MethodChannelMock.flutterLocalNotifications, []); - - final params = { - memIdKey: withCurrentActMemId, - notificationTypeKey: NotificationType.repeat.name - }; - - await scheduleCallback(0, params); + widgetTester.clearAllMockMethodCallHandler(); + }, + ); - widgetTester.clearAllMockMethodCallHandler(); - }); - }); - }); + group( + 'notify repeatByNDay', + () { + testWidgets( + 'withoutAct.', + (widgetTester) async { + int checkPermissionStatusCount = 0; + widgetTester.setMockMethodCallHandler( + MethodChannelMock.permissionHandler, [ + (m) async { + expect(m.method, 'checkPermissionStatus'); + checkPermissionStatusCount++; + return 1; + } + ]); + int initializeCount = 0; + int cancelCount = 0; + int showCount = 0; + widgetTester.setMockMethodCallHandler( + MethodChannelMock.flutterLocalNotifications, + [ + (message) async { + expect(message.method, equals('initialize')); + initializeCount++; + return true; + }, + ...[ + memStartNotificationId(withoutActMemId), + memEndNotificationId(withoutActMemId), + pausedActNotificationId(withoutActMemId), + afterActStartedNotificationId(withoutActMemId), + ].map( + (e) => (message) async { + expect(message.method, equals('cancel')); + expect(message.arguments['id'], equals(e)); + cancelCount++; + return false; + }, + ), + (message) async { + expect(message.method, equals('show')); + expect(message.arguments['id'], + equals(memRepeatedNotificationId(withoutActMemId))); + expect(message.arguments['title'], + equals(withoutActMemName)); + expect(message.arguments['body'], + equals(insertedRepeatNotificationMessage)); + expect(message.arguments['payload'], + equals("{\"$memIdKey\":$withoutActMemId}")); + showCount++; + return false; + }, + ], + ); + + final params = { + memIdKey: withoutActMemId, + notificationTypeKey: NotificationType.repeat.name, + }; + + await scheduleCallback(0, params); + + if (defaultTargetPlatform == TargetPlatform.android) { + expect(checkPermissionStatusCount, equals(1), + reason: 'checkPermissionStatusCount'); + expect(initializeCount, equals(1), reason: 'initializeCount'); + expect(cancelCount, equals(4), reason: 'cancelCount'); + expect(showCount, equals(1), reason: 'showCount'); + } else { + expect(checkPermissionStatusCount, equals(1), + reason: 'checkPermissionStatusCount'); + expect(initializeCount, equals(0), reason: 'initializeCount'); + expect(cancelCount, equals(0), reason: 'cancelCount'); + expect(showCount, equals(0), reason: 'showCount'); + } + + widgetTester.clearAllMockMethodCallHandler(); + }, + ); + + testWidgets( + 'withOldAct', + (widgetTester) async { + int checkPermissionStatusCount = 0; + widgetTester.setMockMethodCallHandler( + MethodChannelMock.permissionHandler, [ + (m) async { + expect(m.method, 'checkPermissionStatus'); + checkPermissionStatusCount++; + return 1; + } + ]); + int initializeCount = 0; + int cancelCount = 0; + int showCount = 0; + widgetTester.setMockMethodCallHandler( + MethodChannelMock.flutterLocalNotifications, + [ + (message) async { + expect(message.method, equals('initialize')); + initializeCount++; + return true; + }, + ...[ + memStartNotificationId(withOldActMemId), + memEndNotificationId(withOldActMemId), + pausedActNotificationId(withOldActMemId), + afterActStartedNotificationId(withOldActMemId), + ].map( + (e) => (message) async { + expect(message.method, equals('cancel')); + expect(message.arguments['id'], equals(e)); + cancelCount++; + return false; + }, + ), + (message) async { + expect(message.method, equals('show')); + expect(message.arguments['id'], + equals(memRepeatedNotificationId(withOldActMemId))); + expect(message.arguments['title'], + equals(withOldActMemName)); + expect(message.arguments['body'], + equals(insertedRepeatNotificationMessage)); + expect(message.arguments['payload'], + equals("{\"$memIdKey\":$withOldActMemId}")); + showCount++; + return false; + }, + ], + ); + + final params = { + memIdKey: withOldActMemId, + notificationTypeKey: NotificationType.repeat.name, + }; + + await scheduleCallback(0, params); + + if (defaultTargetPlatform == TargetPlatform.android) { + expect(checkPermissionStatusCount, equals(1), + reason: 'checkPermissionStatusCount'); + expect(initializeCount, equals(1), reason: 'initializeCount'); + expect(cancelCount, equals(4), reason: 'cancelCount'); + expect(showCount, equals(1), reason: 'showCount'); + } else { + expect(checkPermissionStatusCount, equals(1), + reason: 'checkPermissionStatusCount'); + expect(initializeCount, equals(0), reason: 'initializeCount'); + expect(cancelCount, equals(0), reason: 'cancelCount'); + expect(showCount, equals(0), reason: 'showCount'); + } + + widgetTester.clearAllMockMethodCallHandler(); + }, + ); + + testWidgets( + 'withCurrentAct', + (widgetTester) async { + widgetTester.setMockMethodCallHandler( + MethodChannelMock.flutterLocalNotifications, []); + + final params = { + memIdKey: withCurrentActMemId, + notificationTypeKey: NotificationType.repeat.name, + }; + + await scheduleCallback(0, params); + + widgetTester.clearAllMockMethodCallHandler(); + }, + ); + }, + ); + }, + ); diff --git a/integration_test/scenarios/habit/repeated_habit_scenario.dart b/integration_test/scenarios/habit/repeated_habit_scenario.dart index 2994f3f2d..5c6647ad4 100644 --- a/integration_test/scenarios/habit/repeated_habit_scenario.dart +++ b/integration_test/scenarios/habit/repeated_habit_scenario.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mem/components/date_and_time/time_of_day_view.dart'; import 'package:mem/components/mem/mem_name.dart'; -import 'package:mem/mems/mem_notification.dart'; +import 'package:mem/core/mem_notification.dart'; import 'package:mem/databases/definition.dart'; import 'package:mem/databases/table_definitions/acts.dart'; import 'package:mem/databases/table_definitions/base.dart'; @@ -19,237 +19,279 @@ import '../helpers.dart'; const _name = 'Repeated habit scenario'; -void main() => group(_name, () { - late final DatabaseAccessor dbA; - setUpAll(() async { - dbA = await openTestDatabase(databaseDefinition); - }); - - const baseMemName = "$_name - mem - name"; - const insertedMemName = "$baseMemName - inserted"; - const insertedMemName2 = "$insertedMemName - 2"; - - final now = DateTime.now(); - - late int insertedMemId; - - setUp(() async { - await clearAllTestDatabaseRows(databaseDefinition); - - insertedMemId = await dbA.insert(defTableMems, { - defColMemsName.name: insertedMemName, - defColCreatedAt.name: zeroDate - }); - await dbA.insert(defTableMemNotifications, { - defFkMemNotificationsMemId.name: insertedMemId, - defColMemNotificationsType.name: MemNotificationType.repeat.name, - defColMemNotificationsTime.name: 2, - defColMemNotificationsMessage.name: "never", - defColCreatedAt.name: zeroDate - }); - await dbA.insert(defTableMemNotifications, { - defFkMemNotificationsMemId.name: insertedMemId, - defColMemNotificationsType.name: - MemNotificationType.repeatByNDay.name, - defColMemNotificationsTime.name: 1, - defColMemNotificationsMessage.name: "never", - defColCreatedAt.name: zeroDate +void main() => group( + _name, + () { + late final DatabaseAccessor dbA; + setUpAll(() async { + dbA = await openTestDatabase(databaseDefinition); }); - await dbA.insert(defTableActs, { - defFkActsMemId.name: insertedMemId, - defColActsStart.name: now.toIso8601String(), - defColActsStartIsAllDay.name: 0, - defColActsEnd.name: now.toIso8601String(), - defColActsEndIsAllDay.name: 0, - defColCreatedAt.name: zeroDate - }); - await dbA.insert(defTableMems, { - defColMemsName.name: insertedMemName2, - defColMemsStartOn.name: now.toIso8601String(), - defColCreatedAt.name: zeroDate - }); - }); - - group('show', () { - testWidgets('on new.', (widgetTester) async { - await runApplication(); - await widgetTester.pumpAndSettle(); - await widgetTester.tap(newMemFabFinder); - await widgetTester.pumpAndSettle(defaultTransitionDuration); - - expect( - widgetTester - .widget(find.descendant( - of: find.byKey(keyMemNotificationsView), - matching: find.byType(Text))) - .data, - l10n.noNotifications); - final notificationAddFinder = find.descendant( - of: find.byKey(keyMemNotificationsView), - matching: find.byIcon(Icons.notification_add)); - expect(notificationAddFinder, findsOneWidget); - - await widgetTester.dragUntilVisible(notificationAddFinder, - find.byType(SingleChildScrollView), const Offset(0, 50)); - await widgetTester.tap(notificationAddFinder); - await widgetTester.pumpAndSettle(defaultTransitionDuration); - - expect( - widgetTester - .widget(find.descendant( - of: find.byKey(keyMemRepeatedNotification), - matching: find.byType(TimeOfDayTextFormField))) - .timeOfDay, - defaultStartOfDay); - expect( - find.descendant( - of: find.byKey(keyMemRepeatedNotification), - matching: find.byIcon(Icons.clear)), - findsNothing); - }); - - testWidgets( - 'saved.', - (widgetTester) async { - const repeatText = "12:00 AM every day"; - - await runApplication(); - await widgetTester.pumpAndSettle(); - expect(find.text(repeatText), findsOneWidget); + const baseMemName = "$_name - mem - name"; + const insertedMemName = "$baseMemName - inserted"; + const insertedMemName2 = "$insertedMemName - 2"; - await widgetTester.tap(find.text(insertedMemName)); - await widgetTester.pumpAndSettle(defaultTransitionDuration); + final now = DateTime.now(); - expect( - widgetTester - .widget(find.descendant( - of: find.byKey(keyMemNotificationsView), - matching: find.byType(Text))) - .data, - repeatText); + late int insertedMemId; - await widgetTester.tap(find.descendant( - of: find.byKey(keyMemNotificationsView), - matching: find.byIcon(Icons.edit))); - await widgetTester.pumpAndSettle(defaultTransitionDuration); + setUp(() async { + await clearAllTestDatabaseRows(databaseDefinition); + + insertedMemId = await dbA.insert( + defTableMems, + { + defColMemsName.name: insertedMemName, + defColCreatedAt.name: zeroDate, + }, + ); + await dbA.insert( + defTableMemNotifications, + { + defFkMemNotificationsMemId.name: insertedMemId, + defColMemNotificationsType.name: MemNotificationType.repeat.name, + defColMemNotificationsTime.name: 2, + defColMemNotificationsMessage.name: "never", + defColCreatedAt.name: zeroDate, + }, + ); + await dbA.insert( + defTableMemNotifications, + { + defFkMemNotificationsMemId.name: insertedMemId, + defColMemNotificationsType.name: + MemNotificationType.repeatByNDay.name, + defColMemNotificationsTime.name: 1, + defColMemNotificationsMessage.name: "never", + defColCreatedAt.name: zeroDate, + }, + ); + await dbA.insert( + defTableActs, + { + defFkActsMemId.name: insertedMemId, + defColActsStart.name: now.toIso8601String(), + defColActsStartIsAllDay.name: 0, + defColActsEnd.name: now.toIso8601String(), + defColActsEndIsAllDay.name: 0, + defColCreatedAt.name: zeroDate, + }, + ); + await dbA.insert( + defTableMems, + { + defColMemsName.name: insertedMemName2, + defColMemsStartOn.name: now.toIso8601String(), + defColCreatedAt.name: zeroDate, + }, + ); + }); - expect( - widgetTester - .widget(find.descendant( + group( + 'show', + () { + testWidgets( + 'on new.', + (widgetTester) async { + await runApplication(); + await widgetTester.pumpAndSettle(); + await widgetTester.tap(newMemFabFinder); + await widgetTester.pumpAndSettle(defaultTransitionDuration); + + expect( + widgetTester + .widget(find.descendant( + of: find.byKey(keyMemNotificationsView), + matching: find.byType(Text))) + .data, + l10n.noNotifications); + final notificationAddFinder = find.descendant( + of: find.byKey(keyMemNotificationsView), + matching: find.byIcon(Icons.notification_add)); + expect(notificationAddFinder, findsOneWidget); + + await widgetTester.dragUntilVisible(notificationAddFinder, + find.byType(SingleChildScrollView), const Offset(0, 50)); + await widgetTester.tap(notificationAddFinder); + await widgetTester.pumpAndSettle(defaultTransitionDuration); + + expect( + widgetTester + .widget(find.descendant( + of: find.byKey(keyMemRepeatedNotification), + matching: find.byType(TimeOfDayTextFormField))) + .timeOfDay, + defaultStartOfDay); + expect( + find.descendant( of: find.byKey(keyMemRepeatedNotification), - matching: find.byType(TimeOfDayTextFormField))) - .timeOfDay, - defaultStartOfDay); - expect( - find.descendant( - of: find.byKey(keyMemRepeatedNotification), - matching: find.byIcon(Icons.clear)), - findsOneWidget); + matching: find.byIcon(Icons.clear)), + findsNothing); + }, + ); + + testWidgets( + 'saved.', + (widgetTester) async { + const repeatText = "12:00 AM every day"; + + await runApplication(); + await widgetTester.pumpAndSettle(); + + expect(find.text(repeatText), findsOneWidget); + + await widgetTester.tap(find.text(insertedMemName)); + await widgetTester.pumpAndSettle(defaultTransitionDuration); + + expect( + widgetTester + .widget(find.descendant( + of: find.byKey(keyMemNotificationsView), + matching: find.byType(Text))) + .data, + repeatText); + + await widgetTester.tap(find.descendant( + of: find.byKey(keyMemNotificationsView), + matching: find.byIcon(Icons.edit))); + await widgetTester.pumpAndSettle(defaultTransitionDuration); + + expect( + widgetTester + .widget( + find.descendant( + of: find.byKey(keyMemRepeatedNotification), + matching: find.byType(TimeOfDayTextFormField)), + ) + .timeOfDay, + defaultStartOfDay); + expect( + find.descendant( + of: find.byKey(keyMemRepeatedNotification), + matching: find.byIcon(Icons.clear)), + findsOneWidget); + }, + ); }, ); - }); - group('save', () { - setUp(() async { - await dbA.insert(defTableActs, { - defFkActsMemId.name: insertedMemId, - defColActsStart.name: zeroDate, - defColActsStartIsAllDay.name: 0, - defColCreatedAt.name: zeroDate + group('save', () { + setUp(() async { + await dbA.insert(defTableActs, { + defFkActsMemId.name: insertedMemId, + defColActsStart.name: zeroDate, + defColActsStartIsAllDay.name: 0, + defColCreatedAt.name: zeroDate + }); }); - }); - - testWidgets('create.', (widgetTester) async { - widgetTester - .ignoreMockMethodCallHandler(MethodChannelMock.permissionHandler); - - await runApplication(); - await widgetTester.pumpAndSettle(); - await widgetTester.tap(newMemFabFinder); - await widgetTester.pumpAndSettle(); - const enteringMemName = "$baseMemName - entering"; - await widgetTester.enterText(find.byKey(keyMemName), enteringMemName); - - final notificationAddFinder = find.descendant( - of: find.byKey(keyMemNotificationsView), - matching: find.byIcon(Icons.notification_add)); - await widgetTester.tap(notificationAddFinder); - await widgetTester.pumpAndSettle(defaultTransitionDuration); - - await widgetTester.tap(timeIconFinder); - await widgetTester.pump(); - - await widgetTester.tap(okFinder); - await widgetTester.pump(); - - await widgetTester.pageBack(); - await widgetTester.pumpAndSettle(); - await widgetTester.tap(find.byKey(keySaveMemFab)); - await widgetTester.pumpAndSettle(); - - final savedMem = (await dbA.select(defTableMems, - where: "${defColMemsName.name} = ?", - whereArgs: [enteringMemName])) - .single; - final savedMemNotifications = (await dbA.select( - defTableMemNotifications, - where: "${defFkMemNotificationsMemId.name} = ?", - whereArgs: [savedMem[defPkId.name]])); - final repeat = savedMemNotifications.singleWhere((e) => - e[defColMemNotificationsType.name] == - MemNotificationType.repeat.name); - expect(repeat[defColMemNotificationsTime.name], 0); - }); - testWidgets(": update.", (widgetTester) async { - widgetTester - .ignoreMockMethodCallHandler(MethodChannelMock.permissionHandler); - - await runApplication(); - await widgetTester.pumpAndSettle(); - await widgetTester.tap(find.text(insertedMemName)); - await widgetTester.pumpAndSettle(); - - final notificationAddFinder = find.descendant( - of: find.byKey(keyMemNotificationsView), - matching: find.byIcon(Icons.edit)); - await widgetTester.dragUntilVisible(notificationAddFinder, - find.byType(SingleChildScrollView), const Offset(0, 50)); - await widgetTester.tap(notificationAddFinder); - await widgetTester.pumpAndSettle(defaultTransitionDuration); - - await widgetTester.tap(find.descendant( - of: find.byKey(keyMemRepeatedNotification), - matching: find.byIcon(Icons.clear))); - await widgetTester.pump(); - - await widgetTester.tap(timeIconFinder); - await widgetTester.pump(); - await widgetTester.tap(okFinder); - await widgetTester.pump(); - - await widgetTester.pageBack(); - await widgetTester.pumpAndSettle(); - await widgetTester.tap(find.byKey(keySaveMemFab)); - await widgetTester.pump(waitSideEffectDuration); - - final savedMemId = (await dbA.select(defTableMems, - where: "${defColMemsName.name} = ?", - whereArgs: [insertedMemName])) - .single[defPkId.name]; - final savedMemNotifications = (await dbA.select( - defTableMemNotifications, - where: "${defFkMemNotificationsMemId.name} = ?", - whereArgs: [savedMemId], - orderBy: "id ASC")); - expect(savedMemNotifications, hasLength(2)); - expect(savedMemNotifications[0][defColMemNotificationsType.name], - MemNotificationType.repeat.name); - expect(savedMemNotifications[0][defColMemNotificationsTime.name], 0); - expect(savedMemNotifications[1][defColMemNotificationsType.name], - MemNotificationType.repeatByNDay.name); - expect(savedMemNotifications[1][defColMemNotificationsTime.name], 1); + testWidgets( + 'create.', + (widgetTester) async { + widgetTester.ignoreMockMethodCallHandler( + MethodChannelMock.permissionHandler); + + await runApplication(); + await widgetTester.pumpAndSettle(); + await widgetTester.tap(newMemFabFinder); + await widgetTester.pumpAndSettle(); + const enteringMemName = "$baseMemName - entering"; + await widgetTester.enterText( + find.byKey(keyMemName), enteringMemName); + + final notificationAddFinder = find.descendant( + of: find.byKey(keyMemNotificationsView), + matching: find.byIcon(Icons.notification_add)); + await widgetTester.tap(notificationAddFinder); + await widgetTester.pumpAndSettle(defaultTransitionDuration); + + await widgetTester.tap(timeIconFinder); + await widgetTester.pump(); + + await widgetTester.tap(okFinder); + await widgetTester.pump(); + + await widgetTester.pageBack(); + await widgetTester.pumpAndSettle(); + await widgetTester.tap(find.byKey(keySaveMemFab)); + await widgetTester.pumpAndSettle(); + + final savedMem = (await dbA.select(defTableMems, + where: "${defColMemsName.name} = ?", + whereArgs: [enteringMemName])) + .single; + final savedMemNotifications = (await dbA.select( + defTableMemNotifications, + where: "${defFkMemNotificationsMemId.name} = ?", + whereArgs: [savedMem[defPkId.name]])); + final repeat = savedMemNotifications.singleWhere((e) => + e[defColMemNotificationsType.name] == + MemNotificationType.repeat.name); + expect(repeat[defColMemNotificationsTime.name], 0); + }, + ); + + testWidgets( + ": update.", + (widgetTester) async { + await runApplication(); + await widgetTester.pumpAndSettle(); + await widgetTester.tap(find.text(insertedMemName)); + await widgetTester.pumpAndSettle(); + + final notificationAddFinder = find.descendant( + of: find.byKey(keyMemNotificationsView), + matching: find.byIcon(Icons.edit), + ); + await widgetTester.dragUntilVisible( + notificationAddFinder, + find.byType(SingleChildScrollView), + const Offset(0, 50), + ); + await widgetTester.tap( + notificationAddFinder, + ); + await widgetTester.pumpAndSettle(defaultTransitionDuration); + + await widgetTester.tap( + find.descendant( + of: find.byKey(keyMemRepeatedNotification), + matching: find.byIcon(Icons.clear), + ), + ); + await widgetTester.pump(); + + await widgetTester.tap(timeIconFinder); + await widgetTester.pump(); + await widgetTester.tap(okFinder); + await widgetTester.pump(); + + await widgetTester.pageBack(); + await widgetTester.pumpAndSettle(); + await widgetTester.tap(find.byKey(keySaveMemFab)); + await widgetTester.pump(waitSideEffectDuration); + + final savedMem = (await dbA.select( + defTableMems, + where: "${defColMemsName.name} = ?", + whereArgs: [insertedMemName], + )) + .single; + final savedMemNotification = (await dbA.select( + defTableMemNotifications, + where: "${defFkMemNotificationsMemId.name} = ?", + whereArgs: [savedMem[defPkId.name]], + )); + expect( + savedMemNotification[0][defColMemNotificationsTime.name], + 0, + ); + expect( + savedMemNotification[1][defColMemNotificationsTime.name], + 1, + ); + }, + ); }); - }); - }); + }, + ); diff --git a/integration_test/scenarios/helpers.dart b/integration_test/scenarios/helpers.dart index c40d226b1..1732d0c59 100644 --- a/integration_test/scenarios/helpers.dart +++ b/integration_test/scenarios/helpers.dart @@ -76,7 +76,6 @@ const waitShowSoftwareKeyboardDuration = Duration(milliseconds: 400); const waitSideEffectDuration = Duration(milliseconds: 700); final zeroDate = DateTime(0); const datePlaceHolder = "M/d/y"; -const maxRetryCount = 5; // Value builder int randomInt([int max = 42949671]) => Random().nextInt(max); diff --git a/integration_test/scenarios/memo/detail_scenarios.dart b/integration_test/scenarios/memo/detail_scenarios.dart index e2653078f..58cf4dab1 100644 --- a/integration_test/scenarios/memo/detail_scenarios.dart +++ b/integration_test/scenarios/memo/detail_scenarios.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; import 'package:mem/components/mem/mem_name.dart'; -import 'package:mem/mems/mem_item.dart'; +import 'package:mem/core/mem_item.dart'; import 'package:mem/databases/definition.dart'; import 'package:mem/databases/table_definitions/base.dart'; import 'package:mem/databases/table_definitions/mem_items.dart'; @@ -91,11 +91,6 @@ void main() => group( testWidgets( ": create.", (widgetTester) async { - widgetTester.ignoreMockMethodCallHandler( - MethodChannelMock.permissionHandler); - widgetTester.ignoreMockMethodCallHandler( - MethodChannelMock.flutterLocalNotifications); - await runApplication(); await widgetTester.pumpAndSettle(); await widgetTester.tap(newMemFabFinder); @@ -108,22 +103,27 @@ void main() => group( enteringMemName, ); await widgetTester.enterText( - find.byKey(keyMemMemo), enteringMemMemo); + find.byKey(keyMemMemo), + enteringMemMemo, + ); await widgetTester.tap(find.byKey(keySaveMemFab)); await widgetTester.pump(); - expect(find.text(l10n.saveMemSuccessMessage(enteringMemName)), - findsOneWidget); + expect( + find.text(l10n.saveMemSuccessMessage(enteringMemName)), + findsOneWidget, + ); - final getCreatedMem = Equals(defColMemsName, enteringMemName); + final getCreatedMem = + Equals(defColMemsName.name, enteringMemName); final mems = await dbA.select(defTableMems, where: getCreatedMem.where(), whereArgs: getCreatedMem.whereArgs()); expect(mems.length, 1); final getCreatedMemItem = And([ - Equals(defFkMemItemsMemId, mems[0][defPkId.name]), - Equals(defColMemItemsType, MemItemType.memo.name), - Equals(defColMemItemsValue, enteringMemMemo) + Equals(defFkMemItemsMemId.name, mems[0][defPkId.name]), + Equals(defColMemItemsType.name, MemItemType.memo.name), + Equals(defColMemItemsValue.name, enteringMemMemo) ]); final memItems = await dbA.select(defTableMemItems, where: getCreatedMemItem.where(), @@ -140,29 +140,37 @@ void main() => group( testWidgets( ': update.', (widgetTester) async { - widgetTester.ignoreMockMethodCallHandler( - MethodChannelMock.permissionHandler); - widgetTester.ignoreMockMethodCallHandler( - MethodChannelMock.flutterLocalNotifications); - await runApplication(); await widgetTester.pumpAndSettle(); + await widgetTester.tap(find.text(insertedMemName)); await widgetTester.pumpAndSettle(); await widgetTester.tap(memNameOnDetailPageFinder); await widgetTester.pump(waitShowSoftwareKeyboardDuration); + const enteringMemNameText = - "$_scenarioName: Save: Update - mem name - entering"; + '$_scenarioName: Save: Update - mem name - entering'; await widgetTester.enterText( - memNameOnDetailPageFinder, enteringMemNameText); + memNameOnDetailPageFinder, + enteringMemNameText, + ); await widgetTester.pumpAndSettle(); expect(find.text(enteringMemNameText), findsOneWidget); await widgetTester.tap(saveMemFabFinder); await widgetTester.pumpAndSettle(); - const saveSuccessText = "Save success. $enteringMemNameText"; - expect(find.text(saveSuccessText), findsOneWidget); + const saveSuccessText = 'Save success. $enteringMemNameText'; + expect( + find.text(saveSuccessText), + findsOneWidget, + ); + + await widgetTester.pumpAndSettle(defaultDismissDuration); + expect( + find.text(saveSuccessText), + findsNothing, + ); await widgetTester.pageBack(); await widgetTester.pumpAndSettle(); @@ -176,49 +184,58 @@ void main() => group( }, ); - testWidgets(': twice on create.', retry: maxRetryCount, - (widgetTester) async { - widgetTester.ignoreMockMethodCallHandler( - MethodChannelMock.permissionHandler); - widgetTester.ignoreMockMethodCallHandler( - MethodChannelMock.flutterLocalNotifications); - - await runApplication(); - await widgetTester.pumpAndSettle(); - await widgetTester.tap(newMemFabFinder); - await widgetTester.pumpAndSettle(); - const enteringMemName = - "$saveMemName: twice on create - entering"; - const enteringMemMemo = - "$saveMemMemo: twice on create - entering"; - await widgetTester.enterText( - find.byKey(keyMemName), enteringMemName); - await widgetTester.enterText( - find.byKey(keyMemMemo), enteringMemMemo); - await widgetTester.tap(find.byKey(keySaveMemFab)); - await widgetTester.pumpAndSettle(waitSideEffectDuration); - - const enteringMemMemo2 = "$enteringMemMemo - 2"; - await widgetTester.enterText( - find.byKey(keyMemMemo), enteringMemMemo2); - await widgetTester.tap(find.byKey(keySaveMemFab)); - await widgetTester.pumpAndSettle(waitSideEffectDuration); - - final getCreatedMem = Equals(defColMemsName, enteringMemName); - final mems = await dbA.select(defTableMems, + testWidgets( + 'twice on create.', + retry: 3, + (widgetTester) async { + await runApplication(); + await widgetTester.pumpAndSettle(); + await widgetTester.tap(newMemFabFinder); + await widgetTester.pumpAndSettle(); + const enteringMemName = + "$saveMemName: twice on create - entering"; + const enteringMemMemo = + "$saveMemMemo: twice on create - entering"; + await widgetTester.enterText( + find.byKey(keyMemName), + enteringMemName, + ); + await widgetTester.enterText( + find.byKey(keyMemMemo), + enteringMemMemo, + ); + await widgetTester.tap(find.byKey(keySaveMemFab)); + await widgetTester.pumpAndSettle(waitSideEffectDuration); + + const enteringMemMemo2 = "$enteringMemMemo - 2"; + await widgetTester.enterText( + find.byKey(keyMemMemo), + enteringMemMemo2, + ); + await widgetTester.tap(find.byKey(keySaveMemFab)); + await widgetTester.pumpAndSettle(waitSideEffectDuration); + + final getCreatedMem = + Equals(defColMemsName.name, enteringMemName); + final mems = await dbA.select( + defTableMems, where: getCreatedMem.where(), - whereArgs: getCreatedMem.whereArgs()); - expect(mems.length, 1); - final getCreatedMemItem = And([ - Equals(defFkMemItemsMemId, mems[0][defPkId.name]), - Equals(defColMemItemsType, MemItemType.memo.name) - ]); - final memItems = await dbA.select(defTableMemItems, - where: getCreatedMemItem.where(), - whereArgs: getCreatedMemItem.whereArgs()); - expect( - memItems.single[defColMemItemsValue.name], enteringMemMemo2); - }); + whereArgs: getCreatedMem.whereArgs(), + ); + expect(mems.length, 1); + final getCreatedMemItem = And([ + Equals(defFkMemItemsMemId.name, mems[0][defPkId.name]), + Equals(defColMemItemsType.name, MemItemType.memo.name), + ]); + final memItems = await dbA.select(defTableMemItems, + where: getCreatedMemItem.where(), + whereArgs: getCreatedMemItem.whereArgs()); + expect( + memItems.single[defColMemItemsValue.name], + enteringMemMemo2, + ); + }, + ); group(': Archive', () { const insertedMemName = "$saveMemName: Archive: inserted"; @@ -226,84 +243,94 @@ void main() => group( const archivedMemName = "$insertedMemName - archived"; setUp(() async { - final unarchivedMemId = await dbA.insert(defTableMems, { - defColMemsName.name: unarchivedMemName, - defColCreatedAt.name: DateTime.now(), - }); - await dbA.insert(defTableMemItems, { - defFkMemItemsMemId.name: unarchivedMemId, - defColMemItemsType.name: MemItemType.memo.name, - defColMemItemsValue.name: insertedMemMemo, - defColCreatedAt.name: zeroDate, - }); - final archivedMemId = await dbA.insert(defTableMems, { - defColMemsName.name: archivedMemName, - defColCreatedAt.name: DateTime.now(), - defColArchivedAt.name: DateTime.now(), - }); - await dbA.insert(defTableMemItems, { - defFkMemItemsMemId.name: archivedMemId, - defColMemItemsType.name: MemItemType.memo.name, - defColMemItemsValue.name: insertedMemMemo, - defColCreatedAt.name: zeroDate, - }); + await dbA.insert( + defTableMems, + { + defColMemsName.name: unarchivedMemName, + defColCreatedAt.name: DateTime.now(), + }, + ); + await dbA.insert( + defTableMems, + { + defColMemsName.name: archivedMemName, + defColCreatedAt.name: DateTime.now(), + defColArchivedAt.name: DateTime.now(), + }, + ); }); - testWidgets(": archive.", (widgetTester) async { - await runApplication(); - await widgetTester.pumpAndSettle(); - await widgetTester.tap(find.text(unarchivedMemName)); - await widgetTester.pumpAndSettle(); - - await widgetTester.tap(find.byIcon(Icons.more_vert)); - await widgetTester.pumpAndSettle(); - - await widgetTester.tap(find.byIcon(Icons.archive)); - await widgetTester.pumpAndSettle(); - - expect(find.text(unarchivedMemName), findsNothing); - expect( + testWidgets( + ": archive.", + (widgetTester) async { + await runApplication(); + await widgetTester.pumpAndSettle(); + await widgetTester.tap(find.text(unarchivedMemName)); + await widgetTester.pumpAndSettle(); + + await widgetTester.tap(find.byIcon(Icons.more_vert)); + await widgetTester.pumpAndSettle(); + + await widgetTester.tap(find.byIcon(Icons.archive)); + await widgetTester.pumpAndSettle(); + + expect( + find.text(unarchivedMemName), + findsNothing, + ); + expect( find.text(l10n.archiveMemSuccessMessage(unarchivedMemName)), - findsOneWidget); + findsOneWidget, + ); - final findUnarchivedMem = - Equals(defColMemsName, unarchivedMemName); - final mems = await dbA.select(defTableMems, + final findUnarchivedMem = + Equals(defColMemsName.name, unarchivedMemName); + final mems = await dbA.select( + defTableMems, where: findUnarchivedMem.where(), - whereArgs: findUnarchivedMem.whereArgs()); - expect(mems.length, 1); - expect(mems.single[defColArchivedAt.name], isNotNull); - }); - - testWidgets(": unarchive.", (widgetTester) async { - await runApplication(); - await widgetTester.pumpAndSettle(); - await widgetTester.tap(filterListIconFinder); - await widgetTester.pumpAndSettle(); - await widgetTester.tap(showArchiveSwitchFinder); - await widgetTester.pumpAndSettle(); - await closeMemListFilter(widgetTester); - await widgetTester.pumpAndSettle(); - await widgetTester.tap(find.text(archivedMemName)); - await widgetTester.pumpAndSettle(); - - await widgetTester.tap(find.byIcon(Icons.more_vert)); - await widgetTester.pumpAndSettle(); - - await widgetTester.tap(find.byIcon(Icons.unarchive)); - await widgetTester.pumpAndSettle(); + whereArgs: findUnarchivedMem.whereArgs(), + ); + expect(mems.length, 1); + expect(mems.single[defColArchivedAt.name], isNotNull); + }, + ); - expect( + testWidgets( + ": unarchive.", + (widgetTester) async { + await runApplication(); + await widgetTester.pumpAndSettle(); + await widgetTester.tap(filterListIconFinder); + await widgetTester.pumpAndSettle(); + await widgetTester.tap(showArchiveSwitchFinder); + await widgetTester.pumpAndSettle(); + await closeMemListFilter(widgetTester); + await widgetTester.pumpAndSettle(); + await widgetTester.tap(find.text(archivedMemName)); + await widgetTester.pumpAndSettle(); + + await widgetTester.tap(find.byIcon(Icons.more_vert)); + await widgetTester.pumpAndSettle(); + + await widgetTester.tap(find.byIcon(Icons.unarchive)); + await widgetTester.pumpAndSettle(); + + expect( find.text(l10n.unarchiveMemSuccessMessage(archivedMemName)), - findsOneWidget); + findsOneWidget, + ); - final findArchivedMem = Equals(defColMemsName, archivedMemName); - final mems = await dbA.select(defTableMems, + final findArchivedMem = + Equals(defColMemsName.name, archivedMemName); + final mems = await dbA.select( + defTableMems, where: findArchivedMem.where(), - whereArgs: findArchivedMem.whereArgs()); - expect(mems.length, 1); - expect(mems.single[defColArchivedAt.name], isNull); - }); + whereArgs: findArchivedMem.whereArgs(), + ); + expect(mems.length, 1); + expect(mems.single[defColArchivedAt.name], isNull); + }, + ); }); }, ); @@ -314,7 +341,7 @@ void main() => group( Future>> selectFromMemsWhereName( String name, ) async { - final where = Equals(defColMemsName, name); + final where = Equals(defColMemsName.name, name); return await dbA.select( defTableMems, where: where.where(), diff --git a/integration_test/scenarios/memo/list_scenarios.dart b/integration_test/scenarios/memo/list_scenarios.dart index 5589923f1..e33bbd96f 100644 --- a/integration_test/scenarios/memo/list_scenarios.dart +++ b/integration_test/scenarios/memo/list_scenarios.dart @@ -4,7 +4,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; import 'package:mem/components/mem/mem_name.dart'; -import 'package:mem/mems/mem_item.dart'; +import 'package:mem/core/mem_item.dart'; import 'package:mem/databases/definition.dart'; import 'package:mem/databases/table_definitions/base.dart'; import 'package:mem/databases/table_definitions/mem_items.dart'; diff --git a/integration_test/scenarios/notification_scenario.dart b/integration_test/scenarios/notification_scenario.dart index 6987bc85e..0a840c8b6 100644 --- a/integration_test/scenarios/notification_scenario.dart +++ b/integration_test/scenarios/notification_scenario.dart @@ -4,7 +4,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; -import 'package:mem/mems/mem_notification.dart'; +import 'package:mem/core/mem_notification.dart'; import 'package:mem/databases/definition.dart'; import 'package:mem/databases/table_definitions/acts.dart'; import 'package:mem/databases/table_definitions/base.dart'; diff --git a/integration_test/scenarios/settings/backup_scenario.dart b/integration_test/scenarios/settings/backup_scenario.dart index cf609d099..85c490721 100644 --- a/integration_test/scenarios/settings/backup_scenario.dart +++ b/integration_test/scenarios/settings/backup_scenario.dart @@ -48,7 +48,7 @@ void main() => group( testWidgets( "create.", - retry: maxRetryCount, + retry: 3, (widgetTester) async { String? result; switch (defaultTargetPlatform) { diff --git a/integration_test/scenarios/settings/settings_scenario.dart b/integration_test/scenarios/settings/settings_scenario.dart index 32995b26a..08b82e0af 100644 --- a/integration_test/scenarios/settings/settings_scenario.dart +++ b/integration_test/scenarios/settings/settings_scenario.dart @@ -88,7 +88,7 @@ void main() => group( "Start of day", () { setUp(() async { - await PreferenceClientRepository().discard(startOfDayKey); + await PreferenceClient().discard(startOfDayKey); }); testWidgets( @@ -120,40 +120,49 @@ void main() => group( timeText(zeroDate), ); expect( - (await PreferenceClientRepository().shipByKey(startOfDayKey)) - .value, + (await PreferenceClient().shipByKey(startOfDayKey)).value, TimeOfDay.fromDateTime(zeroDate), ); }, ); - group('with saved', () { - final now = DateTime.now(); - setUp(() async { - await PreferenceClientRepository().receive(PreferenceEntity( - startOfDayKey, TimeOfDay.fromDateTime(now))); - }); - - testWidgets('show saved.', (widgetTester) async { - await runApplication(); - await widgetTester.pumpAndSettle(); - - await widgetTester.tap(drawerIconFinder); - await widgetTester.pumpAndSettle(); - await widgetTester.tap(find.text(l10n.settingsPageTitle)); - await widgetTester.pumpAndSettle(); - - expect( - widgetTester - .widget(find - .descendant( + group( + "with saved", + () { + final now = DateTime.now(); + setUp(() async { + await PreferenceClient().receive(Preference( + startOfDayKey, + TimeOfDay.fromDateTime(now), + )); + }); + + testWidgets( + "show saved.", + (widgetTester) async { + await runApplication(); + await widgetTester.pumpAndSettle(); + + await widgetTester.tap(drawerIconFinder); + await widgetTester.pumpAndSettle(); + await widgetTester.tap(find.text(l10n.settingsPageTitle)); + await widgetTester.pumpAndSettle(); + + expect( + widgetTester + .widget(find + .descendant( of: find.byType(SettingsTile), - matching: find.byType(Text)) - .at(1)) - .data, - timeText(now)); - }); - }); + matching: find.byType(Text), + ) + .at(1)) + .data, + timeText(now), + ); + }, + ); + }, + ); }, ); diff --git a/integration_test/scenarios/task_scenario.dart b/integration_test/scenarios/task_scenario.dart index 999c72781..b4878f0b6 100644 --- a/integration_test/scenarios/task_scenario.dart +++ b/integration_test/scenarios/task_scenario.dart @@ -9,6 +9,7 @@ import 'package:mem/databases/table_definitions/base.dart'; import 'package:mem/databases/table_definitions/mems.dart'; import 'package:mem/framework/database/accessor.dart'; import 'package:mem/databases/definition.dart'; +import 'package:mem/framework/repository/database_tuple_repository.dart'; import 'package:mem/logger/log.dart'; import 'package:mem/logger/log_service.dart'; import 'package:mem/notifications/notification_client.dart'; @@ -340,63 +341,70 @@ void testTaskScenario() => group(': $_scenarioName', () { }, ); - testWidgets(': start is not all day, end is all day.', - (widgetTester) async { - widgetTester.ignoreMockMethodCallHandler( - MethodChannelMock.permissionHandler); - - await PreferenceClientRepository().receive(PreferenceEntity( - startOfDayKey, const TimeOfDay(hour: 1, minute: 0))); + testWidgets( + ": start is not all day, end is all day.", + (widgetTester) async { + await PreferenceClient().receive(Preference( + startOfDayKey, + const TimeOfDay(hour: 1, minute: 0), + )); - await runApplication(); - await widgetTester.pumpAndSettle(); - await widgetTester.tap(newMemFabFinder); - await widgetTester.pumpAndSettle(); - await widgetTester.tap(calendarIconFinder.at(0)); - await widgetTester.pumpAndSettle(); - await widgetTester.tap(find.byIcon(Icons.chevron_right).at(0)); - await widgetTester.pumpAndSettle(); - const pickingDate = 1; - await widgetTester.tap(find.text("$pickingDate")); - await widgetTester.tap(okFinder); - await widgetTester.pumpAndSettle(); + await runApplication(); + await widgetTester.pumpAndSettle(); + await widgetTester.tap(newMemFabFinder); + await widgetTester.pumpAndSettle(); + await widgetTester.tap(calendarIconFinder.at(0)); + await widgetTester.pumpAndSettle(); + await widgetTester.tap(find.byIcon(Icons.chevron_right).at(0)); + await widgetTester.pumpAndSettle(); + const pickingDate = 1; + await widgetTester.tap(find.text("$pickingDate")); + await widgetTester.tap(okFinder); + await widgetTester.pumpAndSettle(); - await widgetTester.tap(find.byType(Switch).at(0)); - await widgetTester.pumpAndSettle(); + await widgetTester.tap(find.byType(Switch).at(0)); + await widgetTester.pumpAndSettle(); - await widgetTester.tap(okFinder); - await widgetTester.pumpAndSettle(); + await widgetTester.tap(okFinder); + await widgetTester.pumpAndSettle(); - await widgetTester.tap(calendarIconFinder.at(1)); - await widgetTester.pumpAndSettle(); + await widgetTester.tap(calendarIconFinder.at(1)); + await widgetTester.pumpAndSettle(); - await widgetTester.tap(find.text("$pickingDate")); - await widgetTester.tap(okFinder); - await widgetTester.pumpAndSettle(); + await widgetTester.tap(find.text("$pickingDate")); + await widgetTester.tap(okFinder); + await widgetTester.pumpAndSettle(); - final now = DateTime.now(); - final endDate = DateTime(now.year, now.month + 1, pickingDate); - expect( + final now = DateTime.now(); + final endDate = DateTime(now.year, now.month + 1, pickingDate); + expect( (widgetTester.widget(find.byType(TextFormField).at(3)) as TextFormField) .initialValue, - dateText(endDate)); - - const enteringMemName = - "$_scenarioName: Save Period: start is not all day, end is all day - mem name - entering"; - await widgetTester.enterText( - memNameOnDetailPageFinder, enteringMemName); - await widgetTester.tap(saveMemFabFinder); - await widgetTester.pumpAndSettle(); + dateText(endDate), + ); - await PreferenceClientRepository().discard(startOfDayKey); + const enteringMemName = + "$_scenarioName: Save Period: start is not all day, end is all day - mem name - entering"; + await widgetTester.enterText( + memNameOnDetailPageFinder, + enteringMemName, + ); + await widgetTester.tap(saveMemFabFinder); + await widgetTester.pumpAndSettle(); - final savedMems = (await dbA.select(defTableMems, + final savedMems = (await dbA.select( + defTableMems, where: "${defColMemsName.name} = ?", - whereArgs: [enteringMemName])); - expect(savedMems, hasLength(1)); - expect(savedMems.single[defColMemsEndOn.name], equals(endDate)); - }); + whereArgs: [enteringMemName], + )); + expect(savedMems, hasLength(1)); + expect( + savedMems.single[defColMemsEndOn.name], + equals(endDate), + ); + }, + ); }, ); @@ -417,6 +425,8 @@ void testTaskScenario() => group(': $_scenarioName', () { testWidgets( 'not notify on active act.', (widgetTester) async { + DatabaseTupleRepository.databaseAccessor = dbA; + widgetTester.setMockMethodCallHandler( MethodChannelMock.flutterLocalNotifications, [], diff --git a/integration_test/scenarios/todo_scenario.dart b/integration_test/scenarios/todo_scenario.dart index efad822bd..9b75398da 100644 --- a/integration_test/scenarios/todo_scenario.dart +++ b/integration_test/scenarios/todo_scenario.dart @@ -6,6 +6,7 @@ import 'package:mem/databases/table_definitions/base.dart'; import 'package:mem/databases/table_definitions/mems.dart'; import 'package:mem/framework/database/accessor.dart'; import 'package:mem/databases/definition.dart'; +import 'package:mem/framework/repository/database_tuple_repository.dart'; import 'package:mem/logger/log.dart'; import 'package:mem/logger/log_service.dart'; import 'package:mem/notifications/notification_client.dart'; @@ -188,6 +189,8 @@ void testTodoScenario() => group(': $_scenarioName', () { testWidgets( 'not notify on done mem.', (widgetTester) async { + DatabaseTupleRepository.databaseAccessor = dbA; + int initializeCount = 0; int cancelCount = 0; widgetTester.setMockMethodCallHandler( diff --git a/lib/act_counter/act_counter.dart b/lib/act_counter/act_counter.dart index 2d3c66478..5837e21a6 100644 --- a/lib/act_counter/act_counter.dart +++ b/lib/act_counter/act_counter.dart @@ -1,50 +1,39 @@ import 'package:collection/collection.dart'; -import 'package:mem/act_counter/home_widget.dart'; +import 'package:mem/core/act.dart'; import 'package:mem/core/date_and_time/date_and_time.dart'; import 'package:mem/core/date_and_time/date_and_time_period.dart'; -import 'package:mem/acts/act_entity.dart'; -import 'package:mem/mems/mem_entity.dart'; - -class ActCounter implements HomeWidget { - @override - String get methodChannelName => 'zin.playground.mem/act_counter'; - - @override - String get initializeMethodName => 'initialize'; - - @override - String get widgetProviderName => 'ActCounterProvider'; +import 'package:mem/framework/repository/entity.dart'; +import 'package:mem/repositories/mem.dart'; +class ActCounter extends EntityV1 { + final SavedMem _mem; + final Iterable _acts; final int memId; final String? name; final int? actCount; - final DateTime? updatedAt; + final SavedAct? lastAct; - ActCounter(this.memId, this.name, this.actCount, this.updatedAt); + ActCounter(this._mem, this._acts) + : memId = _mem.id, + name = _mem.name, + actCount = _acts.length, + lastAct = _acts + .sorted( + (a, b) => (a.updatedAt ?? a.createdAt) + .compareTo(b.updatedAt ?? b.createdAt), + ) + .lastOrNull; - ActCounter.from( - SavedMemEntity savedMem, - Iterable savedActs, - ) : memId = savedMem.id, - name = savedMem.name, - actCount = savedActs.length, - updatedAt = savedActs - .sorted( - (a, b) => (a.updatedAt ?? a.createdAt) - .compareTo(b.updatedAt ?? b.createdAt), - ) - .lastOrNull - ?.period - .end ?? - savedActs - .sorted( - (a, b) => (a.updatedAt ?? a.createdAt) - .compareTo(b.updatedAt ?? b.createdAt), - ) - .lastOrNull - ?.period - .start; + Map widgetData() => { + "memName-$memId": name, + "actCount-$memId": actCount, + "lastUpdatedAtSeconds-$memId": lastAct == null + ? null + : (lastAct?.period.end ?? lastAct?.period.start!) + ?.millisecondsSinceEpoch + .toDouble(), + }; static DateAndTimePeriod period(DateAndTime startDate) { int startHour = 5; @@ -70,3 +59,17 @@ class ActCounter implements HomeWidget { ); } } + +class InitializedActCounter extends ActCounter { + final int homeWidgetId; + + InitializedActCounter(this.homeWidgetId, ActCounter actCounter) + : super(actCounter._mem, actCounter._acts); + + @override + Map widgetData() => super.widgetData() + ..putIfAbsent( + "memId-$homeWidgetId", + () => memId, + ); +} diff --git a/lib/act_counter/act_counter_client.dart b/lib/act_counter/act_counter_client.dart index 9f711073c..45dcb47ee 100644 --- a/lib/act_counter/act_counter_client.dart +++ b/lib/act_counter/act_counter_client.dart @@ -1,9 +1,8 @@ -import 'package:mem/act_counter/act_counter_entity.dart'; +import 'package:mem/acts/act_repository.dart'; import 'package:mem/acts/client.dart'; import 'package:mem/core/date_and_time/date_and_time.dart'; import 'package:mem/logger/log_service.dart'; -import 'package:mem/acts/act_repository.dart'; -import 'package:mem/mems/mem_repository.dart'; +import 'package:mem/repositories/mem_repository.dart'; import 'act_counter.dart'; import 'act_counter_repository.dart'; @@ -15,20 +14,16 @@ class ActCounterClient { final ActCounterRepository _actCounterRepository; Future createNew(int memId) => v( - () async { - await _actCounterRepository.receive( - ActCounterEntity.from( - await _memRepository.ship(id: memId).then((v) => v.single), - await _actRepository.ship( - memId: memId, - period: ActCounter.period(DateAndTime.now()), - ), + () async => await _actCounterRepository.receive( + ActCounter( + await _memRepository.shipById(memId), + await _actRepository.ship( + memId: memId, + period: ActCounter.period(DateAndTime.now()), ), - ); - }, - { - 'memId': memId, - }, + ), + ), + {'memId': memId}, ); Future increment( @@ -43,8 +38,8 @@ class ActCounterClient { ); await _actCounterRepository.replace( - ActCounterEntity.from( - await _memRepository.ship(id: memId).then((v) => v.single), + ActCounter( + await _memRepository.shipById(memId), await _actRepository.ship( memId: memId, period: ActCounter.period(when), diff --git a/lib/act_counter/act_counter_entity.dart b/lib/act_counter/act_counter_entity.dart deleted file mode 100644 index a219c519c..000000000 --- a/lib/act_counter/act_counter_entity.dart +++ /dev/null @@ -1,45 +0,0 @@ -import 'package:mem/act_counter/act_counter.dart'; -import 'package:mem/framework/repository/entity.dart'; -import 'package:mem/framework/repository/home_widget_entity.dart'; - -class ActCounterEntity extends ActCounter with Entity, HomeWidgetEntity { - ActCounterEntity(super.memId, super.name, super.actCount, super.updatedAt) - : super(); - - @override - Map get toMap => { - 'initializeMethodName': initializeMethodName, - 'methodChannelName': methodChannelName, - 'memId': memId, - 'name': name, - 'actCount': actCount, - 'updatedAt': updatedAt, - }; - - @override - Map get toWidgetData => { - "memName-$memId": name, - "actCount-$memId": actCount, - "lastUpdatedAtSeconds-$memId": - updatedAt?.millisecondsSinceEpoch.toDouble(), - }; - - ActCounterEntity.from(super.savedMem, super.savedActs) : super.from(); -} - -class SavedActCounterEntity extends ActCounterEntity - with SavedHomeWidgetEntity { - SavedActCounterEntity.fromMap(Map map) - : super( - map['memId'], - map['name'], - map['actCount'], - map['updatedAt'], - ); - - @override - Map get toWidgetData => super.toWidgetData - ..addAll({ - "memId-$homeWidgetId": memId, - }); -} diff --git a/lib/act_counter/act_counter_repository.dart b/lib/act_counter/act_counter_repository.dart index d8e2fa402..9a6ccd809 100644 --- a/lib/act_counter/act_counter_repository.dart +++ b/lib/act_counter/act_counter_repository.dart @@ -1,9 +1,60 @@ -import 'package:mem/act_counter/act_counter_entity.dart'; -import 'package:mem/framework/repository/home_widget_repository.dart'; +import 'package:flutter/foundation.dart'; +import 'package:mem/framework/repository/repository.dart'; +import 'package:mem/logger/log_service.dart'; + +import 'act_counter.dart'; +import 'home_widget_accessor.dart'; + +// TODO constantsとして別で定義する +// see android\app\src\main\kotlin\zin\playground\mem\ActCounterConfigure.kt +const methodChannelName = 'zin.playground.mem/act_counter'; +const initializeMethodName = 'initialize'; +const widgetProviderName = 'ActCounterProvider'; + +class ActCounterRepository extends RepositoryV1 { + final HomeWidgetAccessor? _homeWidgetAccessor; -class ActCounterRepository - extends HomeWidgetRepository { @override - SavedActCounterEntity pack(Map map) => - SavedActCounterEntity.fromMap(map)..homeWidgetId = map['homeWidgetId']; + Future receive(ActCounter entity) => v( + () async { + final homeWidgetId = await _homeWidgetAccessor?.initialize( + methodChannelName, + initializeMethodName, + ); + + if (homeWidgetId != null) { + InitializedActCounter(homeWidgetId, entity) + .widgetData() + .forEach((key, value) async { + await _homeWidgetAccessor?.saveWidgetData(key, value); + }); + + await _homeWidgetAccessor?.updateWidget(widgetProviderName); + } + }, + entity, + ); + + Future replace(ActCounter entity) => v( + () async { + entity.widgetData().forEach((key, value) async { + await _homeWidgetAccessor?.saveWidgetData(key, value); + }); + + await _homeWidgetAccessor?.updateWidget(widgetProviderName); + + return entity; + }, + entity, + ); + + ActCounterRepository._(this._homeWidgetAccessor); + + static ActCounterRepository? _instance; + + factory ActCounterRepository() => _instance ??= ActCounterRepository._( + defaultTargetPlatform == TargetPlatform.android + ? HomeWidgetAccessor() + : null, + ); } diff --git a/lib/act_counter/home_widget.dart b/lib/act_counter/home_widget.dart deleted file mode 100644 index 7cdb9f1f8..000000000 --- a/lib/act_counter/home_widget.dart +++ /dev/null @@ -1,7 +0,0 @@ -abstract class HomeWidget { - String get methodChannelName; - - String get initializeMethodName; - - String get widgetProviderName; -} diff --git a/lib/act_counter/single_selectable_mem_list_item.dart b/lib/act_counter/single_selectable_mem_list_item.dart index 079819bac..d6af0b86e 100644 --- a/lib/act_counter/single_selectable_mem_list_item.dart +++ b/lib/act_counter/single_selectable_mem_list_item.dart @@ -4,7 +4,7 @@ import 'package:mem/components/mem/list/states.dart'; import 'package:mem/components/mem/mem_name.dart'; import 'package:mem/components/mem/mem_period.dart'; import 'package:mem/logger/log_service.dart'; -import 'package:mem/mems/mem_entity.dart'; +import 'package:mem/repositories/mem.dart'; import 'states.dart'; @@ -31,7 +31,7 @@ class SingleSelectableMemListItem extends ConsumerWidget { class _SingleSelectableMemListItemComponent extends ListTile { _SingleSelectableMemListItemComponent( - SavedMemEntity mem, + SavedMem mem, bool isSelected, void Function(int? memId) onSelected, ) : super( diff --git a/lib/acts/act_entity.dart b/lib/acts/act_entity.dart deleted file mode 100644 index d8b3f3150..000000000 --- a/lib/acts/act_entity.dart +++ /dev/null @@ -1,69 +0,0 @@ -import 'package:mem/acts/act.dart'; -import 'package:mem/core/date_and_time/date_and_time.dart'; -import 'package:mem/core/date_and_time/date_and_time_period.dart'; -import 'package:mem/databases/table_definitions/acts.dart'; -import 'package:mem/framework/repository/database_tuple_entity.dart'; -import 'package:mem/framework/repository/entity.dart'; - -class ActEntity extends Act with Entity, Copyable { - ActEntity(super.memId, super.period); - - ActEntity.fromMap(Map map) - : super( - map[defFkActsMemId.name], - DateAndTimePeriod( - start: DateAndTime.from( - map[defColActsStart.name], - timeOfDay: map[defColActsStartIsAllDay.name] - ? null - : map[defColActsStart.name], - ), - end: map[defColActsEnd.name] == null - ? null - : DateAndTime.from( - map[defColActsEnd.name], - timeOfDay: map[defColActsEndIsAllDay.name] - ? null - : map[defColActsEnd.name], - ), - ), - ); - - @override - ActEntity copiedWith({ - int Function()? memId, - DateAndTimePeriod Function()? period, - }) => - ActEntity( - memId == null ? this.memId : memId(), - period == null ? this.period : period(), - ); - - @override - Map get toMap => { - defFkActsMemId.name: memId, - defColActsStart.name: period.start, - defColActsStartIsAllDay.name: period.start?.isAllDay, - defColActsEnd.name: period.end, - defColActsEndIsAllDay.name: period.end?.isAllDay, - }; -} - -class SavedActEntity extends ActEntity with DatabaseTupleEntity { - SavedActEntity(super.memId, super.period, Map map) - : super() { - withMap(map); - } - - SavedActEntity.fromMap(Map map) : super.fromMap(map) { - withMap(map); - } - - @override - SavedActEntity copiedWith({ - int Function()? memId, - DateAndTimePeriod Function()? period, - }) => - SavedActEntity.fromMap( - toMap..addAll(super.copiedWith(memId: memId, period: period).toMap)); -} diff --git a/lib/acts/act_repository.dart b/lib/acts/act_repository.dart index 00049571f..01ed42e52 100644 --- a/lib/acts/act_repository.dart +++ b/lib/acts/act_repository.dart @@ -1,15 +1,14 @@ +import 'package:mem/core/act.dart'; +import 'package:mem/core/date_and_time/date_and_time.dart'; import 'package:mem/core/date_and_time/date_and_time_period.dart'; -import 'package:mem/databases/definition.dart'; import 'package:mem/databases/table_definitions/acts.dart'; -import 'package:mem/databases/table_definitions/base.dart'; -import 'package:mem/framework/repository/condition/conditions.dart'; import 'package:mem/framework/repository/condition/in.dart'; -import 'package:mem/framework/repository/database_tuple_repository.dart'; import 'package:mem/framework/repository/extra_column.dart'; import 'package:mem/framework/repository/group_by.dart'; import 'package:mem/framework/repository/order_by.dart'; import 'package:mem/logger/log_service.dart'; -import 'package:mem/acts/act_entity.dart'; +import 'package:mem/framework/repository/database_tuple_repository.dart'; +import 'package:mem/framework/repository/condition/conditions.dart'; enum ActOrderBy { descStart } @@ -25,11 +24,32 @@ extension _ActOrderByExt on ActOrderBy { } } -class ActRepository extends DatabaseTupleRepository { - ActRepository() : super(databaseDefinition, defTableActs); - +class ActRepository extends DatabaseTupleRepository { @override - SavedActEntity pack(Map map) => SavedActEntity.fromMap(map); + Future findOneBy({ + int? memId, + bool? latest, + Condition? condition, + List? orderBy, + }) => + v( + () async => await super.findOneBy( + condition: And([ + if (memId != null) Equals(defFkActsMemId.name, memId), + if (condition != null) condition, // coverage:ignore-line + ]), + orderBy: [ + if (latest == true) ActOrderBy.descStart.toQuery, + if (orderBy != null) ...orderBy, // coverage:ignore-line + ], + ), + { + "memId": memId, + "latest": latest, + "condition": condition, + "orderBy": orderBy, + }, + ); @override Future count({ @@ -40,27 +60,26 @@ class ActRepository extends DatabaseTupleRepository { () => super.count( condition: And( [ - if (memId != null) Equals(defFkActsMemId, memId), + if (memId != null) Equals(defFkActsMemId.name, memId), if (condition != null) condition, // coverage:ignore-line ], ), ), { - 'memId': memId, - 'condition': condition, + "memId": memId, + "condition": condition, }, ); @override - Future> ship({ + Future> ship({ int? memId, Iterable? memIdsIn, DateAndTimePeriod? period, bool? latestByMemIds, - bool? isActive, + ActOrderBy? actOrderBy, Condition? condition, GroupBy? groupBy, - ActOrderBy? actOrderBy, List? orderBy, int? offset, int? limit, @@ -69,12 +88,11 @@ class ActRepository extends DatabaseTupleRepository { () => super.ship( condition: And( [ - if (memId != null) Equals(defFkActsMemId, memId), + if (memId != null) Equals(defFkActsMemId.name, memId), if (memIdsIn != null) In(defFkActsMemId.name, memIdsIn), if (period != null) GraterThanOrEqual(defColActsStart, period.start), if (period != null) LessThan(defColActsStart, period.end), - if (isActive != null) IsNull(defColActsEnd.name), if (condition != null) condition, ], ), @@ -95,30 +113,73 @@ class ActRepository extends DatabaseTupleRepository { 'memId': memId, 'memIds': memIdsIn, 'period': period, - 'latestByMemIds': latestByMemIds, - 'isActive': isActive, + 'actOrderBy': actOrderBy, 'condition': condition, 'groupBy': groupBy, - 'actOrderBy': actOrderBy, 'orderBy': orderBy, 'offset': offset, 'limit': limit, }, ); - @override - Future> waste({int? id, Condition? condition}) => v( - () => super.waste( - condition: And( - [ - if (id != null) Equals(defPkId, id), - if (condition != null) condition, // coverage:ignore-line - ], - ), + Future> shipActiveByMemId( + int memId, + ) => + v( + () async => await ship( + condition: And([ + Equals(defFkActsMemId.name, memId), + IsNull(defColActsEnd.name), + ]), ), { - 'id': id, - 'condition': condition, + "memId": memId, }, ); + + @override + SavedAct pack(Map tuple) => SavedAct( + tuple[defFkActsMemId.name], + DateAndTimePeriod( + start: DateAndTime.from( + tuple[defColActsStart.name], + timeOfDay: tuple[defColActsStartIsAllDay.name] + ? null + : tuple[defColActsStart.name], + ), + end: tuple[defColActsEnd.name] == null + ? null + : DateAndTime.from( + tuple[defColActsEnd.name], + timeOfDay: tuple[defColActsEndIsAllDay.name] + ? null + : tuple[defColActsEnd.name], + ), + ), + )..pack(tuple); + + @override + Map unpack(Act entity) { + final map = { + defFkActsMemId.name: entity.memId, + defColActsStart.name: entity.period.start, + defColActsStartIsAllDay.name: entity.period.start?.isAllDay, + defColActsEnd.name: entity.period.end, + defColActsEndIsAllDay.name: entity.period.end?.isAllDay, + }; + + if (entity is SavedAct) { + map.addAll(entity.unpack()); + } + + return map; + } + + ActRepository._() : super(defTableActs); + + static ActRepository? _instance; + + factory ActRepository() => v( + () => _instance ??= ActRepository._(), + ); } diff --git a/lib/acts/act_service.dart b/lib/acts/act_service.dart index 93bf73feb..022f7f2ea 100644 --- a/lib/acts/act_service.dart +++ b/lib/acts/act_service.dart @@ -1,9 +1,10 @@ import 'package:collection/collection.dart'; +import 'package:mem/core/act.dart'; import 'package:mem/core/date_and_time/date_and_time.dart'; import 'package:mem/core/date_and_time/date_and_time_period.dart'; import 'package:mem/logger/log_service.dart'; -import 'package:mem/acts/act_entity.dart'; -import 'package:mem/acts/act_repository.dart'; + +import 'act_repository.dart'; class ListWithTotalCount { final List list; @@ -15,58 +16,57 @@ class ListWithTotalCount { class ActService { final ActRepository _actRepository; - Future> fetch( + Future> fetch( int? memId, int offset, int limit, ) => v( - () async => ListWithTotalCount( - (await _actRepository.ship( + () async { + final acts = await _actRepository.ship( memId: memId, actOrderBy: ActOrderBy.descStart, offset: offset, limit: limit, - )), - await _actRepository.count(memId: memId), - ), + ); + final count = await _actRepository.count(memId: memId); + + return ListWithTotalCount(acts, count); + }, { - 'memId': memId, - 'offset': offset, - 'limit': limit, + "memId": memId, + "offset": offset, + "limit": limit, }, ); - Future start( + Future start( int memId, DateAndTime when, ) => i( () async => await _actRepository.receive( - ActEntity(memId, DateAndTimePeriod(start: when)), + Act(memId, DateAndTimePeriod(start: when)), ), { - 'memId': memId, - 'when': when, + "memId": memId, + "when": when, }, ); - Future finish( + Future finish( int memId, DateAndTime when, ) => i( () async { - final active = (await _actRepository.ship( - memId: memId, - isActive: true, - )) + final active = (await _actRepository.shipActiveByMemId(memId)) .sorted((a, b) => a.createdAt.compareTo(b.createdAt)) .firstOrNull; if (active == null) { return await _actRepository.receive( - ActEntity( + Act( memId, DateAndTimePeriod( start: when, @@ -77,28 +77,28 @@ class ActService { } else { return await _actRepository.replace( active.copiedWith( - period: () => active.period.copiedWith(when), + () => active.period.copiedWith(when), ), ); } }, { - 'memId': memId, - 'when': when, + "memId": memId, + "when": when, }, ); - Future edit(SavedActEntity savedAct) => i( + Future edit(SavedAct savedAct) => i( () async => await _actRepository.replace(savedAct), { - 'savedAct': savedAct, + "savedAct": savedAct, }, ); - Future delete(int actId) => i( - () async => (await _actRepository.waste(id: actId)).single, + Future delete(int actId) => i( + () async => await _actRepository.wasteById(actId), { - 'actId': actId, + "actId": actId, }, ); diff --git a/lib/acts/actions.dart b/lib/acts/actions.dart index 8f82cbf2f..fb36c79f6 100644 --- a/lib/acts/actions.dart +++ b/lib/acts/actions.dart @@ -1,11 +1,10 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:mem/acts/act_repository.dart'; import 'package:mem/components/mem/list/states.dart'; -import 'package:mem/acts/act.dart'; +import 'package:mem/core/act.dart'; import 'package:mem/core/date_and_time/date_and_time.dart'; import 'package:mem/core/date_and_time/date_and_time_period.dart'; import 'package:mem/logger/log_service.dart'; -import 'package:mem/acts/act_entity.dart'; -import 'package:mem/acts/act_repository.dart'; import 'client.dart'; import 'states.dart'; @@ -13,8 +12,7 @@ import 'states.dart'; final _actsClient = ActsClient(); final _actRepository = ActRepository(); -final loadActList = - FutureProvider.autoDispose.family, int?>( +final loadActList = FutureProvider.autoDispose.family, int?>( (ref, memId) => v( () async { // TODO 全件取得する場合、件数的な不安がある @@ -29,7 +27,7 @@ final loadActList = return acts; }, { - 'memId': memId, + "memId": memId, }, ), ); @@ -55,7 +53,7 @@ final startActBy = Provider.autoDispose.family( ), ); -final finishActBy = Provider.autoDispose.family( +final finishActBy = Provider.autoDispose.family( (ref, memId) => v( () { final now = DateAndTime.now(); diff --git a/lib/acts/acts_summary.dart b/lib/acts/acts_summary.dart index 86e85703b..230a5e06d 100644 --- a/lib/acts/acts_summary.dart +++ b/lib/acts/acts_summary.dart @@ -1,5 +1,5 @@ import 'package:collection/collection.dart'; -import 'package:mem/acts/act.dart'; +import 'package:mem/core/act.dart'; import 'package:mem/core/date_and_time/date_and_time.dart'; import 'package:mem/logger/log_service.dart'; diff --git a/lib/acts/client.dart b/lib/acts/client.dart index 82be2ae50..109521c12 100644 --- a/lib/acts/client.dart +++ b/lib/acts/client.dart @@ -1,8 +1,8 @@ import 'package:mem/acts/act_service.dart'; +import 'package:mem/core/act.dart'; import 'package:mem/core/date_and_time/date_and_time.dart'; import 'package:mem/logger/log_service.dart'; import 'package:mem/notifications/notification_client.dart'; -import 'package:mem/acts/act_entity.dart'; class ListWithTotalPage { final List list; @@ -16,7 +16,7 @@ class ActsClient { final NotificationClient _notificationClient; - Future> fetch( + Future> fetch( int? memId, int page, ) => @@ -43,7 +43,7 @@ class ActsClient { }, ); - Future start( + Future start( int memId, DateAndTime when, ) => @@ -61,8 +61,8 @@ class ActsClient { }, ); - Future edit( - SavedActEntity savedAct, + Future edit( + SavedAct savedAct, ) => i( () async { @@ -81,7 +81,7 @@ class ActsClient { }, ); - Future pause( + Future pause( int memId, DateAndTime when, ) => @@ -97,7 +97,7 @@ class ActsClient { }, ); - Future finish( + Future finish( int memId, DateAndTime when, ) => @@ -117,7 +117,7 @@ class ActsClient { }, ); - Future delete(int actId) => i( + Future delete(int actId) => i( () async { final deleted = await _actService.delete(actId); diff --git a/lib/acts/line_chart/line_chart_page.dart b/lib/acts/line_chart/line_chart_page.dart index d8d5faf35..dcff3e182 100644 --- a/lib/acts/line_chart/line_chart_page.dart +++ b/lib/acts/line_chart/line_chart_page.dart @@ -29,9 +29,11 @@ class ActLineChartPage extends ConsumerWidget { child: AsyncValueView( loadActList(_memId), (loaded) => LineChartWrapper( - ActsSummary(ref - .watch(actListProvider(_memId)) - .where((element) => element.memId == _memId)), + ActsSummary( + ref + .watch(actListProvider(_memId)) + .where((element) => element.memId == _memId), + ), ), ), ), diff --git a/lib/acts/list/act_list.dart b/lib/acts/list/act_list.dart index fbc8667ca..b742e998e 100644 --- a/lib/acts/list/act_list.dart +++ b/lib/acts/list/act_list.dart @@ -4,9 +4,9 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_sticky_header/flutter_sticky_header.dart'; import 'package:mem/acts/states.dart'; import 'package:mem/components/mem/list/states.dart'; -import 'package:mem/acts/act.dart'; +import 'package:mem/core/act.dart'; import 'package:mem/logger/log_service.dart'; -import 'package:mem/mems/mem_entity.dart'; +import 'package:mem/repositories/mem.dart'; import 'app_bar.dart'; import 'item/builder.dart'; @@ -50,7 +50,7 @@ class ActList extends ConsumerWidget { _memId, ref.watch(dateViewProvider), ref.watch(timeViewProvider), - ref.watch(actListProvider(_memId)), + ref.watch(actListProvider(_memId)) ?? [], (_memId == null ? ref.watch(memListProvider) : []), _scrollController, ); @@ -66,7 +66,7 @@ class _ActList extends StatelessWidget { final bool _isDateView; final bool _isTimeView; final List _actList; - final List _memList; + final List _memList; final ScrollController? _scrollController; const _ActList( @@ -100,11 +100,8 @@ class _ActList extends StatelessWidget { header: ActListSubHeader(e, _isDateView), sliver: SliverList( delegate: () { - final builder = ActListItemBuilder( - e, - _memList, - _isTimeView, - ); + final builder = + ActListItemBuilder(e, _memList, _isTimeView); return SliverChildBuilderDelegate( builder.build, diff --git a/lib/acts/list/add_act_fab.dart b/lib/acts/list/add_act_fab.dart index b6bb43023..0db98e0a6 100644 --- a/lib/acts/list/add_act_fab.dart +++ b/lib/acts/list/add_act_fab.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:mem/acts/actions.dart'; import 'package:mem/acts/states.dart'; -import 'package:mem/acts/act_entity.dart'; +import 'package:mem/core/act.dart'; class ActFab extends ConsumerWidget { final int _memId; @@ -19,18 +19,14 @@ class ActFab extends ConsumerWidget { () => ref.read(actListProvider(_memId).notifier).upsertAll( [ref.read(startActBy(_memId))], (tmp, item) => - tmp is SavedActEntity && - item is SavedActEntity && -// coverage:ignore-start - tmp.id == item.id, -// coverage:ignore-end + tmp is SavedAct && item is SavedAct && tmp.id == item.id, ), ); } else { return _FinishActFab( () => ref.read(actListProvider(_memId).notifier).removeWhere( (element) => - element is SavedActEntity && + element is SavedAct && element.id == ref.read(finishActBy(activeActList.last.memId)).id, ), diff --git a/lib/acts/list/item/actions.dart b/lib/acts/list/item/actions.dart index 6c039cae6..0b16fd6bb 100644 --- a/lib/acts/list/item/actions.dart +++ b/lib/acts/list/item/actions.dart @@ -1,7 +1,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:mem/acts/client.dart'; import 'package:mem/acts/states.dart'; -import 'package:mem/acts/act.dart'; +import 'package:mem/core/act.dart'; import 'package:mem/logger/log_service.dart'; import 'states.dart'; diff --git a/lib/acts/list/item/builder.dart b/lib/acts/list/item/builder.dart index e82563992..9d5ebb566 100644 --- a/lib/acts/list/item/builder.dart +++ b/lib/acts/list/item/builder.dart @@ -1,16 +1,15 @@ import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; -import 'package:mem/acts/act.dart'; +import 'package:mem/core/act.dart'; import 'package:mem/logger/log_service.dart'; -import 'package:mem/acts/act_entity.dart'; -import 'package:mem/mems/mem_entity.dart'; +import 'package:mem/repositories/mem.dart'; import 'total_act_time_item.dart'; import 'view.dart'; class ActListItemBuilder { final MapEntry> _actListWithDatetime; - final List _memList; + final List _memList; final bool _isTimeView; late final Map _actListGroupedByMemId; @@ -37,7 +36,7 @@ class ActListItemBuilder { ); } else { final act = _actListWithDatetime.value[index]; - if (act is SavedActEntity) { + if (act is SavedAct) { return ActListItemView( act, _memList diff --git a/lib/acts/list/item/editing_act_dialog.dart b/lib/acts/list/item/editing_act_dialog.dart index c2ffd2018..14aba888f 100644 --- a/lib/acts/list/item/editing_act_dialog.dart +++ b/lib/acts/list/item/editing_act_dialog.dart @@ -2,9 +2,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:mem/acts/states.dart'; import 'package:mem/components/date_and_time/date_and_time_period_view.dart'; +import 'package:mem/core/act.dart'; import 'package:mem/core/date_and_time/date_and_time_period.dart'; import 'package:mem/logger/log_service.dart'; -import 'package:mem/acts/act_entity.dart'; import 'actions.dart'; import 'states.dart'; @@ -23,7 +23,7 @@ class EditingActDialog extends ConsumerWidget { (pickedPeriod) => v( () => ref.read(editingActProvider(_actId).notifier).updatedBy( editingAct.copiedWith( - period: pickedPeriod == null ? null : () => pickedPeriod, + pickedPeriod == null ? null : () => pickedPeriod, ), ), pickedPeriod, @@ -33,16 +33,14 @@ class EditingActDialog extends ConsumerWidget { () => ref.read(actListProvider(editingAct.memId).notifier).upsertAll( [ref.read(editAct(_actId))], (tmp, item) => - tmp is SavedActEntity && - item is SavedActEntity && - tmp.id == item.id, + tmp is SavedAct && item is SavedAct && tmp.id == item.id, )), ); } } class _EditingActDialogComponent extends StatelessWidget { - final SavedActEntity _editingAct; + final SavedAct _editingAct; final Function(DateAndTimePeriod? picked) _onPeriodChanged; final Function() _onDeleteTapped; final Function() _onSaveTapped; diff --git a/lib/acts/list/item/states.dart b/lib/acts/list/item/states.dart index 153d2cdbe..6c604766f 100644 --- a/lib/acts/list/item/states.dart +++ b/lib/acts/list/item/states.dart @@ -1,11 +1,11 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:mem/acts/states.dart'; import 'package:mem/components/value_state_notifier.dart'; +import 'package:mem/core/act.dart'; import 'package:mem/logger/log_service.dart'; -import 'package:mem/acts/act_entity.dart'; final editingActProvider = StateNotifierProvider.autoDispose - .family, SavedActEntity, int>( + .family, SavedAct, int>( (ref, actId) => v( () => ValueStateNotifier( ref.read(actsProvider).singleWhere((act) => act.id == actId), diff --git a/lib/acts/list/item/total_act_time_item.dart b/lib/acts/list/item/total_act_time_item.dart index 791b3b9b7..ca50a6656 100644 --- a/lib/acts/list/item/total_act_time_item.dart +++ b/lib/acts/list/item/total_act_time_item.dart @@ -1,13 +1,13 @@ import 'package:flutter/material.dart'; -import 'package:mem/acts/act.dart'; +import 'package:mem/core/act.dart'; import 'package:mem/core/date_and_time/duration.dart'; import 'package:mem/logger/log_service.dart'; import 'package:mem/mems/transitions.dart'; -import 'package:mem/mems/mem_entity.dart'; +import 'package:mem/repositories/mem.dart'; class TotalActTimeListItem extends StatelessWidget { final List _actList; - final SavedMemEntity? _mem; + final SavedMem? _mem; const TotalActTimeListItem(this._actList, this._mem, {super.key}); diff --git a/lib/acts/list/item/view.dart b/lib/acts/list/item/view.dart index 4b3b1d572..fa84ed08f 100644 --- a/lib/acts/list/item/view.dart +++ b/lib/acts/list/item/view.dart @@ -1,12 +1,12 @@ import 'package:flutter/material.dart'; import 'package:mem/components/date_and_time/date_and_time_period_view.dart'; +import 'package:mem/core/act.dart'; import 'package:mem/logger/log_service.dart'; -import 'package:mem/acts/act_entity.dart'; import 'editing_act_dialog.dart'; class ActListItemView extends StatelessWidget { - final SavedActEntity _act; + final SavedAct _act; // Act一覧の要素に対してMemがあったら名前を表示するという実装は合っているだろうか? final String? _memName; diff --git a/lib/acts/list/sub_header.dart b/lib/acts/list/sub_header.dart index 02b122dfc..e7726f699 100644 --- a/lib/acts/list/sub_header.dart +++ b/lib/acts/list/sub_header.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:mem/components/date_and_time/date_and_time_view.dart'; -import 'package:mem/acts/act.dart'; +import 'package:mem/core/act.dart'; import 'package:mem/core/date_and_time/date_and_time.dart'; import 'package:mem/core/date_and_time/duration.dart'; import 'package:mem/logger/log_service.dart'; diff --git a/lib/acts/states.dart b/lib/acts/states.dart index ec97c2aa2..e9edc9796 100644 --- a/lib/acts/states.dart +++ b/lib/acts/states.dart @@ -3,14 +3,13 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:mem/acts/client.dart'; import 'package:mem/components/list_value_state_notifier.dart'; import 'package:mem/components/value_state_notifier.dart'; -import 'package:mem/acts/act.dart'; +import 'package:mem/core/act.dart'; import 'package:mem/logger/log_service.dart'; -import 'package:mem/acts/act_entity.dart'; final _actsClient = ActsClient(); -final actsProvider = StateNotifierProvider< - ListValueStateNotifier, List>( +final actsProvider = + StateNotifierProvider, List>( (ref) => v(() => ListValueStateNotifier([])), ); @@ -56,7 +55,7 @@ final actListProvider = StateNotifierProvider.autoDispose final latest = await _actsClient.fetch(memId, 1); final c = ref.read(currentPage(memId)); - ListWithTotalPage? byPage; + ListWithTotalPage? byPage; if (c != 1) { byPage = await _actsClient.fetch(memId, c); } @@ -75,6 +74,7 @@ final actListProvider = StateNotifierProvider.autoDispose ref .watch(actsProvider) .where((act) => memId == null || act.memId == memId) + .toList() .sorted((a, b) => b.period.compareTo(a.period)), ); }, diff --git a/lib/components/created_and_updated_at_texts.dart b/lib/components/created_and_updated_at_texts.dart index 209eba7a7..de837e6fe 100644 --- a/lib/components/created_and_updated_at_texts.dart +++ b/lib/components/created_and_updated_at_texts.dart @@ -1,9 +1,9 @@ import 'package:flutter/material.dart'; import 'package:mem/components/date_and_time/date_and_time_view.dart'; import 'package:mem/core/date_and_time/date_and_time.dart'; -import 'package:mem/mems/mem.dart'; +import 'package:mem/core/mem.dart'; import 'package:mem/logger/log_service.dart'; -import 'package:mem/mems/mem_entity.dart'; +import 'package:mem/repositories/mem.dart'; import 'package:mem/values/colors.dart'; class CreatedAndUpdatedAtTexts extends StatelessWidget { @@ -14,7 +14,7 @@ class CreatedAndUpdatedAtTexts extends StatelessWidget { @override Widget build(BuildContext context) => v( () { - if (_entity is SavedMemEntity) { + if (_entity is SavedMem) { return Wrap( direction: Axis.horizontal, children: [ diff --git a/lib/components/date_and_time/date_and_time_period_view.dart b/lib/components/date_and_time/date_and_time_period_view.dart index 1c5aad530..d867fb883 100644 --- a/lib/components/date_and_time/date_and_time_period_view.dart +++ b/lib/components/date_and_time/date_and_time_period_view.dart @@ -58,7 +58,7 @@ class DateAndTimePeriodTextFormFields extends StatelessWidget { direction: Axis.vertical, mainAxisSize: MainAxisSize.min, children: [ - DateAndTimeTextFormField( + DateAndTimeTextFormFieldV2( _dateAndTimePeriod?.start, (pickedDateAndTime) => v( () => @@ -76,7 +76,7 @@ class DateAndTimePeriodTextFormFields extends StatelessWidget { end: _dateAndTimePeriod?.end, ), ), - DateAndTimeTextFormField( + DateAndTimeTextFormFieldV2( _dateAndTimePeriod?.end, (pickedDateAndTime) => v( () => _dateAndTimePeriod?.start == null && diff --git a/lib/components/date_and_time/date_and_time_text_form_field.dart b/lib/components/date_and_time/date_and_time_text_form_field.dart index a0a879408..3ed632499 100644 --- a/lib/components/date_and_time/date_and_time_text_form_field.dart +++ b/lib/components/date_and_time/date_and_time_text_form_field.dart @@ -5,12 +5,12 @@ import 'package:mem/core/date_and_time/date_and_time.dart'; import 'package:mem/core/date_and_time/date_and_time_period.dart'; import 'package:mem/logger/log_service.dart'; -class DateAndTimeTextFormField extends StatelessWidget { +class DateAndTimeTextFormFieldV2 extends StatelessWidget { final DateAndTime? _dateAndTime; final void Function(DateAndTime? pickedDateAndTime) _onChanged; final DateAndTimePeriod? _selectableRange; - const DateAndTimeTextFormField( + const DateAndTimeTextFormFieldV2( this._dateAndTime, this._onChanged, { super.key, diff --git a/lib/components/mem/list/actions.dart b/lib/components/mem/list/actions.dart index 731959206..8fb04d1b4 100644 --- a/lib/components/mem/list/actions.dart +++ b/lib/components/mem/list/actions.dart @@ -1,10 +1,10 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:mem/components/mem/list/states.dart'; import 'package:mem/logger/log_service.dart'; +import 'package:mem/mems/mem_item.dart'; import 'package:mem/mems/mem_item_repository.dart'; -import 'package:mem/mems/mem_entity.dart'; -import 'package:mem/mems/mem_item_entity.dart'; -import 'package:mem/mems/mem_repository.dart'; +import 'package:mem/repositories/mem.dart'; +import 'package:mem/repositories/mem_repository.dart'; import 'package:mem/mems/states.dart'; final loadMemList = FutureProvider( @@ -30,16 +30,15 @@ final loadMemList = FutureProvider( ref.read(memsProvider.notifier).upsertAll( mems, - (tmp, item) => tmp is SavedMemEntity && item is SavedMemEntity - ? tmp.id == item.id - : false, + (tmp, item) => + tmp is SavedMem && item is SavedMem ? tmp.id == item.id : false, ); for (var mem in mems) { ref.read(memItemsProvider.notifier).upsertAll( - await MemItemRepository().ship(memId: mem.id), + await MemItemRepository().shipByMemId(mem.id), (current, updating) => - current is SavedMemItemEntity && - updating is SavedMemItemEntity && + current is SavedMemItem && + updating is SavedMemItem && current.id == updating.id, ); } diff --git a/lib/components/mem/list/states.dart b/lib/components/mem/list/states.dart index 3a68d8464..aa9a3551f 100644 --- a/lib/components/mem/list/states.dart +++ b/lib/components/mem/list/states.dart @@ -1,7 +1,8 @@ import 'package:collection/collection.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:mem/acts/act_repository.dart'; import 'package:mem/acts/states.dart'; -import 'package:mem/acts/act.dart'; +import 'package:mem/core/act.dart'; import 'package:mem/core/date_and_time/date_and_time_period.dart'; import 'package:mem/components/list_value_state_notifier.dart'; import 'package:mem/components/value_state_notifier.dart'; @@ -9,11 +10,9 @@ import 'package:mem/logger/log_service.dart'; import 'package:mem/mems/list/states.dart'; import 'package:mem/mems/states.dart'; import 'package:mem/notifications/mem_notifications.dart'; -import 'package:mem/acts/act_entity.dart'; -import 'package:mem/acts/act_repository.dart'; -import 'package:mem/mems/mem_entity.dart'; -import 'package:mem/mems/mem_notification_entity.dart'; -import 'package:mem/mems/mem_notification_repository.dart'; +import 'package:mem/repositories/mem.dart'; +import 'package:mem/repositories/mem_notification.dart'; +import 'package:mem/repositories/mem_notification_repository.dart'; import 'package:mem/settings/states.dart'; final showNotArchivedProvider = @@ -40,9 +39,9 @@ final showDoneProvider = StateNotifierProvider, bool>( ), ); final _filteredMemsProvider = StateNotifierProvider.autoDispose< - ListValueStateNotifier, List>( + ListValueStateNotifier, List>( (ref) { - final savedMems = ref.watch(memsProvider).whereType(); + final savedMems = ref.watch(memsProvider).map((e) => e as SavedMem); final showNotArchived = ref.watch(showNotArchivedProvider); final showArchived = ref.watch(showArchivedProvider); @@ -86,7 +85,7 @@ final _filteredMemsProvider = StateNotifierProvider.autoDispose< ); final memListProvider = StateNotifierProvider.autoDispose< - ValueStateNotifier>, List>((ref) { + ValueStateNotifier>, List>((ref) { final filtered = ref.watch(_filteredMemsProvider); final latestActsByMem = ref.watch(latestActsByMemProvider); final savedMemNotifications = ref.watch(savedMemNotificationsProvider); @@ -98,10 +97,8 @@ final memListProvider = StateNotifierProvider.autoDispose< latestActsByMem.singleWhereOrNull((act) => act.memId == a.id); final latestActOfB = latestActsByMem.singleWhereOrNull((act) => act.memId == b.id); - final comparedByActiveAct = Act.activeCompare( - latestActOfA, - latestActOfB, - ); + final comparedByActiveAct = + Act.activeCompare(latestActOfA, latestActOfB); if (comparedByActiveAct != 0) { return comparedByActiveAct; } @@ -183,7 +180,7 @@ int _compareTime( ); final latestActsByMemProvider = StateNotifierProvider.autoDispose< - ListValueStateNotifier, List>( + ListValueStateNotifier, List>( (ref) => v( () => ListValueStateNotifier( ref.watch( @@ -199,15 +196,15 @@ final latestActsByMemProvider = StateNotifierProvider.autoDispose< initializer: (current, notifier) => v( () async { if (current.isEmpty) { - ref.read(actsProvider.notifier).addAll( - await ActRepository().ship( - memIdsIn: ref - .read(memsProvider) - .whereType() - .map((e) => e.id), - latestByMemIds: true, - ), - ); + final memIds = + ref.read(memsProvider).whereType().map((e) => e.id); + + final actsByMemIds = await ActRepository().ship( + memIdsIn: memIds, + latestByMemIds: true, + ); + + ref.read(actsProvider.notifier).addAll(actsByMemIds); } }, {'current': current}, @@ -216,21 +213,18 @@ final latestActsByMemProvider = StateNotifierProvider.autoDispose< ), ); final savedMemNotificationsProvider = StateNotifierProvider.autoDispose< - ListValueStateNotifier, - List>( + ListValueStateNotifier, List>( (ref) => v( () => ListValueStateNotifier( ref.watch( memNotificationsProvider.select( - (value) => value.whereType().toList()), + (value) => value.whereType().toList()), ), initializer: (current, notifier) => v( () async { if (current.isEmpty) { - final memIds = ref - .read(memsProvider) - .whereType() - .map((e) => e.id); + final memIds = + ref.read(memsProvider).whereType().map((e) => e.id); final actsByMemIds = await MemNotificationRepository().ship( memIdsIn: memIds, @@ -239,8 +233,8 @@ final savedMemNotificationsProvider = StateNotifierProvider.autoDispose< ref.read(memNotificationsProvider.notifier).upsertAll( actsByMemIds, (current, updating) => - current is SavedMemNotificationEntity && - updating is SavedMemNotificationEntity && + current is SavedMemNotification && + updating is SavedMemNotification && current.id == updating.id, ); } @@ -252,10 +246,8 @@ final savedMemNotificationsProvider = StateNotifierProvider.autoDispose< ); final activeActsProvider = StateNotifierProvider.autoDispose< - ListValueStateNotifier, List>( - (ref) => v( - () => ListValueStateNotifier( - ref.watch(actsProvider).where((act) => act.isActive).toList(), - ), - ), + ListValueStateNotifier, List>( + (ref) => v(() => ListValueStateNotifier( + ref.watch(actsProvider).where((act) => act.period.end == null).toList(), + )), ); diff --git a/lib/components/mem/list/view.dart b/lib/components/mem/list/view.dart index 838dbde66..13a8cbd32 100644 --- a/lib/components/mem/list/view.dart +++ b/lib/components/mem/list/view.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:mem/components/async_value_view.dart'; import 'package:mem/logger/log_service.dart'; -import 'package:mem/mems/mem_entity.dart'; +import 'package:mem/repositories/mem.dart'; import 'actions.dart'; import 'states.dart'; @@ -32,7 +32,7 @@ class MemListView extends ConsumerWidget { } class _MemListViewComponent extends StatelessWidget { - final List _memList; + final List _memList; final Widget _appBar; final Widget Function(int memId) _itemBuilder; final ScrollController? _scrollController; diff --git a/lib/components/mem/mem_done_checkbox.dart b/lib/components/mem/mem_done_checkbox.dart index 6b6f6a798..b40fc87e4 100644 --- a/lib/components/mem/mem_done_checkbox.dart +++ b/lib/components/mem/mem_done_checkbox.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; -import 'package:mem/mems/mem.dart'; +import 'package:mem/core/mem.dart'; import 'package:mem/components/hero_view.dart'; import 'package:mem/logger/log_service.dart'; -import 'package:mem/mems/mem_entity.dart'; +import 'package:mem/repositories/mem.dart'; class MemDoneCheckbox extends StatelessWidget { final Mem _mem; @@ -19,11 +19,11 @@ class MemDoneCheckbox extends StatelessWidget { () => HeroView( heroTag( 'mem-done', - _mem is SavedMemEntity ? _mem.id : null, + _mem is SavedMem ? _mem.id : null, ), Checkbox( value: _mem.isDone, - onChanged: (_mem is SavedMemEntity ? _mem.isArchived : false) + onChanged: (_mem is SavedMem ? _mem.isArchived : false) ? null : _onChanged, ), diff --git a/lib/components/mem/mem_name.dart b/lib/components/mem/mem_name.dart index 7db60a2d3..34219108a 100644 --- a/lib/components/mem/mem_name.dart +++ b/lib/components/mem/mem_name.dart @@ -4,14 +4,14 @@ import 'package:mem/components/hero_view.dart'; import 'package:mem/components/l10n.dart'; import 'package:mem/logger/log_service.dart'; import 'package:mem/mems/detail/states.dart'; -import 'package:mem/mems/mem_entity.dart'; +import 'package:mem/repositories/mem.dart'; Key keyMemName = const Key("mem-name"); String _memNameTag(int? memId) => heroTag('mem-name', memId); class MemNameText extends StatelessWidget { - final SavedMemEntity _mem; + final SavedMem _mem; const MemNameText(this._mem, {super.key}); diff --git a/lib/acts/act.dart b/lib/core/act.dart similarity index 56% rename from lib/acts/act.dart rename to lib/core/act.dart index 4dad2248a..275417125 100644 --- a/lib/acts/act.dart +++ b/lib/core/act.dart @@ -1,10 +1,12 @@ import 'dart:core'; +import 'package:mem/framework/repository/entity.dart'; +import 'package:mem/framework/repository/database_tuple_entity.dart'; import 'package:mem/logger/log_service.dart'; -import '../core/date_and_time/date_and_time_period.dart'; +import 'date_and_time/date_and_time_period.dart'; -class Act { +class Act extends EntityV1 { final int memId; final DateAndTimePeriod period; @@ -30,4 +32,23 @@ class Act { }, {'a': a, 'b': b}, ); + + @override + String toString() => "${super.toString()}: ${{ + "memId": memId, + "period": period, + }}"; +} + +class SavedAct extends Act with SavedDatabaseTupleMixin { + SavedAct(super.memId, super.period); + + SavedAct copiedWith(DateAndTimePeriod Function()? period) => SavedAct( + memId, + period == null ? this.period : period(), + ) + ..id = id + ..createdAt = createdAt + ..updatedAt = updatedAt + ..archivedAt = archivedAt; } diff --git a/lib/mems/mem.dart b/lib/core/mem.dart similarity index 73% rename from lib/mems/mem.dart rename to lib/core/mem.dart index c1064f1ce..660256578 100644 --- a/lib/mems/mem.dart +++ b/lib/core/mem.dart @@ -1,12 +1,13 @@ // FIXME coreからflutterへの依存は排除したい import 'package:flutter/material.dart'; import 'package:mem/core/date_and_time/date_and_time_period.dart'; +import 'package:mem/framework/repository/entity.dart'; import 'package:mem/logger/log_service.dart'; import 'package:mem/notifications/notification/type.dart'; import 'package:mem/notifications/schedule.dart'; -import 'package:mem/mems/mem_entity.dart'; +import 'package:mem/repositories/mem.dart'; -class Mem { +class Mem extends EntityV1 { final String name; final DateTime? doneAt; final DateAndTimePeriod? period; @@ -15,13 +16,14 @@ class Mem { bool get isDone => doneAt != null; + factory Mem.defaultNew() => Mem("", null, null); + Iterable periodSchedules( TimeOfDay startOfDay, ) => v( () { - final id = - this is SavedMemEntity ? (this as SavedMemEntity).id : null; + final id = this is SavedMem ? (this as SavedMem).id : null; return id == null ? throw Exception() // coverage:ignore-line @@ -61,4 +63,22 @@ class Mem { 'startOfDay': startOfDay, }, ); + + Mem copiedWith({ + String Function()? name, + DateTime? Function()? doneAt, + DateAndTimePeriod? Function()? period, + }) => + Mem( + name == null ? this.name : name(), + doneAt == null ? this.doneAt : doneAt(), + period == null ? this.period : period(), + ); + + @override + String toString() => "${super.toString()}: ${{ + "name": name, + "doneAt": doneAt, + "period": period, + }}"; } diff --git a/lib/core/mem_detail.dart b/lib/core/mem_detail.dart new file mode 100644 index 000000000..7cf993f59 --- /dev/null +++ b/lib/core/mem_detail.dart @@ -0,0 +1,18 @@ +import 'package:mem/core/mem.dart'; +import 'package:mem/core/mem_item.dart'; +import 'package:mem/core/mem_notification.dart'; + +class MemDetail { + final Mem mem; + final List memItems; + final List? notifications; + + MemDetail(this.mem, this.memItems, [this.notifications]); + + @override + String toString() => { + 'mem': mem, + 'memItems': memItems, + 'notifications': notifications, + }.toString(); +} diff --git a/lib/core/mem_item.dart b/lib/core/mem_item.dart new file mode 100644 index 000000000..0845ebbe7 --- /dev/null +++ b/lib/core/mem_item.dart @@ -0,0 +1,33 @@ +import 'package:mem/framework/repository/entity.dart'; + +enum MemItemType { + memo, +} + +class MemItem extends EntityV1 { + // 未保存のMemに紐づくMemItemはmemIdをintで持つことができないため暫定的にnullableにしている + final int? memId; + final MemItemType type; + final dynamic value; + + MemItem(this.memId, this.type, this.value); + + factory MemItem.memo(int? memId) => MemItem(memId, MemItemType.memo, ""); + + MemItem copiedWith({ + int Function()? memId, + dynamic Function()? value, + }) => + MemItem( + memId == null ? this.memId : memId(), + type, + value == null ? this.value : value(), + ); + + @override + String toString() => "${super.toString()}: ${{ + "memId": memId, + "type": type, + "value": value, + }}"; +} diff --git a/lib/mems/mem_notification.dart b/lib/core/mem_notification.dart similarity index 72% rename from lib/mems/mem_notification.dart rename to lib/core/mem_notification.dart index e037a02d9..1be20de26 100644 --- a/lib/mems/mem_notification.dart +++ b/lib/core/mem_notification.dart @@ -1,26 +1,14 @@ import 'package:collection/collection.dart'; import 'package:intl/intl.dart'; import 'package:mem/core/date_and_time/date_and_time.dart'; +import 'package:mem/framework/repository/entity.dart'; import 'package:mem/logger/log_service.dart'; const _repeatedMessage = "Repeat"; const _repeatByDayOfWeekMessage = "Repeat by day of week"; const _afterActStartedMessage = "Finish?"; -enum MemNotificationType { - repeat, - repeatByNDay, - repeatByDayOfWeek, - afterActStarted; - - factory MemNotificationType.fromName(String name) => - MemNotificationType.values.singleWhere( - (element) => element.name == name, - orElse: () => throw Exception('Unexpected name: "$name".'), - ); -} - -class MemNotification { +class MemNotification extends EntityV1 { // 未保存のMemに紐づくMemNotificationはmemIdをintで持つことができないため暫定的にnullableにしている final int? memId; final MemNotificationType type; @@ -29,29 +17,6 @@ class MemNotification { MemNotification(this.memId, this.type, this.time, this.message); - static MemNotification initialByType( - int? memId, - MemNotificationType type, { - int? Function()? time, - }) { - switch (type) { -// coverage:ignore-start - case MemNotificationType.repeat: - return MemNotification( - memId, type, time == null ? null : time(), _repeatedMessage); - case MemNotificationType.repeatByNDay: - return MemNotification( - memId, type, time == null ? 1 : time(), _repeatedMessage); - case MemNotificationType.repeatByDayOfWeek: - return MemNotification(memId, type, time == null ? null : time(), - _repeatByDayOfWeekMessage); - case MemNotificationType.afterActStarted: - return MemNotification( - memId, type, time == null ? null : time(), _afterActStartedMessage); -// coverage:ignore-end - } - } - bool isEnabled() => time != null; bool isRepeated() => type == MemNotificationType.repeat; @@ -62,6 +27,39 @@ class MemNotification { bool isAfterActStarted() => type == MemNotificationType.afterActStarted; + factory MemNotification.repeated(int? memId) => MemNotification( + memId, MemNotificationType.repeat, null, _repeatedMessage); + + factory MemNotification.repeatByNDay(int? memId) => MemNotification( + memId, MemNotificationType.repeatByNDay, 1, _repeatedMessage); + + factory MemNotification.repeatByDayOfWeek(int? memId, int time) => + MemNotification(memId, MemNotificationType.repeatByDayOfWeek, time, + _repeatByDayOfWeekMessage); + + factory MemNotification.afterActStarted(int? memId) => MemNotification(memId, + MemNotificationType.afterActStarted, null, _afterActStartedMessage); + + MemNotification copiedWith({ + int Function()? memId, + int? Function()? time, + String Function()? message, + }) => + MemNotification( + memId == null ? this.memId : memId(), + type, + time == null ? this.time : time(), + message == null ? this.message : message(), + ); + + @override + String toString() => "${super.toString()}: ${{ + "memId": memId, + "type": type, + "time": time, + "message": message, + }}"; + static String? toOneLine( Iterable memNotifications, String Function(String at) buildRepeatedNotificationText, @@ -120,27 +118,20 @@ class MemNotification { String Function(String nDay, String at) buildRepeatEveryNDayNotificationText, String Function(DateAndTime dateAndTime) formatToTimeOfDay, - ) => - v( - () { - if (repeatByNDay != null && (repeatByNDay.time ?? 0) > 1) { - return buildRepeatEveryNDayNotificationText( - repeatByNDay.time.toString(), - formatToTimeOfDay( - DateAndTime(0, 0, 0, 0, 0, repeat.time), - ), - ); - } else { - return buildRepeatedNotificationText(formatToTimeOfDay( - DateAndTime(0, 0, 0, 0, 0, repeat.time), - )); - } - }, - { - 'repeat': repeat, - 'repeatByNDay': repeatByNDay, - }, + ) { + if (repeatByNDay != null && (repeatByNDay.time ?? 0) > 1) { + return buildRepeatEveryNDayNotificationText( + repeatByNDay.time.toString(), + formatToTimeOfDay( + DateAndTime(0, 0, 0, 0, 0, repeat.time), + ), ); + } else { + return buildRepeatedNotificationText(formatToTimeOfDay( + DateAndTime(0, 0, 0, 0, 0, repeat.time), + )); + } + } static String _oneLineRepeatByDaysOfWeek( Iterable repeatByDayOfWeeks, @@ -156,9 +147,7 @@ class MemNotification { .map((e) => dateFormat.format(e)) .join(", "); }, - { - 'repeatByDayOfWeeks': repeatByDayOfWeeks, - }, + {'repeatByDayOfWeeks': repeatByDayOfWeeks}, ); static String _oneLineAfterAct( @@ -168,3 +157,16 @@ class MemNotification { buildAfterActStartedNotificationText(DateFormat(DateFormat.HOUR24_MINUTE) .format(DateAndTime(0, 0, 0, 0, 0, afterActStarted.time))); } + +enum MemNotificationType { + repeat, + repeatByNDay, + repeatByDayOfWeek, + afterActStarted; + + factory MemNotificationType.fromName(String name) => + MemNotificationType.values.singleWhere( + (element) => element.name == name, + orElse: () => throw Exception('Unexpected name: "$name".'), + ); +} diff --git a/lib/framework/database/definition/database_definition.dart b/lib/framework/database/definition/database_definition.dart index 88138b6eb..df614c5b0 100644 --- a/lib/framework/database/definition/database_definition.dart +++ b/lib/framework/database/definition/database_definition.dart @@ -2,7 +2,7 @@ import 'package:mem/framework/database/definition/exceptions.dart'; import 'package:mem/framework/database/definition/table_definition.dart'; import 'package:mem/framework/repository/entity.dart'; -class DatabaseDefinition with Entity { +class DatabaseDefinition extends Entity { final String name; final int version; final List tableDefinitions; @@ -22,9 +22,9 @@ class DatabaseDefinition with Entity { } @override - Map get toMap => { - 'name': name, - 'version': version, - 'tableDefinitions': tableDefinitions.map((e) => e.toString()) - }; + String toString() => "${super.toString()}: ${{ + "name": name, + "version": version, + "tableDefinitions": tableDefinitions.map((e) => e.toString()) + }}"; } diff --git a/lib/framework/repository/condition/conditions.dart b/lib/framework/repository/condition/conditions.dart index 7ebc3b8e9..44bcb7a9c 100644 --- a/lib/framework/repository/condition/conditions.dart +++ b/lib/framework/repository/condition/conditions.dart @@ -1,4 +1,3 @@ -import 'package:mem/framework/database/converter.dart'; import 'package:mem/framework/database/definition/column/column_definition.dart'; abstract class Condition { @@ -8,19 +7,19 @@ abstract class Condition { } class Equals extends Condition { - final ColumnDefinition _columnDefinition; + final String _key; final dynamic _value; - Equals(this._columnDefinition, this._value); + Equals(this._key, this._value); @override - String? where() => "${_columnDefinition.name} = ?"; + String where() => '$_key = ?'; @override - List? whereArgs() => [DatabaseConverter().to(_value)]; + List? whereArgs() => [_value]; @override - String toString() => "${_columnDefinition.name} = $_value"; + String toString() => '$_key = $_value'; } class IsNull extends Condition { diff --git a/lib/framework/repository/database_repository.dart b/lib/framework/repository/database_repository.dart index e9f76112d..382314796 100644 --- a/lib/framework/repository/database_repository.dart +++ b/lib/framework/repository/database_repository.dart @@ -8,7 +8,8 @@ import 'package:mem/framework/database/factory.dart'; import 'package:mem/framework/repository/repository.dart'; import 'package:mem/logger/log_service.dart'; -class DatabaseRepository extends Repository { +class DatabaseRepository extends Repository + with Receiver { static DatabaseRepository? _instance; final _cache = {}; @@ -17,6 +18,7 @@ class DatabaseRepository extends Repository { factory DatabaseRepository() => _instance ??= DatabaseRepository._(); + @override Future receive(DatabaseDefinition entity) => v( () async => _cache[entity.name] ?? diff --git a/lib/framework/repository/database_tuple_entity.dart b/lib/framework/repository/database_tuple_entity.dart index 5040c28ea..ba4cab577 100644 --- a/lib/framework/repository/database_tuple_entity.dart +++ b/lib/framework/repository/database_tuple_entity.dart @@ -1,28 +1,36 @@ import 'package:mem/databases/table_definitions/base.dart'; import 'package:mem/framework/repository/entity.dart'; -mixin DatabaseTupleEntity on Entity { - late PrimaryKey id; +mixin SavedDatabaseTupleMixin on EntityV1 { + late T id; late DateTime createdAt; - late DateTime? updatedAt; - late DateTime? archivedAt; + DateTime? updatedAt; + DateTime? archivedAt; bool get isArchived => archivedAt != null; - DatabaseTupleEntity withMap(Map map) { - id = map[defPkId.name]; - createdAt = map[defColCreatedAt.name]; - updatedAt = map[defColUpdatedAt.name]; - archivedAt = map[defColArchivedAt.name]; - return this; + void pack(Map tuple) { + id = tuple[defPkId.name]; + createdAt = tuple[defColCreatedAt.name]; + updatedAt = tuple[defColUpdatedAt.name]; + archivedAt = tuple[defColArchivedAt.name]; } - @override - Map get toMap => super.toMap - ..addAll({ + Map unpack() { + return { defPkId.name: id, defColCreatedAt.name: createdAt, defColUpdatedAt.name: updatedAt, defColArchivedAt.name: archivedAt, - }); + }; + } + + SavedDatabaseTupleMixin copiedFrom(SavedDatabaseTupleMixin origin) => this + ..id = origin.id + ..createdAt = origin.createdAt + ..updatedAt = origin.updatedAt + ..archivedAt = origin.archivedAt; + + @override + String toString() => "${super.toString()}${unpack()}"; } diff --git a/lib/framework/repository/database_tuple_repository.dart b/lib/framework/repository/database_tuple_repository.dart index 4c534df6c..aaeede53c 100644 --- a/lib/framework/repository/database_tuple_repository.dart +++ b/lib/framework/repository/database_tuple_repository.dart @@ -1,78 +1,58 @@ import 'package:mem/databases/table_definitions/base.dart'; import 'package:mem/framework/database/accessor.dart'; -import 'package:mem/framework/database/definition/database_definition.dart'; import 'package:mem/framework/database/definition/table_definition.dart'; -import 'package:mem/framework/repository/condition/conditions.dart'; -import 'package:mem/framework/repository/database_repository.dart'; -import 'package:mem/framework/repository/database_tuple_entity.dart'; import 'package:mem/framework/repository/entity.dart'; import 'package:mem/framework/repository/group_by.dart'; import 'package:mem/framework/repository/order_by.dart'; import 'package:mem/framework/repository/repository.dart'; import 'package:mem/logger/log_service.dart'; +import 'package:mem/framework/repository/condition/conditions.dart'; -abstract class DatabaseTupleRepository extends Repository { - final DatabaseDefinition _databaseDefinition; - final TableDefinition _tableDefinition; +// FIXME byIdの引数の型のためにSavedEntityの型以外にIが必要になっている +// Rにidの型情報が含まれているのに改めて渡す必要があるのはおかしい +// DatabaseTupleに型情報を付与することでズレは発生しなくなった +// ただ、これだと未保存のDatabaseTupleが +// FIXME SavedEntityはSavedDatabaseTupleをmixinしている必要があるが型制約を定義できていない +abstract class DatabaseTupleRepository implements RepositoryV1 { + Map unpack(E entity); - // FIXME DatabaseDefinitionの中にTableDefinitionがあるのでEから取得できるのでは? - DatabaseTupleRepository(this._databaseDefinition, this._tableDefinition); + SavedEntity pack(Map tuple); - static DatabaseAccessor? _databaseAccessor; + @override + Future receive(E entity) => v( + () async { + final entityMap = unpack(entity); - late final Future _dbA = (() async => _databaseAccessor ??= - await DatabaseRepository().receive(_databaseDefinition))(); + entityMap[defColCreatedAt.name] = DateTime.now(); - static Future close() => v( - () async { - await _databaseAccessor?.close(); - _databaseAccessor = null; + final id = await _databaseAccessor!.insert( + _tableDefinition, + entityMap, + ); + + entityMap[defPkId.name] = id; + + return pack(entityMap); }, + entity, ); Future count({ Condition? condition, }) => v( - () async => (await _dbA).count( + () async => await _databaseAccessor!.count( _tableDefinition, where: condition?.where(), whereArgs: condition?.whereArgs(), ), { - 'condition': condition, + "condition": condition, }, ); - SAVED pack(Map map); - - Future receive( - ENTITY entity, { - DateTime? createdAt, - }) => - v( - () async { - final entityMap = entity.toMap; - - entityMap[defColCreatedAt.name] = createdAt ?? DateTime.now(); - - final id = await (await _dbA).insert( - _tableDefinition, - entityMap, - ); - - entityMap[defPkId.name] = id; - - return pack(entityMap); - }, - { - 'entity': entity, - 'createdAt': createdAt, - }, - ); - - Future> ship({ + Future> ship({ Condition? condition, GroupBy? groupBy, List? orderBy, @@ -80,7 +60,7 @@ abstract class DatabaseTupleRepository v( - () async => (await (await _dbA).select( + () async => (await _databaseAccessor!.select( _tableDefinition, groupBy: groupBy?.toQuery, extraColumns: groupBy?.toExtraColumns, @@ -103,104 +83,150 @@ abstract class DatabaseTupleRepository replace( - SAVED savedEntity, { - DateTime? updatedAt, + Future shipById(Id id) => v( + () async { + final condition = Equals(defPkId.name, id); + return pack( + (await _databaseAccessor!.select( + _tableDefinition, + where: condition.where(), + whereArgs: condition.whereArgs(), + )) + .single, + ); + }, + id, + ); + + Future findOneBy({ + Condition? condition, + List? orderBy, }) => v( + () async => await ship( + condition: condition, + orderBy: orderBy, + limit: 1, + ).then((value) => value.length == 1 ? value.single : null), + { + "condition": condition, + "orderBy": orderBy, + }, + ); + + Future replace(SavedEntity payload) => v( () async { - final entityMap = savedEntity.toMap; + final entityMap = unpack(payload); - entityMap[defColUpdatedAt.name] = updatedAt ?? DateTime.now(); + entityMap[defColUpdatedAt.name] = DateTime.now(); - final byId = Equals(defPkId, entityMap[defPkId.name]); - await (await _dbA).update( + final condition = Equals(defPkId.name, entityMap[defPkId.name]); + await _databaseAccessor!.update( _tableDefinition, entityMap, - where: byId.where(), - whereArgs: byId.whereArgs(), + where: condition.where(), + whereArgs: condition.whereArgs(), ); return pack(entityMap); }, - { - 'savedEntity': savedEntity, - 'updatedAt': updatedAt, - }, + payload, ); - Future archive( - SAVED savedEntity, { - DateTime? archivedAt, - }) => - v( + Future archive(SavedEntity payload) => v( () async { - final entityMap = savedEntity.toMap; + final entityMap = unpack(payload); - entityMap[defColArchivedAt.name] = archivedAt ?? DateTime.now(); + entityMap[defColArchivedAt.name] = DateTime.now(); - final byId = Equals(defPkId, entityMap[defPkId.name]); - await (await _dbA).update( + final condition = Equals(defPkId.name, entityMap[defPkId.name]); + await _databaseAccessor!.update( _tableDefinition, entityMap, - where: byId.where(), - whereArgs: byId.whereArgs(), + where: condition.where(), + whereArgs: condition.whereArgs(), ); return pack(entityMap); }, - { - 'savedEntity': savedEntity, - 'archivedAt': archivedAt, - }, + payload, ); - Future unarchive( - SAVED savedEntity, { - DateTime? updatedAt, - }) => - v( + Future unarchive(SavedEntity payload) => v( () async { - final entityMap = savedEntity.toMap; + final entityMap = unpack(payload); - entityMap[defColUpdatedAt.name] = updatedAt ?? DateTime.now(); + entityMap[defColUpdatedAt.name] = DateTime.now(); entityMap[defColArchivedAt.name] = null; - final byId = Equals(defPkId, entityMap[defPkId.name]); - await (await _dbA).update( + final condition = Equals(defPkId.name, entityMap[defPkId.name]); + await _databaseAccessor!.update( _tableDefinition, entityMap, - where: byId.where(), - whereArgs: byId.whereArgs(), + where: condition.where(), + whereArgs: condition.whereArgs(), ); return pack(entityMap); }, - { - 'savedEntity': savedEntity, - 'updatedAt': updatedAt, - }, + payload, ); - Future> waste({ - Condition? condition, - }) => - v( + Future> waste([Condition? condition]) => v( () async { - final targets = await ship( - condition: condition, - ); + final targets = (await _databaseAccessor!.select( + _tableDefinition, + where: condition?.where(), + whereArgs: condition?.whereArgs(), + )) + .map((e) => pack(e)) + .toList(); - await _databaseAccessor!.delete( + final count = await _databaseAccessor!.delete( _tableDefinition, where: condition?.where(), whereArgs: condition?.whereArgs(), ); + assert(count == targets.length); + return targets; }, - { - 'condition': condition, + condition, + ); + + Future wasteById(Id id) => v( + () async { + final condition = Equals(defPkId.name, id); + final payload = (await _databaseAccessor!.select( + _tableDefinition, + where: condition.where(), + whereArgs: condition.whereArgs(), + )) + .single; + await _databaseAccessor!.delete( + _tableDefinition, + where: condition.where(), + whereArgs: condition.whereArgs(), + ); + return pack(payload); }, + id, ); + + static Future close() => v( + () async { + await _databaseAccessor?.close(); + _databaseAccessor = null; + }, + ); + + static DatabaseAccessor? _databaseAccessor; + + static set databaseAccessor(DatabaseAccessor? databaseAccessor) => + _databaseAccessor = databaseAccessor; + + final TableDefinition _tableDefinition; + + DatabaseTupleRepository(this._tableDefinition); } diff --git a/lib/framework/repository/entity.dart b/lib/framework/repository/entity.dart index 7e4fa82ab..de1e74306 100644 --- a/lib/framework/repository/entity.dart +++ b/lib/framework/repository/entity.dart @@ -4,28 +4,9 @@ // # 語源 // // 「存在するもの」、「実体」 -mixin Entity { - Map get toMap; +abstract class EntityV1 {} - @override - String toString() => "${super.toString()}: $toMap"; - - @override - int get hashCode => toMap.entries.fold( - 0, - (value, element) => - value ^ element.key.hashCode ^ element.value.hashCode, - ); - - @override - bool operator ==(Object other) => - identical(this, other) || - (runtimeType == other.runtimeType && hashCode == other.hashCode); -} - -mixin Copyable on Entity { - T copiedWith(); -} +abstract class Entity {} // memo // - view, domain, dataのそれぞれの領域で似た内容でも型が変わることになるはず // これをしっかりと定義したい diff --git a/lib/framework/repository/home_widget_entity.dart b/lib/framework/repository/home_widget_entity.dart deleted file mode 100644 index d4edc0539..000000000 --- a/lib/framework/repository/home_widget_entity.dart +++ /dev/null @@ -1,14 +0,0 @@ -import 'package:mem/framework/repository/entity.dart'; - -mixin HomeWidgetEntity on Entity { - String get methodChannelName; - - String get initializeMethodName; - - String get widgetProviderName; - - Map get toWidgetData; -} -mixin SavedHomeWidgetEntity on HomeWidgetEntity { - late final int homeWidgetId; -} diff --git a/lib/framework/repository/home_widget_repository.dart b/lib/framework/repository/home_widget_repository.dart deleted file mode 100644 index b74619fe9..000000000 --- a/lib/framework/repository/home_widget_repository.dart +++ /dev/null @@ -1,59 +0,0 @@ -import 'package:flutter/foundation.dart'; -import 'package:mem/act_counter/home_widget_accessor.dart'; -import 'package:mem/framework/repository/home_widget_entity.dart'; -import 'package:mem/framework/repository/repository.dart'; -import 'package:mem/logger/log_service.dart'; - -abstract class HomeWidgetRepository extends Repository { - static final _homeWidgetAccessor = - defaultTargetPlatform == TargetPlatform.android - ? HomeWidgetAccessor() - : null; - - SAVED pack(Map map); - - Future receive( - ENTITY entity, - ) => - v( - () async { - final homeWidgetId = await _homeWidgetAccessor?.initialize( - entity.methodChannelName, - entity.initializeMethodName, - ); - - if (homeWidgetId != null) { - final saved = pack(entity.toMap - ..addAll({ - 'homeWidgetId': homeWidgetId, - })); - - saved.toWidgetData.forEach( - (key, value) async => - await _homeWidgetAccessor?.saveWidgetData(key, value), - ); - await _homeWidgetAccessor?.updateWidget(saved.widgetProviderName); - } - }, - { - 'entity': entity, - }, - ); - - Future replace( - ENTITY entity, - ) => - v( - () async { - entity.toWidgetData.forEach((key, value) async { - await _homeWidgetAccessor?.saveWidgetData(key, value); - }); - - await _homeWidgetAccessor?.updateWidget(entity.widgetProviderName); - }, - { - 'entity': entity, - }, - ); -} diff --git a/lib/framework/repository/key_with_value.dart b/lib/framework/repository/key_with_value.dart index c46d1010a..1f005c79c 100644 --- a/lib/framework/repository/key_with_value.dart +++ b/lib/framework/repository/key_with_value.dart @@ -1,10 +1,14 @@ import 'package:mem/framework/repository/entity.dart'; -// FIXME IdWithValueの方が命名として適切なのでは? -mixin KeyWithValue on Entity { - late final KEY key; - late final VALUE value; +abstract class KeyWithValue extends Entity { + final Key key; + final Value value; + + KeyWithValue(this.key, this.value); @override - Map get toMap => {'key': key, 'value': value}; + String toString() => { + "key": key, + "value": value, + }.toString(); } diff --git a/lib/framework/repository/key_with_value_repository.dart b/lib/framework/repository/key_with_value_repository.dart index ee6be118d..638d11364 100644 --- a/lib/framework/repository/key_with_value_repository.dart +++ b/lib/framework/repository/key_with_value_repository.dart @@ -1,14 +1,11 @@ import 'package:mem/framework/repository/key_with_value.dart'; import 'package:mem/framework/repository/repository.dart'; -abstract class KeyWithValueRepository, - KEY> extends Repository { - Future receive(ENTITY entity); +abstract class KeyWithValueRepository, Key> + extends Repository + with Receiver, DiscarderByKey {} - Future discard(KEY key); -} - -mixin DiscardAll, KEY> - on KeyWithValueRepository { - Future discardAll(); +mixin DiscarderByKey, Key, Result> + on Repository { + Future discard(Key key); } diff --git a/lib/framework/repository/order_by.dart b/lib/framework/repository/order_by.dart index 6cf1a1d7f..21c7d48ca 100644 --- a/lib/framework/repository/order_by.dart +++ b/lib/framework/repository/order_by.dart @@ -8,12 +8,12 @@ abstract class OrderBy { String toQuery(); } -// class Ascending extends OrderBy { -// Ascending(super.columnDefinition); -// -// @override -// String toQuery() => "${columnDefinition.name} ASC"; -// } +class Ascending extends OrderBy { + Ascending(super.columnDefinition); + + @override + String toQuery() => "${columnDefinition.name} ASC"; +} class Descending extends OrderBy { Descending(super.columnDefinition); diff --git a/lib/framework/repository/repository.dart b/lib/framework/repository/repository.dart index 181d1b43c..8f0807747 100644 --- a/lib/framework/repository/repository.dart +++ b/lib/framework/repository/repository.dart @@ -19,4 +19,18 @@ import 'package:mem/framework/repository/entity.dart'; // 抽象的には得ると捉える事もできるだろうが、では`update`(更新する)ことはあるだろうか? // 更新することはないように感じる // よって、ここでは`receive`(受け取る)、`replace`(置き換える)などの荷物や事物を扱う際の単語を採用する -abstract class Repository {} +abstract class Repository {} + +mixin Receiver on Repository { + Future receive(E entity); +} + +// FIXME 型指定は不要なはずなので、おかしい?(間違っている気がする +// というか、mixinの利用自体がなんか変なので辞めたほうが良いかも +mixin Discarder on Repository { + Future discardAll(); +} + +abstract class RepositoryV1 { + Future receive(E entity); +} diff --git a/lib/logger/log.dart b/lib/logger/log.dart index 201c103af..f0a9f9f90 100644 --- a/lib/logger/log.dart +++ b/lib/logger/log.dart @@ -36,7 +36,7 @@ import 'package:mem/framework/repository/entity.dart'; // 言葉の意味としては「丸太を投げ込め!」という号令で、「航海開始」を表す // // 航海日誌においては、最初の記録ということになる -class Log with Entity { +class Log extends Entity { final Level level; final List prefixes; final dynamic target; @@ -102,12 +102,6 @@ class Log with Entity { return elements.join('\n'); } - -// coverage:ignore-start - @override - // TODO: implement toMap - Map get toMap => throw UnimplementedError(); -// coverage:ignore-end } enum Level { diff --git a/lib/logger/log_repository.dart b/lib/logger/log_repository.dart index c203f787b..be98fce0d 100644 --- a/lib/logger/log_repository.dart +++ b/lib/logger/log_repository.dart @@ -3,9 +3,10 @@ import 'package:mem/framework/repository/repository.dart'; import 'log.dart'; import 'logger_wrapper.dart'; -class LogRepository extends Repository { +class LogRepository extends Repository with Receiver { LoggerWrapper _loggerWrapper; + @override Future receive(Log entity) async => _loggerWrapper.log( entity.level, entity.buildMessage(), diff --git a/lib/main.dart b/lib/main.dart index 375e8099d..77e3f36f5 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,10 +1,14 @@ import 'package:flutter/material.dart'; +import 'package:mem/act_counter/act_counter_repository.dart'; import 'package:mem/act_counter/act_counter_client.dart'; import 'package:mem/core/date_and_time/date_and_time.dart'; +import 'package:mem/databases/definition.dart'; import 'package:mem/logger/log_service.dart'; import 'package:mem/notifications/flutter_local_notifications_wrapper.dart'; import 'package:mem/notifications/notification_actions.dart'; import 'package:mem/notifications/notification_repository.dart'; +import 'package:mem/framework/repository/database_repository.dart'; +import 'package:mem/framework/repository/database_tuple_repository.dart'; import 'package:mem/router.dart'; import 'application.dart'; @@ -13,7 +17,7 @@ Future main({String? languageCode}) => i( () async { WidgetsFlutterBinding.ensureInitialized(); - await NotificationRepository().ship(); + await NotificationRepository().checkNotification(); return _runApplication(languageCode: languageCode); }, @@ -46,6 +50,7 @@ Future launchActCounterConfigure() => i( Future onNotificationResponseReceived(dynamic details) => i( () async { WidgetsFlutterBinding.ensureInitialized(); + await openDatabase(); await onDidReceiveNotificationResponse( details, @@ -85,6 +90,9 @@ Future _runApplication({ }) => i( () async { + await openDatabase(); + ActCounterRepository(); + runApp( MemApplication( initialPath: initialPath, @@ -97,6 +105,10 @@ Future _runApplication({ }, ); +// FIXME 先に初期化が必要なのではなく、Repositoryを利用する際に勝手に初期化されるようにする +Future openDatabase() async => DatabaseTupleRepository.databaseAccessor = + await DatabaseRepository().receive(databaseDefinition); + // FIXME HomeWidget関連の処理、場所が適切ではない const uriSchema = 'mem'; const appId = 'zin.playground.mem'; @@ -107,6 +119,8 @@ const memIdParamName = 'mem_id'; Future backgroundCallback(Uri? uri) => i( () async { if (uri != null && uri.scheme == uriSchema && uri.host == appId) { + await openDatabase(); + if (uri.pathSegments.contains(actCounter)) { final memId = uri.queryParameters[memIdParamName]; diff --git a/lib/mems/actions.dart b/lib/mems/actions.dart index 72f7ae16c..78baaa4b5 100644 --- a/lib/mems/actions.dart +++ b/lib/mems/actions.dart @@ -1,9 +1,9 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:mem/logger/log_service.dart'; +import 'package:mem/mems/mem_item.dart'; import 'package:mem/mems/mem_service.dart'; import 'package:mem/mems/states.dart'; -import 'package:mem/mems/mem_entity.dart'; -import 'package:mem/mems/mem_item_entity.dart'; +import 'package:mem/repositories/mem.dart'; final undoRemoveMem = FutureProvider.autoDispose.family( (ref, memId) => v( @@ -15,16 +15,14 @@ final undoRemoveMem = FutureProvider.autoDispose.family( await MemService().save(removedMemDetail, undo: true); final removeUndoneMem = removeUndone.mem; - if (removeUndoneMem is SavedMemEntity) { + if (removeUndoneMem is SavedMem) { ref.read(memsProvider.notifier).add(removeUndoneMem); for (var element in removeUndone.memItems) { ref.read(memItemsProvider.notifier).upsertAll( - [ - element, - ], + [element], (current, updating) => - current is SavedMemItemEntity && - updating is SavedMemItemEntity && + current is SavedMemItem && + updating is SavedMemItem && current.id == updating.id, ); } diff --git a/lib/mems/detail/actions.dart b/lib/mems/detail/actions.dart index f75860211..544ae8bc3 100644 --- a/lib/mems/detail/actions.dart +++ b/lib/mems/detail/actions.dart @@ -1,11 +1,11 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:mem/mems/mem_detail.dart'; +import 'package:mem/core/mem_detail.dart'; import 'package:mem/logger/log_service.dart'; import 'package:mem/mems/detail/states.dart'; import 'package:mem/mems/mem_client.dart'; +import 'package:mem/repositories/mem.dart'; +import 'package:mem/repositories/mem_notification.dart'; import 'package:mem/mems/states.dart'; -import 'package:mem/mems/mem_entity.dart'; -import 'package:mem/mems/mem_notification_entity.dart'; final _memClient = MemClient(); @@ -19,10 +19,8 @@ final saveMem = ); ref.read(memsProvider.notifier).upsertAll( - [ - saved.mem, - ], - (tmp, item) => tmp is SavedMemEntity && item is SavedMemEntity + [saved.mem], + (tmp, item) => tmp is SavedMem && item is SavedMem ? tmp.id == item.id : false, ); @@ -39,8 +37,8 @@ final saveMem = ref.read(memNotificationsProvider.notifier).upsertAll( saved.notifications ?? [], (tmp, item) => - tmp is SavedMemNotificationEntity && - item is SavedMemNotificationEntity && + tmp is SavedMemNotification && + item is SavedMemNotification && tmp.id == item.id, removeWhere: (current) => current.isRepeatByDayOfWeek(), ); @@ -55,17 +53,14 @@ final archiveMem = Provider.autoDispose.family, int?>( () async { final mem = ref.read(memByMemIdProvider(memId)); - final archived = await _memClient.archive(mem!); + final archived = await _memClient.archive(mem as SavedMem); ref .read(editingMemByMemIdProvider(memId).notifier) .updatedBy(archived.mem); ref.read(memsProvider.notifier).upsertAll( - [ - archived.mem, - ], - (tmp, item) => tmp is SavedMemEntity && item is SavedMemEntity - ? tmp.id == item.id - : false); + [archived.mem], + (tmp, item) => + tmp is SavedMem && item is SavedMem ? tmp.id == item.id : false); return archived; }, @@ -76,22 +71,17 @@ final archiveMem = Provider.autoDispose.family, int?>( final unarchiveMem = Provider.autoDispose.family, int?>( (ref, memId) => v( () async { - final unarchived = await _memClient.unarchive( - ref.read( - memByMemIdProvider(memId), - )!, - ); + final mem = ref.read(memByMemIdProvider(memId)); + + final unarchived = await _memClient.unarchive(mem as SavedMem); ref .read(editingMemByMemIdProvider(memId).notifier) .updatedBy(unarchived.mem); ref.read(memsProvider.notifier).upsertAll( - [ - unarchived.mem, - ], - (tmp, item) => tmp is SavedMemEntity && item is SavedMemEntity - ? tmp.id == item.id - : false); + [unarchived.mem], + (tmp, item) => + tmp is SavedMem && item is SavedMem ? tmp.id == item.id : false); return unarchived; }, @@ -105,17 +95,16 @@ final removeMem = Provider.autoDispose.family, int?>( if (memId != null) { final removeSuccess = await _memClient.remove(memId); - final mem = ref.read(memByMemIdProvider(memId)); - ref.read(removedMemProvider(memId).notifier).updatedBy( - mem, - ); + ref + .read(removedMemProvider(memId).notifier) + .updatedBy(ref.read(memByMemIdProvider(memId))); ref.read(removedMemItemsProvider(memId).notifier).updatedBy( ref.read(memItemsByMemIdProvider(memId)), ); // TODO mem notificationsにも同様の処理が必要では? ref.read(memsProvider.notifier).removeWhere( - (element) => element is SavedMemEntity && element.id == memId); + (element) => element is SavedMem && element.id == memId); return removeSuccess; } diff --git a/lib/mems/detail/body.dart b/lib/mems/detail/body.dart index 51e58b866..f4cdf4946 100644 --- a/lib/mems/detail/body.dart +++ b/lib/mems/detail/body.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:mem/components/created_and_updated_at_texts.dart'; -import 'package:mem/mems/mem.dart'; +import 'package:mem/core/mem.dart'; import 'package:mem/logger/log_service.dart'; import 'package:mem/mems/detail/notifications/mem_notifications_view.dart'; import 'package:mem/components/mem/mem_done_checkbox.dart'; @@ -72,9 +72,7 @@ class _MemDetailBodyComponent extends StatelessWidget { ), Padding( padding: pageBottomPadding, - child: CreatedAndUpdatedAtTexts( - _mem, - ), + child: CreatedAndUpdatedAtTexts(_mem), ), ], ), diff --git a/lib/mems/detail/mem_items_view.dart b/lib/mems/detail/mem_items_view.dart index f37638dc3..88d035089 100644 --- a/lib/mems/detail/mem_items_view.dart +++ b/lib/mems/detail/mem_items_view.dart @@ -1,9 +1,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:mem/components/l10n.dart'; +import 'package:mem/core/mem_item.dart'; import 'package:mem/logger/log_service.dart'; import 'package:mem/mems/detail/states.dart'; -import 'package:mem/mems/mem_item_entity.dart'; +import 'package:mem/mems/mem_item.dart'; const keyMemMemo = Key("mem-memo"); @@ -20,8 +21,7 @@ class MemItemsFormFields extends ConsumerWidget { () => ref.read(memItemsByMemIdProvider(_memId).notifier).upsertAll( [previous.copiedWith(value: () => entered)], (current, updating) => current.type == updating.type && - (current is SavedMemItemEntity && - updating is SavedMemItemEntity) + (current is SavedMemItem && updating is SavedMemItem) ? current.id == updating.id : true, ), @@ -35,8 +35,8 @@ class MemItemsFormFields extends ConsumerWidget { } class _MemItemsFormFields extends StatelessWidget { - final List _memItems; - final void Function(dynamic entered, MemItemEntity previous) _onChanged; + final List _memItems; + final void Function(dynamic entered, MemItem previous) _onChanged; const _MemItemsFormFields(this._memItems, this._onChanged); diff --git a/lib/mems/detail/notifications/after_act_started_notification_view.dart b/lib/mems/detail/notifications/after_act_started_notification_view.dart index ddba00a8b..2e08a5d4a 100644 --- a/lib/mems/detail/notifications/after_act_started_notification_view.dart +++ b/lib/mems/detail/notifications/after_act_started_notification_view.dart @@ -3,7 +3,6 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:mem/components/time_text_form_field.dart'; import 'package:mem/logger/log_service.dart'; import 'package:mem/mems/detail/states.dart'; -import 'package:mem/mems/mem_notification_entity.dart'; const keyMemAfterActStartedNotification = Key("mem-after-act-started-notification"); @@ -32,19 +31,13 @@ class AfterActStartedNotificationView extends ConsumerWidget { onTimeChanged: (picked) => ref .read(memNotificationsByMemIdProvider(_memId).notifier) .upsertAll( - [ - (notification as MemNotificationEntity) - .copiedWith(time: () => picked) - ], + [notification.copiedWith(time: () => picked)], (current, updating) => current.type == updating.type, ), onMessageChanged: (value) => ref .read(memNotificationsByMemIdProvider(_memId).notifier) .upsertAll( - [ - (notification as MemNotificationEntity) - .copiedWith(message: () => value) - ], + [notification.copiedWith(message: () => value)], (current, updating) => current.type == updating.type, ), ); diff --git a/lib/mems/detail/notifications/mem_notifications_text.dart b/lib/mems/detail/notifications/mem_notifications_text.dart index 5ad873df0..60a05d745 100644 --- a/lib/mems/detail/notifications/mem_notifications_text.dart +++ b/lib/mems/detail/notifications/mem_notifications_text.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:mem/components/l10n.dart'; -import 'package:mem/mems/mem_notification.dart'; +import 'package:mem/core/mem_notification.dart'; import 'package:mem/logger/log_service.dart'; import 'package:mem/mems/detail/states.dart'; import 'package:mem/values/colors.dart'; diff --git a/lib/mems/detail/notifications/mem_notifications_view.dart b/lib/mems/detail/notifications/mem_notifications_view.dart index 539e01e5d..9502bf5b0 100644 --- a/lib/mems/detail/notifications/mem_notifications_view.dart +++ b/lib/mems/detail/notifications/mem_notifications_view.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:mem/components/l10n.dart'; -import 'package:mem/mems/mem_notification.dart'; +import 'package:mem/core/mem_notification.dart'; import 'package:mem/logger/log_service.dart'; import 'package:mem/mems/detail/notifications/mem_notifications_text.dart'; import 'package:mem/mems/detail/states.dart'; @@ -11,7 +11,7 @@ import 'package:mem/values/durations.dart'; import 'mem_notifications_page.dart'; -const keyMemNotificationsView = Key('mem-notifications'); +const keyMemNotificationsView = Key("mem-notifications"); class MemNotificationsView extends ConsumerWidget { final int? _memId; @@ -25,14 +25,14 @@ class MemNotificationsView extends ConsumerWidget { ref.watch(memNotificationsByMemIdProvider(_memId)), ), { - '_memId': _memId, + "_memId": _memId, }, ); } class _MemNotificationsView extends StatelessWidget { final int? _memId; - final Iterable _memNotifications; + final List _memNotifications; const _MemNotificationsView( this._memId, @@ -83,8 +83,8 @@ class _MemNotificationsView extends StatelessWidget { ); }, { - '_memId': _memId, - '_memNotifications': _memNotifications, + "_memId": _memId, + "_memNotifications": _memNotifications, }, ); } diff --git a/lib/mems/detail/notifications/mem_repeat_by_day_of_week_notification_view.dart b/lib/mems/detail/notifications/mem_repeat_by_day_of_week_notification_view.dart index c471bf798..3ca5d37e5 100644 --- a/lib/mems/detail/notifications/mem_repeat_by_day_of_week_notification_view.dart +++ b/lib/mems/detail/notifications/mem_repeat_by_day_of_week_notification_view.dart @@ -3,13 +3,12 @@ import 'package:day_picker/day_picker.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:intl/intl.dart'; -import 'package:mem/mems/mem_notification.dart'; +import 'package:mem/core/mem_notification.dart'; import 'package:mem/logger/log_service.dart'; import 'package:mem/mems/detail/states.dart'; -import 'package:mem/mems/mem_notification_entity.dart'; const keyMemRepeatByDaysOfWeekNotification = - Key('mem-repeat-by-days-of-week-notification'); + Key("mem-repeat-by-days-of-week-notification"); class MemRepeatByDaysOfWeekNotificationView extends ConsumerWidget { final int? _memId; @@ -34,11 +33,7 @@ class MemRepeatByDaysOfWeekNotificationView extends ConsumerWidget { (e) => daysOfWeek.singleWhereOrNull( (element) => element.time == e) ?? - MemNotificationEntity.initialByType( - _memId, - MemNotificationType.repeatByDayOfWeek, - time: () => e, - ), + MemNotification.repeatByDayOfWeek(_memId, e), ), (current, updating) => current.type == updating.type && @@ -48,17 +43,12 @@ class MemRepeatByDaysOfWeekNotificationView extends ConsumerWidget { current.memId == _memId && !selected.contains(current.time), ), -// coverage:ignore-start - { -// coverage:ignore-end - 'selected': selected, - 'daysOfWeek': daysOfWeek, - }, + {'selected': selected, 'daysOfWeek': daysOfWeek}, ), ); }, { - '_memId': _memId, + "_memId": _memId, }, ); } diff --git a/lib/mems/detail/notifications/mem_repeat_by_n_day_notification_view.dart b/lib/mems/detail/notifications/mem_repeat_by_n_day_notification_view.dart index f8fe13f76..44833aa52 100644 --- a/lib/mems/detail/notifications/mem_repeat_by_n_day_notification_view.dart +++ b/lib/mems/detail/notifications/mem_repeat_by_n_day_notification_view.dart @@ -3,7 +3,6 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:mem/components/l10n.dart'; import 'package:mem/logger/log_service.dart'; import 'package:mem/mems/detail/states.dart'; -import 'package:mem/mems/mem_notification_entity.dart'; import 'package:mem/values/dimens.dart'; const keyMemRepeatByNDayNotification = Key("mem-repeat-by-n-day-notification"); @@ -28,9 +27,9 @@ class MemRepeatByNDayNotificationView extends ConsumerWidget { ) .upsertAll( [ - (notification as MemNotificationEntity).copiedWith( + notification.copiedWith( time: () => value, - ), + ) ], (current, updating) => current.type == updating.type, ); diff --git a/lib/mems/detail/notifications/mem_repeated_notification_view.dart b/lib/mems/detail/notifications/mem_repeated_notification_view.dart index c11e4435f..621d1a89d 100644 --- a/lib/mems/detail/notifications/mem_repeated_notification_view.dart +++ b/lib/mems/detail/notifications/mem_repeated_notification_view.dart @@ -3,7 +3,6 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:mem/components/date_and_time/time_of_day_view.dart'; import 'package:mem/logger/log_service.dart'; import 'package:mem/mems/detail/states.dart'; -import 'package:mem/mems/mem_notification_entity.dart'; import 'package:mem/settings/states.dart'; import 'package:mem/values/constants.dart'; @@ -17,13 +16,12 @@ class MemRepeatedNotificationView extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) => v( () { - final memRepeatNotification = ref.watch( - memNotificationsByMemIdProvider(_memId).select( - (v) => v.singleWhere( - (element) => element.isRepeated(), - ), + final memRepeatNotification = + ref.watch(memNotificationsByMemIdProvider(_memId).select( + (v) => v.singleWhere( + (element) => element.isRepeated(), ), - ); + )); return _MemRepeatedNotificationView( memRepeatNotification.time, @@ -32,7 +30,7 @@ class MemRepeatedNotificationView extends ConsumerWidget { .read(memNotificationsByMemIdProvider(_memId).notifier) .upsertAll( [ - (memRepeatNotification as MemNotificationEntity).copiedWith( + memRepeatNotification.copiedWith( time: () => picked, ) ], diff --git a/lib/mems/detail/page.dart b/lib/mems/detail/page.dart index 98e044f7e..6744c8054 100644 --- a/lib/mems/detail/page.dart +++ b/lib/mems/detail/page.dart @@ -9,7 +9,7 @@ import 'package:mem/mems/detail/app_bar/remove_mem_action.dart'; import 'package:mem/mems/detail/states.dart'; import 'package:mem/mems/detail/app_bar/transit_act_list_action.dart'; import 'package:mem/mems/detail/app_bar/transit_chart_action.dart'; -import 'package:mem/mems/mem_entity.dart'; +import 'package:mem/repositories/mem.dart'; import 'package:mem/values/colors.dart'; class MemDetailPage extends ConsumerWidget { @@ -21,13 +21,13 @@ class MemDetailPage extends ConsumerWidget { Widget build(BuildContext context, WidgetRef ref) => v( () => _MemDetailPage( ref.watch(editingMemByMemIdProvider(_memId).select( - (value) => value is SavedMemEntity ? value.id : null, + (value) => value is SavedMem ? value.id : null, )), ref.watch(editingMemByMemIdProvider(_memId).select( - (value) => value is SavedMemEntity, + (value) => value is SavedMem, )), ref.watch(editingMemByMemIdProvider(_memId).select( - (value) => value is SavedMemEntity ? value.isArchived : false, + (value) => value is SavedMem ? value.isArchived : false, )), ), { diff --git a/lib/mems/detail/states.dart b/lib/mems/detail/states.dart index 3528d8fd6..54ae021ae 100644 --- a/lib/mems/detail/states.dart +++ b/lib/mems/detail/states.dart @@ -1,32 +1,29 @@ import 'package:collection/collection.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:mem/mems/mem_item.dart'; -import 'package:mem/mems/mem_notification.dart'; +import 'package:mem/core/mem.dart'; +import 'package:mem/core/mem_item.dart'; +import 'package:mem/core/mem_notification.dart'; import 'package:mem/components/list_value_state_notifier.dart'; import 'package:mem/logger/log_service.dart'; import 'package:mem/components/value_state_notifier.dart'; +import 'package:mem/mems/mem_item.dart'; import 'package:mem/mems/mem_item_repository.dart'; import 'package:mem/mems/states.dart'; -import 'package:mem/mems/mem_entity.dart'; -import 'package:mem/mems/mem_item_entity.dart'; -import 'package:mem/mems/mem_notification_entity.dart'; -import 'package:mem/mems/mem_notification_repository.dart'; +import 'package:mem/repositories/mem_notification.dart'; +import 'package:mem/repositories/mem_notification_repository.dart'; final editingMemByMemIdProvider = StateNotifierProvider.autoDispose - .family, MemEntity, int?>( + .family, Mem, int?>( (ref, memId) => v( - () { - final mem = ref.watch(memByMemIdProvider(memId)); - return ValueStateNotifier( - mem ?? MemEntity("", null, null), - ); - }, + () => ValueStateNotifier( + ref.watch(memByMemIdProvider(memId)) ?? Mem.defaultNew(), + ), {"memId": memId}, ), ); final memItemsByMemIdProvider = StateNotifierProvider.family< - ListValueStateNotifier, List, int?>( + ListValueStateNotifier, List, int?>( (ref, memId) => v( () => ListValueStateNotifier( [ @@ -37,21 +34,21 @@ final memItemsByMemIdProvider = StateNotifierProvider.family< ), ), ) ?? - MemItemEntity(memId, MemItemType.memo, "") + MemItem.memo(memId), ], initializer: (current, notifier) async { if (memId != null) { ref.read(memItemsProvider.notifier).upsertAll( - await MemItemRepository().ship(memId: memId), + await MemItemRepository().shipByMemId(memId), (current, updating) => - current is SavedMemItemEntity && - updating is SavedMemItemEntity && + current is SavedMemItem && + updating is SavedMemItem && current.id == updating.id, ); } }, ), - {'memId': memId}, + {"memId": memId}, ), ); @@ -106,23 +103,20 @@ final memNotificationsByMemIdProvider = StateNotifierProvider.autoDispose [ ...memNotificationsByMemId, if (memNotificationsByMemId.every((element) => !element.isRepeated())) - MemNotificationEntity.initialByType( - memId, MemNotificationType.repeat), + MemNotification.repeated(memId), if (memNotificationsByMemId .every((element) => !element.isRepeatByNDay())) - MemNotificationEntity.initialByType( - memId, MemNotificationType.repeatByNDay), + MemNotification.repeatByNDay(memId), if (memNotificationsByMemId .every((element) => !element.isAfterActStarted())) - MemNotificationEntity.initialByType( - memId, MemNotificationType.afterActStarted), + MemNotification.afterActStarted(memId), ], initializer: (current, notifier) => v( () async { if (memId != null && - current.whereType().isEmpty) { + current.whereType().isEmpty) { ref.read(memNotificationsProvider.notifier).upsertAll( - await MemNotificationRepository().ship(memId: memId), + await MemNotificationRepository().shipByMemId(memId), (current, updating) => updating.isRepeatByDayOfWeek() ? current.memId == updating.memId && current.type == updating.type && diff --git a/lib/mems/list/item/actions.dart b/lib/mems/list/item/actions.dart index fc8591d53..ebebf3b82 100644 --- a/lib/mems/list/item/actions.dart +++ b/lib/mems/list/item/actions.dart @@ -2,47 +2,46 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:mem/logger/log_service.dart'; import 'package:mem/mems/mem_service.dart'; import 'package:mem/mems/states.dart'; -import 'package:mem/mems/mem_entity.dart'; +import 'package:mem/repositories/mem.dart'; -final doneMem = Provider.autoDispose.family( +final doneMem = Provider.autoDispose.family( (ref, memId) => v( () { + final mem = ref + .read(memByMemIdProvider(memId))! + .copiedWith(doneAt: () => DateTime.now()); + MemService().doneByMemId(memId).then( (doneMemDetail) => ref.read(memsProvider.notifier).upsertAll( - [ - doneMemDetail.mem, - ], - (tmp, item) => tmp is SavedMemEntity && item is SavedMemEntity + [doneMemDetail.mem], + (tmp, item) => tmp is SavedMem && item is SavedMem ? tmp.id == item.id : false, ), ); - return ref - .read(memByMemIdProvider(memId))! - .copiedWith(doneAt: () => DateTime.now()); + return mem; }, memId, ), ); -final undoneMem = Provider.autoDispose.family( +final undoneMem = Provider.autoDispose.family( (ref, memId) => v( () { + final mem = + ref.read(memByMemIdProvider(memId))!.copiedWith(doneAt: () => null); + MemService().undoneByMemId(memId).then( (undoneMemDetail) => ref.read(memsProvider.notifier).upsertAll( - [ - undoneMemDetail.mem, - ], - (tmp, item) => tmp is SavedMemEntity && item is SavedMemEntity + [undoneMemDetail.mem], + (tmp, item) => tmp is SavedMem && item is SavedMem ? tmp.id == item.id : false, ), ); - return ref - .read(memByMemIdProvider(memId))! - .copiedWith(doneAt: () => null); + return mem; }, memId, ), diff --git a/lib/mems/list/item/subtitle.dart b/lib/mems/list/item/subtitle.dart index c27364dc7..bf280ff7c 100644 --- a/lib/mems/list/item/subtitle.dart +++ b/lib/mems/list/item/subtitle.dart @@ -3,12 +3,12 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:mem/components/l10n.dart'; import 'package:mem/components/mem/mem_period.dart'; import 'package:mem/core/date_and_time/date_and_time_period.dart'; -import 'package:mem/mems/mem_notification.dart'; +import 'package:mem/core/mem_notification.dart'; import 'package:mem/logger/log_service.dart'; import 'package:mem/mems/detail/notifications/mem_notifications_text.dart'; import 'package:mem/mems/detail/states.dart'; import 'package:mem/mems/states.dart'; -import 'package:mem/mems/mem_notification_entity.dart'; +import 'package:mem/repositories/mem_notification.dart'; class MemListItemSubtitle extends ConsumerWidget { final int _memId; @@ -22,7 +22,7 @@ class MemListItemSubtitle extends ConsumerWidget { ref.watch(memByMemIdProvider(_memId))?.period, ref.watch( memNotificationsByMemIdProvider(_memId).select( - (v) => v.whereType(), + (v) => v.whereType(), ), ), ), @@ -35,7 +35,7 @@ class MemListItemSubtitle extends ConsumerWidget { class _MemListItemSubtitle extends StatelessWidget { final int _memId; final DateAndTimePeriod? _memPeriod; - final Iterable _savedMemNotifications; + final Iterable _savedMemNotifications; const _MemListItemSubtitle( this._memId, diff --git a/lib/mems/list/item/view.dart b/lib/mems/list/item/view.dart index 6b81b8f68..a82cd9a82 100644 --- a/lib/mems/list/item/view.dart +++ b/lib/mems/list/item/view.dart @@ -6,14 +6,14 @@ import 'package:mem/components/mem/list/states.dart'; import 'package:mem/components/mem/mem_done_checkbox.dart'; import 'package:mem/components/mem/mem_name.dart'; import 'package:mem/components/timer.dart'; -import 'package:mem/mems/mem_notification.dart'; +import 'package:mem/core/act.dart'; +import 'package:mem/core/mem_notification.dart'; import 'package:mem/logger/log_service.dart'; import 'package:mem/mems/detail/states.dart'; import 'package:mem/mems/list/item/subtitle.dart'; import 'package:mem/mems/states.dart'; -import 'package:mem/acts/act_entity.dart'; -import 'package:mem/mems/mem_entity.dart'; -import 'package:mem/mems/mem_notification_entity.dart'; +import 'package:mem/repositories/mem.dart'; +import 'package:mem/repositories/mem_notification.dart'; import 'package:mem/values/colors.dart'; import 'actions.dart'; @@ -40,7 +40,7 @@ class MemListItemView extends ConsumerWidget { ? ref.read(doneMem(_memId)) : ref.read(undoneMem(_memId)) ], - (tmp, item) => tmp is SavedMemEntity && item is SavedMemEntity + (tmp, item) => tmp is SavedMem && item is SavedMem ? tmp.id == item.id : false, ); @@ -65,16 +65,15 @@ class MemListItemView extends ConsumerWidget { class _MemListItemView extends ListTile { _MemListItemView( - SavedMemEntity mem, - SavedActEntity? activeAct, + SavedMem mem, + SavedAct? activeAct, Iterable memNotifications, void Function(int memId) onTap, void Function(bool? value, int memId) onMemDoneCheckboxTapped, - void Function(SavedActEntity? act) onActButtonTapped, + void Function(SavedAct? act) onActButtonTapped, ) : super( leading: memNotifications - .where((e) => - e is SavedMemNotificationEntity && e.isEnabled()) + .where((e) => e is SavedMemNotification && e.isEnabled()) .isEmpty && activeAct == null ? MemDoneCheckbox( @@ -101,15 +100,13 @@ class _MemListItemView extends ListTile { ), subtitle: mem.period == null && memNotifications - .where((e) => - e is SavedMemNotificationEntity && e.isEnabled()) + .where((e) => e is SavedMemNotification && e.isEnabled()) .isEmpty ? null : MemListItemSubtitle(mem.id), isThreeLine: mem.period != null && memNotifications - .where( - (e) => e is SavedMemNotificationEntity && e.isEnabled()) + .where((e) => e is SavedMemNotification && e.isEnabled()) .isNotEmpty, tileColor: mem.isArchived ? secondaryGreyColor : null, onTap: () => onTap(mem.id), diff --git a/lib/mems/mem_client.dart b/lib/mems/mem_client.dart index cb2b6d7e0..046fbccf9 100644 --- a/lib/mems/mem_client.dart +++ b/lib/mems/mem_client.dart @@ -1,11 +1,11 @@ -import 'package:mem/mems/mem.dart'; -import 'package:mem/mems/mem_detail.dart'; -import 'package:mem/mems/mem_notification.dart'; +import 'package:mem/core/mem.dart'; +import 'package:mem/core/mem_detail.dart'; +import 'package:mem/core/mem_item.dart'; +import 'package:mem/core/mem_notification.dart'; import 'package:mem/logger/log_service.dart'; import 'package:mem/notifications/notification_client.dart'; -import 'package:mem/mems/mem_entity.dart'; -import 'package:mem/mems/mem_item_entity.dart'; -import 'package:mem/mems/mem_notification_entity.dart'; +import 'package:mem/repositories/mem.dart'; +import 'package:mem/repositories/mem_notification.dart'; import 'mem_service.dart'; @@ -14,8 +14,8 @@ class MemClient { final NotificationClient _notificationClient; Future save( - MemEntity mem, - List memItemList, + Mem mem, + List memItemList, List memNotificationList, ) => v( @@ -29,10 +29,10 @@ class MemClient { ); _notificationClient.registerMemNotifications( - (saved.mem as SavedMemEntity).id, - savedMem: saved.mem as SavedMemEntity, + (saved.mem as SavedMem).id, + savedMem: saved.mem as SavedMem, savedMemNotifications: - saved.notifications?.whereType(), + saved.notifications?.whereType(), ); return saved; @@ -44,15 +44,15 @@ class MemClient { }, ); - Future archive(MemEntity mem) => v( + Future archive(Mem mem) => v( () async { // FIXME MemServiceの責務 - if (mem is SavedMemEntity) { + if (mem is SavedMem) { final archived = await _memService.archive(mem); final archivedMem = archived.mem; // FIXME archive後のMemDetailなので、必ずSavedMemのはず - if (archivedMem is SavedMemEntity) { + if (archivedMem is SavedMem) { _notificationClient.cancelMemNotifications(archivedMem.id); } @@ -71,16 +71,15 @@ class MemClient { Future unarchive(Mem mem) => v( () async { - // FIXME 保存済みかどうかを判定するのはMemServiceの責務? - // Client sideで判定できるものではない気がする - if (mem is SavedMemEntity) { + // FIXME MemServiceの責務 + if (mem is SavedMem) { final unarchived = await _memService.unarchive(mem); _notificationClient.registerMemNotifications( - (unarchived.mem as SavedMemEntity).id, - savedMem: unarchived.mem as SavedMemEntity, - savedMemNotifications: unarchived.notifications - ?.whereType(), + (unarchived.mem as SavedMem).id, + savedMem: unarchived.mem as SavedMem, + savedMemNotifications: + unarchived.notifications?.whereType(), ); return unarchived; diff --git a/lib/mems/mem_detail.dart b/lib/mems/mem_detail.dart deleted file mode 100644 index 9724586bc..000000000 --- a/lib/mems/mem_detail.dart +++ /dev/null @@ -1,23 +0,0 @@ -import 'package:mem/mems/mem_notification.dart'; -import 'package:mem/mems/mem_entity.dart'; -import 'package:mem/mems/mem_item_entity.dart'; - -// FIXME 定義するべきではない気がする -// - Mem, MemItems, MemNotificationsの関係はどのレイヤーのもの? -// - Entity~~かDomain~~ -// - DBのFK制約が絡むしEntityかも -// - Repositoryも絡んでいくはず -class MemDetail { - final MemEntity mem; - final List memItems; - final List? notifications; - - MemDetail(this.mem, this.memItems, [this.notifications]); - - @override - String toString() => { - 'mem': mem, - 'memItems': memItems, - 'notifications': notifications, - }.toString(); -} diff --git a/lib/mems/mem_entity.dart b/lib/mems/mem_entity.dart deleted file mode 100644 index ef06727cc..000000000 --- a/lib/mems/mem_entity.dart +++ /dev/null @@ -1,82 +0,0 @@ -import 'package:mem/core/date_and_time/date_and_time.dart'; -import 'package:mem/core/date_and_time/date_and_time_period.dart'; -import 'package:mem/mems/mem.dart'; -import 'package:mem/databases/table_definitions/mems.dart'; -import 'package:mem/framework/repository/database_tuple_entity.dart'; -import 'package:mem/framework/repository/entity.dart'; - -class MemEntity extends Mem with Entity, Copyable { - MemEntity(super.name, super.doneAt, super.period); - - MemEntity.fromMap(Map map) - : super( - map[defColMemsName.name], - map[defColMemsDoneAt.name], - map[defColMemsStartOn.name] == null && - map[defColMemsEndOn.name] == null - ? null - : DateAndTimePeriod( - start: map[defColMemsStartOn.name] == null - ? null - : DateAndTime.from(map[defColMemsStartOn.name], - timeOfDay: map[defColMemsStartAt.name]), - end: map[defColMemsEndOn.name] == null - ? null - : DateAndTime.from(map[defColMemsEndOn.name], - timeOfDay: map[defColMemsEndAt.name]), - ), - ); - - @override - MemEntity copiedWith({ - String Function()? name, - DateTime? Function()? doneAt, - DateAndTimePeriod? Function()? period, - }) => - MemEntity( - name == null ? this.name : name(), - doneAt == null ? this.doneAt : doneAt(), - period == null ? this.period : period(), - ); - - @override - Map get toMap => { - defColMemsName.name: name, - defColMemsDoneAt.name: doneAt, - defColMemsStartOn.name: period?.start, - defColMemsStartAt.name: - period?.start?.isAllDay == true ? null : period?.start, - defColMemsEndOn.name: period?.end, - defColMemsEndAt.name: - period?.end?.isAllDay == true ? null : period?.end, - }; -} - -class SavedMemEntity extends MemEntity with DatabaseTupleEntity { - SavedMemEntity(super.name, super.doneAt, super.period); - - SavedMemEntity.fromMap( - Map map, - ) : super.fromMap(map) { - withMap(map); - } - - @override - SavedMemEntity copiedWith({ - String Function()? name, - DateTime? Function()? doneAt, - DateAndTimePeriod? Function()? period, - }) => - SavedMemEntity.fromMap( - toMap - ..addAll( - super - .copiedWith( - name: name, - doneAt: doneAt, - period: period, - ) - .toMap, - ), - ); -} diff --git a/lib/mems/mem_item.dart b/lib/mems/mem_item.dart index fd47a58c0..9422f7c04 100644 --- a/lib/mems/mem_item.dart +++ b/lib/mems/mem_item.dart @@ -1,11 +1,20 @@ -enum MemItemType { - memo, -} +import 'package:mem/core/mem_item.dart'; +import 'package:mem/framework/repository/database_tuple_entity.dart'; + +class SavedMemItem extends MemItem with SavedDatabaseTupleMixin { + @override + int get memId => super.memId as int; -class MemItem { - final int? memId; - final MemItemType type; - final dynamic value; + SavedMemItem(super.memId, super.type, super.value); - MemItem(this.memId, this.type, this.value); + @override + SavedMemItem copiedWith({ + int Function()? memId, + dynamic Function()? value, + }) => + SavedMemItem( + memId == null ? this.memId : memId(), + type, + value == null ? this.value : value(), + )..copiedFrom(this); } diff --git a/lib/mems/mem_item_entity.dart b/lib/mems/mem_item_entity.dart deleted file mode 100644 index a71539d02..000000000 --- a/lib/mems/mem_item_entity.dart +++ /dev/null @@ -1,58 +0,0 @@ -import 'package:mem/mems/mem_item.dart'; -import 'package:mem/databases/table_definitions/mem_items.dart'; -import 'package:mem/framework/repository/database_tuple_entity.dart'; -import 'package:mem/framework/repository/entity.dart'; - -class MemItemEntity extends MemItem with Entity, Copyable { - MemItemEntity(super.memId, super.type, super.value); - - MemItemEntity.fromMap(Map map) - : super( - map[defFkMemItemsMemId.name], - MemItemType.values.firstWhere( - (v) => v.name == map[defColMemItemsType.name], - ), - map[defColMemItemsValue.name], - ); - - @override - Map get toMap => { - defFkMemItemsMemId.name: memId, - defColMemItemsType.name: type.name, - defColMemItemsValue.name: value, - }; - - @override - MemItemEntity copiedWith({ - int Function()? memId, - dynamic Function()? value, - }) => - MemItemEntity( - memId == null ? this.memId : memId(), - type, - value == null ? this.value : value(), - ); -} - -class SavedMemItemEntity extends MemItemEntity with DatabaseTupleEntity { - SavedMemItemEntity.fromMap(Map map) : super.fromMap(map) { - withMap(map); - } - - @override - MemItemEntity copiedWith({ - int Function()? memId, - dynamic Function()? value, - }) => - SavedMemItemEntity.fromMap( - toMap - ..addAll( - super - .copiedWith( - memId: memId, - value: value, - ) - .toMap, - ), - ); -} diff --git a/lib/mems/mem_item_repository.dart b/lib/mems/mem_item_repository.dart index cc75ef413..699f7ff11 100644 --- a/lib/mems/mem_item_repository.dart +++ b/lib/mems/mem_item_repository.dart @@ -1,118 +1,63 @@ -import 'package:mem/databases/definition.dart'; -import 'package:mem/databases/table_definitions/base.dart'; +import 'package:mem/core/mem_item.dart'; import 'package:mem/databases/table_definitions/mem_items.dart'; -import 'package:mem/framework/repository/database_tuple_repository.dart'; -import 'package:mem/framework/repository/group_by.dart'; -import 'package:mem/framework/repository/order_by.dart'; import 'package:mem/logger/log_service.dart'; +import 'package:mem/framework/repository/database_tuple_repository.dart'; import 'package:mem/framework/repository/condition/conditions.dart'; -import 'package:mem/mems/mem_item_entity.dart'; +import 'package:mem/mems/mem_item.dart'; class MemItemRepository - extends DatabaseTupleRepository { - MemItemRepository() : super(databaseDefinition, defTableMemItems); - - @override - SavedMemItemEntity pack(Map map) => - SavedMemItemEntity.fromMap(map); - - @override - Future> ship({ - int? memId, - Condition? condition, - GroupBy? groupBy, - List? orderBy, - int? offset, - int? limit, - }) => - v( - () => super.ship( - condition: And( - [ - if (memId != null) Equals(defFkMemItemsMemId, memId), - if (condition != null) condition, - ], - ), - groupBy: groupBy, - orderBy: orderBy, - offset: offset, - limit: limit, - ), - { - 'memId': memId, - 'condition': condition, - 'groupBy': groupBy, - 'orderBy': orderBy, - 'offset': offset, - 'limit': limit, - }, + extends DatabaseTupleRepository { + Future> shipByMemId(int memId) => v( + () => super.ship(condition: Equals(defFkMemItemsMemId.name, memId)), + {'memId': memId}, ); - Future> archiveBy({ - int? memId, - DateTime? archivedAt, - }) => - v( + Future> archiveByMemId(int memId) => v( () async => Future.wait( - await ship( - condition: And( - [ - if (memId != null) Equals(defFkMemItemsMemId, memId), - ], - ), - ).then( - (v) => v.map( - (e) => archive(e, archivedAt: archivedAt), - ), - ), - ), - { - 'memId': memId, - 'archivedAt': archivedAt, - }, + (await shipByMemId(memId)).map((e) => super.archive(e))), + {'memId': memId}, ); - Future> unarchiveBy({ - int? memId, - DateTime? updatedAt, - }) => - v( + Future> unarchiveByMemId(int memId) => v( () async => Future.wait( - await ship( - condition: And( - [ - if (memId != null) Equals(defFkMemItemsMemId, memId), - ], - ), - ).then( - (v) => v.map( - (e) => unarchive(e, updatedAt: updatedAt), - ), - ), - ), - { - 'memId': memId, - 'updatedAt': updatedAt, - }, + (await shipByMemId(memId)).map((e) => super.unarchive(e))), + {'memId': memId}, + ); + + Future> wasteByMemId(int memId) => v( + () async => await super.waste(Equals(defFkMemItemsMemId.name, memId)), + {'memId': memId}, ); @override - Future> waste({ - int? memId, - Condition? condition, - }) => - v( - () => super.waste( - condition: And( - [ - if (memId != null) Equals(defPkId, memId), - if (condition != null) condition, // coverage:ignore-line - ], - ), + SavedMemItem pack(Map tuple) => SavedMemItem( + tuple[defFkMemItemsMemId.name], + MemItemType.values.firstWhere( + (v) => v.name == tuple[defColMemItemsType.name], ), - { - 'memId': memId, - 'condition': condition, - }, + tuple[defColMemItemsValue.name], + )..pack(tuple); + + @override + Map unpack(MemItem entity) { + final map = { + defFkMemItemsMemId.name: entity.memId, + defColMemItemsType.name: entity.type.name, + defColMemItemsValue.name: entity.value, + }; + + if (entity is SavedMemItem) { + map.addAll(entity.unpack()); + } + + return map; + } + + MemItemRepository._() : super(defTableMemItems); + + static MemItemRepository? _instance; + + factory MemItemRepository() => v( + () => _instance ??= MemItemRepository._(), ); } diff --git a/lib/mems/mem_notification_entity.dart b/lib/mems/mem_notification_entity.dart deleted file mode 100644 index 6dfd72628..000000000 --- a/lib/mems/mem_notification_entity.dart +++ /dev/null @@ -1,76 +0,0 @@ -import 'package:mem/mems/mem_notification.dart'; -import 'package:mem/databases/table_definitions/mem_notifications.dart'; -import 'package:mem/framework/repository/database_tuple_entity.dart'; -import 'package:mem/framework/repository/entity.dart'; - -class MemNotificationEntity extends MemNotification - with Entity, Copyable { - MemNotificationEntity(super.memId, super.type, super.time, super.message) - : super(); - - MemNotificationEntity.fromMap(Map map) - : super( - map[defFkMemNotificationsMemId.name], - MemNotificationType.fromName(map[defColMemNotificationsType.name]), - map[defColMemNotificationsTime.name], - map[defColMemNotificationsMessage.name], - ); - - static MemNotificationEntity initialByType( - int? memId, - MemNotificationType type, { - int? Function()? time, - }) { - final core = MemNotification.initialByType(memId, type, time: time); - return MemNotificationEntity(memId, type, core.time, core.message); - } - - @override - Map get toMap => { - defFkMemNotificationsMemId.name: memId, - defColMemNotificationsType.name: type.name, - defColMemNotificationsTime.name: time, - defColMemNotificationsMessage.name: message, - }; - - @override - MemNotificationEntity copiedWith({ - int? Function()? memId, - int? Function()? time, - String Function()? message, - }) => - MemNotificationEntity( - memId == null ? this.memId : memId(), - type, - time == null ? this.time : time(), - message == null ? this.message : message(), - ); -} - -class SavedMemNotificationEntity extends MemNotificationEntity - with DatabaseTupleEntity { - SavedMemNotificationEntity.fromMap( - Map map, - ) : super.fromMap(map) { - withMap(map); - } - - @override - SavedMemNotificationEntity copiedWith({ - int? Function()? memId, - int? Function()? time, - String Function()? message, - }) => - SavedMemNotificationEntity.fromMap( - toMap - ..addAll( - super - .copiedWith( - memId: memId, - time: time, - message: message, - ) - .toMap, - ), - ); -} diff --git a/lib/mems/mem_notification_repository.dart b/lib/mems/mem_notification_repository.dart deleted file mode 100644 index 6bdf99e4e..000000000 --- a/lib/mems/mem_notification_repository.dart +++ /dev/null @@ -1,109 +0,0 @@ -import 'package:mem/mems/mem_notification.dart'; -import 'package:mem/databases/definition.dart'; -import 'package:mem/databases/table_definitions/mem_notifications.dart'; -import 'package:mem/framework/repository/condition/in.dart'; -import 'package:mem/framework/repository/database_tuple_repository.dart'; -import 'package:mem/framework/repository/group_by.dart'; -import 'package:mem/framework/repository/order_by.dart'; -import 'package:mem/logger/log_service.dart'; -import 'package:mem/framework/repository/condition/conditions.dart'; -import 'package:mem/mems/mem_notification_entity.dart'; - -class MemNotificationRepository extends DatabaseTupleRepository< - MemNotificationEntity, SavedMemNotificationEntity> { - MemNotificationRepository() - : super(databaseDefinition, defTableMemNotifications); - - @override - SavedMemNotificationEntity pack(Map map) => - SavedMemNotificationEntity.fromMap(map); - - @override - Future> ship({ - int? memId, - Iterable? memIdsIn, - Condition? condition, - GroupBy? groupBy, - List? orderBy, - int? offset, - int? limit, - }) => - v( - () => super.ship( - condition: And( - [ - if (memId != null) Equals(defFkMemNotificationsMemId, memId), - if (memIdsIn != null) - In(defFkMemNotificationsMemId.name, memIdsIn), - if (condition != null) condition, // coverage:ignore-line - ], - ), - groupBy: groupBy, - orderBy: orderBy, - offset: offset, - limit: limit, - ), - { - 'memId': memId, - 'memIdsIn': memIdsIn, - 'condition': condition, - 'groupBy': groupBy, - 'orderBy': orderBy, - 'offset': offset, - 'limit': limit, - }, - ); - - Future> archiveBy({ - int? memId, - Condition? condition, - DateTime? archivedAt, - }) => - v( - () async => await ship(memId: memId, condition: condition).then((v) => - Future.wait(v.map((e) => archive(e, archivedAt: archivedAt)))), - { - 'memId': memId, - 'condition': condition, - 'archivedAt': archivedAt, - }, - ); - - Future> unarchiveBy({ - int? memId, - Condition? condition, - DateTime? updatedAt, - }) => - v( - () async => await ship(memId: memId, condition: condition).then((v) => - Future.wait(v.map((e) => unarchive(e, updatedAt: updatedAt)))), - { - 'memId': memId, - 'condition': condition, - 'updatedAt': updatedAt, - }, - ); - - @override - Future> waste({ - int? memId, - MemNotificationType? type, - Condition? condition, - }) => - v( - () => super.waste( - condition: And( - [ - if (memId != null) Equals(defFkMemNotificationsMemId, memId), - if (type != null) Equals(defColMemNotificationsType, type.name), - if (condition != null) condition, // coverage:ignore-line - ], - ), - ), - { - 'memId': memId, - 'type': type, - 'condition': condition, - }, - ); -} diff --git a/lib/mems/mem_repository.dart b/lib/mems/mem_repository.dart deleted file mode 100644 index bdaccbe19..000000000 --- a/lib/mems/mem_repository.dart +++ /dev/null @@ -1,82 +0,0 @@ -import 'package:mem/databases/definition.dart'; -import 'package:mem/databases/table_definitions/base.dart'; -import 'package:mem/databases/table_definitions/mems.dart'; -import 'package:mem/framework/repository/database_tuple_repository.dart'; -import 'package:mem/framework/repository/group_by.dart'; -import 'package:mem/framework/repository/order_by.dart'; -import 'package:mem/logger/log_service.dart'; -import 'package:mem/framework/repository/condition/conditions.dart'; -import 'package:mem/mems/mem_entity.dart'; - -class MemRepository extends DatabaseTupleRepository { - MemRepository() : super(databaseDefinition, defTableMems); - - @override - SavedMemEntity pack(Map map) => SavedMemEntity.fromMap(map); - - @override - Future> ship({ - int? id, - bool? archived, - bool? done, - Condition? condition, - GroupBy? groupBy, - List? orderBy, - int? offset, - int? limit, - }) => - v( - () => super.ship( - condition: And( - [ - if (id != null) Equals(defPkId, id), - if (archived != null) - archived - ? IsNotNull(defColArchivedAt.name) - : IsNull(defColArchivedAt.name), - if (done != null) - done - ? IsNotNull(defColMemsDoneAt.name) - : IsNull(defColMemsDoneAt.name), - if (condition != null) condition, - ], - ), - groupBy: groupBy, - orderBy: orderBy, - offset: offset, - limit: limit, - ), - { - 'id': id, - 'archived': archived, - 'done': done, - 'condition': condition, - 'groupBy': groupBy, - 'orderBy': orderBy, - 'offset': offset, - 'limit': limit, - }, - ); - - @override - Future> waste({ - int? id, - Condition? condition, - }) => - v( - () => super.waste( - condition: And( - [ - if (id != null) Equals(defPkId, id), -// coverage:ignore-start - if (condition != null) condition, -// coverage:ignore-end - ], - ), - ), - { - 'id': id, - 'condition': condition, - }, - ); -} diff --git a/lib/mems/mem_service.dart b/lib/mems/mem_service.dart index 00e14ff50..7f49691ea 100644 --- a/lib/mems/mem_service.dart +++ b/lib/mems/mem_service.dart @@ -1,108 +1,92 @@ import 'package:collection/collection.dart'; -import 'package:mem/mems/mem_detail.dart'; -import 'package:mem/mems/mem_notification.dart'; +import 'package:mem/core/mem_detail.dart'; +import 'package:mem/core/mem_notification.dart'; import 'package:mem/logger/log_service.dart'; +import 'package:mem/mems/mem_item.dart'; import 'package:mem/mems/mem_item_repository.dart'; -import 'package:mem/mems/mem_entity.dart'; -import 'package:mem/mems/mem_item_entity.dart'; -import 'package:mem/mems/mem_notification_entity.dart'; -import 'package:mem/mems/mem_notification_repository.dart'; -import 'package:mem/mems/mem_repository.dart'; +import 'package:mem/repositories/mem.dart'; +import 'package:mem/repositories/mem_notification.dart'; +import 'package:mem/repositories/mem_notification_repository.dart'; +import 'package:mem/repositories/mem_repository.dart'; class MemService { final MemRepository _memRepository; final MemItemRepository _memItemRepository; final MemNotificationRepository _memNotificationRepository; - Future save( - MemDetail memDetail, { - bool undo = false, - }) => - i( + Future save(MemDetail memDetail, {bool undo = false}) => i( () async { final mem = memDetail.mem; - final savedMem = (mem is SavedMemEntity && !undo + final savedMem = (mem is SavedMem && !undo ? await _memRepository.replace(mem) : await _memRepository.receive(mem)); - final savedMemItems = await Future.wait( - memDetail.memItems.map((e) => (e is SavedMemItemEntity && !undo - ? _memItemRepository.replace( - e.copiedWith(memId: () => savedMem.id) - as SavedMemItemEntity, - ) - : _memItemRepository.receive( - e.copiedWith(memId: () => savedMem.id), - ))), - ); + final savedMemItems = (await Future.wait( + memDetail.memItems.map((e) => (e is SavedMemItem && !undo + ? _memItemRepository.replace( + e.copiedWith(memId: () => savedMem.id), + ) + : _memItemRepository.receive( + e.copiedWith(memId: () => savedMem.id), + ))))); final memNotifications = memDetail.notifications; final returnMemNotifications = - List.empty(growable: true); + List.empty(growable: true); if (memNotifications == null) { - await _memNotificationRepository.waste(memId: savedMem.id); + await _memNotificationRepository.waste(null, savedMem.id); } else { returnMemNotifications.addAll(await Future.wait(memNotifications .where((e) => !e.isRepeatByDayOfWeek()) .map((e) { if (e.isEnabled()) { - return (e is SavedMemNotificationEntity && !undo - ? _memNotificationRepository.replace((e).copiedWith( + return (e is SavedMemNotification && !undo + ? _memNotificationRepository.replace(e.copiedWith( memId: () => savedMem.id, )) - : _memNotificationRepository.receive( - (e as MemNotificationEntity).copiedWith( - memId: () => savedMem.id, - ), - )); + : _memNotificationRepository.receive(e.copiedWith( + memId: () => savedMem.id, + ))); } else { - return _memNotificationRepository - .waste( - memId: savedMem.id, - type: e.type, - ) - .then((v) => null); + _memNotificationRepository.waste( + null, + savedMem.id, + e.type, + ); + return Future.value(null); } }))); await _memNotificationRepository.waste( - memId: savedMem.id, - type: MemNotificationType.repeatByDayOfWeek, + null, + savedMem.id, + MemNotificationType.repeatByDayOfWeek, ); for (var entry in memNotifications .where((e) => e.isRepeatByDayOfWeek()) .groupListsBy((e) => e.time) .entries) { - returnMemNotifications.add( - await _memNotificationRepository.receive( - (entry.value.first as MemNotificationEntity).copiedWith( - memId: () => savedMem.id, - ), - ), - ); + returnMemNotifications.add(await _memNotificationRepository + .receive(entry.value.first.copiedWith( + memId: () => savedMem.id, + ))); } } return MemDetail( savedMem, savedMemItems, - returnMemNotifications - .whereType() - .toList(), + returnMemNotifications.whereType().toList(), ); }, - { - 'memDetail': memDetail, - 'undo': undo, - }, + {'memDetail': memDetail}, ); Future doneByMemId(int memId) => i( () async { - final done = - (await _memRepository.ship(id: memId).then((v) => v.single)) - .copiedWith(doneAt: () => DateTime.now()); + final done = (await _memRepository.shipById(memId)) + .copiedWith(doneAt: () => DateTime.now()); return save(MemDetail(done, [])); }, {'memId': memId}, @@ -110,21 +94,20 @@ class MemService { Future undoneByMemId(int memId) => i( () async { - final undone = - (await _memRepository.ship(id: memId).then((v) => v.single)) - .copiedWith(doneAt: () => null); + final undone = (await _memRepository.shipById(memId)) + .copiedWith(doneAt: () => null); return save(MemDetail(undone, [])); }, {'memId': memId}, ); - Future archive(SavedMemEntity mem) => i( + Future archive(SavedMem mem) => i( () async { final archivedMem = await _memRepository.archive(mem); final archivedMemItems = - await _memItemRepository.archiveBy(memId: archivedMem.id); + await _memItemRepository.archiveByMemId(archivedMem.id); final archivedMemNotifications = - await _memNotificationRepository.archiveBy(memId: archivedMem.id); + await _memNotificationRepository.archiveByMemId(archivedMem.id); return MemDetail( archivedMem, @@ -137,13 +120,13 @@ class MemService { }, ); - Future unarchive(SavedMemEntity mem) => i( + Future unarchive(SavedMem mem) => i( () async { final unarchivedMem = await _memRepository.unarchive(mem); final unarchivedMemItems = - await _memItemRepository.unarchiveBy(memId: unarchivedMem.id); + await _memItemRepository.unarchiveByMemId(unarchivedMem.id); final unarchivedMemNotifications = await _memNotificationRepository - .unarchiveBy(memId: unarchivedMem.id); + .unarchiveByMemId(unarchivedMem.id); return MemDetail( unarchivedMem, @@ -157,9 +140,9 @@ class MemService { Future remove(int memId) => v( () async { // TODO https://github.com/zin-/mem/issues/284 - await _memNotificationRepository.waste(memId: memId); - await _memItemRepository.waste(memId: memId); - await _memRepository.waste(id: memId); + await _memNotificationRepository.waste(null, memId); + await _memItemRepository.wasteByMemId(memId); + await _memRepository.wasteById(memId); return true; }, diff --git a/lib/mems/states.dart b/lib/mems/states.dart index e3abd4d68..5f9e19c46 100644 --- a/lib/mems/states.dart +++ b/lib/mems/states.dart @@ -1,20 +1,21 @@ import 'package:collection/collection.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:mem/components/list_value_state_notifier.dart'; -import 'package:mem/mems/mem_detail.dart'; +import 'package:mem/core/mem.dart'; +import 'package:mem/core/mem_detail.dart'; +import 'package:mem/core/mem_item.dart'; import 'package:mem/components/value_state_notifier.dart'; -import 'package:mem/mems/mem_notification.dart'; +import 'package:mem/core/mem_notification.dart'; import 'package:mem/logger/log_service.dart'; -import 'package:mem/mems/mem_entity.dart'; -import 'package:mem/mems/mem_item_entity.dart'; -import 'package:mem/mems/mem_repository.dart'; +import 'package:mem/repositories/mem.dart'; +import 'package:mem/repositories/mem_repository.dart'; final memsProvider = - StateNotifierProvider, List>( - (ref) => v(() => ListValueStateNotifier([])), + StateNotifierProvider, List>( + (ref) => v(() => ListValueStateNotifier([])), ); -final memItemsProvider = StateNotifierProvider< - ListValueStateNotifier, List>( +final memItemsProvider = + StateNotifierProvider, List>( (ref) => v( () => ListValueStateNotifier([]), ), @@ -25,23 +26,20 @@ final memNotificationsProvider = StateNotifierProvider< ); final memByMemIdProvider = StateNotifierProvider.autoDispose - .family, SavedMemEntity?, int?>( + .family, SavedMem?, int?>( (ref, memId) => v( () => ValueStateNotifier( ref.watch(memsProvider).singleWhereOrNull( - (element) => - element is SavedMemEntity ? element.id == memId : false, - ) as SavedMemEntity?, + (element) => element is SavedMem ? element.id == memId : false, + ) as SavedMem?, initializer: (current, notifier) => v( () async { if (memId != null) { - final savedMem = await MemRepository() - .ship(id: memId) - .then((value) => value.singleOrNull); + final savedMem = await MemRepository().findOneBy(id: memId); ref.read(memsProvider.notifier).upsertAll( [if (savedMem != null) savedMem], (current, updating) => - (current is SavedMemEntity && updating is SavedMemEntity) + (current is SavedMem && updating is SavedMem) ? current.id == updating.id : true, ); @@ -76,15 +74,15 @@ final removedMemDetailProvider = StateNotifierProvider.autoDispose memId, ), ); -final removedMemProvider = StateNotifierProvider.family< - ValueStateNotifier, MemEntity?, int>( +final removedMemProvider = + StateNotifierProvider.family, Mem?, int>( (ref, memId) => v( - () => ValueStateNotifier(null), + () => ValueStateNotifier(null), memId, ), ); final removedMemItemsProvider = StateNotifierProvider.family< - ValueStateNotifier?>, List?, int>( + ValueStateNotifier?>, List?, int>( (ref, memId) => v( () => ValueStateNotifier(null), memId, diff --git a/lib/notifications/mem_notifications.dart b/lib/notifications/mem_notifications.dart index d98c3cd95..1a1742fde 100644 --- a/lib/notifications/mem_notifications.dart +++ b/lib/notifications/mem_notifications.dart @@ -1,10 +1,10 @@ import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; -import 'package:mem/acts/act.dart'; -import 'package:mem/mems/mem_notification.dart'; +import 'package:mem/core/act.dart'; +import 'package:mem/core/mem_notification.dart'; import 'package:mem/logger/log_service.dart'; -import 'package:mem/mems/mem_entity.dart'; -import 'package:mem/mems/mem_notification_entity.dart'; +import 'package:mem/repositories/mem.dart'; +import 'package:mem/repositories/mem_notification.dart'; import 'notification_client.dart'; import 'notification/type.dart'; @@ -15,9 +15,9 @@ const memIdKey = 'memId'; class MemNotifications { static Schedule periodicScheduleOf( - SavedMemEntity savedMem, + SavedMem savedMem, TimeOfDay startOfDay, - Iterable memNotifications, + Iterable memNotifications, Act? latestAct, DateTime now, ) => @@ -69,7 +69,7 @@ class MemNotifications { if (latestAct?.isActive == true) { return null; } else if (memNotifications - .whereType() + .whereType() .where((e) => !e.isAfterActStarted()) .isNotEmpty) { final repeatAt = memNotifications diff --git a/lib/notifications/notification/notification.dart b/lib/notifications/notification/notification.dart index ec2597828..df8acfe7e 100644 --- a/lib/notifications/notification/notification.dart +++ b/lib/notifications/notification/notification.dart @@ -1,8 +1,7 @@ -import 'package:mem/framework/repository/entity.dart'; import 'package:mem/framework/repository/key_with_value.dart'; import 'package:mem/notifications/notification/channel.dart'; -class Notification with Entity, KeyWithValue> { +class Notification extends KeyWithValue> { final String title; final String body; final NotificationChannel channel; @@ -14,13 +13,19 @@ class Notification with Entity, KeyWithValue> { this.body, this.channel, this.payload, - ) { - key = id; - value = { - 'title': title, - 'body': body, - 'channel': channel, - 'payload': payload, - }; - } + ) : super( + id, + { + "title": title, + "body": body, + "channel": channel, + "payload": payload, + }, + ); + + @override + String toString() => "${super.toString()}: ${{ + "key": key, + "value": value, + }}"; } diff --git a/lib/notifications/notification_channels.dart b/lib/notifications/notification_channels.dart index fd59f4bce..e29a34922 100644 --- a/lib/notifications/notification_channels.dart +++ b/lib/notifications/notification_channels.dart @@ -2,8 +2,8 @@ import 'package:collection/collection.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:mem/logger/log_service.dart'; import 'package:mem/notifications/mem_notifications.dart'; -import 'package:mem/mems/mem_notification_repository.dart'; -import 'package:mem/mems/mem_repository.dart'; +import 'package:mem/repositories/mem_notification_repository.dart'; +import 'package:mem/repositories/mem_repository.dart'; import 'notification/action.dart'; import 'notification/notification.dart'; @@ -22,9 +22,7 @@ class NotificationChannels { ) => v( () async { - final title = await MemRepository() - .ship(id: memId) - .then((value) => value.single.name); + final title = (await MemRepository().shipById(memId)).name; String body; switch (notificationType) { case NotificationType.startMem: @@ -34,13 +32,13 @@ class NotificationChannels { body = "end"; break; case NotificationType.repeat: - body = ((await MemNotificationRepository().ship(memId: memId))) + body = ((await MemNotificationRepository().shipByMemId(memId))) .singleWhereOrNull((element) => element.isRepeated()) ?.message ?? "Repeat"; break; case NotificationType.afterActStarted: - body = ((await MemNotificationRepository().ship(memId: memId))) + body = ((await MemNotificationRepository().shipByMemId(memId))) .singleWhere((element) => element.isAfterActStarted()) .message; break; diff --git a/lib/notifications/notification_client.dart b/lib/notifications/notification_client.dart index 3064b2fb8..0a68b124a 100644 --- a/lib/notifications/notification_client.dart +++ b/lib/notifications/notification_client.dart @@ -2,13 +2,13 @@ import 'dart:async'; import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; +import 'package:mem/acts/act_repository.dart'; import 'package:mem/components/l10n.dart'; import 'package:mem/logger/log_service.dart'; -import 'package:mem/acts/act_repository.dart'; -import 'package:mem/mems/mem_entity.dart'; -import 'package:mem/mems/mem_notification_entity.dart'; -import 'package:mem/mems/mem_notification_repository.dart'; -import 'package:mem/mems/mem_repository.dart'; +import 'package:mem/repositories/mem.dart'; +import 'package:mem/repositories/mem_notification.dart'; +import 'package:mem/repositories/mem_notification_repository.dart'; +import 'package:mem/repositories/mem_repository.dart'; import 'package:mem/settings/client.dart'; import 'package:mem/settings/keys.dart'; import 'package:mem/values/constants.dart'; @@ -26,7 +26,7 @@ class NotificationClient { final ScheduleClient _scheduleClient; final NotificationRepository _notificationRepository; - final PreferenceClientRepository _preferenceClientRepository; + final PreferenceClient _preferenceClient; final MemRepository _memRepository; final MemNotificationRepository _memNotificationRepository; @@ -34,7 +34,7 @@ class NotificationClient { this.notificationChannels, this._scheduleClient, this._notificationRepository, - this._preferenceClientRepository, + this._preferenceClient, this._memRepository, this._memNotificationRepository, ); @@ -46,7 +46,7 @@ class NotificationClient { NotificationChannels(buildL10n(context)), ScheduleClient(), NotificationRepository(), - PreferenceClientRepository(), + PreferenceClient(), MemRepository(), MemNotificationRepository(), ), @@ -59,7 +59,7 @@ class NotificationClient { static void resetSingleton() => v( () { ScheduleClient.resetSingleton(); - NotificationRepository.reset(); + NotificationRepository.resetSingleton(); _instance = null; }, { @@ -73,8 +73,7 @@ class NotificationClient { ) => v( () async { - final savedMem = - await _memRepository.ship(id: memId).then((v) => v.singleOrNull); + final savedMem = await _memRepository.findOneBy(id: memId); if (savedMem == null || savedMem.isDone || savedMem.isArchived) { await cancelMemNotifications(memId); @@ -126,31 +125,21 @@ class NotificationClient { Future registerMemNotifications( int memId, { - SavedMemEntity? savedMem, - Iterable? savedMemNotifications, + SavedMem? savedMem, + Iterable? savedMemNotifications, }) => v( () async { - final mem = savedMem ?? - await _memRepository - .ship( - id: memId, - ) - .then( - (v) => v.single, - ); - if (mem!.isDone || mem.isArchived) { + final mem = savedMem ?? await _memRepository.shipById(memId); + if (mem.isDone || mem.isArchived) { cancelMemNotifications(memId); } else { - final latestAct = await ActRepository() - .ship( - memId: memId, - latestByMemIds: true, - ) - .then((value) => value.singleOrNull); + final latestAct = await ActRepository().findOneBy( + memId: memId, + latest: true, + ); final startOfDay = - (await _preferenceClientRepository.shipByKey(startOfDayKey)) - .value ?? + (await _preferenceClient.shipByKey(startOfDayKey)).value ?? defaultStartOfDay; for (var schedule in [ ...mem.periodSchedules(startOfDay), @@ -158,7 +147,7 @@ class NotificationClient { mem, startOfDay, savedMemNotifications ?? - await _memNotificationRepository.ship(memId: memId), + await _memNotificationRepository.shipByMemId(memId), latestAct, DateTime.now(), ) @@ -200,7 +189,7 @@ class NotificationClient { final now = DateTime.now(); final memNotifications = - await _memNotificationRepository.ship(memId: memId); + await _memNotificationRepository.shipByMemId(memId); for (var notification in memNotifications.where((element) => element.isEnabled() && element.isAfterActStarted())) { await _scheduleClient.receive( @@ -259,7 +248,7 @@ class NotificationClient { Future _shouldNotify(int memId) => v( () async { final savedMemNotifications = - await _memNotificationRepository.ship(memId: memId); + await MemNotificationRepository().shipByMemId(memId); final repeatByDayOfWeekMemNotifications = savedMemNotifications.where( (element) => element.isEnabled() && element.isRepeatByDayOfWeek(), ); @@ -278,11 +267,11 @@ class NotificationClient { (element) => element.isEnabled() && element.isRepeatByNDay(), ); final lastActTime = await ActRepository() - .ship(memId: memId, latestByMemIds: true) + .findOneBy(memId: memId, latest: true) .then((value) => - value.singleOrNull?.period.end ?? + value?.period.end ?? // FIXME 永続化されている時点でstartは必ずあるので型で表現する - value.singleOrNull?.period.start!); + value?.period.start!); if (lastActTime != null) { if (Duration( diff --git a/lib/notifications/notification_repository.dart b/lib/notifications/notification_repository.dart index 2ad0e7837..5ee90afc2 100644 --- a/lib/notifications/notification_repository.dart +++ b/lib/notifications/notification_repository.dart @@ -1,5 +1,6 @@ import 'package:flutter/foundation.dart'; import 'package:mem/framework/repository/key_with_value_repository.dart'; +import 'package:mem/framework/repository/repository.dart'; import 'package:mem/logger/log_service.dart'; import 'package:mem/notifications/notification/type.dart'; import 'package:mem/permissions/permission.dart'; @@ -10,14 +11,40 @@ import 'notification/notification.dart'; import 'flutter_local_notifications_wrapper.dart'; class NotificationRepository extends KeyWithValueRepository - with DiscardAll { - final FlutterLocalNotificationsWrapper? _flutterLocalNotificationsWrapper = + with Discarder { + FlutterLocalNotificationsWrapper? _flutterLocalNotificationsWrapper = defaultTargetPlatform == TargetPlatform.android ? FlutterLocalNotificationsWrapper(androidDefaultIconPath) : null; + NotificationRepository._(); + + static NotificationRepository? _instance; + + factory NotificationRepository() => v( + () => _instance ??= NotificationRepository._(), + { + "_instance": _instance, + }, + ); + + static void resetSingleton() => v( + () { + FlutterLocalNotificationsWrapper.resetSingleton(); + _instance?._flutterLocalNotificationsWrapper = null; + _instance = null; + }, + { + '_instance': _instance, + }, + ); + + Future checkNotification() => v( + () async => _flutterLocalNotificationsWrapper?.handleAppLaunchDetails(), + ); + @override - Future receive(Notification entity) => v( + Future receive(Notification entity) => v( () async { if (await PermissionHandlerWrapper().grant(Permission.notification)) { await _flutterLocalNotificationsWrapper?.show( @@ -27,23 +54,25 @@ class NotificationRepository extends KeyWithValueRepository entity.channel, entity.payload, ); + return true; + } else { + return false; } }, { - 'entity': entity, + "entity": entity, }, ); - Future ship() => v( - () async => - await _flutterLocalNotificationsWrapper?.handleAppLaunchDetails(), - ); - @override - Future discard(int key) => v( - () async => await _flutterLocalNotificationsWrapper?.cancel(key), + Future discard(int key) => v( + () async { + _flutterLocalNotificationsWrapper?.cancel(key); + + return true; + }, { - 'key': key, + "key": key, }, ); @@ -57,10 +86,4 @@ class NotificationRepository extends KeyWithValueRepository )); }, ); - - static void reset() => v( - () { - FlutterLocalNotificationsWrapper.resetSingleton(); - }, - ); } diff --git a/lib/notifications/schedule.dart b/lib/notifications/schedule.dart index 93bf94d30..47eec7635 100644 --- a/lib/notifications/schedule.dart +++ b/lib/notifications/schedule.dart @@ -4,7 +4,7 @@ import 'package:mem/notifications/notification_client.dart'; import 'package:mem/notifications/mem_notifications.dart'; import 'package:mem/notifications/notification/type.dart'; -abstract class Schedule with Entity { +abstract class Schedule extends Entity { final int id; Schedule(this.id); @@ -35,9 +35,9 @@ abstract class Schedule with Entity { ); @override - Map get toMap => { - 'id': id, - }; + String toString() => "${super.toString()}: ${{ + "id": id, + }}"; } class CancelSchedule extends Schedule { @@ -55,11 +55,10 @@ class TimedSchedule extends Schedule { ); @override - Map get toMap => super.toMap - ..addAll({ - 'startAt': startAt, - 'params': params, - }); + String toString() => "${super.toString()}${{ + "startAt": startAt, + "params": params, + }}"; } class PeriodicSchedule extends TimedSchedule { @@ -73,8 +72,5 @@ class PeriodicSchedule extends TimedSchedule { ); @override - Map get toMap => super.toMap - ..addAll({ - 'duration': duration, - }); + String toString() => "${super.toString()}${{"duration": duration}}"; } diff --git a/lib/notifications/schedule_client.dart b/lib/notifications/schedule_client.dart index 315aba172..31554c05c 100644 --- a/lib/notifications/schedule_client.dart +++ b/lib/notifications/schedule_client.dart @@ -1,5 +1,6 @@ import 'package:mem/framework/repository/repository.dart'; import 'package:mem/logger/log_service.dart'; +import 'package:mem/main.dart'; import 'package:mem/notifications/android_alarm_manager_wrapper.dart'; import 'package:mem/notifications/notification_client.dart'; import 'package:mem/notifications/mem_notifications.dart'; @@ -8,7 +9,8 @@ import 'package:mem/notifications/schedule.dart'; import 'package:mem/permissions/permission.dart'; import 'package:mem/permissions/permission_handler_wrapper.dart'; -class ScheduleClient extends Repository { +class ScheduleClient extends Repository + with Receiver { static ScheduleClient? _instance; final AndroidAlarmManagerWrapper _androidAlarmManagerWrapper; final Future Function(int id, Map params) @@ -39,6 +41,7 @@ class ScheduleClient extends Repository { }, ); + @override Future receive(Schedule entity) => v( () async { if (await PermissionHandlerWrapper().grant(Permission.notification)) { @@ -76,6 +79,8 @@ class ScheduleClient extends Repository { @pragma('vm:entry-point') Future scheduleCallback(int id, Map params) => i( () async { + await openDatabase(); + await NotificationClient().show( NotificationType.values.singleWhere( (element) => element.name == params[notificationTypeKey], diff --git a/lib/repositories/mem.dart b/lib/repositories/mem.dart new file mode 100644 index 000000000..663065e2b --- /dev/null +++ b/lib/repositories/mem.dart @@ -0,0 +1,19 @@ +import 'package:mem/core/date_and_time/date_and_time_period.dart'; +import 'package:mem/core/mem.dart'; +import 'package:mem/framework/repository/database_tuple_entity.dart'; + +class SavedMem extends Mem with SavedDatabaseTupleMixin { + SavedMem(super.name, super.doneAt, super.period); + + @override + SavedMem copiedWith({ + String Function()? name, + DateTime? Function()? doneAt, + DateAndTimePeriod? Function()? period, + }) => + SavedMem( + name == null ? this.name : name(), + doneAt == null ? this.doneAt : doneAt(), + period == null ? this.period : period(), + )..copiedFrom(this); +} diff --git a/lib/repositories/mem_notification.dart b/lib/repositories/mem_notification.dart new file mode 100644 index 000000000..2e14ca11a --- /dev/null +++ b/lib/repositories/mem_notification.dart @@ -0,0 +1,23 @@ +import 'package:mem/core/mem_notification.dart'; +import 'package:mem/framework/repository/database_tuple_entity.dart'; + +class SavedMemNotification extends MemNotification + with SavedDatabaseTupleMixin { + @override + int get memId => super.memId as int; + + SavedMemNotification(super.memId, super.type, super.time, super.message); + + @override + SavedMemNotification copiedWith({ + int Function()? memId, + int? Function()? time, + String Function()? message, + }) => + SavedMemNotification( + memId == null ? this.memId : memId(), + type, + time == null ? this.time : time(), + message == null ? this.message : message(), + )..copiedFrom(this); +} diff --git a/lib/repositories/mem_notification_repository.dart b/lib/repositories/mem_notification_repository.dart new file mode 100644 index 000000000..41d919d29 --- /dev/null +++ b/lib/repositories/mem_notification_repository.dart @@ -0,0 +1,131 @@ +import 'package:mem/core/mem_notification.dart'; +import 'package:mem/databases/table_definitions/mem_notifications.dart'; +import 'package:mem/framework/repository/condition/in.dart'; +import 'package:mem/framework/repository/group_by.dart'; +import 'package:mem/framework/repository/order_by.dart'; +import 'package:mem/logger/log_service.dart'; +import 'package:mem/framework/repository/database_tuple_repository.dart'; +import 'package:mem/framework/repository/condition/conditions.dart'; +import 'package:mem/repositories/mem_notification.dart'; + +class MemNotificationRepository extends DatabaseTupleRepository { + @override + Future> ship({ + Iterable? memIdsIn, + Condition? condition, + GroupBy? groupBy, + List? orderBy, + int? offset, + int? limit, + }) => + v( + () => super.ship( + condition: And( + [ + if (memIdsIn != null) + In(defFkMemNotificationsMemId.name, memIdsIn), + if (condition != null) condition, // coverage:ignore-line + ], + ), + groupBy: groupBy, + orderBy: orderBy, + offset: offset, + limit: limit, + ), + { + 'memIds': memIdsIn, + 'condition': condition, + 'groupBy': groupBy, + 'orderBy': orderBy, + 'offset': offset, + 'limit': limit, + }, + ); + + Future> shipByMemId( + int memId, + ) => + v( + () => super + .ship(condition: Equals(defFkMemNotificationsMemId.name, memId)), + { + "memId": memId, + }, + ); + + Future> archiveByMemId(int memId) => v( + () async => Future.wait( + (await shipByMemId(memId)).map((e) => super.archive(e))), + { + "memId": memId, + }, + ); + + Future> unarchiveByMemId(int memId) => v( + () async => Future.wait( + (await shipByMemId(memId)).map((e) => super.unarchive(e))), + { + "memId": memId, + }, + ); + + @override + Future> waste([ + Condition? condition, + int? memIdIs, + MemNotificationType? type, + ]) => + v( + () => super.waste( + And( + [ +// coverage:ignore-start + if (condition != null) condition, +// coverage:ignore-end + if (memIdIs != null) + Equals(defFkMemNotificationsMemId.name, memIdIs), + if (type != null) + Equals(defColMemNotificationsType.name, type.name), + ], + ), + ), + { + 'condition': condition, + 'memId': memIdIs, + 'type': type, + }, + ); + + @override + SavedMemNotification pack(Map tuple) => SavedMemNotification( + tuple[defFkMemNotificationsMemId.name], + MemNotificationType.fromName(tuple[defColMemNotificationsType.name]), + tuple[defColMemNotificationsTime.name], + tuple[defColMemNotificationsMessage.name], + )..pack(tuple); + + @override + Map unpack(MemNotification entity) { + final map = { + defFkMemNotificationsMemId.name: entity.memId, + defColMemNotificationsType.name: entity.type.name, + defColMemNotificationsTime.name: entity.time, + defColMemNotificationsMessage.name: entity.message, + }; + + if (entity is SavedMemNotification) { + map.addAll(entity.unpack()); + } + + return map; + } + + MemNotificationRepository._() : super(defTableMemNotifications); + + static MemNotificationRepository? _instance; + + factory MemNotificationRepository() => v( + () => _instance ??= MemNotificationRepository._(), + ); +} diff --git a/lib/repositories/mem_repository.dart b/lib/repositories/mem_repository.dart new file mode 100644 index 000000000..361922b25 --- /dev/null +++ b/lib/repositories/mem_repository.dart @@ -0,0 +1,123 @@ +import 'package:mem/core/date_and_time/date_and_time.dart'; +import 'package:mem/core/date_and_time/date_and_time_period.dart'; +import 'package:mem/core/mem.dart'; +import 'package:mem/databases/table_definitions/base.dart'; +import 'package:mem/databases/table_definitions/mems.dart'; +import 'package:mem/framework/repository/group_by.dart'; +import 'package:mem/framework/repository/order_by.dart'; +import 'package:mem/logger/log_service.dart'; +import 'package:mem/framework/repository/database_tuple_repository.dart'; +import 'package:mem/framework/repository/condition/conditions.dart'; +import 'package:mem/repositories/mem.dart'; + +class MemRepository extends DatabaseTupleRepository { + @override + Future> ship({ + bool? archived, + bool? done, + Condition? condition, + GroupBy? groupBy, + List? orderBy, + int? offset, + int? limit, + }) => + v( + () => super.ship( + condition: And([ + if (archived != null) + archived + ? IsNotNull(defColArchivedAt.name) + : IsNull(defColArchivedAt.name), + if (done != null) + done + ? IsNotNull(defColMemsDoneAt.name) + : IsNull(defColMemsDoneAt.name), + if (condition != null) condition, // coverage:ignore-line + ]), + orderBy: orderBy, + offset: offset, + limit: limit, + ), + { + 'archived': archived, + 'done': done, + 'condition': condition, + 'groupBy': groupBy, + 'orderBy': orderBy, + 'offset': offset, + 'limit': limit, + }, + ); + + @override + Future findOneBy({ + int? id, + Condition? condition, + List? orderBy, + }) => + v( + () => super.findOneBy( + condition: And([ + if (id != null) Equals(defPkId.name, id), + if (condition != null) condition, // coverage:ignore-line + ]), + orderBy: orderBy, + ), + { + "id": id, + "condition": condition, + "orderBy": orderBy, + }, + ); + + @override + SavedMem pack(Map tuple) { + final startOn = tuple[defColMemsStartOn.name]; + final endOn = tuple[defColMemsEndOn.name]; + + return SavedMem( + tuple[defColMemsName.name], + tuple[defColMemsDoneAt.name], + startOn == null && endOn == null + ? null + : DateAndTimePeriod( + start: startOn == null + ? null + : DateAndTime.from(startOn, + timeOfDay: tuple[defColMemsStartAt.name]), + end: endOn == null + ? null + : DateAndTime.from(endOn, + timeOfDay: tuple[defColMemsEndAt.name]), + ), + )..pack(tuple); + } + + @override + Map unpack(Mem entity) { + final map = { + defColMemsName.name: entity.name, + defColMemsDoneAt.name: entity.doneAt, + defColMemsStartOn.name: entity.period?.start, + defColMemsStartAt.name: + entity.period?.start?.isAllDay == true ? null : entity.period?.start, + defColMemsEndOn.name: entity.period?.end, + defColMemsEndAt.name: + entity.period?.end?.isAllDay == true ? null : entity.period?.end, + }; + + if (entity is SavedMem) { + map.addAll(entity.unpack()); + } + + return map; + } + + MemRepository._() : super(defTableMems); + + static MemRepository? _instance; + + factory MemRepository() => v( + () => _instance ??= MemRepository._(), + ); +} diff --git a/lib/settings/actions.dart b/lib/settings/actions.dart index 7705fcb0b..d35ebe7ef 100644 --- a/lib/settings/actions.dart +++ b/lib/settings/actions.dart @@ -3,19 +3,14 @@ import 'package:mem/settings/preference.dart'; import 'package:mem/settings/client.dart'; import 'package:mem/settings/preference_key.dart'; -final _client = PreferenceClientRepository(); +final _client = PreferenceClient(); -Future loadByKey( - PreferenceKey key, -) => - v( +Future loadByKey(PreferenceKey key) => v( () async => (await _client.shipByKey(key)).value, - { - 'key': key, - }, + {"key": key}, ); -Future update, Value>( +Future update, Value>( Key key, Value? value, ) => @@ -24,9 +19,6 @@ Future update, Value>( // coverage:ignore-start ? _client.discard(key) // coverage:ignore-end - : _client.receive(PreferenceEntity(key, value))), - { - 'key': key, - 'value': value, - }, + : _client.receive(Preference(key, value))), + {"key": key, "value": value}, ); diff --git a/lib/settings/client.dart b/lib/settings/client.dart index 4ac0c4d5e..80b8b9fcf 100644 --- a/lib/settings/client.dart +++ b/lib/settings/client.dart @@ -4,22 +4,22 @@ import 'package:mem/settings/preference.dart'; import 'package:mem/settings/preference_key.dart'; import 'package:shared_preferences/shared_preferences.dart'; -class PreferenceClientRepository - extends KeyWithValueRepository { - Future> shipByKey(PreferenceKey key) => v( +class PreferenceClient + extends KeyWithValueRepository { + Future> shipByKey(PreferenceKey key) => v( () async { final saved = (await SharedPreferences.getInstance()).get(key.value); - return PreferenceEntity( + return Preference( key, key.deserialize(saved), ); }, - {'key': key}, + key, ); @override - Future receive(PreferenceEntity entity) => v( + Future receive(Preference entity) => v( () async { final serialized = entity.key.serialize(entity.value); @@ -34,12 +34,18 @@ class PreferenceClientRepository throw UnimplementedError(); // coverage:ignore-line } }, - {'entity': entity}, + entity, ); @override - Future discard(PreferenceKey key) => v( + Future discard(PreferenceKey key) => v( () async => (await SharedPreferences.getInstance()).remove(key.value), - {'key': key}, + key, ); + + PreferenceClient._(); + + static PreferenceClient? _instance; + + factory PreferenceClient() => _instance ??= PreferenceClient._(); } diff --git a/lib/settings/preference.dart b/lib/settings/preference.dart index 15caf25ed..ff697c271 100644 --- a/lib/settings/preference.dart +++ b/lib/settings/preference.dart @@ -1,11 +1,6 @@ -import 'package:mem/framework/repository/entity.dart'; import 'package:mem/framework/repository/key_with_value.dart'; import 'package:mem/settings/preference_key.dart'; -class PreferenceEntity - with Entity, KeyWithValue, VALUE?> { - PreferenceEntity(PreferenceKey key, VALUE? value) { - this.key = key; - this.value = value; - } +class Preference extends KeyWithValue, T?> { + Preference(super.key, super.value); } diff --git a/pubspec.yaml b/pubspec.yaml index 2b9c1d820..bdb7b3dad 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -17,7 +17,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. # FIXME https://github.com/zin-/mem/issues/321 -version: 0.5.2 +version: 0.5.1 environment: sdk: ^3.5.0 diff --git a/test/act_counter/act_counter_test.dart b/test/act_counter/act_counter_test.dart index 6f0950e80..dfbdbcd7d 100644 --- a/test/act_counter/act_counter_test.dart +++ b/test/act_counter/act_counter_test.dart @@ -1,54 +1,31 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:mem/act_counter/act_counter.dart'; +import 'package:mem/core/act.dart'; import 'package:mem/core/date_and_time/date_and_time.dart'; import 'package:mem/core/date_and_time/date_and_time_period.dart'; -import 'package:mem/databases/table_definitions/base.dart'; -import 'package:mem/acts/act_entity.dart'; -import 'package:mem/mems/mem_entity.dart'; +import 'package:mem/repositories/mem.dart'; void main() { - group('ActCounter.from', () { - test(": updatedAt is last act start.", () { + test( + ": constructor.", + () { const memId = 1; final zeroDate = DateTime(0); final oneDate = DateTime(1); - final savedMem = SavedMemEntity("constructor", null, null)..id = memId; + final savedMem = SavedMem("constructor", null, null)..id = memId; final acts = [ - SavedActEntity(memId, DateAndTimePeriod.startNow(), { - defPkId.name: 1, - defColCreatedAt.name: zeroDate, - defColUpdatedAt.name: oneDate - }), - SavedActEntity(memId, DateAndTimePeriod.startNow(), - {defPkId.name: 2, defColCreatedAt.name: zeroDate}) + SavedAct(memId, DateAndTimePeriod(end: DateAndTime.now())) + ..createdAt = zeroDate + ..updatedAt = oneDate, + SavedAct(memId, DateAndTimePeriod.startNow())..createdAt = zeroDate, ]; - final actCounter = ActCounter.from(savedMem, acts); + final actCounter = ActCounter(savedMem, acts); - expect(actCounter.updatedAt, equals(acts[0].period.start)); - }); - test(": updatedAt is last act end.", () { - const memId = 1; - final zeroDate = DateTime(0); - final oneDate = DateTime(1); - - final savedMem = SavedMemEntity("constructor", null, null)..id = memId; - final acts = [ - SavedActEntity(memId, DateAndTimePeriod(end: DateAndTime.now()), { - defPkId.name: 3, - defColCreatedAt.name: zeroDate, - defColUpdatedAt.name: oneDate - }), - SavedActEntity(memId, DateAndTimePeriod.startNow(), - {defPkId.name: 4, defColCreatedAt.name: zeroDate}) - ]; - - final actCounter = ActCounter.from(savedMem, acts); - - expect(actCounter.updatedAt, equals(acts[0].period.end)); - }); - }); + expect(actCounter.lastAct, equals(acts[0])); + }, + ); group('period', () { test(': startDate time is 5:00', () { diff --git a/test/components/date_and_time/date_and_time_period_text_form_fields_test.dart b/test/components/date_and_time/date_and_time_period_text_form_fields_test.dart index f4b78aeea..3ed28ef22 100644 --- a/test/components/date_and_time/date_and_time_period_text_form_fields_test.dart +++ b/test/components/date_and_time/date_and_time_period_text_form_fields_test.dart @@ -12,14 +12,14 @@ void main() { for (final testCase in [ TestCase( - name: 'pick start', + 'pick start', DateAndTimePeriod(start: DateAndTime.from(now)), - null, + (input) => null, ), TestCase( - name: 'pick end', + 'pick end', DateAndTimePeriod(end: DateAndTime.from(now)), - null, + (input) => null, ), ]) { testWidgets( diff --git a/test/components/date_and_time/date_and_time_text_form_field_test.dart b/test/components/date_and_time/date_and_time_text_form_field_test.dart index a587cdc07..5530b1d7c 100644 --- a/test/components/date_and_time/date_and_time_text_form_field_test.dart +++ b/test/components/date_and_time/date_and_time_text_form_field_test.dart @@ -17,7 +17,7 @@ void main() { }) async { await widgetTester.pumpWidget(MaterialApp( home: Scaffold( - body: DateAndTimeTextFormField( + body: DateAndTimeTextFormFieldV2( dateAndTime, onChanged, selectableRange: selectableRange, diff --git a/test/core/act_test.dart b/test/core/act_test.dart index dc32f7290..fa33bf608 100644 --- a/test/core/act_test.dart +++ b/test/core/act_test.dart @@ -1,5 +1,5 @@ import 'package:flutter_test/flutter_test.dart'; -import 'package:mem/acts/act.dart'; +import 'package:mem/core/act.dart'; import 'package:mem/core/date_and_time/date_and_time.dart'; import 'package:mem/core/date_and_time/date_and_time_period.dart'; diff --git a/test/core/date_and_time_period_test.dart b/test/core/date_and_time_period_test.dart index 011f96107..4a56369f5 100644 --- a/test/core/date_and_time_period_test.dart +++ b/test/core/date_and_time_period_test.dart @@ -266,7 +266,7 @@ void main() { group(": compare", () { for (var testCase in [ - TestCase( + TestCaseV2( name: "both are not null", [ DateAndTimePeriod.startNow(), @@ -275,17 +275,17 @@ void main() { DateAndTimePeriod.startNow() .compareTo(DateAndTimePeriod(end: DateAndTime.now())), ), - TestCase( + TestCaseV2( name: "both are null", [null, null], 0, ), - TestCase( + TestCaseV2( name: "a is null, b is not null", [null, DateAndTimePeriod.startNow()], 1, ), - TestCase( + TestCaseV2( name: "a is not null, b is null", [DateAndTimePeriod.startNow(), null], -1, diff --git a/test/core/mem_notification_test.dart b/test/core/mem_notification_test.dart index c146eae84..39caf79cc 100644 --- a/test/core/mem_notification_test.dart +++ b/test/core/mem_notification_test.dart @@ -1,7 +1,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:intl/intl.dart'; import 'package:mem/core/date_and_time/date_and_time.dart'; -import 'package:mem/mems/mem_notification.dart'; +import 'package:mem/core/mem_notification.dart'; void main() { group('MemNotification', () { @@ -11,8 +11,8 @@ void main() { const memId = 1; const time = 2; - final repeatByDayOfWeek = MemNotification(memId, - MemNotificationType.repeatByDayOfWeek, time, "repeatByDayOfWeek"); + final repeatByDayOfWeek = + MemNotification.repeatByDayOfWeek(memId, time); expect(repeatByDayOfWeek.isRepeatByDayOfWeek(), isTrue); expect(repeatByDayOfWeek.memId, equals(memId)); @@ -20,118 +20,190 @@ void main() { }, ); - group('toOneLine', () { - String buildRepeatedNotificationText(String at) => "repeat at $at"; - String buildRepeatEveryNDayNotificationText(String nDay, String at) => - "repeat at $at by $nDay"; - String buildAfterActStartedNotificationText(String at) => - "after act at $at"; - String formatToTimeOfDay(DateAndTime dateAndTime) => - "${dateAndTime.hour}:${dateAndTime.minute}"; - - test('no enables.', () { - const memId = 1; - - final oneLine = MemNotification.toOneLine([ - MemNotification(memId, MemNotificationType.repeat, null, "repeat") - ], (at) => fail("no call"), (nDay, at) => fail("no call"), - (a) => fail("no call"), (dateAndTime) => fail("no call")); - - expect(oneLine, isNull); - }); - - group('repeat', () { - test('repeat at 0:0.', () { - const memId = 1; - const repeatAt = 0; - - final oneLine = MemNotification.toOneLine([ - MemNotification(memId, MemNotificationType.repeat, repeatAt, "") - ], buildRepeatedNotificationText, (nDay, at) => fail("no call"), - (a) => fail("no call"), formatToTimeOfDay); - - expect( - oneLine, - equals(buildRepeatedNotificationText( - formatToTimeOfDay(DateAndTime(0, 0, 0, 0, 0, repeatAt))))); - }); - - test('repeat at 05:00 by 2 day.', () { - const memId = 1; - const repeatAt = (5 * 60) * 60; - const repeatByNDay = 2; - - final oneLine = MemNotification.toOneLine([ - MemNotification(memId, MemNotificationType.repeat, repeatAt, ""), - MemNotification( - memId, MemNotificationType.repeatByNDay, repeatByNDay, "") - ], (at) => fail("no call"), buildRepeatEveryNDayNotificationText, - (at) => fail("no call"), formatToTimeOfDay); - - expect( - oneLine, - equals(buildRepeatEveryNDayNotificationText( - repeatByNDay.toString(), - formatToTimeOfDay(DateAndTime(0, 0, 0, 0, 0, repeatAt))))); - }); - - test('repeat at 12:00 by 3 day on Mon.', () { - const memId = 1; - const repeatAt = (5 * 60) * 60; - const repeatByNDay = 2; - - final oneLine = MemNotification.toOneLine([ - MemNotification(memId, MemNotificationType.repeat, repeatAt, ""), - MemNotification( - memId, MemNotificationType.repeatByNDay, repeatByNDay, ""), - MemNotification(memId, MemNotificationType.repeatByDayOfWeek, 1, "") - ], (at) => fail("no call"), buildRepeatEveryNDayNotificationText, - (at) => fail("no call"), formatToTimeOfDay); - - expect( - oneLine, - equals("${buildRepeatEveryNDayNotificationText( - repeatByNDay.toString(), - formatToTimeOfDay(DateAndTime(0, 0, 0, 0, 0, repeatAt)), - )}, Mon")); - }); - }); - - test('repeat by Tue.', () { - const memId = 1; - - final oneLine = MemNotification.toOneLine([ - MemNotification(memId, MemNotificationType.repeatByDayOfWeek, 2, - "repeatByDayOfWeek") - ], buildRepeatedNotificationText, (a, b) => "$a, $b", (a) => a, - formatToTimeOfDay); - - expect(oneLine, equals("Tue")); - }); - - test('after act', () { - const memId = 1; - const time = 2; - final oneLine = MemNotification.toOneLine([ - MemNotification(memId, MemNotificationType.afterActStarted, time, "") - ], (a) => fail("no call"), (a, b) => fail("no call"), - buildAfterActStartedNotificationText, formatToTimeOfDay); - - expect( - oneLine, - buildAfterActStartedNotificationText( - DateFormat(DateFormat.HOUR24_MINUTE) - .format(DateAndTime(0, 0, 0, 0, 0, time)))); - }); - }); + group( + 'toOneLine', + () { + String buildRepeatedNotificationText(String at) => "repeat at $at"; + String buildRepeatEveryNDayNotificationText(String nDay, String at) => + "repeat at $at by $nDay"; + String buildAfterActStartedNotificationText(String at) => + "after act at $at"; + String formatToTimeOfDay(DateAndTime dateAndTime) => + "${dateAndTime.hour}:${dateAndTime.minute}"; + + test( + 'no enables.', + () { + const memId = 1; + + final oneLine = MemNotification.toOneLine( + [ + MemNotification( + memId, MemNotificationType.repeat, null, "message") + ], + (at) => fail("no call"), + (nDay, at) => fail("no call"), + (a) => fail("no call"), + (dateAndTime) => fail("no call"), + ); + + expect(oneLine, isNull); + }, + ); + + group( + 'repeat', + () { + test( + 'repeat at 0:0.', + () { + const memId = 1; + const repeatAt = 0; + + final oneLine = MemNotification.toOneLine( + [ + MemNotification.repeated(memId).copiedWith( + time: () => repeatAt, + ) + ], + buildRepeatedNotificationText, + (nDay, at) => fail("no call"), + (a) => fail("no call"), + formatToTimeOfDay, + ); + + expect( + oneLine, + equals(buildRepeatedNotificationText(formatToTimeOfDay( + DateAndTime(0, 0, 0, 0, 0, repeatAt))))); + }, + ); + + test( + 'repeat at 05:00 by 2 day.', + () { + const memId = 1; + const repeatAt = (5 * 60) * 60; + const repeatByNDay = 2; + + final oneLine = MemNotification.toOneLine( + [ + MemNotification.repeated(memId).copiedWith( + time: () => repeatAt, + ), + MemNotification.repeatByNDay(memId).copiedWith( + time: () => repeatByNDay, + ) + ], + (at) => fail("no call"), + buildRepeatEveryNDayNotificationText, + (at) => fail("no call"), + formatToTimeOfDay, + ); + + expect( + oneLine, + equals(buildRepeatEveryNDayNotificationText( + repeatByNDay.toString(), + formatToTimeOfDay( + DateAndTime(0, 0, 0, 0, 0, repeatAt))))); + }, + ); + + test( + 'repeat at 12:00 by 3 day on Mon.', + () { + const memId = 1; + const repeatAt = (5 * 60) * 60; + const repeatByNDay = 2; + + final oneLine = MemNotification.toOneLine( + [ + MemNotification.repeated(memId).copiedWith( + time: () => repeatAt, + ), + MemNotification.repeatByNDay(memId).copiedWith( + time: () => repeatByNDay, + ), + MemNotification.repeatByDayOfWeek(memId, 1), + ], + (at) => fail("no call"), + buildRepeatEveryNDayNotificationText, + (at) => fail("no call"), + formatToTimeOfDay, + ); + + expect( + oneLine, + equals( + "${buildRepeatEveryNDayNotificationText( + repeatByNDay.toString(), + formatToTimeOfDay( + DateAndTime(0, 0, 0, 0, 0, repeatAt), + ), + )}, Mon", + )); + }, + ); + }, + ); + + test( + 'repeat by Tue.', + () { + const memId = 1; + + final oneLine = MemNotification.toOneLine( + [MemNotification.repeatByDayOfWeek(memId, 2)], + buildRepeatedNotificationText, + (a, b) => "$a, $b", + (a) => a, + formatToTimeOfDay, + ); + + expect(oneLine, equals("Tue")); + }, + ); + + test( + 'after act', + () { + const memId = 1; + const time = 2; + final oneLine = MemNotification.toOneLine( + [ + MemNotification.afterActStarted(memId).copiedWith( + time: () => time, + ) + ], + (a) => fail("no call"), + (a, b) => fail("no call"), + buildAfterActStartedNotificationText, + formatToTimeOfDay, + ); + + expect( + oneLine, + buildAfterActStartedNotificationText( + DateFormat(DateFormat.HOUR24_MINUTE) + .format(DateAndTime(0, 0, 0, 0, 0, time)))); + }, + ); + }, + ); }); test('MemNotificationType from unexpected name throw.', () { const name = 'unexpected name'; - expect(() => MemNotificationType.fromName(name), throwsA((e) { - expect(e.message, 'Unexpected name: "$name".'); - return true; - })); + expect( + () => MemNotificationType.fromName(name), + throwsA( + (e) { + expect(e.message, 'Unexpected name: "$name".'); + return true; + }, + ), + ); }); } diff --git a/test/framework/repository/database_tuple_entity_test.dart b/test/framework/repository/database_tuple_entity_test.dart deleted file mode 100644 index 536d42ea9..000000000 --- a/test/framework/repository/database_tuple_entity_test.dart +++ /dev/null @@ -1,67 +0,0 @@ -import 'package:flutter_test/flutter_test.dart'; -import 'package:mem/databases/table_definitions/base.dart'; -import 'package:mem/framework/repository/database_tuple_entity.dart'; - -import 'entity_test.dart'; - -const _name = 'DatabaseTupleEntity test'; - -class TestObjectDatabaseTupleEntity extends TestObjectEntity - with DatabaseTupleEntity { - TestObjectDatabaseTupleEntity(super.a); - - TestObjectDatabaseTupleEntity.fromMap(Map map) - : super.fromMap(map) { - withMap(map); - } -} - -void main() => group( - _name, - () { - test( - '#new', - () { - const a = false; - - final testObject = TestObjectDatabaseTupleEntity(a); - - expect(testObject.a, equals(a)); - }, - ); - - test( - '#fromMap', - () { - final map = { - TestObjectEntity.fieldNames[0]: false, - defPkId.name: 1, - defColCreatedAt.name: DateTime.now(), - defColUpdatedAt.name: DateTime.now(), - defColArchivedAt.name: null - }; - - final testObject = TestObjectDatabaseTupleEntity.fromMap(map); - - expect(testObject.a, map[TestObjectEntity.fieldNames[0]]); - }, - ); - - test( - '#toMap', - () { - final map = { - TestObjectEntity.fieldNames[0]: false, - defPkId.name: 1, - defColCreatedAt.name: DateTime.now(), - defColUpdatedAt.name: DateTime.now(), - defColArchivedAt.name: null - }; - - final testObject = TestObjectDatabaseTupleEntity.fromMap(map); - - expect(testObject.toMap, map); - }, - ); - }, - ); diff --git a/test/framework/repository/entity_test.dart b/test/framework/repository/entity_test.dart deleted file mode 100644 index 47bc819b6..000000000 --- a/test/framework/repository/entity_test.dart +++ /dev/null @@ -1,74 +0,0 @@ -import 'package:flutter_test/flutter_test.dart'; -import 'package:mem/framework/repository/entity.dart'; - -const _name = 'Entity test'; - -class _TestObject { - final bool a; - - _TestObject(this.a); -} - -class TestObjectEntity extends _TestObject with Entity { - static List fieldNames = ['a']; - - TestObjectEntity(super.a); - - TestObjectEntity.fromMap(Map map) - : super(map[TestObjectEntity.fieldNames[0]]); - - @override - Map get toMap => {fieldNames[0]: a}; -} - -void main() => group( - _name, - () { - test( - '#new', - () { - const a = false; - - final testObject = TestObjectEntity(a); - - expect(testObject.a, equals(a)); - }, - ); - - test( - '#fromMap', - () { - final map = {TestObjectEntity.fieldNames[0]: false}; - - final testObject = TestObjectEntity.fromMap(map); - - expect(testObject.a, equals(map[TestObjectEntity.fieldNames[0]])); - }, - ); - - test( - '#toMap', - () { - const a = false; - - final testObject = TestObjectEntity(a); - - expect( - testObject.toMap, equals({TestObjectEntity.fieldNames[0]: a})); - }, - ); - - test( - '#==', - () { - const a = false; - const b = false; - - final testObjectA = TestObjectEntity(a); - final testObjectB = TestObjectEntity(b); - - expect(testObjectA, equals(testObjectB)); - }, - ); - }, - ); diff --git a/test/helpers.dart b/test/helpers.dart index bafb2a757..7412bab98 100644 --- a/test/helpers.dart +++ b/test/helpers.dart @@ -4,10 +4,12 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:mem/act_counter/act_counter_repository.dart'; import 'package:mem/act_counter/act_counter_client.dart'; import 'package:mem/act_counter/home_widget_accessor.dart'; import 'package:mem/components/l10n.dart'; import 'package:mem/logger/logger_wrapper.dart'; +import 'package:mem/notifications/notification_repository.dart'; import 'package:mem/notifications/flutter_local_notifications_wrapper.dart'; import 'package:mockito/annotations.dart'; @@ -23,6 +25,8 @@ int randomInt([int max = 42949671]) => Random().nextInt(max); FlutterLocalNotificationsWrapper, // FIXME RepositoryではなくTableをmockする // Repositoryはシステム固有の処理であるのに対して、Tableは永続仮想をラップする役割を持つため + NotificationRepository, + ActCounterRepository, ActCounterClient, ]) void main() {} @@ -44,12 +48,20 @@ Widget buildTestAppWithProvider( child: buildTestApp(widget), ); -class TestCase { - final INPUT input; - final EXPECTED expected; +class TestCaseV2 { + final I input; + final dynamic expected; final String? name; - TestCase(this.input, this.expected, {this.name}); + TestCaseV2(this.input, this.expected, {this.name}); +} + +class TestCase { + final String name; + final T input; + final Function(T input) verify; + + TestCase(this.name, this.input, this.verify); } // Finders diff --git a/test/logger/logger_service_test.dart b/test/logger/logger_service_test.dart index b142842c6..e53a4ad8a 100644 --- a/test/logger/logger_service_test.dart +++ b/test/logger/logger_service_test.dart @@ -23,18 +23,12 @@ void main() { group(": on service level is info", () { for (final testCase in [ - TestCase( - name: "verbose", - Level.verbose, - (Level input) => - verifyNever(mockedLoggerWrapper.log(any, any, any, any)), - ), - TestCase( - name: "info", - Level.info, - (Level input) => - verify(mockedLoggerWrapper.log(input, any, null, null)).called(1), - ), + TestCase("verbose", Level.verbose, (Level input) { + verifyNever(mockedLoggerWrapper.log(any, any, any, any)); + }), + TestCase("info", Level.info, (Level input) { + verify(mockedLoggerWrapper.log(input, any, null, null)).called(1); + }), ]) { test(": log level is ${testCase.name}.", () { final level = testCase.input; @@ -44,7 +38,7 @@ void main() { expect(result, target); - testCase.expected(level); + testCase.verify(level); }); } }); @@ -337,17 +331,12 @@ void main() { }); for (final testCase in [ - TestCase( - name: "verbose", - Level.verbose, - (input) => - verifyNever(mockedLoggerWrapper.log(any, any, any, any))), - TestCase( - name: "info", - Level.info, - (input) => - verifyNever(mockedLoggerWrapper.log(any, any, any, any)), - ), + TestCase("verbose", Level.verbose, (input) { + verifyNever(mockedLoggerWrapper.log(any, any, any, any)); + }), + TestCase("info", Level.info, (input) { + verifyNever(mockedLoggerWrapper.log(any, any, any, any)); + }), ]) { test( ": on error: ${testCase.name}.", @@ -356,7 +345,6 @@ void main() { const errorMessage = "test message future"; final e = Exception(errorMessage); - verifyNever(mockedLoggerWrapper.log(any, any, any, any)); expect( () => LogService().valueLog(level, Future.error(e)), throwsA((thrown) { @@ -383,32 +371,25 @@ void main() { group(": alias", () { for (final testCase in [ + TestCase("verbose", verbose, (input) { + verifyNever(mockedLoggerWrapper.log(any, any, any, any)); + }), + TestCase("info", info, (input) { + verify(mockedLoggerWrapper.log(Level.info, "info", null, null)) + .called(1); + }), + TestCase("warn", warn, (input) { + verify(mockedLoggerWrapper.log(Level.warning, "warn", null, null)) + .called(1); + }), TestCase( - name: "verbose", - verbose, - (input) => verifyNever(mockedLoggerWrapper.log(any, any, any, any)), - ), - TestCase( - name: "info", - info, - (input) => - verify(mockedLoggerWrapper.log(Level.info, "info", null, null)) - .called(1), - ), - TestCase( - name: "warn", - warn, - (input) => - verify(mockedLoggerWrapper.log(Level.warning, "warn", null, null)) - .called(1), - ), - TestCase( - name: "debug", + "debug", // ignore: deprecated_member_use_from_same_package debug, - (input) => - verify(mockedLoggerWrapper.log(Level.debug, "debug", null, null)) - .called(1), + (input) { + verify(mockedLoggerWrapper.log(Level.debug, "debug", null, null)) + .called(1); + }, ), ]) { test(": ${testCase.name}", () { @@ -418,7 +399,7 @@ void main() { expect(result, testMessage); - testCase.expected(testCase.input); + testCase.verify(testCase.input); }); } }); @@ -849,37 +830,26 @@ void main() { group(": alias", () { for (final testCase in [ + TestCase("v", v, (input) { + verifyNever(mockedLoggerWrapper.log(any, any, any, any)); + }), + TestCase("i", i, (input) { + verify(mockedLoggerWrapper.log( + Level.info, "[start] :: null", null, null)) + .called(1); + verify(mockedLoggerWrapper.log(Level.info, "[end] => i", null, null)) + .called(1); + }), + TestCase("w", w, (input) { + verify(mockedLoggerWrapper.log( + Level.warning, "[start] :: null", null, null)) + .called(1); + verify(mockedLoggerWrapper.log( + Level.warning, "[end] => w", null, null)) + .called(1); + }), TestCase( - name: "v", - v, - (input) => verifyNever(mockedLoggerWrapper.log(any, any, any, any)), - ), - TestCase( - name: "i", - i, - (input) { - verify(mockedLoggerWrapper.log( - Level.info, "[start] :: null", null, null)) - .called(1); - verify(mockedLoggerWrapper.log( - Level.info, "[end] => i", null, null)) - .called(1); - }, - ), - TestCase( - name: "w", - w, - (input) { - verify(mockedLoggerWrapper.log( - Level.warning, "[start] :: null", null, null)) - .called(1); - verify(mockedLoggerWrapper.log( - Level.warning, "[end] => w", null, null)) - .called(1); - }, - ), - TestCase( - name: "d", + "d", // ignore: deprecated_member_use_from_same_package d, (input) { @@ -901,7 +871,7 @@ void main() { expect(result, testMessage); - testCase.expected(testCase.input); + testCase.verify(testCase.input); }); } }); diff --git a/test/mems/mem_list_body_test.dart b/test/mems/mem_list_body_test.dart index 257776fa3..d0a596c84 100644 --- a/test/mems/mem_list_body_test.dart +++ b/test/mems/mem_list_body_test.dart @@ -9,7 +9,7 @@ import 'package:mem/mems/detail/states.dart'; import 'package:mem/mems/list/body.dart'; import 'package:mem/mems/list/show_new_mem_fab.dart'; import 'package:mem/mems/states.dart'; -import 'package:mem/mems/mem_entity.dart'; +import 'package:mem/repositories/mem.dart'; import '../../integration_test/scenarios/helpers.dart'; @@ -18,15 +18,13 @@ void main() { final scrollController = ScrollController(); final samples = List.generate( 20, - (index) => SavedMemEntity( + (index) => SavedMem( 'Hide & show ShowNewMemFab: mem name - $index', null, null, ) ..id = index - ..createdAt = zeroDate - ..updatedAt = null - ..archivedAt = null, + ..createdAt = zeroDate, ); await widgetTester.pumpWidget(ProviderScope(