Skip to content

Commit

Permalink
Add notebook system
Browse files Browse the repository at this point in the history
  • Loading branch information
CodeDoctorDE committed May 9, 2024
1 parent c9ff2a1 commit 2a6ce00
Show file tree
Hide file tree
Showing 14 changed files with 277 additions and 77 deletions.
69 changes: 67 additions & 2 deletions api/lib/models/note/database.dart
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,19 @@ abstract class NoteDatabaseConnector<T> extends NoteConnector<T>
}

class NoteDatabaseService extends NoteService with TableService {
Future<void> _createNotebookDatabase() async {
await db?.execute("""
CREATE TABLE IF NOT EXISTS notebooks (
id BLOB(16) PRIMARY KEY,
name VARCHAR(100) NOT NULL DEFAULT '',
description TEXT
)
""");
}

@override
Future<void> create(Database db) {
return db.execute("""
Future<void> create(Database db) async {
await db.execute("""
CREATE TABLE IF NOT EXISTS notes (
id BLOB(16) PRIMARY KEY,
name VARCHAR(100) NOT NULL DEFAULT '',
Expand All @@ -132,12 +142,14 @@ class NoteDatabaseService extends NoteService with TableService {
parentId BLOB(16)
)
""");
await _createNotebookDatabase();
}

@override
Future<void> migrate(Database db, int version) async {
if (version < 3) {
await db.execute("ALTER TABLE notes ADD notebookId BLOB(16)");
await _createNotebookDatabase();
}
}

Expand Down Expand Up @@ -247,4 +259,57 @@ LIMIT 1;""",
);
return result?.map(Note.fromDatabase).firstOrNull;
}

@override
Future<Notebook?> createNotebook(Notebook notebook) async {
final id = notebook.id ?? createUniqueMultihash();
notebook = notebook.copyWith(id: id);
final row = await db?.insert('notebooks', notebook.toDatabase());
if (row == null) return null;
return notebook;
}

@override
Future<bool> deleteNotebook(Multihash id) async {
return await db?.delete(
'notebooks',
where: 'id = ?',
whereArgs: [id.fullBytes],
) ==
1;
}

@override
Future<Notebook?> getNotebook(Multihash id) async {
final result = await db?.query(
'notebooks',
where: 'id = ?',
whereArgs: [id.fullBytes],
);
return result?.map(Notebook.fromDatabase).firstOrNull;
}

@override
Future<List<Notebook>> getNotebooks(
{int offset = 0, int limit = 50, String search = ''}) async {
final result = await db?.query(
'notebooks',
where: 'name LIKE ?',
whereArgs: ['%$search%'],
offset: offset,
limit: limit,
);
return result?.map(Notebook.fromDatabase).toList() ?? [];
}

@override
Future<bool> updateNotebook(Notebook notebook) async {
return await db?.update(
'notebooks',
notebook.toDatabase()..remove('id'),
where: 'id = ?',
whereArgs: [notebook.id?.fullBytes],
) ==
1;
}
}
14 changes: 14 additions & 0 deletions api/lib/models/note/service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,20 @@ abstract class NoteService extends ModelService {
FutureOr<bool> deleteNote(Multihash id);

FutureOr<Note?> getNote(Multihash id, {bool fallback = false});

FutureOr<List<Notebook>> getNotebooks({
int offset = 0,
int limit = 50,
String search = '',
});

FutureOr<Notebook?> createNotebook(Notebook notebook);

FutureOr<bool> updateNotebook(Notebook notebook);

FutureOr<bool> deleteNotebook(Multihash id);

FutureOr<Notebook?> getNotebook(Multihash id);
}

abstract class NoteConnector<T> extends ModelService {
Expand Down
4 changes: 3 additions & 1 deletion app/lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -295,5 +295,7 @@
"highContrast": "High contrast",
"labels": "Labels",
"systemLocale": "System locale",
"notebooks": "Notebooks"
"notebooks": "Notebooks",
"createNotebook": "Create notebook",
"editNotebook": "Edit notebook"
}
6 changes: 3 additions & 3 deletions app/lib/pages/calendar/item.dart
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ class _CalendarItemDialogState extends State<CalendarItemDialog> {
source: _source,
value: _item.eventId,
onChanged: (value) {
_item = _item.copyWith(eventId: value);
_item = _item.copyWith(eventId: value?.model);
},
),
const SizedBox(height: 16),
Expand Down Expand Up @@ -267,14 +267,14 @@ class _CalendarItemDialogState extends State<CalendarItemDialog> {
source: _source,
value: _item.groupId,
onChanged: (value) {
_item = _item.copyWith(groupId: value);
_item = _item.copyWith(groupId: value?.model);
},
),
PlaceSelectTile(
source: _source,
value: _item.placeId,
onChanged: (value) {
_item = _item.copyWith(placeId: value);
_item = _item.copyWith(placeId: value?.model);
},
),
const SizedBox(height: 8),
Expand Down
4 changes: 2 additions & 2 deletions app/lib/pages/events/event.dart
Original file line number Diff line number Diff line change
Expand Up @@ -89,15 +89,15 @@ class EventDialog extends StatelessWidget {
source: currentSource,
value: currentEvent.groupId,
onChanged: (value) {
currentEvent = currentEvent.copyWith(groupId: value);
currentEvent = currentEvent.copyWith(groupId: value?.model);
},
),
const SizedBox(height: 16),
PlaceSelectTile(
source: currentSource,
value: currentEvent.placeId,
onChanged: (value) {
currentEvent = currentEvent.copyWith(placeId: value);
currentEvent = currentEvent.copyWith(placeId: value?.model);
},
),
const SizedBox(height: 8),
Expand Down
14 changes: 7 additions & 7 deletions app/lib/pages/events/select.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ import 'package:flow_api/models/model.dart';
import 'event.dart';

class EventSelectTile extends StatelessWidget {
final String source;
final String? source;
final Multihash? value;
final ValueChanged<Multihash?> onChanged;
final ValueChanged<SourcedModel<Multihash>?> onChanged;

const EventSelectTile({
super.key,
required this.source,
this.source,
this.value,
required this.onChanged,
});
Expand All @@ -30,12 +30,12 @@ class EventSelectTile extends StatelessWidget {
? PhosphorIconsLight.calendar
: PhosphorIconsFill.calendar),
dialogBuilder: (context, sourcedModel) => EventDialog(
source: sourcedModel.source,
event: sourcedModel.model,
create: sourcedModel.model == null,
source: sourcedModel?.source,
event: sourcedModel?.model,
create: sourcedModel?.model == null,
),
selectBuilder: (context, model) => EventSelectDialog(
selected: model.toIdentifierModel(),
selected: model?.toIdentifierModel(),
source: source,
),
);
Expand Down
10 changes: 5 additions & 5 deletions app/lib/pages/groups/select.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import 'group.dart';
class GroupSelectTile extends StatelessWidget {
final String source;
final Multihash? value;
final ValueChanged<Multihash?> onChanged;
final ValueChanged<SourcedModel<Multihash>?> onChanged;

const GroupSelectTile({
super.key,
Expand All @@ -30,12 +30,12 @@ class GroupSelectTile extends StatelessWidget {
? PhosphorIconsLight.users
: PhosphorIconsFill.users),
dialogBuilder: (context, sourcedModel) => GroupDialog(
source: sourcedModel.source,
group: sourcedModel.model,
create: sourcedModel.model == null,
source: sourcedModel?.source,
group: sourcedModel?.model,
create: sourcedModel?.model == null,
),
selectBuilder: (context, model) => GroupSelectDialog(
selected: model.toIdentifierModel(),
selected: model?.toIdentifierModel(),
source: source,
),
);
Expand Down
2 changes: 2 additions & 0 deletions app/lib/pages/notes/navigator/drawer.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import 'package:flow/cubits/flow.dart';
import 'package:flow/helpers/sourced_paging_controller.dart';
import 'package:flow/pages/notes/label.dart';
import 'package:flow/pages/notes/notebook.dart';
import 'package:flow/widgets/builder_delegate.dart';
import 'package:flow/widgets/select.dart';
import 'package:flow_api/models/label/model.dart';
import 'package:flow_api/models/model.dart';
import 'package:flow_api/models/note/model.dart';
Expand Down
56 changes: 34 additions & 22 deletions app/lib/pages/notes/navigator/notebooks.dart
Original file line number Diff line number Diff line change
@@ -1,41 +1,53 @@
part of 'drawer.dart';

class _NotebooksView extends StatefulWidget {
class _NotebooksView extends StatelessWidget {
final SourcedModel<Notebook?>? model;

const _NotebooksView({
this.model,
});

@override
State<_NotebooksView> createState() => _NotebooksViewState();
Widget build(BuildContext context) {
return SelectTile(
source: model?.source,
onChanged: (model) {},
onModelFetch: (source, service, id) async =>
service.note?.getNotebook(id),
title: AppLocalizations.of(context).notebooks,
leadingBuilder: (context, model) => PhosphorIcon(
model.model == null ? PhosphorIconsLight.book : PhosphorIconsFill.book,
),
dialogBuilder: (context, model) => NotebookDialog(
source: model?.source,
notebook: model?.model,
create: model?.model == null,
),
selectBuilder: (context, model) => _NotebooksSelectDialog(
selected: model?.toIdentifierModel(),
),
);
}
}

class _NotebooksViewState extends State<_NotebooksView> {
late final SourcedPagingController<Notebook> _controller;
class _NotebooksSelectDialog extends StatelessWidget {
final SourcedModel<Multihash>? selected;

@override
void initState() {
super.initState();

_controller = SourcedPagingController(
context.read<FlowCubit>(),
);
_controller.addFetchListener((source, service, offset, limit) async {
return [];
});
}
const _NotebooksSelectDialog({
this.selected,
});

@override
Widget build(BuildContext context) {
return PagedListView(
pagingController: _controller,
builderDelegate: buildMaterialPagedDelegate<SourcedModel<Notebook>>(
_controller,
(ctx, item, index) => ListTile(
title: Text(item.model.name),
),
return SelectDialog(
onFetch: (source, service, search, offset, limit) async =>
service.note?.getNotebooks(
offset: offset,
limit: limit,
search: search,
),
title: AppLocalizations.of(context).notebooks,
selected: selected,
);
}
}
Loading

0 comments on commit 2a6ce00

Please sign in to comment.