Skip to content

Commit

Permalink
refactor(review): review system refactored, deprecated Provider
Browse files Browse the repository at this point in the history
  • Loading branch information
SethCohen committed Apr 11, 2023
1 parent c52d3b6 commit 2f516ad
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 99 deletions.
35 changes: 0 additions & 35 deletions src/lib/common/utils/data_provider.dart
Original file line number Diff line number Diff line change
@@ -1,39 +1,13 @@
import 'package:collection/collection.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:spaced_repetition/sm.dart';
import '../../features/flashcard/flashcard_model.dart';
import '../../features/review/review_model.dart';

final currentUser = FirebaseAuth.instance.currentUser!;
const pageSize = 5;

class DataProvider extends ChangeNotifier {
Map<String, List<ReviewModel>> _reviews = {};

Map<String, List<ReviewModel>> get reviews => _reviews;

Future<void> loadReviews() async {
try {
final reviews = await FirebaseFirestore.instance
.collectionGroup('cards')
.where('nextReview', isLessThan: DateTime.now())
.where('userId', isEqualTo: currentUser.uid)
.get();

final reviewModelList = reviews.docs.map((doc) {
return ReviewModel.fromMap(doc.id, doc.data());
}).toList();
_reviews =
groupBy(reviewModelList, (ReviewModel review) => review.deckTitle);
} on Exception catch (e) {
debugPrint(e.toString());
}

notifyListeners();
}

Future<void> updateCardProgress(FlashcardModel flashcard, int quality) async {
Sm sm = Sm();

Expand Down Expand Up @@ -130,13 +104,4 @@ class DataProvider extends ChangeNotifier {
notifyListeners();
}
}

void removeReview(String deckTitle) {
if (deckTitle == 'Review All') {
_reviews = {};
} else {
_reviews.remove(deckTitle);
}
notifyListeners();
}
}
2 changes: 0 additions & 2 deletions src/lib/features/review/review_details_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import 'package:provider/provider.dart';
import '../authentication/google_provider.dart';
import '../flashcard/flashcard_model.dart';
import 'review_model.dart';
import '../../common/utils/data_provider.dart';
import '../flashcard/flashcard.dart';

class Review extends StatefulWidget {
Expand Down Expand Up @@ -51,7 +50,6 @@ class _ReviewState extends State<Review> {
'lastLearnt': DateTime.now(),
}, SetOptions(merge: true));

context.read<DataProvider>().removeReview(widget.title);
Navigator.pop(context);
} else {
_currentCardIndex++;
Expand Down
155 changes: 93 additions & 62 deletions src/lib/features/review/reviews_list_page.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:provider/provider.dart';
Expand All @@ -15,77 +16,92 @@ class ReviewPage extends StatefulWidget {

class _ReviewPageState extends State<ReviewPage> {
Map<String, List<ReviewModel>> _decks = {};
late Future<QuerySnapshot> _nextReviewFuture;

@override
void initState() {
super.initState();
context.read<DataProvider>().loadReviews();
Widget build(BuildContext context) {
return Center(
child: StreamBuilder(
stream: _getAvailableReviews(),
builder: (BuildContext context,
AsyncSnapshot<Map<String, List<ReviewModel>>> snapshot) {
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return const CircularProgressIndicator();
}

_decks = snapshot.data!;

if (_decks.isEmpty) {
return _buildNoReviewsAvailable();
}

return Column(
children: [
_buildReviewAll(_decks),
Expanded(
child: ListView.builder(
itemCount: _decks.length,
itemBuilder: (context, index) => _buildListItem(index)),
),
],
);
}),
);
}

_nextReviewFuture = _getNextReview();
Widget _buildNoReviewsAvailable() {
return FutureBuilder(
future: _getNextAvailableReview(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return const CircularProgressIndicator();
}
if (snapshot.data!.docs.isEmpty) {
return _buildLessonWarning();
}
return _buildNextAvailableReviewWarning(snapshot);
});
}

@override
Widget build(BuildContext context) {
_decks = context.watch<DataProvider>().reviews;

if (_decks.isEmpty) {
// TODO cleanup
return Center(
child: FutureBuilder(
future: _nextReviewFuture,
builder:
(BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return const CircularProgressIndicator();
}
if (snapshot.data!.docs.isEmpty) {
return const Align(
alignment: Alignment.topCenter,
child: Text('Please do a lesson first to get a review.'));
}

final card =
snapshot.data!.docs.first.data() as Map<String, dynamic>;
final formattedDate = DateFormat.yMMMd()
.add_jm()
.format((card['nextReview'] as Timestamp).toDate());

return Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const Text(
'No reviews available. Please come back again later.'),
snapshot.data!.docs.isEmpty
? const Text('Please do a lesson first to get a review.')
: Text('Next review: $formattedDate'),
],
);
}),
);
}

final allCards = _decks.values.expand((element) => element).toList();
Widget _buildNextAvailableReviewWarning(snapshot) {
final card = snapshot.data!.docs.first.data() as Map<String, dynamic>;
final formattedDate = DateFormat.yMMMd()
.add_jm()
.format((card['nextReview'] as Timestamp).toDate());

return Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Card(
child: ListTile(
title: const Text('Review All'),
trailing: Text('${allCards.length}'),
onTap: () => _navigateToReview('Review All', allCards))),
Expanded(
child: ListView.builder(
itemCount: _decks.length,
itemBuilder: (context, index) => _buildListItem(index)),
)
const Text('No reviews available. Please come back again later.'),
Text('Next review: $formattedDate'),
],
);
}

Widget _buildLessonWarning() {
return const Align(
alignment: Alignment.topCenter,
child: Text(
'No reviews available. Please do a lesson first to get a review.'));
}

Widget _buildReviewAll(Map<String, List<ReviewModel>> decks) {
final List<ReviewModel> allCards =
decks.values.expand((element) => element).toList();

return Card(
child: ListTile(
title: const Text('Review All'),
trailing: Text('${allCards.length}'),
onTap: () => _navigateToReview('Review All', allCards)));
}

Widget _buildListItem(index) {
final deckTitle = _decks.keys.elementAt(index);
final cards = _decks[deckTitle];
Expand All @@ -98,7 +114,7 @@ class _ReviewPageState extends State<ReviewPage> {
);
}

_navigateToReview(String deckTitle, List<ReviewModel> cards) {
void _navigateToReview(String deckTitle, List<ReviewModel> cards) {
final url = '/${deckTitle.toLowerCase().replaceAll('lesson ', 'review_')}';

Navigator.pushNamed(
Expand All @@ -111,7 +127,22 @@ class _ReviewPageState extends State<ReviewPage> {
);
}

Future<QuerySnapshot> _getNextReview() async {
Stream<Map<String, List<ReviewModel>>> _getAvailableReviews() {
return FirebaseFirestore.instance
.collectionGroup('cards')
.where('nextReview', isLessThan: DateTime.now())
.where('userId', isEqualTo: currentUser.uid)
.snapshots()
.map((querySnapshot) {
final reviewModelList = querySnapshot.docs.map((doc) {
return ReviewModel.fromMap(doc.id, doc.data());
}).toList();

return groupBy(reviewModelList, (ReviewModel review) => review.deckTitle);
});
}

Future<QuerySnapshot> _getNextAvailableReview() async {
final currentUser = context.read<GoogleSignInProvider>().user;

return await FirebaseFirestore.instance
Expand Down

0 comments on commit 2f516ad

Please sign in to comment.