diff --git a/kernel/api/riff.go b/kernel/api/riff.go index bd1081088..a1ec3fead 100644 --- a/kernel/api/riff.go +++ b/kernel/api/riff.go @@ -216,13 +216,22 @@ func removeRiffCards(c *gin.Context) { for _, blockID := range blockIDsArg { blockIDs = append(blockIDs, blockID.(string)) } - err := model.RemoveFlashcardsByBlockIDs(deckID, blockIDs) - if nil != err { - ret.Code = -1 - ret.Msg = err.Error() - return + + transactions := []*model.Transaction{ + { + DoOperations: []*model.Operation{ + { + Action: "removeFlashcards", + DeckID: deckID, + BlockIDs: blockIDs, + }, + }, + }, } + model.PerformTransactions(&transactions) + model.WaitForWritingFiles() + if "" != deckID { deck := model.Decks[deckID] ret.Data = deckData(deck) @@ -245,13 +254,22 @@ func addRiffCards(c *gin.Context) { for _, blockID := range blockIDsArg { blockIDs = append(blockIDs, blockID.(string)) } - err := model.AddFlashcards(deckID, blockIDs) - if nil != err { - ret.Code = -1 - ret.Msg = err.Error() - return + + transactions := []*model.Transaction{ + { + DoOperations: []*model.Operation{ + { + Action: "addFlashcards", + DeckID: deckID, + BlockIDs: blockIDs, + }, + }, + }, } + model.PerformTransactions(&transactions) + model.WaitForWritingFiles() + deck := model.Decks[deckID] ret.Data = deckData(deck) } diff --git a/kernel/model/flashcard.go b/kernel/model/flashcard.go index d325e6510..1ea03dea8 100644 --- a/kernel/model/flashcard.go +++ b/kernel/model/flashcard.go @@ -456,95 +456,20 @@ func getAllDueFlashcards(reviewedCardIDs []string) (ret []*Flashcard) { return } -func RemoveFlashcardsByCardIDs(deckID string, cardIDs []string) (err error) { +func (tx *Transaction) doRemoveFlashcards(operation *Operation) (ret *TxErr) { deckLock.Lock() defer deckLock.Unlock() if syncingStorages { - err = errors.New(Conf.Language(81)) + ret = &TxErr{code: TxErrCodeDataIsSyncing} return } - var needRemoveDeckAttrBlockIDs []string - if "" == deckID { - // 在 All 卡包中移除 - var affectedBlockIDs []string - for _, deck := range Decks { - changed := false - for _, cardID := range cardIDs { - card := deck.GetCard(cardID) - if nil == card { - continue - } + deckID := operation.DeckID + blockIDs := operation.BlockIDs - affectedBlockIDs = append(affectedBlockIDs, card.BlockID()) - deck.RemoveCard(cardID) - changed = true - } - - if changed { - if err = deck.Save(); nil != err { - return - } - } - - // 检查刚刚移除的卡片关联的块是否还存在更多关联的卡片 - affectedBlockIDs = gulu.Str.RemoveDuplicatedElem(affectedBlockIDs) - for _, blockID := range affectedBlockIDs { - moreRelatedCards := deck.GetCardsByBlockID(blockID) - if 1 > len(moreRelatedCards) { - needRemoveDeckAttrBlockIDs = append(needRemoveDeckAttrBlockIDs, blockID) - } - } - } - } else { - // 在指定卡包中移除 - deck := Decks[deckID] - if nil == deck { - return - } - - var affectedBlockIDs []string - for _, cardID := range cardIDs { - card := deck.GetCard(cardID) - if nil == card { - continue - } - - affectedBlockIDs = append(affectedBlockIDs, card.BlockID()) - deck.RemoveCard(cardID) - if err = deck.Save(); nil != err { - return - } - } - - // 检查刚刚移除的卡片关联的块是否还存在更多关联的卡片 - affectedBlockIDs = gulu.Str.RemoveDuplicatedElem(affectedBlockIDs) - for _, blockID := range affectedBlockIDs { - moreRelatedCards := deck.GetCardsByBlockID(blockID) - if 1 > len(moreRelatedCards) { - needRemoveDeckAttrBlockIDs = append(needRemoveDeckAttrBlockIDs, blockID) - } - } - } - - if err = removeBlocksDeckAttr(needRemoveDeckAttrBlockIDs, deckID); nil != err { - return - } - return -} - -func RemoveFlashcardsByBlockIDs(deckID string, blockIDs []string) (err error) { - deckLock.Lock() - defer deckLock.Unlock() - - if syncingStorages { - err = errors.New(Conf.Language(81)) - return - } - - if err = removeBlocksDeckAttr(blockIDs, deckID); nil != err { - return + if err := tx.removeBlocksDeckAttr(blockIDs, deckID); nil != err { + return &TxErr{code: TxErrCodeWriteTree, msg: err.Error(), id: deckID} } if "" == deckID { // 支持在 All 卡包中移除闪卡 https://github.com/siyuan-note/siyuan/issues/7425 @@ -557,7 +482,7 @@ func RemoveFlashcardsByBlockIDs(deckID string, blockIDs []string) (err error) { return } -func removeBlocksDeckAttr(blockIDs []string, deckID string) (err error) { +func (tx *Transaction) removeBlocksDeckAttr(blockIDs []string, deckID string) (err error) { var rootIDs []string blockRoots := map[string]string{} for _, blockID := range blockIDs { @@ -577,7 +502,7 @@ func removeBlocksDeckAttr(blockIDs []string, deckID string) (err error) { tree := trees[rootID] if nil == tree { - tree, _ = loadTreeByBlockID(blockID) + tree, _ = tx.loadTree(blockID) } if nil == tree { continue @@ -612,7 +537,7 @@ func removeBlocksDeckAttr(blockIDs []string, deckID string) (err error) { node.SetIALAttr("custom-riff-decks", val) } - if err = indexWriteJSONQueue(tree); nil != err { + if err = tx.writeTree(tree); nil != err { return } @@ -643,15 +568,18 @@ func removeFlashcardsByBlockIDs(blockIDs []string, deck *riff.Deck) { } } -func AddFlashcards(deckID string, blockIDs []string) (err error) { +func (tx *Transaction) doAddFlashcards(operation *Operation) (ret *TxErr) { deckLock.Lock() defer deckLock.Unlock() if syncingStorages { - err = errors.New(Conf.Language(81)) + ret = &TxErr{code: TxErrCodeDataIsSyncing} return } + deckID := operation.DeckID + blockIDs := operation.BlockIDs + blockRoots := map[string]string{} for _, blockID := range blockIDs { bt := treenode.GetBlockTree(blockID) @@ -668,7 +596,7 @@ func AddFlashcards(deckID string, blockIDs []string) (err error) { tree := trees[rootID] if nil == tree { - tree, _ = loadTreeByBlockID(blockID) + tree, _ = tx.loadTree(blockID) } if nil == tree { continue @@ -691,8 +619,8 @@ func AddFlashcards(deckID string, blockIDs []string) (err error) { val = strings.TrimSuffix(val, ",") node.SetIALAttr("custom-riff-decks", val) - if err = indexWriteJSONQueue(tree); nil != err { - return + if err := tx.writeTree(tree); nil != err { + return &TxErr{code: TxErrCodeWriteTree, msg: err.Error(), id: deckID} } cache.PutBlockIAL(blockID, parse.IAL2Map(node.KramdownIAL)) @@ -715,8 +643,8 @@ func AddFlashcards(deckID string, blockIDs []string) (err error) { cardID := ast.NewNodeID() deck.AddCard(cardID, blockID) } - err = deck.Save() - if nil != err { + + if err := deck.Save(); nil != err { logging.LogErrorf("save deck [%s] failed: %s", deckID, err) return } diff --git a/kernel/model/transaction.go b/kernel/model/transaction.go index 28b1f69ea..813577781 100644 --- a/kernel/model/transaction.go +++ b/kernel/model/transaction.go @@ -155,6 +155,7 @@ func PerformTransactions(transactions *[]*Transaction) { const ( TxErrCodeBlockNotFound = 0 + TxErrCodeDataIsSyncing = 1 TxErrCodeWriteTree = 2 TxErrWriteAttributeView = 3 ) @@ -208,7 +209,11 @@ func performTx(tx *Transaction) (ret *TxErr) { case "unfoldHeading": ret = tx.doUnfoldHeading(op) case "setAttrs": - ret = tx.setAttrs(op) + ret = tx.doSetAttrs(op) + case "addFlashcards": + ret = tx.doAddFlashcards(op) + case "removeFlashcards": + ret = tx.doRemoveFlashcards(op) case "insertAttrViewBlock": ret = tx.doInsertAttrViewBlock(op) case "removeAttrViewBlock": @@ -927,7 +932,7 @@ func (tx *Transaction) doCreate(operation *Operation) (ret *TxErr) { return } -func (tx *Transaction) setAttrs(operation *Operation) (ret *TxErr) { +func (tx *Transaction) doSetAttrs(operation *Operation) (ret *TxErr) { id := operation.ID tree, err := tx.loadTree(id) if nil != err { @@ -968,7 +973,7 @@ func (tx *Transaction) setAttrs(operation *Operation) (ret *TxErr) { } } - if err = indexWriteJSONQueue(tree); nil != err { + if err = tx.writeTree(tree); nil != err { return } cache.PutBlockIAL(id, parse.IAL2Map(node.KramdownIAL)) @@ -1007,6 +1012,9 @@ type Operation struct { PreviousID string `json:"previousID"` NextID string `json:"nextID"` RetData interface{} `json:"retData"` + BlockIDs []string `json:"blockIDs"` + + DeckID string `json:"deckID"` // 用于添加/删除闪卡 SrcIDs []string `json:"srcIDs"` // 用于将块拖拽到属性视图中 Name string `json:"name"` // 用于属性视图列名