Skip to content
Merged
36 changes: 36 additions & 0 deletions lib/core/designsystem/organisms/show_error_dialog.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import 'package:flutter/material.dart';

import '../molecules/buttons/primary_button.dart';

Future<void> showErrorDialog(
BuildContext context,
String title,
String content,
String ctaText,
Function()? onCtaPressed,
) {
return showDialog<void>(
context: context,
builder: (context) => AlertDialog(
title: Text(
title,
textAlign: TextAlign.center,
),
content: Text(
content,
textAlign: TextAlign.center,
),
actions: [
Center(
child: PrimaryButton(
title: ctaText,
onPressed: () {
onCtaPressed?.call();
Navigator.of(context).pop();
},
),
),
],
),
);
}
2 changes: 1 addition & 1 deletion lib/core/di/di.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ import 'di.config.dart';
final getIt = GetIt.instance;

@InjectableInit()
void configureDependencies() {
Future<void> configureDependencies() async {
getIt.init(environment: 'prod');
}
4 changes: 4 additions & 0 deletions lib/layers/data/api/custom_errors.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,7 @@ import 'package:dio/dio.dart';
class UnauthorizedError extends DioException {
UnauthorizedError({required super.requestOptions});
}

class DuplicatedReadingError extends Error {
DuplicatedReadingError();
}
51 changes: 51 additions & 0 deletions lib/layers/data/api/storage_client.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import 'dart:convert';
import 'dart:io';

import 'package:injectable/injectable.dart';
import 'package:mibook/layers/data/api/custom_errors.dart';
import 'package:mibook/layers/data/models/reading_data.dart';
import 'package:path_provider/path_provider.dart';

abstract class IStorageClient {
Future<void> saveReading(ReadingData readingData);
Future<List<ReadingData>> getReadingList();
}

@Singleton(as: IStorageClient)
class StorageClient implements IStorageClient {
Future<File> _getLocalFile(String fileName) async {
final directory = await getApplicationDocumentsDirectory();
return File('${directory.path}/$fileName.json');
}

@override
Future<void> saveReading(ReadingData readingData) async {
final file = await _getLocalFile('reading_list');
List<ReadingData> currentList = await getReadingList();

if (currentList.any((item) => item.bookId == readingData.bookId)) {
throw DuplicatedReadingError();
}
currentList.add(readingData);
final jsonString = jsonEncode(currentList.map((e) => e.toJson()).toList());
await file.writeAsString(jsonString);
}

@override
Future<List<ReadingData>> getReadingList() async {
final file = await _getLocalFile('reading_list');

if (!await file.exists()) {
return [];
}

final jsonString = await file.readAsString();

if (jsonString.isEmpty) {
return [];
}

final List<dynamic> decoded = jsonDecode(jsonString);
return decoded.map((e) => ReadingData.fromJson(e)).toList();
}
}
12 changes: 8 additions & 4 deletions lib/layers/data/datasource/reading_data_source.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:injectable/injectable.dart';
import 'package:mibook/layers/data/api/storage_client.dart';
import 'package:mibook/layers/data/models/reading_data.dart';

abstract class IReadingDataSource {
Expand All @@ -10,13 +11,16 @@ abstract class IReadingDataSource {

@Injectable(as: IReadingDataSource)
class ReadingDataSource implements IReadingDataSource {
final IStorageClient _storageClient;

ReadingDataSource(this._storageClient);

@override
Future<void> startReading({
required ReadingData readingData,
}) async {
// TO DO
}
}) async => _storageClient.saveReading(readingData);

@override
Future<List<ReadingData>> getReadingData() async => [];
Future<List<ReadingData>> getReadingData() async =>
await _storageClient.getReadingList();
}
40 changes: 32 additions & 8 deletions lib/layers/data/models/reading_data.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,49 @@ part 'reading_data.g.dart';
@JsonSerializable()
class ReadingData {
final String bookId;
final String bookName;
final String? bookThumb;
final double progress;

ReadingData({
required this.bookId,
required this.progress,
});
ReadingData(
this.bookId,
this.bookName,
this.bookThumb,
this.progress,
);

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

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

factory ReadingData.fromDomain(ReadingDomain domain) => ReadingData(
bookId: domain.bookId,
progress: domain.progress,
factory ReadingData.fromDomainModel(ReadingDomain domainModel) => ReadingData(
domainModel.bookId,
domainModel.bookName,
domainModel.bookThumb,
domainModel.progress,
);

ReadingDomain toDomain() => ReadingDomain(
ReadingDomain toDomainModel() => ReadingDomain(
bookId: bookId,
bookName: bookName,
bookThumb: bookThumb,
progress: progress,
);

@override
bool operator ==(Object other) =>
identical(this, other) ||
other is ReadingData &&
bookId == other.bookId &&
bookName == other.bookName &&
bookThumb == other.bookThumb &&
progress == other.progress;

@override
int get hashCode =>
bookId.hashCode ^
bookName.hashCode ^
bookThumb.hashCode ^
progress.hashCode;
}
10 changes: 6 additions & 4 deletions lib/layers/data/repository/reading_repository.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import 'package:mibook/layers/data/models/reading_data.dart';
import 'package:mibook/layers/domain/models/reading_domain.dart';
import 'package:mibook/layers/domain/repository/reading_repository.dart';

@Injectable(as: IReadingRepository)
@Singleton(as: IReadingRepository)
class ReadingRepository implements IReadingRepository {
final IReadingDataSource _dataSource;

Expand All @@ -15,15 +15,17 @@ class ReadingRepository implements IReadingRepository {
required ReadingDomain reading,
}) async {
final data = ReadingData(
bookId: reading.bookId,
progress: reading.progress,
reading.bookId,
reading.bookName,
reading.bookThumb,
reading.progress,
);
await _dataSource.startReading(readingData: data);
}

@override
Future<List<ReadingDomain>> getReadings() async {
final data = await _dataSource.getReadingData();
return data.map((e) => e.toDomain()).toList();
return data.map((e) => e.toDomainModel()).toList();
}
}
4 changes: 4 additions & 0 deletions lib/layers/domain/models/reading_domain.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
class ReadingDomain {
final String bookId;
final String bookName;
final String? bookThumb;
final double progress;

ReadingDomain({
required this.bookId,
required this.bookName,
this.bookThumb,
required this.progress,
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,14 @@ part 'book_details_state.freezed.dart';

@freezed
class BookDetailsState with _$BookDetailsState {
@override
final bool isLoading;
@override
final String? errorMessage;
@override
final BookDetailsUI? bookDetails;
@override
final double bookProgress;
const factory BookDetailsState({
String? errorMessage,
BookDetailsUI? bookDetails,
@Default(false) bool isLoading,
@Default(0.0) double bookProgress,
}) = _BookDetailsState;

BookDetailsState(
this.errorMessage,
this.bookDetails, {
required this.isLoading,
required this.bookProgress,
});
const BookDetailsState._(); // allows adding custom getters or methods later

factory BookDetailsState.initial() => BookDetailsState(
null,
null,
isLoading: false,
bookProgress: 0.0,
);
factory BookDetailsState.initial() => const BookDetailsState();
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ class BookDetailsViewModel extends Bloc<BookDetailsEvent, BookDetailsState> {
await _startReading(
reading: ReadingDomain(
bookId: bookId!,
bookName: state.bookDetails?.title ?? '',
bookThumb: state.bookDetails?.thumbnail ?? '',
progress: progress,
),
);
Expand Down
38 changes: 10 additions & 28 deletions lib/layers/presentation/screens/booksearch/book_search_state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,34 +5,16 @@ part 'book_search_state.freezed.dart';

@freezed
class BookSearchState with _$BookSearchState {
@override
final bool isLoading;
@override
final String? errorMessage;
@override
final String searchText;
@override
final List<BookUI> books;
@override
final bool isPageLoading;
@override
final bool canLoadNextPage;
const factory BookSearchState({
@Default(false) bool isLoading,
String? errorMessage,
@Default('') String searchText,
@Default([]) List<BookUI> books,
@Default(false) bool isPageLoading,
@Default(true) bool canLoadNextPage,
}) = _BookSearchState;

BookSearchState({
required this.isLoading,
required this.errorMessage,
required this.searchText,
required this.books,
required this.isPageLoading,
required this.canLoadNextPage,
});
const BookSearchState._();

factory BookSearchState.initial() => BookSearchState(
isLoading: false,
errorMessage: null,
searchText: '',
books: [],
isPageLoading: false,
canLoadNextPage: true,
);
factory BookSearchState.initial() => const BookSearchState();
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@ class DidEditProgressEvent extends StartReadingEvent {
class DidClickConfirmEvent extends StartReadingEvent {}

class DidClickFinishBookEvent extends StartReadingEvent {}

class DidClickSavingErrorDismissEvent extends StartReadingEvent {}
Loading