Skip to content

Commit

Permalink
feat: checklist items
Browse files Browse the repository at this point in the history
  • Loading branch information
matthiasn committed Nov 18, 2024
1 parent 8a84064 commit 6f60465
Show file tree
Hide file tree
Showing 11 changed files with 186 additions and 19 deletions.
36 changes: 35 additions & 1 deletion lib/features/tasks/state/checklist_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,43 @@ class ChecklistController extends _$ChecklistController {
);
await _persistenceLogic.updateChecklist(
checklistId: entryId,
title: title ?? '',
data: updated.data,
);
state = AsyncData(updated);
}
}

Future<void> createChecklistItem(String? title) async {
final current = state.value;
final data = current?.data;
if (current != null && data != null && title != null) {
final created = await _persistenceLogic.createChecklistItem(
title: title,
checklist: current,
);

if (created != null) {
final updated = current.copyWith(
data: current.data.copyWith(
linkedChecklistItems: [
...data.linkedChecklistItems,
created.id,
],
),
);

await _persistenceLogic.updateChecklist(
checklistId: current.id,
data: updated.data.copyWith(
linkedChecklistItems: [
...data.linkedChecklistItems,
created.id,
],
),
);

state = AsyncData(updated);
}
}
}
}
2 changes: 1 addition & 1 deletion lib/features/tasks/state/checklist_controller.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 25 additions & 0 deletions lib/features/tasks/state/checklist_item_controller.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'dart:async';

import 'package:flutter/cupertino.dart';
import 'package:lotti/classes/journal_entities.dart';
import 'package:lotti/database/database.dart';
import 'package:lotti/get_it.dart';
Expand Down Expand Up @@ -61,6 +62,30 @@ class ChecklistItemController extends _$ChecklistItemController {
isChecked: !data.isChecked,
),
);

getIt<PersistenceLogic>().updateChecklistItem(
checklistItemId: entryId,
data: updated.data,
);

state = AsyncData(updated);
}
}

void updateTitle(String? title) {
debugPrint('updateTitle $title');
final current = state.value;
final data = current?.data;
if (current != null && data != null && title != null) {
final updated = current.copyWith(
data: data.copyWith(title: title),
);

getIt<PersistenceLogic>().updateChecklistItem(
checklistItemId: entryId,
data: updated.data,
);

state = AsyncData(updated);
}
}
Expand Down
2 changes: 1 addition & 1 deletion lib/features/tasks/state/checklist_item_controller.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions lib/features/tasks/ui/checkbox_item_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class CheckboxItemWidget extends StatefulWidget {
required this.title,
required this.isChecked,
required this.onChanged,
this.onTitleChange,
this.onEdit,
super.key,
});
Expand All @@ -18,6 +19,7 @@ class CheckboxItemWidget extends StatefulWidget {
final bool isChecked;
final BoolCallback onChanged;
final VoidCallback? onEdit;
final StringCallback? onTitleChange;

@override
State<CheckboxItemWidget> createState() => _CheckboxItemWidgetState();
Expand All @@ -38,7 +40,6 @@ class _CheckboxItemWidgetState extends State<CheckboxItemWidget> {
return CheckboxListTile(
title: GestureDetector(
onTap: () {
debugPrint('Tapped');
setState(() {
_isEditing = true;
});
Expand All @@ -48,10 +49,10 @@ class _CheckboxItemWidgetState extends State<CheckboxItemWidget> {
firstChild: TitleTextField(
initialValue: widget.title,
onSave: (title) {
debugPrint('Saved: $title');
setState(() {
_isEditing = false;
});
widget.onTitleChange?.call(title);
},
resetToInitialValue: true,
onClear: () {
Expand Down Expand Up @@ -80,7 +81,6 @@ class _CheckboxItemWidgetState extends State<CheckboxItemWidget> {
)
: null,
onChanged: (bool? value) {
debugPrint('Checked $value');
setState(() {
_isChecked = value ?? false;
});
Expand Down
3 changes: 2 additions & 1 deletion lib/features/tasks/ui/checkbox_item_wrapper.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ class CheckboxItemWrapper extends ConsumerWidget {
return CheckboxItemWidget(
title: item.data.title,
isChecked: item.data.isChecked,
onChanged: (checked) => ref.read(provider.notifier).toggleChecked(),
onChanged: (_) => ref.read(provider.notifier).toggleChecked(),
onTitleChange: ref.read(provider.notifier).updateTitle,
);
},
error: ErrorWidget.new,
Expand Down
12 changes: 7 additions & 5 deletions lib/features/tasks/ui/checklist_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@ class ChecklistWidget extends StatefulWidget {
const ChecklistWidget({
required this.title,
required this.itemIds,
this.onTitleSave,
required this.onTitleSave,
required this.onCreateChecklistItem,
super.key,
});

final String title;
final List<String> itemIds;
final StringCallback? onTitleSave;
final StringCallback onTitleSave;
final StringCallback onCreateChecklistItem;

@override
State<ChecklistWidget> createState() => _ChecklistWidgetState();
Expand All @@ -35,7 +37,7 @@ class _ChecklistWidgetState extends State<ChecklistWidget> {
child: TitleTextField(
initialValue: widget.title,
onSave: (title) {
widget.onTitleSave?.call(title);
widget.onTitleSave.call(title);
setState(() {
_isEditing = false;
});
Expand Down Expand Up @@ -79,8 +81,7 @@ class _ChecklistWidgetState extends State<ChecklistWidget> {
),
child: TitleTextField(
onSave: (title) {
debugPrint('Saved: $title');
widget.onTitleSave?.call(title);
widget.onCreateChecklistItem.call(title);
},
clearOnSave: true,
semanticsLabel: 'Add item to checklist',
Expand Down Expand Up @@ -114,6 +115,7 @@ class ChecklistWrapper extends ConsumerWidget {
title: checklist.data.title,
itemIds: checklist.data.linkedChecklistItems,
onTitleSave: notifier.updateTitle,
onCreateChecklistItem: notifier.createChecklistItem,
);
}
}
2 changes: 1 addition & 1 deletion lib/features/tasks/ui/checklists_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import 'package:lotti/logic/create/create_entry.dart';
class ChecklistsWidget extends ConsumerWidget {
const ChecklistsWidget({
required this.entryId,
super.key,
required this.task,
super.key,
});

final String entryId;
Expand Down
3 changes: 1 addition & 2 deletions lib/features/tasks/ui/task_form.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,12 @@ import 'package:lotti/classes/journal_entities.dart';
import 'package:lotti/features/journal/state/entry_controller.dart';
import 'package:lotti/features/journal/ui/widgets/editor/editor_widget.dart';
import 'package:lotti/features/journal/util/entry_tools.dart';
import 'package:lotti/features/tasks/ui/checklists_widget.dart';
import 'package:lotti/l10n/app_localizations_context.dart';
import 'package:lotti/themes/theme.dart';
import 'package:lotti/widgets/categories/category_field.dart';
import 'package:lotti/widgets/date_time/duration_bottom_sheet.dart';

import 'checklists_widget.dart';

class TaskForm extends ConsumerStatefulWidget {
const TaskForm(
this.task, {
Expand Down
112 changes: 108 additions & 4 deletions lib/logic/persistence_logic.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:lotti/classes/audio_note.dart';
import 'package:lotti/classes/checklist_data.dart';
import 'package:lotti/classes/checklist_item_data.dart';
import 'package:lotti/classes/entity_definitions.dart';
import 'package:lotti/classes/entry_links.dart';
import 'package:lotti/classes/entry_text.dart';
Expand Down Expand Up @@ -576,9 +577,64 @@ class PersistenceLogic {
}
}

Future<JournalEntity?> createChecklistItem({
required Checklist checklist,
required String title,
}) async {
try {
final now = DateTime.now();
final id = uuid.v1();
final vc = await _vectorClockService.getNextVectorClock();

final newChecklistItem = JournalEntity.checklistItem(
meta: Metadata(
createdAt: now,
updatedAt: now,
dateFrom: now,
dateTo: now,
id: id,
vectorClock: vc,
timezone: await getLocalTimezone(),
utcOffset: now.timeZoneOffset.inMinutes,
),
data: ChecklistItemData(
title: title,
isChecked: false,
linkedChecklists: [],
),
);

await createDbEntity(
newChecklistItem,
enqueueSync: true,
);
addGeolocation(id);

await updateChecklist(
checklistId: checklist.id,
data: checklist.data.copyWith(
linkedChecklistItems: [
...checklist.data.linkedChecklistItems,
newChecklistItem.meta.id,
],
),
);

return newChecklistItem;
} catch (exception, stackTrace) {
_loggingDb.captureException(
exception,
domain: 'persistence_logic',
subDomain: 'createChecklistEntry',
stackTrace: stackTrace,
);
return null;
}
}

Future<bool> updateChecklist({
required String checklistId,
required String title,
required ChecklistData data,
}) async {
try {
final now = DateTime.now();
Expand All @@ -600,12 +656,12 @@ class PersistenceLogic {
vectorClock: vc,
);

final newTask = checklist.copyWith(
final updatedChecklist = checklist.copyWith(
meta: newMeta,
data: checklist.data.copyWith(title: title),
data: data,
);

await updateDbEntity(newTask, enqueueSync: true);
await updateDbEntity(updatedChecklist, enqueueSync: true);
},
orElse: () async => _loggingDb.captureException(
'not a checklist',
Expand All @@ -624,6 +680,54 @@ class PersistenceLogic {
return true;
}

Future<bool> updateChecklistItem({
required String checklistItemId,
required ChecklistItemData data,
}) async {
try {
final now = DateTime.now();
final journalEntity = await _journalDb.journalEntityById(checklistItemId);

if (journalEntity == null) {
return false;
}

await journalEntity.maybeMap(
checklistItem: (ChecklistItem checklistItem) async {
final vc = await _vectorClockService.getNextVectorClock(
previous: journalEntity.meta.vectorClock,
);

final oldMeta = journalEntity.meta;
final newMeta = oldMeta.copyWith(
updatedAt: now,
vectorClock: vc,
);

final updatedChecklist = checklistItem.copyWith(
meta: newMeta,
data: data,
);

await updateDbEntity(updatedChecklist, enqueueSync: true);
},
orElse: () async => _loggingDb.captureException(
'not a checklist item',
domain: 'persistence_logic',
subDomain: 'updateChecklistItem',
),
);
} catch (exception, stackTrace) {
_loggingDb.captureException(
exception,
domain: 'persistence_logic',
subDomain: 'updateChecklistItem',
stackTrace: stackTrace,
);
}
return true;
}

Future<bool> createLink({
required String fromId,
required String toId,
Expand Down
2 changes: 2 additions & 0 deletions lib/widgetbook.dart
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,8 @@ class WidgetbookApp extends StatelessWidget {
checklistItem2.meta.id,
checklistItem3.meta.id,
],
onCreateChecklistItem: (title) {},
onTitleSave: (title) {},
),
),
),
Expand Down

0 comments on commit 6f60465

Please sign in to comment.