Skip to content

Commit

Permalink
feat: add TomeLibrary
Browse files Browse the repository at this point in the history
  • Loading branch information
nesquikm committed Oct 25, 2023
1 parent 094f8d5 commit 61232e9
Show file tree
Hide file tree
Showing 18 changed files with 304 additions and 1 deletion.
108 changes: 108 additions & 0 deletions lib/features/library/tome_library/tome_library.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import 'dart:async';
import 'dart:collection';

import 'package:bsr/features/library/library.dart';
import 'package:logging/logging.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';

export 'tome_list/tome_list.dart';

part 'tome_library.g.dart';

@Riverpod(keepAlive: true)
class TomeLibrary extends _$TomeLibrary {
final _log = Logger('TomeLibrary');

final Set<String> _markedAsRemoved = {};

@override
Future<Map<String, CachedTome>> build() async {
final tomeList = await ref.watch(tomeListProvider.future);
return {...tomeList}..removeWhere(
(key, value) => _markedAsRemoved.contains(key),
);
}

Future<LinkedHashMap<String, CachedTome>> getSortedByTitle() async {
return _getSortedByTitle(await future);
}

Future<SplayTreeMap<String, LinkedHashMap<String, CachedTome>>>
getGroupedByAuthorAndSortedByTitle() async {
final tomeList = await future;
final result = SplayTreeMap<String, LinkedHashMap<String, CachedTome>>(
(a, b) => a.compareTo(b),
);

for (final tome in tomeList.entries) {
final author = tome.value.tomeInfo.author ?? '';

final authorTomeList = result[author] ?? <String, CachedTome>{}
..addAll({tome.key: tome.value});

result[author] = _getSortedByTitle(authorTomeList);
}

return result;
}

Future<void> setDirectory(String directoryPath) async {
_log.fine('setDirectory($directoryPath)');
return ref.read(tomeListProvider.notifier).setDirectory(directoryPath);
}

Future<void> refresh() async {
_log.fine('refresh()');
return ref.read(tomeListProvider.notifier).refresh();
}

Future<String> addFile(String filePath) async {
_log.fine('addFile $filePath');
return ref.read(tomeListProvider.notifier).addFile(filePath);
}

Future<void> remove(String id) async {
_log.fine('remove $id');
_markedAsRemoved.remove(id);
return ref.read(tomeListProvider.notifier).remove(id);
}

Future<void> clear() async {
_log.fine('clear');
return ref.read(tomeListProvider.notifier).clear();
}

Future<void> markAsRemoved(String id) async {
_log.fine('markAsRemoved $id');
_markedAsRemoved.add(id);
ref.invalidateSelf();
}

Future<void> unmarkAsRemoved(String id) async {
_log.fine('unmarkAsRemoved $id');
_markedAsRemoved.remove(id);
ref.invalidateSelf();
}

LinkedHashMap<String, CachedTome> _getSortedByTitle(
Map<String, CachedTome> tomeList,
) {
final sortedList = tomeList.entries.toList()
..sort((a, b) {
final aTitle = a.value.tomeInfo.title;
final bTitle = b.value.tomeInfo.title;
return (aTitle == null || bTitle == null)
? 0
: aTitle.compareTo(bTitle);
});

return LinkedHashMap<String, CachedTome>.fromEntries(
sortedList.map(
(e) => MapEntry(
e.key,
e.value,
),
),
);
}
}
25 changes: 25 additions & 0 deletions lib/features/library/tome_library/tome_library.g.dart

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

2 changes: 2 additions & 0 deletions lib/features/library/tome_library/tome_list/tome_list.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ class TomeList extends _$TomeList {
}

Future<void> setDirectory(String directoryPath) async {
_log.fine('setDirectory($directoryPath)');

_directoryPath = directoryPath;

await refresh();
Expand Down
2 changes: 1 addition & 1 deletion pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -442,7 +442,7 @@ packages:
source: hosted
version: "0.18.1"
io:
dependency: transitive
dependency: "direct dev"
description:
name: io
sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e"
Expand Down
1 change: 1 addition & 0 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ dev_dependencies:
flutter_test:
sdk: flutter
freezed: ^2.4.5
io: ^1.0.4
json_serializable: ^6.7.1
melos: ^3.1.1
riverpod_generator: ^2.3.3
Expand Down
161 changes: 161 additions & 0 deletions test/library/tome_library_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
import 'dart:io';

import 'package:bsr/features/library/library.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:io/io.dart';
import 'package:path/path.dart';
import 'package:riverpod/riverpod.dart';

import '../helpers/helpers.dart';

void main() {
ProviderContainer? container;
group('TomeLibrary', () {
String getTomeLibraryPath() {
return join(Directory.systemTemp.path, 'bsr_test_tome_library');
}

void createTomeLibraryDirectory() {
final tomeLibraryPath = getTomeLibraryPath();
if (!Directory(tomeLibraryPath).existsSync()) {
Directory(tomeLibraryPath).createSync(recursive: true);
}
copyPathSync('test/test_tomes/library', tomeLibraryPath);
}

void deleteTomeLibraryDirectory() {
final tomeLibraryPath = getTomeLibraryPath();
if (Directory(tomeLibraryPath).existsSync()) {
Directory(tomeLibraryPath).deleteSync(recursive: true);
}
}

setUp(() async {
container = createContainer();
createTomeLibraryDirectory();
});

tearDown(() async {
container?.dispose();
container = null;
deleteTomeLibraryDirectory();
});

Future<void> expectEmpty() async {
await expectLater(
container!.read(tomeLibraryProvider.future),
completion(
{},
),
);
}

Future<void> expectTomeCount(int count) async {
await expectLater(
container!.read(tomeLibraryProvider.future),
completion(
(Map<String, CachedTome> res) => res.length == count,
),
);
}

Future<void> setDirectory() async {
await container!.read(tomeLibraryProvider.notifier).setDirectory(
getTomeLibraryPath(),
);
}

Future<void> markAsRemoved(String id) async {
await container!.read(tomeLibraryProvider.notifier).markAsRemoved(
id,
);
}

Future<void> unmarkAsRemoved(String id) async {
await container!.read(tomeLibraryProvider.notifier).unmarkAsRemoved(
id,
);
}

test('Instantiate TomeLibrary', () async {
await expectEmpty();
});

test('Instantiate TomeLibrary with library', () async {
await setDirectory();
await expectTomeCount(6);
});

test('Mark and unmark tome as removed', () async {
await setDirectory();
await expectTomeCount(6);
await markAsRemoved('id3');
await expectTomeCount(5);
await unmarkAsRemoved('id3');
await expectTomeCount(6);
});

test('Check sorting', () async {
await setDirectory();
await expectLater(
container!.read(tomeLibraryProvider.notifier).getSortedByTitle(),
completion(
(Map<String, CachedTome> res) {
final values = res.values.toList();
for (var i = 0; i < 5; i++) {
if (!values[i].tomeInfo.title!.startsWith('$i')) {
return false;
}
}
return true;
},
),
);
});

test('Check SortedByTitle', () async {
await setDirectory();
await expectLater(
container!.read(tomeLibraryProvider.notifier).getSortedByTitle(),
completion(
(Map<String, CachedTome> res) {
final values = res.values.toList();
for (var i = 0; i < 5; i++) {
if (!values[i].tomeInfo.title!.startsWith('$i')) {
return false;
}
}
return true;
},
),
);
});

test('Check GroupedByAuthorAndSortedByTitle', () async {
await setDirectory();
await expectLater(
container!
.read(tomeLibraryProvider.notifier)
.getGroupedByAuthorAndSortedByTitle(),
completion(
(Map<String, Map<String, CachedTome>> res) {
final authors = res.keys.toList();

expect(authors, ['author0', 'author1', 'author2']);

final author0 = res['author0']!.keys.toList();
expect(author0, ['id5']);

final author1 = res['author1']!.keys.toList();
expect(author1, ['id4', 'id1', 'id3']);

final author2 = res['author2']!.keys.toList();
expect(author2, ['id2', 'id0']);

return true;
},
),
);
});
});
}
Empty file.
1 change: 1 addition & 0 deletions test/test_tomes/library/id0/tome.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"author":"author2","title":"5 title0","authors":[]}
Empty file.
1 change: 1 addition & 0 deletions test/test_tomes/library/id1/tome.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"author":"author1","title":"3 title1","authors":[]}
Empty file.
1 change: 1 addition & 0 deletions test/test_tomes/library/id2/tome.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"author":"author2","title":"1 title2","authors":[]}
Empty file.
1 change: 1 addition & 0 deletions test/test_tomes/library/id3/tome.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"author":"author1","title":"4 title3","authors":[]}
Empty file.
1 change: 1 addition & 0 deletions test/test_tomes/library/id4/tome.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"author":"author1","title":"0 title4","authors":[]}
Empty file.
1 change: 1 addition & 0 deletions test/test_tomes/library/id5/tome.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"author":"author0","title":"2 title5","authors":[]}

0 comments on commit 61232e9

Please sign in to comment.