Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,16 @@
- 添加了常见问题页面
- 给部分文本框添加了动画
- 添加了听力题型 [#51](https://github.com/OctagonalStar/arabic_learning/issues/51)
- 为每次学习添加了是否计算复习的提示 [#52](https://github.com/OctagonalStar/arabic_learning/issues/52)
- 在复习部分添加了自我评级方案 [#52](https://github.com/OctagonalStar/arabic_learning/issues/52)
- 添加了每日单词推送功能 [#52](https://github.com/OctagonalStar/arabic_learning/issues/52)

### Improvement

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

### Fix

Expand Down
36 changes: 24 additions & 12 deletions lib/funcs/fsrs_func.dart
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,11 @@ class FSRS {
return config.cards[index].due.toLocal().difference(DateTime.now()).inDays;
}

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

bool isContained(int wordId) {
for(Card card in config.cards) {
if(card.cardId == wordId) return true;
}
return false;
return config.cards.any((Card card) => card.cardId == wordId);
}

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

const FSRSConfig({
bool? enabled,
Expand All @@ -134,15 +134,19 @@ class FSRSConfig {
double? desiredRetention,
int? easyDuration,
int? goodDuration,
bool? preferSimilar
bool? preferSimilar,
bool? selfEvaluate,
int? pushAmount
}) :
enabled = enabled??false,
cards = cards??const [],
reviewLogs = reviewLogs??const [],
desiredRetention = desiredRetention??0.9,
easyDuration = easyDuration??3000,
goodDuration = goodDuration??6000,
preferSimilar = preferSimilar??false;
preferSimilar = preferSimilar??false,
selfEvaluate = selfEvaluate??false,
pushAmount = pushAmount??0;

Map<String, dynamic> toMap(){
return {
Expand All @@ -153,7 +157,9 @@ class FSRSConfig {
"desiredRetention": desiredRetention,
"easyDuration": easyDuration,
"goodDuration": goodDuration,
"preferSimilar": preferSimilar
"preferSimilar": preferSimilar,
"selfEvaluate": selfEvaluate,
"pushAmount": pushAmount
};
}

Expand All @@ -165,7 +171,9 @@ class FSRSConfig {
double? desiredRetention,
int? easyDuration,
int? goodDuration,
bool? preferSimilar
bool? preferSimilar,
bool? selfEvaluate,
int? pushAmount
}) {
return FSRSConfig(
enabled: enabled??this.enabled,
Expand All @@ -175,7 +183,9 @@ class FSRSConfig {
desiredRetention: desiredRetention??this.desiredRetention,
easyDuration: easyDuration??this.easyDuration,
goodDuration: goodDuration??this.goodDuration,
preferSimilar: preferSimilar??this.preferSimilar
preferSimilar: preferSimilar??this.preferSimilar,
selfEvaluate: selfEvaluate??this.selfEvaluate,
pushAmount: pushAmount??this.pushAmount
);
}

Expand All @@ -189,7 +199,9 @@ class FSRSConfig {
desiredRetention: configData["desiredRetention"],
easyDuration: configData["easyDuration"],
goodDuration: configData["goodDuration"],
preferSimilar: configData["preferSimilar"]
preferSimilar: configData["preferSimilar"],
selfEvaluate: configData["selfEvaluate"],
pushAmount: configData["pushAmount"]
);
}
return FSRSConfig(enabled: false);
Expand Down
60 changes: 39 additions & 21 deletions lib/funcs/ui.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import 'dart:convert';
import 'dart:ui';

import 'package:arabic_learning/vars/config_structure.dart' show ClassItem, SourceItem, WordItem;
import 'package:arabic_learning/vars/config_structure.dart' show ClassItem, SourceItem, WordItem, ClassSelection;
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

Expand Down Expand Up @@ -36,7 +36,7 @@ import 'package:arabic_learning/funcs/utili.dart';
/// List<List<String>> value = await popSelectClasses(context);
/// List<Map<String, dynamic>> words = getSelectedWords(context , forceSelectClasses: value);
/// ```
Future<List<ClassItem>> popSelectClasses(BuildContext context, {bool withCache = false}) async {
Future<ClassSelection> popSelectClasses(BuildContext context, {bool withCache = false, bool withReviewChoose = true}) async {
context.read<Global>().uiLogger.info("弹出课程选择(ClassSelectPage),withCache: $withCache");
final List<ClassItem> beforeSelectedClasses = [];
if(withCache) {
Expand All @@ -59,15 +59,14 @@ Future<List<ClassItem>> popSelectClasses(BuildContext context, {bool withCache =
}
context.read<Global>().uiLogger.fine("已缓存课程选择: $beforeSelectedClasses");
}
List<ClassItem>? selectedClasses = await showModalBottomSheet<List<ClassItem>>(
ClassSelection? selectedClasses = await showModalBottomSheet<ClassSelection>(
context: context,
// 假装圆角... :)
shape: RoundedRectangleBorder(side: BorderSide(width: 1.0, color: Theme.of(context).colorScheme.onSurface.withAlpha(150)), borderRadius: StaticsVar.br),
isDismissible: false,
isScrollControlled: context.read<Global>().isWideScreen,
enableDrag: true,
builder: (BuildContext context) {
return ClassSelectPage(beforeSelectedClasses: beforeSelectedClasses);
return ClassSelectPage(beforeSelectedClasses: beforeSelectedClasses, withReviewChoose: withReviewChoose);
}
);
if(withCache && selectedClasses != null && context.mounted) {
Expand All @@ -78,7 +77,7 @@ Future<List<ClassItem>> popSelectClasses(BuildContext context, {bool withCache =
context.read<Global>().uiLogger.info("课程选择缓存完成");
}
if(context.mounted) context.read<Global>().uiLogger.fine("选择的课程: $selectedClasses");
return selectedClasses??[];
return selectedClasses??ClassSelection(selectedClass: [], countInReview: false);
}


Expand Down Expand Up @@ -573,19 +572,23 @@ class WordCard extends StatelessWidget {
/// 注意:如果你要进行课程选择,请先考虑 [popSelectClasses] 函数,这是一个已经基本成熟的实现
class ClassSelectPage extends StatelessWidget {
final List<ClassItem> beforeSelectedClasses;
const ClassSelectPage({super.key, this.beforeSelectedClasses = const []});
final bool withReviewChoose;
const ClassSelectPage({super.key, this.beforeSelectedClasses = const [], this.withReviewChoose = false});
@override
Widget build(BuildContext context) {
final MediaQueryData mediaQuery = MediaQuery.of(context);
List<ClassItem> selectedClass = beforeSelectedClasses.toList();
ClassSelection classSelection = ClassSelection(
selectedClass: beforeSelectedClasses.toList(),
countInReview: context.read<Global>().globalFSRS.config.enabled
);
void addClass(ClassItem classInfo) {
selectedClass.add(classInfo);
classSelection.selectedClass.add(classInfo);
}
void removeClass(ClassItem classInfo) {
selectedClass.remove(classInfo);
classSelection.selectedClass.remove(classInfo);
}
bool isClassSelected(ClassItem classInfo) {
return selectedClass.any((e) => e==classInfo);
return classSelection.selectedClass.any((e) => e==classInfo);
}
void onClassChanged(ClassItem classInfo) {
if(isClassSelected(classInfo)) {
Expand All @@ -594,14 +597,6 @@ class ClassSelectPage extends StatelessWidget {
addClass(classInfo);
}
}
// 和监听器脱钩...
// if(!context.watch<ClassSelectModel>().initialized) {
// return Scaffold(
// body: Center(
// child: CircularProgressIndicator(),
// ),
// );
// }

return Scaffold(
appBar: AppBar(
Expand All @@ -614,14 +609,35 @@ class ClassSelectPage extends StatelessWidget {
children: classesSelectionList(context, onClassChanged, isClassSelected)
),
),
if(withReviewChoose) StatefulBuilder(
builder: (context, setLocalState) {
return Row(
children: [
Expanded(child: Text("本次学习${classSelection.countInReview?"将":"不会"}计入复习系统")),
Switch(
value: classSelection.countInReview,
onChanged: (value){
if(value == true && !context.read<Global>().globalFSRS.config.enabled) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text("请先启用复习系统")));
return ;
}
setLocalState(() {
classSelection.countInReview = value;
});
}
)
],
);
}
),
ElevatedButton(
style: ElevatedButton.styleFrom(
fixedSize: Size(mediaQuery.size.width, mediaQuery.size.height * 0.08),
shape: ContinuousRectangleBorder(borderRadius: StaticsVar.br),
),
child: Text('确认'),
onPressed: () {
Navigator.pop(context, selectedClass);
Navigator.pop(context, classSelection);
},
),
],
Expand Down Expand Up @@ -700,6 +716,7 @@ class ChoiceQuestions extends StatefulWidget {
final List<String> choices;
final bool? Function(int) onSelected;
final String? hint;
final Widget? midWidget;
final Widget? bottomWidget;
final Function? onDisAllowMutipleSelect;
final bool allowMutipleSelect;
Expand All @@ -712,6 +729,7 @@ class ChoiceQuestions extends StatefulWidget {
required this.allowAudio,
required this.onSelected,
this.hint,
this.midWidget,
this.bottomWidget,
this.onDisAllowMutipleSelect,
this.bottonLayout = -1,
Expand Down Expand Up @@ -742,7 +760,7 @@ class _ChoiceQuestions extends State<ChoiceQuestions> {
children: [
if(widget.hint!=null) TextContainer(text: widget.hint!, animated: true),
Expanded(
child: StatefulBuilder(
child: widget.midWidget ?? StatefulBuilder(
builder: (context, setLocalState) {
return ElevatedButton.icon(
icon: Icon(widget.allowAudio ? (playing ? Icons.multitrack_audio : Icons.volume_up) : Icons.short_text, size: 24.0),
Expand Down
4 changes: 2 additions & 2 deletions lib/pages/home_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ class HomePage extends StatelessWidget {
Widget build(BuildContext context) {
context.read<Global>().uiLogger.fine("构建 HomePage");
final themeColor = Theme.of(context).colorScheme;
final mediaQuery = MediaQuery.of(context);
final FSRS fsrs = FSRS()..init(outerPrefs: context.read<Global>().prefs);
final MediaQueryData mediaQuery = MediaQuery.of(context);
final FSRS fsrs = context.read<Global>().globalFSRS;
return Column(
children: [
DailyWord(),
Expand Down
Loading