Skip to content

Commit 3424dd4

Browse files
committed
feat: add daily word push
综合学习和FSRS练习共通 Fixes #52 Signed-off-by: OctagonalStar <76486554+OctagonalStar@users.noreply.github.com>
1 parent 0545deb commit 3424dd4

4 files changed

Lines changed: 100 additions & 91 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
- 添加了听力题型 [#51](https://github.com/OctagonalStar/arabic_learning/issues/51)
1313
- 为每次学习添加了是否计算复习的提示 [#52](https://github.com/OctagonalStar/arabic_learning/issues/52)
1414
- 在复习部分添加了自我评级方案 [#52](https://github.com/OctagonalStar/arabic_learning/issues/52)
15+
- 添加了每日单词推送功能 [#52](https://github.com/OctagonalStar/arabic_learning/issues/52)
1516

1617
### Improvement
1718

lib/funcs/fsrs_func.dart

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ class FSRSConfig {
124124
final int goodDuration;
125125
final bool preferSimilar;
126126
final bool selfEvaluate;
127+
final int pushAmount;
127128

128129
const FSRSConfig({
129130
bool? enabled,
@@ -134,7 +135,8 @@ class FSRSConfig {
134135
int? easyDuration,
135136
int? goodDuration,
136137
bool? preferSimilar,
137-
bool? selfEvaluate
138+
bool? selfEvaluate,
139+
int? pushAmount
138140
}) :
139141
enabled = enabled??false,
140142
cards = cards??const [],
@@ -143,7 +145,8 @@ class FSRSConfig {
143145
easyDuration = easyDuration??3000,
144146
goodDuration = goodDuration??6000,
145147
preferSimilar = preferSimilar??false,
146-
selfEvaluate = selfEvaluate??false;
148+
selfEvaluate = selfEvaluate??false,
149+
pushAmount = pushAmount??0;
147150

148151
Map<String, dynamic> toMap(){
149152
return {
@@ -155,7 +158,8 @@ class FSRSConfig {
155158
"easyDuration": easyDuration,
156159
"goodDuration": goodDuration,
157160
"preferSimilar": preferSimilar,
158-
"selfEvaluate": selfEvaluate
161+
"selfEvaluate": selfEvaluate,
162+
"pushAmount": pushAmount
159163
};
160164
}
161165

@@ -168,7 +172,8 @@ class FSRSConfig {
168172
int? easyDuration,
169173
int? goodDuration,
170174
bool? preferSimilar,
171-
bool? selfEvaluate
175+
bool? selfEvaluate,
176+
int? pushAmount
172177
}) {
173178
return FSRSConfig(
174179
enabled: enabled??this.enabled,
@@ -179,7 +184,8 @@ class FSRSConfig {
179184
easyDuration: easyDuration??this.easyDuration,
180185
goodDuration: goodDuration??this.goodDuration,
181186
preferSimilar: preferSimilar??this.preferSimilar,
182-
selfEvaluate: selfEvaluate??this.selfEvaluate
187+
selfEvaluate: selfEvaluate??this.selfEvaluate,
188+
pushAmount: pushAmount??this.pushAmount
183189
);
184190
}
185191

@@ -194,7 +200,8 @@ class FSRSConfig {
194200
easyDuration: configData["easyDuration"],
195201
goodDuration: configData["goodDuration"],
196202
preferSimilar: configData["preferSimilar"],
197-
selfEvaluate: configData["selfEvaluate"]
203+
selfEvaluate: configData["selfEvaluate"],
204+
pushAmount: configData["pushAmount"]
198205
);
199206
}
200207
return FSRSConfig(enabled: false);

lib/pages/learning_page.dart

Lines changed: 51 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import 'dart:math';
2+
13
import 'package:arabic_learning/sub_pages_builder/setting_pages/questions_setting_page.dart' show QuestionsSettingPage;
24
import 'package:arabic_learning/vars/config_structure.dart';
35
import 'package:flutter/material.dart';
@@ -7,7 +9,7 @@ import 'package:arabic_learning/funcs/ui.dart';
79
import 'package:arabic_learning/funcs/utili.dart';
810
import 'package:arabic_learning/vars/global.dart';
911
import 'package:arabic_learning/vars/statics_var.dart';
10-
import 'package:arabic_learning/sub_pages_builder/learning_pages/fsrs_pages.dart' show ForeFSRSSettingPage;
12+
import 'package:arabic_learning/sub_pages_builder/learning_pages/fsrs_pages.dart' show FSRSLearningPage, ForeFSRSSettingPage;
1113
import 'package:arabic_learning/sub_pages_builder/learning_pages/learning_pages_build.dart';
1214

1315
class LearningPage extends StatelessWidget {
@@ -67,13 +69,19 @@ class LearningPage extends StatelessWidget {
6769
),
6870
),
6971
onPressed: (){
70-
context.read<Global>().uiLogger.info("跳转: LearningPage => ForeFSRSSettingPage");
71-
Navigator.push(
72-
context,
73-
MaterialPageRoute(
74-
builder: (context) => ForeFSRSSettingPage()
75-
)
76-
);
72+
if(context.read<Global>().globalFSRS.getWillDueCount() != 0) {
73+
context.read<Global>().uiLogger.info("跳转: LearningPage => ForeFSRSSettingPage");
74+
Navigator.push(
75+
context,
76+
MaterialPageRoute(
77+
builder: (context) => ForeFSRSSettingPage()
78+
)
79+
);
80+
} else {
81+
ScaffoldMessenger.of(context).showSnackBar(
82+
SnackBar(content: Text("目前没有要复习的单词"), duration: Duration(seconds: 1),),
83+
);
84+
}
7785
},
7886
child: FittedBox(
7987
fit: BoxFit.contain,
@@ -88,6 +96,41 @@ class LearningPage extends StatelessWidget {
8896
],
8997
),
9098
SizedBox(height: mediaQuery.size.height * 0.05),
99+
if(context.read<Global>().globalFSRS.config.pushAmount != 0) ElevatedButton.icon(
100+
style: ElevatedButton.styleFrom(
101+
backgroundColor: Theme.of(context).colorScheme.onPrimary.withAlpha(150),
102+
fixedSize: Size(mediaQuery.size.width * 0.7, mediaQuery.size.height * 0.2),
103+
shape: RoundedRectangleBorder(borderRadius: StaticsVar.br),
104+
),
105+
onPressed: (){
106+
final DateTime now = DateTime.now();
107+
final int seed = now.year * 10000 + now.month * 100 + now.day;
108+
final List<WordItem> pushWords = [];
109+
final Random rnd = Random(seed);
110+
for(int i = 0; i < context.read<Global>().globalFSRS.config.pushAmount; i++){
111+
int chosen = rnd.nextInt(context.read<Global>().wordData.words.length);
112+
if(!context.read<Global>().globalFSRS.isContained(chosen)) {
113+
pushWords.add(context.read<Global>().wordData.words.elementAt(chosen));
114+
}
115+
}
116+
if(pushWords.isEmpty) {
117+
ScaffoldMessenger.of(context).showSnackBar(
118+
SnackBar(content: Text("今日的推送已完成"), duration: Duration(seconds: 1),),
119+
);
120+
return;
121+
}
122+
context.read<Global>().uiLogger.info("跳转: LearningPage => FSRSLearningPage");
123+
Navigator.push(
124+
context,
125+
MaterialPageRoute(
126+
builder: (context) => FSRSLearningPage(fsrs: context.read<Global>().globalFSRS, words: pushWords)
127+
)
128+
);
129+
},
130+
icon: Icon(Icons.push_pin, size: 24),
131+
label: Text("学习推送单词", style: TextStyle(fontSize: 40.0, fontWeight: FontWeight.bold)),
132+
),
133+
SizedBox(height: mediaQuery.size.height * 0.05),
91134
ElevatedButton.icon(
92135
style: ElevatedButton.styleFrom(
93136
backgroundColor: Theme.of(context).colorScheme.onPrimary.withAlpha(150),

lib/sub_pages_builder/learning_pages/fsrs_pages.dart

Lines changed: 35 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -18,21 +18,18 @@ class ForeFSRSSettingPage extends StatelessWidget {
1818
@override
1919
Widget build(BuildContext context) {
2020
context.read<Global>().uiLogger.info("构建 ForeFSRSSettingPage");
21-
MediaQueryData mediaQuery = MediaQuery.of(context);
2221
final FSRS fsrs = context.read<Global>().globalFSRS;
2322
if(fsrs.config.enabled && !forceChoosing) {
2423
return MainFSRSPage(fsrs: fsrs);
2524
}
2625
return Scaffold(
2726
appBar: AppBar(
28-
title: const Text("FSRS-抗遗忘学习 预设置"),
27+
title: const Text("单词规律复习设置"),
2928
),
3029
body: StatefulBuilder(
3130
builder: (context, setState) {
3231
return ListView(
3332
children: [
34-
TextContainer(text: "本软件通过FSRS(Forgetting Spaced Repetition System)遗忘曲线的间隔重复学习算法,帮助用户更有效地记忆单词。\n你可以通过调整以下参数来个性化学习方案:"),
35-
SizedBox(height: mediaQuery.size.height * 0.02),
3633
TextContainer(text: "参数配置", textAlign: TextAlign.center),
3734
Container(
3835
decoration: BoxDecoration(
@@ -168,6 +165,39 @@ class ForeFSRSSettingPage extends StatelessWidget {
168165
],
169166
),
170167
),
168+
Container(
169+
decoration: BoxDecoration(
170+
borderRadius: StaticsVar.br,
171+
color: Theme.of(context).colorScheme.onSecondary
172+
),
173+
margin: EdgeInsets.all(8.0),
174+
padding: EdgeInsets.all(8.0),
175+
child: Column(
176+
crossAxisAlignment: CrossAxisAlignment.start,
177+
children: [
178+
Row(
179+
children: [
180+
Expanded(child: Text("每日单词推送", style: Theme.of(context).textTheme.bodyLarge)),
181+
Slider(
182+
max: 20.0,
183+
min: 0.0,
184+
divisions: 20,
185+
value: fsrs.config.pushAmount.toDouble(),
186+
onChanged: (double value){
187+
setState(() {
188+
fsrs.config = fsrs.config.copyWith(pushAmount: value.round());
189+
});
190+
},
191+
label: fsrs.config.pushAmount == 0 ? "禁用" : fsrs.config.pushAmount.toString(),
192+
)
193+
],
194+
),
195+
Text("单词推送 开启后每天会推送新单词 但数量不一定是你所指定的(大概率会少几个) 你可以在学习页面入口进入推送单词学习"),
196+
Text("学习的推送单词会加入复习中"),
197+
Text("当天是否学习新单词对连胜计数没有影响 学不学可以看你心情")
198+
],
199+
),
200+
),
171201
if(!fsrs.config.selfEvaluate) Container(
172202
decoration: BoxDecoration(
173203
borderRadius: StaticsVar.br,
@@ -226,13 +256,9 @@ class MainFSRSPage extends StatelessWidget {
226256
@override
227257
Widget build(BuildContext context) {
228258
context.read<Global>().uiLogger.info("构建 MainFSRSPage");
229-
bool isAnyDue = fsrs.getWillDueCount() != 0;
230259
MediaQueryData mediaQuery = MediaQuery.of(context);
231260
final PageController controller = PageController();
232261
Random sharedRnd = Random();
233-
if(!isAnyDue) {
234-
return FSRSOverViewPage(fsrs: fsrs);
235-
}
236262
return Scaffold(
237263
appBar: AppBar(
238264
title: const Text("规律学习"),
@@ -325,11 +351,6 @@ class _FSRSReviewCardPage extends State<FSRSReviewCardPage> {
325351
allowAnitmation: !widget.fsrs.config.selfEvaluate,
326352
allowMutipleSelect: false,
327353
hint: "单词ID: ${widget.wordID}${choosed ? " 用时: ${end.difference(start).inMilliseconds}毫秒" : ""}",
328-
onDisAllowMutipleSelect: (value) {
329-
ScaffoldMessenger.of(context).showSnackBar(
330-
SnackBar(content: Text("人要向前看 题要向下翻 :)"), duration: Duration(seconds: 1),),
331-
);
332-
},
333354
onSelected: (value) {
334355
setState(() {
335356
choosed = true;
@@ -395,69 +416,6 @@ class _FSRSReviewCardPage extends State<FSRSReviewCardPage> {
395416
}
396417
}
397418

398-
// 没有东西复习的时候
399-
class FSRSOverViewPage extends StatefulWidget {
400-
final FSRS fsrs;
401-
const FSRSOverViewPage({super.key, required this.fsrs});
402-
403-
@override
404-
State<FSRSOverViewPage> createState() => _FSRSOverViewPageState();
405-
}
406-
407-
class _FSRSOverViewPageState extends State<FSRSOverViewPage> {
408-
@override
409-
Widget build(BuildContext context) {
410-
MediaQueryData mediaQuery = MediaQuery.of(context);
411-
return Scaffold(
412-
appBar: AppBar(
413-
title: const Text("进度概览"),
414-
actions: [
415-
IconButton(
416-
icon: Icon(Icons.keyboard_option_key),
417-
onPressed: () {
418-
showModalBottomSheet(
419-
context: context,
420-
isScrollControlled: true,
421-
builder: (context) => ForeFSRSSettingPage(forceChoosing: true)
422-
);
423-
},
424-
),
425-
],
426-
),
427-
body: ListView(
428-
children: [
429-
TextContainer(text: "目前没有需要复习的单词!"),
430-
SizedBox(height: mediaQuery.size.height * 0.01),
431-
ElevatedButton.icon(
432-
style: ElevatedButton.styleFrom(
433-
fixedSize: Size.fromHeight(mediaQuery.size.height * 0.1)
434-
),
435-
icon: const Icon(Icons.label_important, size: 24.0),
436-
label: const Text("去学习新单词"),
437-
onPressed: () async {
438-
late ClassSelection selectedClasses;
439-
late List<WordItem> words;
440-
selectedClasses = await popSelectClasses(context, withCache: false, withReviewChoose: false);
441-
if(!context.mounted || selectedClasses.selectedClass.isEmpty) return;
442-
words = getSelectedWords(context, forceSelectClasses: selectedClasses.selectedClass, doShuffle: true, doDouble: false);
443-
// 去除已经学习的项目
444-
words.removeWhere((WordItem item) => widget.fsrs.isContained(item.id));
445-
context.read<Global>().uiLogger.info("跳转: FSRSOverViewPage => FSRSLearningPage");
446-
Navigator.push(
447-
context,
448-
MaterialPageRoute(
449-
builder: (context) => FSRSLearningPage(words: words, fsrs: widget.fsrs,),
450-
)
451-
);
452-
},
453-
),
454-
// TextContainer(text: "统计数据")
455-
],
456-
)
457-
);
458-
}
459-
}
460-
461419
// 学习新东西的页面: 展示释义 -> 选择题
462420
class FSRSLearningPage extends StatefulWidget {
463421
final List<WordItem> words;
@@ -491,7 +449,7 @@ class _FSRSLearningPageState extends State<FSRSLearningPage> {
491449
if(widget.words.isEmpty) {
492450
return Scaffold(
493451
appBar: AppBar(),
494-
body: Center(child: TextContainer(text: "你选择的课程中所有的单词都已经学习过了\n等复习吧")),
452+
body: Center(child: TextContainer(text: "你选择的所有的单词都已经学习过了\n等复习吧")),
495453
);
496454
}
497455
return Scaffold(

0 commit comments

Comments
 (0)