Bläddra i källkod

:art: 支持基于文档复习闪卡 https://github.com/siyuan-note/siyuan/issues/7057

Liang Ding 2 år sedan
förälder
incheckning
26e62e1eda
3 ändrade filer med 131 tillägg och 14 borttagningar
  1. 27 0
      kernel/api/riff.go
  2. 1 0
      kernel/api/router.go
  3. 103 14
      kernel/model/flashcard.go

+ 27 - 0
kernel/api/riff.go

@@ -66,6 +66,33 @@ func reviewRiffCard(c *gin.Context) {
 	}
 }
 
+func getTreeRiffDueCards(c *gin.Context) {
+	ret := gulu.Ret.NewResult()
+	defer c.JSON(http.StatusOK, ret)
+
+	arg, ok := util.JsonArg(c, ret)
+	if !ok {
+		return
+	}
+
+	rootID := arg["rootID"].(string)
+	err := model.AddTreeFlashcards(rootID)
+	if nil != err {
+		ret.Code = -1
+		ret.Msg = err.Error()
+		return
+	}
+
+	cards, err := model.GetTreeDueFlashcards(rootID)
+	if nil != err {
+		ret.Code = -1
+		ret.Msg = err.Error()
+		return
+	}
+
+	ret.Data = cards
+}
+
 func getRiffDueCards(c *gin.Context) {
 	ret := gulu.Ret.NewResult()
 	defer c.JSON(http.StatusOK, ret)

+ 1 - 0
kernel/api/router.go

@@ -311,6 +311,7 @@ func ServeAPI(ginServer *gin.Engine) {
 	ginServer.Handle("POST", "/api/riff/addRiffCards", model.CheckAuth, addRiffCards)
 	ginServer.Handle("POST", "/api/riff/removeRiffCards", model.CheckAuth, removeRiffCards)
 	ginServer.Handle("POST", "/api/riff/getRiffDueCards", model.CheckAuth, getRiffDueCards)
+	ginServer.Handle("POST", "/api/riff/getTreeRiffDueCards", model.CheckAuth, getTreeRiffDueCards)
 	ginServer.Handle("POST", "/api/riff/reviewRiffCard", model.CheckAuth, reviewRiffCard)
 	ginServer.Handle("POST", "/api/riff/getRiffCards", model.CheckAuth, getRiffCards)
 

+ 103 - 14
kernel/model/flashcard.go

@@ -122,6 +122,56 @@ type Flashcard struct {
 	NextDues map[riff.Rating]string `json:"nextDues"`
 }
 
+func GetTreeDueFlashcards(rootID string) (ret []*Flashcard, err error) {
+	deckLock.Lock()
+	defer deckLock.Unlock()
+
+	if syncingStorages {
+		err = errors.New(Conf.Language(81))
+		return
+	}
+
+	deck := Decks[builtinDeckID]
+	if nil == deck {
+		logging.LogWarnf("builtin deck not found")
+		return
+	}
+
+	tree, err := loadTreeByBlockID(rootID)
+	if nil != err {
+		return
+	}
+
+	blockIDs := map[string]bool{}
+	for n := tree.Root.FirstChild; nil != n; n = n.Next {
+		blockIDs[n.ID] = true
+	}
+
+	cards := deck.Dues()
+	now := time.Now()
+	for _, card := range cards {
+		blockID := card.BlockID()
+		if !blockIDs[blockID] {
+			continue
+		}
+
+		nextDues := map[riff.Rating]string{}
+		for rating, due := range card.NextDues() {
+			nextDues[rating] = strings.TrimSpace(humanize.RelTime(due, now, "", ""))
+		}
+
+		ret = append(ret, &Flashcard{
+			DeckID:   builtinDeckID,
+			BlockID:  blockID,
+			NextDues: nextDues,
+		})
+	}
+	if 1 > len(ret) {
+		ret = []*Flashcard{}
+	}
+	return
+}
+
 func GetDueFlashcards(deckID string) (ret []*Flashcard, err error) {
 	deckLock.Lock()
 	defer deckLock.Unlock()
@@ -286,6 +336,19 @@ func RemoveFlashcards(deckID string, blockIDs []string) (err error) {
 	return
 }
 
+func AddTreeFlashcards(rootID string) (err error) {
+	tree, err := loadTreeByBlockID(rootID)
+	if nil != err {
+		return
+	}
+
+	var blockIDs []string
+	for n := tree.Root.FirstChild; nil != n; n = n.Next {
+		blockIDs = append(blockIDs, n.ID)
+	}
+	return AddFlashcards(builtinDeckID, blockIDs)
+}
+
 func AddFlashcards(deckID string, blockIDs []string) (err error) {
 	deckLock.Lock()
 	defer deckLock.Unlock()
@@ -295,7 +358,6 @@ func AddFlashcards(deckID string, blockIDs []string) (err error) {
 		return
 	}
 
-	var rootIDs []string
 	blockRoots := map[string]string{}
 	for _, blockID := range blockIDs {
 		bt := treenode.GetBlockTree(blockID)
@@ -303,10 +365,8 @@ func AddFlashcards(deckID string, blockIDs []string) (err error) {
 			continue
 		}
 
-		rootIDs = append(rootIDs, bt.RootID)
 		blockRoots[blockID] = bt.RootID
 	}
-	rootIDs = gulu.Str.RemoveDuplicatedElem(rootIDs)
 
 	trees := map[string]*parse.Tree{}
 	for _, blockID := range blockIDs {
@@ -353,16 +413,19 @@ func AddFlashcards(deckID string, blockIDs []string) (err error) {
 	}
 
 	deck := Decks[deckID]
-	if nil != deck {
-		for _, blockID := range blockIDs {
-			cardID := ast.NewNodeID()
-			deck.AddCard(cardID, blockID)
-		}
-		err = deck.Save()
-		if nil != err {
-			logging.LogErrorf("save deck [%s] failed: %s", deckID, err)
-			return
-		}
+	if nil == deck {
+		logging.LogWarnf("deck [%s] not found", deckID)
+		return
+	}
+
+	for _, blockID := range blockIDs {
+		cardID := ast.NewNodeID()
+		deck.AddCard(cardID, blockID)
+	}
+	err = deck.Save()
+	if nil != err {
+		logging.LogErrorf("save deck [%s] failed: %s", deckID, err)
+		return
 	}
 	return
 }
@@ -408,8 +471,25 @@ func LoadFlashcards() {
 			Decks[deck.ID] = deck
 		}
 	}
+
+	// 支持基于文档复习闪卡 https://github.com/siyuan-note/siyuan/issues/7057
+	foudBuiltinDeck := false
+	for _, deck := range Decks {
+		if builtinDeckID == deck.ID {
+			foudBuiltinDeck = true
+			break
+		}
+	}
+	if !foudBuiltinDeck {
+		deck, createErr := createDeck0("Built-in Deck", builtinDeckID)
+		if nil == createErr {
+			Decks[deck.ID] = deck
+		}
+	}
 }
 
+const builtinDeckID = "20230218211946-2kw8jgx"
+
 func RenameDeck(deckID, name string) (err error) {
 	deckLock.Lock()
 	defer deckLock.Unlock()
@@ -469,8 +549,13 @@ func createDeck(name string) (deck *riff.Deck, err error) {
 		return
 	}
 
-	riffSavePath := getRiffDir()
 	deckID := ast.NewNodeID()
+	deck, err = createDeck0(name, deckID)
+	return
+}
+
+func createDeck0(name string, deckID string) (deck *riff.Deck, err error) {
+	riffSavePath := getRiffDir()
 	deck, err = riff.LoadDeck(riffSavePath, deckID)
 	if nil != err {
 		logging.LogErrorf("load deck [%s] failed: %s", deckID, err)
@@ -491,6 +576,10 @@ func GetDecks() (decks []*riff.Deck) {
 	defer deckLock.Unlock()
 
 	for _, deck := range Decks {
+		if deck.ID == builtinDeckID {
+			continue
+		}
+
 		decks = append(decks, deck)
 	}
 	if 1 > len(decks) {