1- import { FSRS , Rating , createEmptyCard , generatorParameters } from 'ts-fsrs' ;
1+ import { FSRS , Rating , S_MIN , State , TypeConvert , createEmptyCard , dateDiffInDays , generatorParameters } from 'ts-fsrs' ;
22
33// 1. 创建自定义参数
44const params = generatorParameters ( {
@@ -26,77 +26,42 @@ const qualityToRating = (quality) => {
2626export const calculateNextReview = ( problem , feedback ) => {
2727 try {
2828 const now = new Date ( ) ;
29+ const lastReview = problem . fsrsState && problem . fsrsState . lastReview
30+ ? new Date ( problem . fsrsState . lastReview )
31+ : new Date ( problem . submissionTime || now . getTime ( ) ) ;
2932
3033 // 如果没有 fsrsState,创建一个默认的
3134 if ( ! problem . fsrsState ) {
32- problem . fsrsState = {
33- difficulty : null ,
34- quality : null ,
35- lastReview : problem . submissionTime || now . getTime ( ) ,
36- nextReview : null ,
37- reviewCount : 0 ,
38- stability : 0 ,
39- state : 'New' ,
40- lapses : 0
41- } ;
35+ problem . fsrsState = createEmptyCard ( lastReview , ( card ) => {
36+ return {
37+ nextReview : + card . due ,
38+ stability : card . stability ,
39+ difficulty : card . difficulty ,
40+ state : card . state ,
41+ reps : card . reps ,
42+ lapses : card . lapses
43+ }
44+ } ) ;
4245 }
46+ let card = problem . fsrsState
4347
44- const lastReview = problem . fsrsState . lastReview
45- ? new Date ( problem . fsrsState . lastReview )
46- : new Date ( problem . submissionTime || now . getTime ( ) ) ;
47-
48- let card = createEmptyCard ( lastReview ) ;
49-
50- if ( problem . fsrsState . state !== 'New' ) {
51- card = {
52- ...card ,
53- state : problem . fsrsState . state ,
54- stability : problem . fsrsState . stability || 0 ,
55- difficulty : problem . fsrsState . difficulty || 0 ,
56- reps : problem . fsrsState . reviewCount || 0 ,
57- lapses : problem . fsrsState . lapses || 0 ,
58- // 添加时间相关字段
59- elapsed_days : problem . fsrsState . lastReview
60- ? ( now - new Date ( problem . fsrsState . lastReview ) ) / ( 24 * 60 * 60 * 1000 )
61- : 0 ,
62- scheduled_days : problem . fsrsState . nextReview
63- ? ( new Date ( problem . fsrsState . nextReview ) - new Date ( problem . fsrsState . lastReview ) ) / ( 24 * 60 * 60 * 1000 )
64- : 0
65- } ;
66- }
67-
6848 const rating = qualityToRating ( feedback . quality ) ;
69- const scheduling_cards = fsrs . repeat ( card , now ) ;
70- const result = scheduling_cards [ rating ] ;
49+ const result = fsrs . next ( {
50+ due : card . nextReview ,
51+ stability : card . stability ,
52+ difficulty : card . difficulty ,
53+ elapsed_days : ( now . getTime ( ) - lastReview . getTime ( ) ) / ( 1000 * 60 * 60 * 24 ) ,
54+ scheduled_days : Math . floor ( ( card . nextReview - card . lastReview ) / ( 1000 * 60 * 60 * 24 ) ) ,
55+ reps : card . reviewCount ,
56+ lapse_count : card . lapses ,
57+ state : card . state ,
58+ last_review : card . lastReview || undefined ,
59+ } , now , rating ) ;
7160
72- if ( ! result || ! result . card ) {
73- console . error ( 'FSRS calculation failed:' , result ) ;
74- // 默认间隔
75- const defaultDays = {
76- [ Rating . Again ] : 1 ,
77- [ Rating . Hard ] : 3 ,
78- [ Rating . Good ] : 7 ,
79- [ Rating . Easy ] : 14
80- } [ rating ] || 1 ;
81-
82- return {
83- nextReview : now . getTime ( ) + ( defaultDays * 24 * 60 * 60 * 1000 ) ,
84- stability : card . stability ,
85- difficulty : card . difficulty ,
86- state : card . state ,
87- reps : card . reps + 1 ,
88- lapses : card . lapses
89- } ;
90- }
91-
92- // 确保间隔至少为1天
93- const nextReviewTime = Math . max (
94- result . card . due . getTime ( ) ,
95- now . getTime ( ) + ( 24 * 60 * 60 * 1000 )
96- ) ;
9761
9862 return {
99- nextReview : nextReviewTime ,
63+ /**长期调度模式,ivl一定大于1d */
64+ nextReview : + result . card . due ,
10065 stability : result . card . stability ,
10166 difficulty : result . card . difficulty ,
10267 state : result . card . state ,
@@ -107,25 +72,17 @@ export const calculateNextReview = (problem, feedback) => {
10772 console . error ( 'Error in calculateNextReview:' , error ) ;
10873 return {
10974 nextReview : now . getTime ( ) + ( 24 * 60 * 60 * 1000 ) ,
110- stability : problem . fsrsState . stability || 0 ,
111- difficulty : problem . fsrsState . difficulty || 0 ,
112- state : problem . fsrsState . state || 'New' ,
75+ stability : problem . fsrsState . stability || S_MIN ,
76+ /** ref: https://github.com/open-spaced-repetition/ts-fsrs/blob/5eabd189d4740027ce1018cc968e67ca46c048a3/src/fsrs/default.ts#L20-L40 */
77+ difficulty : problem . fsrsState . difficulty || params . w [ 4 ] ,
78+ /** 长期调度下状态一定是New或Review */
79+ state : problem . fsrsState . state || State . Review ,
11380 reps : ( problem . fsrsState . reviewCount || 0 ) + 1 ,
11481 lapses : problem . fsrsState . lapses || 0
11582 } ;
11683 }
11784} ;
11885
119- // 将状态转换为数字
120- const stateToNumber = ( state ) => {
121- switch ( state ) {
122- case 'New' : return 0 ;
123- case 'Learning' : return 1 ;
124- case 'Review' : return 2 ;
125- case 'Relearning' : return 3 ;
126- default : return 0 ;
127- }
128- } ;
12986
13087// 5. 更新问题状态
13188export const updateProblemWithFSRS = ( problem , feedback ) => {
@@ -137,7 +94,7 @@ export const updateProblemWithFSRS = (problem, feedback) => {
13794 card_id : problem . index , // 使用问题索引作为卡片ID
13895 review_time : now , // 复习时间(毫秒时间戳)
13996 review_rating : qualityToRating ( feedback . quality ) , // 复习评分 (1-4)
140- review_state : stateToNumber ( problem . fsrsState ?. state || 'New' ) // 复习状态 (0-3)
97+ review_state : TypeConvert . state ( problem . fsrsState ? problem . fsrsState ?. state : 'New' ) // 复习状态 (0-3)
14198 } ;
14299
143100 // 将复习日志存储到单独的 localStorage 键中
@@ -149,7 +106,7 @@ export const updateProblemWithFSRS = (problem, feedback) => {
149106 difficulty : fsrsResult . difficulty ,
150107 stability : fsrsResult . stability ,
151108 state : fsrsResult . state ,
152- lastReview : now ,
109+ lastReview : fsrsResult . lastReview ,
153110 nextReview : fsrsResult . nextReview ,
154111 reviewCount : fsrsResult . reps ,
155112 lapses : fsrsResult . lapses ,
0 commit comments