diff --git a/kernel/api/riff.go b/kernel/api/riff.go index c40293310..41dcb2475 100644 --- a/kernel/api/riff.go +++ b/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) diff --git a/kernel/api/router.go b/kernel/api/router.go index b85967587..c135ef0a6 100644 --- a/kernel/api/router.go +++ b/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) diff --git a/kernel/model/flashcard.go b/kernel/model/flashcard.go index 72692c3a9..542412b50 100644 --- a/kernel/model/flashcard.go +++ b/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) {