Skip to content

Commit 431b415

Browse files
authored
Merge pull request #17 from CodandoTV/issue-16
Issue 16: Configure Local Storage
2 parents 3dcf64c + bc7ff68 commit 431b415

22 files changed

Lines changed: 409 additions & 212 deletions
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import 'package:flutter/material.dart';
2+
3+
import '../molecules/buttons/primary_button.dart';
4+
5+
Future<void> showErrorDialog(
6+
BuildContext context,
7+
String title,
8+
String content,
9+
String ctaText,
10+
Function()? onCtaPressed,
11+
) {
12+
return showDialog<void>(
13+
context: context,
14+
builder: (context) => AlertDialog(
15+
title: Text(
16+
title,
17+
textAlign: TextAlign.center,
18+
),
19+
content: Text(
20+
content,
21+
textAlign: TextAlign.center,
22+
),
23+
actions: [
24+
Center(
25+
child: PrimaryButton(
26+
title: ctaText,
27+
onPressed: () {
28+
onCtaPressed?.call();
29+
Navigator.of(context).pop();
30+
},
31+
),
32+
),
33+
],
34+
),
35+
);
36+
}

lib/core/di/di.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@ import 'di.config.dart';
55
final getIt = GetIt.instance;
66

77
@InjectableInit()
8-
void configureDependencies() {
8+
Future<void> configureDependencies() async {
99
getIt.init(environment: 'prod');
1010
}

lib/layers/data/api/custom_errors.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,7 @@ import 'package:dio/dio.dart';
33
class UnauthorizedError extends DioException {
44
UnauthorizedError({required super.requestOptions});
55
}
6+
7+
class DuplicatedReadingError extends Error {
8+
DuplicatedReadingError();
9+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import 'dart:convert';
2+
import 'dart:io';
3+
4+
import 'package:injectable/injectable.dart';
5+
import 'package:mibook/layers/data/api/custom_errors.dart';
6+
import 'package:mibook/layers/data/models/reading_data.dart';
7+
import 'package:path_provider/path_provider.dart';
8+
9+
abstract class IStorageClient {
10+
Future<void> saveReading(ReadingData readingData);
11+
Future<List<ReadingData>> getReadingList();
12+
}
13+
14+
@Singleton(as: IStorageClient)
15+
class StorageClient implements IStorageClient {
16+
Future<File> _getLocalFile(String fileName) async {
17+
final directory = await getApplicationDocumentsDirectory();
18+
return File('${directory.path}/$fileName.json');
19+
}
20+
21+
@override
22+
Future<void> saveReading(ReadingData readingData) async {
23+
final file = await _getLocalFile('reading_list');
24+
List<ReadingData> currentList = await getReadingList();
25+
26+
if (currentList.any((item) => item.bookId == readingData.bookId)) {
27+
throw DuplicatedReadingError();
28+
}
29+
currentList.add(readingData);
30+
final jsonString = jsonEncode(currentList.map((e) => e.toJson()).toList());
31+
await file.writeAsString(jsonString);
32+
}
33+
34+
@override
35+
Future<List<ReadingData>> getReadingList() async {
36+
final file = await _getLocalFile('reading_list');
37+
38+
if (!await file.exists()) {
39+
return [];
40+
}
41+
42+
final jsonString = await file.readAsString();
43+
44+
if (jsonString.isEmpty) {
45+
return [];
46+
}
47+
48+
final List<dynamic> decoded = jsonDecode(jsonString);
49+
return decoded.map((e) => ReadingData.fromJson(e)).toList();
50+
}
51+
}
Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import 'package:injectable/injectable.dart';
2+
import 'package:mibook/layers/data/api/storage_client.dart';
23
import 'package:mibook/layers/data/models/reading_data.dart';
34

45
abstract class IReadingDataSource {
@@ -10,13 +11,16 @@ abstract class IReadingDataSource {
1011

1112
@Injectable(as: IReadingDataSource)
1213
class ReadingDataSource implements IReadingDataSource {
14+
final IStorageClient _storageClient;
15+
16+
ReadingDataSource(this._storageClient);
17+
1318
@override
1419
Future<void> startReading({
1520
required ReadingData readingData,
16-
}) async {
17-
// TO DO
18-
}
21+
}) async => _storageClient.saveReading(readingData);
1922

2023
@override
21-
Future<List<ReadingData>> getReadingData() async => [];
24+
Future<List<ReadingData>> getReadingData() async =>
25+
await _storageClient.getReadingList();
2226
}

lib/layers/data/models/reading_data.dart

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,25 +5,49 @@ part 'reading_data.g.dart';
55
@JsonSerializable()
66
class ReadingData {
77
final String bookId;
8+
final String bookName;
9+
final String? bookThumb;
810
final double progress;
911

10-
ReadingData({
11-
required this.bookId,
12-
required this.progress,
13-
});
12+
ReadingData(
13+
this.bookId,
14+
this.bookName,
15+
this.bookThumb,
16+
this.progress,
17+
);
1418

1519
factory ReadingData.fromJson(Map<String, dynamic> json) =>
1620
_$ReadingDataFromJson(json);
1721

1822
Map<String, dynamic> toJson() => _$ReadingDataToJson(this);
1923

20-
factory ReadingData.fromDomain(ReadingDomain domain) => ReadingData(
21-
bookId: domain.bookId,
22-
progress: domain.progress,
24+
factory ReadingData.fromDomainModel(ReadingDomain domainModel) => ReadingData(
25+
domainModel.bookId,
26+
domainModel.bookName,
27+
domainModel.bookThumb,
28+
domainModel.progress,
2329
);
2430

25-
ReadingDomain toDomain() => ReadingDomain(
31+
ReadingDomain toDomainModel() => ReadingDomain(
2632
bookId: bookId,
33+
bookName: bookName,
34+
bookThumb: bookThumb,
2735
progress: progress,
2836
);
37+
38+
@override
39+
bool operator ==(Object other) =>
40+
identical(this, other) ||
41+
other is ReadingData &&
42+
bookId == other.bookId &&
43+
bookName == other.bookName &&
44+
bookThumb == other.bookThumb &&
45+
progress == other.progress;
46+
47+
@override
48+
int get hashCode =>
49+
bookId.hashCode ^
50+
bookName.hashCode ^
51+
bookThumb.hashCode ^
52+
progress.hashCode;
2953
}

lib/layers/data/repository/reading_repository.dart

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import 'package:mibook/layers/data/models/reading_data.dart';
44
import 'package:mibook/layers/domain/models/reading_domain.dart';
55
import 'package:mibook/layers/domain/repository/reading_repository.dart';
66

7-
@Injectable(as: IReadingRepository)
7+
@Singleton(as: IReadingRepository)
88
class ReadingRepository implements IReadingRepository {
99
final IReadingDataSource _dataSource;
1010

@@ -15,15 +15,17 @@ class ReadingRepository implements IReadingRepository {
1515
required ReadingDomain reading,
1616
}) async {
1717
final data = ReadingData(
18-
bookId: reading.bookId,
19-
progress: reading.progress,
18+
reading.bookId,
19+
reading.bookName,
20+
reading.bookThumb,
21+
reading.progress,
2022
);
2123
await _dataSource.startReading(readingData: data);
2224
}
2325

2426
@override
2527
Future<List<ReadingDomain>> getReadings() async {
2628
final data = await _dataSource.getReadingData();
27-
return data.map((e) => e.toDomain()).toList();
29+
return data.map((e) => e.toDomainModel()).toList();
2830
}
2931
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
class ReadingDomain {
22
final String bookId;
3+
final String bookName;
4+
final String? bookThumb;
35
final double progress;
46

57
ReadingDomain({
68
required this.bookId,
9+
required this.bookName,
10+
this.bookThumb,
711
required this.progress,
812
});
913
}

lib/layers/presentation/screens/bookdetails/book_details_state.dart

Lines changed: 8 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,14 @@ part 'book_details_state.freezed.dart';
44

55
@freezed
66
class BookDetailsState with _$BookDetailsState {
7-
@override
8-
final bool isLoading;
9-
@override
10-
final String? errorMessage;
11-
@override
12-
final BookDetailsUI? bookDetails;
13-
@override
14-
final double bookProgress;
7+
const factory BookDetailsState({
8+
String? errorMessage,
9+
BookDetailsUI? bookDetails,
10+
@Default(false) bool isLoading,
11+
@Default(0.0) double bookProgress,
12+
}) = _BookDetailsState;
1513

16-
BookDetailsState(
17-
this.errorMessage,
18-
this.bookDetails, {
19-
required this.isLoading,
20-
required this.bookProgress,
21-
});
14+
const BookDetailsState._(); // allows adding custom getters or methods later
2215

23-
factory BookDetailsState.initial() => BookDetailsState(
24-
null,
25-
null,
26-
isLoading: false,
27-
bookProgress: 0.0,
28-
);
16+
factory BookDetailsState.initial() => const BookDetailsState();
2917
}

lib/layers/presentation/screens/bookdetails/book_details_view_model.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ class BookDetailsViewModel extends Bloc<BookDetailsEvent, BookDetailsState> {
6666
await _startReading(
6767
reading: ReadingDomain(
6868
bookId: bookId!,
69+
bookName: state.bookDetails?.title ?? '',
70+
bookThumb: state.bookDetails?.thumbnail ?? '',
6971
progress: progress,
7072
),
7173
);

0 commit comments

Comments
 (0)