Skip to content

Commit 87c7665

Browse files
Merge pull request #60 from OctagonalStar/feat/connect-learn-review
Feat/connect-learn-review
2 parents d9da900 + 3424dd4 commit 87c7665

11 files changed

Lines changed: 614 additions & 507 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,16 @@
1010
- 添加了常见问题页面
1111
- 给部分文本框添加了动画
1212
- 添加了听力题型 [#51](https://github.com/OctagonalStar/arabic_learning/issues/51)
13+
- 为每次学习添加了是否计算复习的提示 [#52](https://github.com/OctagonalStar/arabic_learning/issues/52)
14+
- 在复习部分添加了自我评级方案 [#52](https://github.com/OctagonalStar/arabic_learning/issues/52)
15+
- 添加了每日单词推送功能 [#52](https://github.com/OctagonalStar/arabic_learning/issues/52)
1316

1417
### Improvement
1518

1619
- 优化了选择题单词挑选逻辑 [#48](https://github.com/OctagonalStar/arabic_learning/issues/48)
1720
- 修复了BKTree日志刷屏问题
1821
- 移除了提醒配置的中间页面
22+
- 对于拼写题型放宽了时间要求 [#52](https://github.com/OctagonalStar/arabic_learning/issues/52)
1923

2024
### Fix
2125

lib/funcs/fsrs_func.dart

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,11 @@ class FSRS {
5050
return config.cards[index].due.toLocal().difference(DateTime.now()).inDays;
5151
}
5252

53-
void reviewCard(int wordId, int duration, bool isCorrect) {
53+
void reviewCard(int wordId, int duration, bool isCorrect, {Rating? forceRate}) {
5454
logger.fine("记录复习卡片: Id: $wordId; duration: $duration; isCorrect: $isCorrect");
5555
int index = config.cards.indexWhere((Card card) => card.cardId == wordId); // 避免有时候cardId != wordId
5656
logger.fine("定位复习卡片地址: $index, 目前阶段: ${config.cards[index].step}, 难度: ${config.cards[index].difficulty}, 稳定: ${config.cards[index].stability}, 过期时间(+8): ${config.cards[index].due.toLocal()}");
57-
final (:card, :reviewLog) = config.scheduler!.reviewCard(config.cards[index], calculate(duration, isCorrect), reviewDateTime: DateTime.now().toUtc(), reviewDuration: duration);
57+
final (:card, :reviewLog) = config.scheduler!.reviewCard(config.cards[index], forceRate ?? calculate(duration, isCorrect), reviewDateTime: DateTime.now().toUtc(), reviewDuration: duration);
5858
config.cards[index] = card;
5959
config.reviewLogs[index] = reviewLog;
6060
logger.fine("卡片 $index 复习后: 目前阶段: ${config.cards[index].step}, 难度: ${config.cards[index].difficulty}, 稳定: ${config.cards[index].stability}, 过期时间(+8): ${config.cards[index].due.toLocal()}");
@@ -83,13 +83,11 @@ class FSRS {
8383
}
8484

8585
bool isContained(int wordId) {
86-
for(Card card in config.cards) {
87-
if(card.cardId == wordId) return true;
88-
}
89-
return false;
86+
return config.cards.any((Card card) => card.cardId == wordId);
9087
}
9188

9289
void addWordCard(int wordId) {
90+
logger.fine("添加复习卡片: Id: $wordId");
9391
// os the wordID == cardID
9492
config.cards.add(Card(cardId: wordId, state: State.learning));
9593
config.reviewLogs.add(ReviewLog(cardId: wordId, rating: Rating.good, reviewDateTime: DateTime.now()));
@@ -125,6 +123,8 @@ class FSRSConfig {
125123
final int easyDuration;
126124
final int goodDuration;
127125
final bool preferSimilar;
126+
final bool selfEvaluate;
127+
final int pushAmount;
128128

129129
const FSRSConfig({
130130
bool? enabled,
@@ -134,15 +134,19 @@ class FSRSConfig {
134134
double? desiredRetention,
135135
int? easyDuration,
136136
int? goodDuration,
137-
bool? preferSimilar
137+
bool? preferSimilar,
138+
bool? selfEvaluate,
139+
int? pushAmount
138140
}) :
139141
enabled = enabled??false,
140142
cards = cards??const [],
141143
reviewLogs = reviewLogs??const [],
142144
desiredRetention = desiredRetention??0.9,
143145
easyDuration = easyDuration??3000,
144146
goodDuration = goodDuration??6000,
145-
preferSimilar = preferSimilar??false;
147+
preferSimilar = preferSimilar??false,
148+
selfEvaluate = selfEvaluate??false,
149+
pushAmount = pushAmount??0;
146150

147151
Map<String, dynamic> toMap(){
148152
return {
@@ -153,7 +157,9 @@ class FSRSConfig {
153157
"desiredRetention": desiredRetention,
154158
"easyDuration": easyDuration,
155159
"goodDuration": goodDuration,
156-
"preferSimilar": preferSimilar
160+
"preferSimilar": preferSimilar,
161+
"selfEvaluate": selfEvaluate,
162+
"pushAmount": pushAmount
157163
};
158164
}
159165

@@ -165,7 +171,9 @@ class FSRSConfig {
165171
double? desiredRetention,
166172
int? easyDuration,
167173
int? goodDuration,
168-
bool? preferSimilar
174+
bool? preferSimilar,
175+
bool? selfEvaluate,
176+
int? pushAmount
169177
}) {
170178
return FSRSConfig(
171179
enabled: enabled??this.enabled,
@@ -175,7 +183,9 @@ class FSRSConfig {
175183
desiredRetention: desiredRetention??this.desiredRetention,
176184
easyDuration: easyDuration??this.easyDuration,
177185
goodDuration: goodDuration??this.goodDuration,
178-
preferSimilar: preferSimilar??this.preferSimilar
186+
preferSimilar: preferSimilar??this.preferSimilar,
187+
selfEvaluate: selfEvaluate??this.selfEvaluate,
188+
pushAmount: pushAmount??this.pushAmount
179189
);
180190
}
181191

@@ -189,7 +199,9 @@ class FSRSConfig {
189199
desiredRetention: configData["desiredRetention"],
190200
easyDuration: configData["easyDuration"],
191201
goodDuration: configData["goodDuration"],
192-
preferSimilar: configData["preferSimilar"]
202+
preferSimilar: configData["preferSimilar"],
203+
selfEvaluate: configData["selfEvaluate"],
204+
pushAmount: configData["pushAmount"]
193205
);
194206
}
195207
return FSRSConfig(enabled: false);

lib/funcs/ui.dart

Lines changed: 39 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import 'dart:convert';
22
import 'dart:ui';
33

4-
import 'package:arabic_learning/vars/config_structure.dart' show ClassItem, SourceItem, WordItem;
4+
import 'package:arabic_learning/vars/config_structure.dart' show ClassItem, SourceItem, WordItem, ClassSelection;
55
import 'package:flutter/material.dart';
66
import 'package:provider/provider.dart';
77

@@ -36,7 +36,7 @@ import 'package:arabic_learning/funcs/utili.dart';
3636
/// List<List<String>> value = await popSelectClasses(context);
3737
/// List<Map<String, dynamic>> words = getSelectedWords(context , forceSelectClasses: value);
3838
/// ```
39-
Future<List<ClassItem>> popSelectClasses(BuildContext context, {bool withCache = false}) async {
39+
Future<ClassSelection> popSelectClasses(BuildContext context, {bool withCache = false, bool withReviewChoose = true}) async {
4040
context.read<Global>().uiLogger.info("弹出课程选择(ClassSelectPage),withCache: $withCache");
4141
final List<ClassItem> beforeSelectedClasses = [];
4242
if(withCache) {
@@ -59,15 +59,14 @@ Future<List<ClassItem>> popSelectClasses(BuildContext context, {bool withCache =
5959
}
6060
context.read<Global>().uiLogger.fine("已缓存课程选择: $beforeSelectedClasses");
6161
}
62-
List<ClassItem>? selectedClasses = await showModalBottomSheet<List<ClassItem>>(
62+
ClassSelection? selectedClasses = await showModalBottomSheet<ClassSelection>(
6363
context: context,
64-
// 假装圆角... :)
6564
shape: RoundedRectangleBorder(side: BorderSide(width: 1.0, color: Theme.of(context).colorScheme.onSurface.withAlpha(150)), borderRadius: StaticsVar.br),
6665
isDismissible: false,
6766
isScrollControlled: context.read<Global>().isWideScreen,
6867
enableDrag: true,
6968
builder: (BuildContext context) {
70-
return ClassSelectPage(beforeSelectedClasses: beforeSelectedClasses);
69+
return ClassSelectPage(beforeSelectedClasses: beforeSelectedClasses, withReviewChoose: withReviewChoose);
7170
}
7271
);
7372
if(withCache && selectedClasses != null && context.mounted) {
@@ -78,7 +77,7 @@ Future<List<ClassItem>> popSelectClasses(BuildContext context, {bool withCache =
7877
context.read<Global>().uiLogger.info("课程选择缓存完成");
7978
}
8079
if(context.mounted) context.read<Global>().uiLogger.fine("选择的课程: $selectedClasses");
81-
return selectedClasses??[];
80+
return selectedClasses??ClassSelection(selectedClass: [], countInReview: false);
8281
}
8382

8483

@@ -573,19 +572,23 @@ class WordCard extends StatelessWidget {
573572
/// 注意:如果你要进行课程选择,请先考虑 [popSelectClasses] 函数,这是一个已经基本成熟的实现
574573
class ClassSelectPage extends StatelessWidget {
575574
final List<ClassItem> beforeSelectedClasses;
576-
const ClassSelectPage({super.key, this.beforeSelectedClasses = const []});
575+
final bool withReviewChoose;
576+
const ClassSelectPage({super.key, this.beforeSelectedClasses = const [], this.withReviewChoose = false});
577577
@override
578578
Widget build(BuildContext context) {
579579
final MediaQueryData mediaQuery = MediaQuery.of(context);
580-
List<ClassItem> selectedClass = beforeSelectedClasses.toList();
580+
ClassSelection classSelection = ClassSelection(
581+
selectedClass: beforeSelectedClasses.toList(),
582+
countInReview: context.read<Global>().globalFSRS.config.enabled
583+
);
581584
void addClass(ClassItem classInfo) {
582-
selectedClass.add(classInfo);
585+
classSelection.selectedClass.add(classInfo);
583586
}
584587
void removeClass(ClassItem classInfo) {
585-
selectedClass.remove(classInfo);
588+
classSelection.selectedClass.remove(classInfo);
586589
}
587590
bool isClassSelected(ClassItem classInfo) {
588-
return selectedClass.any((e) => e==classInfo);
591+
return classSelection.selectedClass.any((e) => e==classInfo);
589592
}
590593
void onClassChanged(ClassItem classInfo) {
591594
if(isClassSelected(classInfo)) {
@@ -594,14 +597,6 @@ class ClassSelectPage extends StatelessWidget {
594597
addClass(classInfo);
595598
}
596599
}
597-
// 和监听器脱钩...
598-
// if(!context.watch<ClassSelectModel>().initialized) {
599-
// return Scaffold(
600-
// body: Center(
601-
// child: CircularProgressIndicator(),
602-
// ),
603-
// );
604-
// }
605600

606601
return Scaffold(
607602
appBar: AppBar(
@@ -614,14 +609,35 @@ class ClassSelectPage extends StatelessWidget {
614609
children: classesSelectionList(context, onClassChanged, isClassSelected)
615610
),
616611
),
612+
if(withReviewChoose) StatefulBuilder(
613+
builder: (context, setLocalState) {
614+
return Row(
615+
children: [
616+
Expanded(child: Text("本次学习${classSelection.countInReview?"将":"不会"}计入复习系统")),
617+
Switch(
618+
value: classSelection.countInReview,
619+
onChanged: (value){
620+
if(value == true && !context.read<Global>().globalFSRS.config.enabled) {
621+
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text("请先启用复习系统")));
622+
return ;
623+
}
624+
setLocalState(() {
625+
classSelection.countInReview = value;
626+
});
627+
}
628+
)
629+
],
630+
);
631+
}
632+
),
617633
ElevatedButton(
618634
style: ElevatedButton.styleFrom(
619635
fixedSize: Size(mediaQuery.size.width, mediaQuery.size.height * 0.08),
620636
shape: ContinuousRectangleBorder(borderRadius: StaticsVar.br),
621637
),
622638
child: Text('确认'),
623639
onPressed: () {
624-
Navigator.pop(context, selectedClass);
640+
Navigator.pop(context, classSelection);
625641
},
626642
),
627643
],
@@ -700,6 +716,7 @@ class ChoiceQuestions extends StatefulWidget {
700716
final List<String> choices;
701717
final bool? Function(int) onSelected;
702718
final String? hint;
719+
final Widget? midWidget;
703720
final Widget? bottomWidget;
704721
final Function? onDisAllowMutipleSelect;
705722
final bool allowMutipleSelect;
@@ -712,6 +729,7 @@ class ChoiceQuestions extends StatefulWidget {
712729
required this.allowAudio,
713730
required this.onSelected,
714731
this.hint,
732+
this.midWidget,
715733
this.bottomWidget,
716734
this.onDisAllowMutipleSelect,
717735
this.bottonLayout = -1,
@@ -742,7 +760,7 @@ class _ChoiceQuestions extends State<ChoiceQuestions> {
742760
children: [
743761
if(widget.hint!=null) TextContainer(text: widget.hint!, animated: true),
744762
Expanded(
745-
child: StatefulBuilder(
763+
child: widget.midWidget ?? StatefulBuilder(
746764
builder: (context, setLocalState) {
747765
return ElevatedButton.icon(
748766
icon: Icon(widget.allowAudio ? (playing ? Icons.multitrack_audio : Icons.volume_up) : Icons.short_text, size: 24.0),

lib/pages/home_page.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ class HomePage extends StatelessWidget {
1717
Widget build(BuildContext context) {
1818
context.read<Global>().uiLogger.fine("构建 HomePage");
1919
final themeColor = Theme.of(context).colorScheme;
20-
final mediaQuery = MediaQuery.of(context);
21-
final FSRS fsrs = FSRS()..init(outerPrefs: context.read<Global>().prefs);
20+
final MediaQueryData mediaQuery = MediaQuery.of(context);
21+
final FSRS fsrs = context.read<Global>().globalFSRS;
2222
return Column(
2323
children: [
2424
DailyWord(),

0 commit comments

Comments
 (0)