Improve spaced repetition interface (#15627)

* Improve spaced repetition interface

fix https://github.com/siyuan-note/siyuan/issues/10331

* Improve spaced repetition interface

fix https://github.com/siyuan-note/siyuan/issues/14149

* Improve spaced repetition interface

fix https://github.com/siyuan-note/siyuan/issues/10331

* Improve spaced repetition interface

fix https://github.com/siyuan-note/siyuan/issues/10331
This commit is contained in:
Jeffrey Chen 2025-10-14 09:46:23 +08:00 committed by GitHub
parent 404d9f63d0
commit b6a9ef2bb5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 113 additions and 64 deletions

View file

@ -1,6 +1,6 @@
import {Tab} from "../layout/Tab"; import {Tab} from "../layout/Tab";
import {Custom} from "../layout/dock/Custom"; import {Custom} from "../layout/dock/Custom";
import {bindCardEvent, genCardHTML} from "./openCard"; import {bindCardEvent, genCardHTML, initCardComponent} from "./openCard";
import {fetchPost} from "../util/fetch"; import {fetchPost} from "../util/fetch";
import {Protyle} from "../protyle"; import {Protyle} from "../protyle";
import {setPanelFocus} from "../layout/util"; import {setPanelFocus} from "../layout/util";
@ -19,6 +19,57 @@ export const newCardModel = (options: {
} }
}) => { }) => {
let editor: Protyle; let editor: Protyle;
const fetchCardsData = async (): Promise<ICardData> => {
return new Promise((resolve) => {
fetchPost(options.data.cardType === "all" ? "/api/riff/getRiffDueCards" :
(options.data.cardType === "doc" ? "/api/riff/getTreeRiffDueCards" : "/api/riff/getNotebookRiffDueCards"), {
rootID: options.data.id,
deckID: options.data.id,
notebook: options.data.id,
}, async (response) => {
let cardsData: ICardData = response.data;
for (let i = 0; i < options.app.plugins.length; i++) {
cardsData = await options.app.plugins[i].updateCards(response.data);
}
resolve(cardsData);
});
});
};
const renderCardsAndBindEvents = async (element: HTMLElement, data: any, cardsData: ICardData, index?: number, isUpdate?: boolean) => {
customObj.editors.forEach(editor => {
editor.destroy();
});
customObj.editors.length = 0;
element.innerHTML = genCardHTML({
id: data.id,
cardType: data.cardType,
cardsData,
isTab: true,
});
const cardOptions = {
app: options.app,
element: element,
id: data.id,
title: data.title,
cardType: data.cardType,
cardsData,
index,
};
if (isUpdate) {
const initResult = await initCardComponent(cardOptions);
editor = initResult.editor;
} else {
editor = await bindCardEvent(cardOptions);
}
customObj.editors.push(editor);
};
const customObj = new Custom({ const customObj = new Custom({
app: options.app, app: options.app,
type: "siyuan-card", type: "siyuan-card",
@ -26,58 +77,18 @@ export const newCardModel = (options: {
data: options.data, data: options.data,
async init() { async init() {
if (options.data.cardsData) { if (options.data.cardsData) {
// 使用现有的 cardsData
for (let i = 0; i < options.app.plugins.length; i++) { for (let i = 0; i < options.app.plugins.length; i++) {
options.data.cardsData = await options.app.plugins[i].updateCards(options.data.cardsData); options.data.cardsData = await options.app.plugins[i].updateCards(options.data.cardsData);
} }
this.element.innerHTML = genCardHTML({ await renderCardsAndBindEvents(this.element, this.data, options.data.cardsData, options.data.index);
id: this.data.id,
cardType: this.data.cardType,
cardsData: options.data.cardsData,
isTab: true,
});
editor = await bindCardEvent({
app: options.app,
element: this.element,
id: this.data.id,
title: this.data.title,
cardType: this.data.cardType,
cardsData: options.data.cardsData,
index: options.data.index,
});
customObj.editors.push(editor);
// https://github.com/siyuan-note/siyuan/issues/9561#issuecomment-1794473512 // https://github.com/siyuan-note/siyuan/issues/9561#issuecomment-1794473512
delete options.data.cardsData; delete options.data.cardsData;
delete options.data.index; delete options.data.index;
} else { } else {
fetchPost(this.data.cardType === "all" ? "/api/riff/getRiffDueCards" : // 获取新的 cardsData
(this.data.cardType === "doc" ? "/api/riff/getTreeRiffDueCards" : "/api/riff/getNotebookRiffDueCards"), { const cardsData = await fetchCardsData();
rootID: this.data.id, await renderCardsAndBindEvents(this.element, this.data, cardsData);
deckID: this.data.id,
notebook: this.data.id,
}, async (response) => {
let cardsData: ICardData = response.data;
for (let i = 0; i < options.app.plugins.length; i++) {
cardsData = await options.app.plugins[i].updateCards(response.data);
}
this.element.innerHTML = genCardHTML({
id: this.data.id,
cardType: this.data.cardType,
cardsData,
isTab: true,
});
editor = await bindCardEvent({
app: options.app,
element: this.element,
id: this.data.id,
title: this.data.title,
cardType: this.data.cardType,
cardsData,
});
customObj.editors.push(editor);
});
} }
}, },
destroy() { destroy() {
@ -90,23 +101,9 @@ export const newCardModel = (options: {
editor.resize(); editor.resize();
} }
}, },
update() { async update() {
fetchPost(this.data.cardType === "all" ? "/api/riff/getRiffDueCards" : const cardsData = await fetchCardsData();
(this.data.cardType === "doc" ? "/api/riff/getTreeRiffDueCards" : "/api/riff/getNotebookRiffDueCards"), { await renderCardsAndBindEvents(this.element, this.data, cardsData ,undefined, true);
rootID: this.data.id,
deckID: this.data.id,
notebook: this.data.id,
}, async (response) => {
for (let i = 0; i < options.app.plugins.length; i++) {
options.data.cardsData = await options.app.plugins[i].updateCards(options.data.cardsData);
}
this.element.innerHTML = genCardHTML({
id: this.data.id,
cardType: this.data.cardType,
cardsData: response.data,
isTab: true,
});
});
} }
}); });
customObj.element.addEventListener("click", () => { customObj.element.addEventListener("click", () => {

View file

@ -225,7 +225,7 @@ const getEditor = (id: string, protyle: IProtyle, element: Element, currentCard:
}; };
export const bindCardEvent = async (options: { export const initCardComponent = async (options: {
app: App, app: App,
element: Element, element: Element,
title?: string, title?: string,
@ -301,6 +301,33 @@ export const bindCardEvent = async (options: {
}; };
countElement.innerHTML = genCardCount(options.cardsData, index); countElement.innerHTML = genCardCount(options.cardsData, index);
return {
editor,
index,
actionElements,
countElement,
filterElement,
fetchNewRound
};
};
export const bindCardEvent = async (options: {
app: App,
element: Element,
title?: string,
cardsData: ICardData
cardType: TCardType,
id?: string,
dialog?: Dialog,
index?: number
}) => {
// 初始化卡片组件
const initResult = await initCardComponent(options);
const { editor, actionElements, countElement, filterElement, fetchNewRound } = initResult;
let index = initResult.index;
// 绑定点击事件
options.element.addEventListener("click", (event: MouseEvent) => { options.element.addEventListener("click", (event: MouseEvent) => {
const target = event.target as HTMLElement; const target = event.target as HTMLElement;
let type = ""; let type = "";
@ -489,6 +516,29 @@ export const bindCardEvent = async (options: {
const sticktabElement = hasClosestByAttribute(target, "data-type", "sticktab"); const sticktabElement = hasClosestByAttribute(target, "data-type", "sticktab");
if (sticktabElement) { if (sticktabElement) {
const stickMenu = new Menu(); const stickMenu = new Menu();
stickMenu.addItem({
id: "openInNewTab",
icon: "iconOpen",
label: window.siyuan.languages.openInNewTab,
click() {
openFile({
app: options.app,
custom: {
icon: "iconRiffCard",
title: window.siyuan.languages.spaceRepetition,
data: {
cardsData: options.cardsData,
index,
cardType: filterElement.getAttribute("data-cardtype") as TCardType,
id: docId,
title: options.title
},
id: "siyuan-card"
},
});
options.dialog.destroy();
}
});
stickMenu.addItem({ stickMenu.addItem({
id: "insertRight", id: "insertRight",
icon: "iconLayoutRight", icon: "iconLayoutRight",
@ -527,6 +577,8 @@ export const bindCardEvent = async (options: {
"instance": "Custom", "instance": "Custom",
"customModelType": "siyuan-card", "customModelType": "siyuan-card",
"customModelData": { "customModelData": {
"cardsData": options.cardsData,
"index": index,
"cardType": filterElement.getAttribute("data-cardtype"), "cardType": filterElement.getAttribute("data-cardtype"),
"id": docId, "id": docId,
"title": options.title "title": options.title