mirror of
https://github.com/dralkh/spaceforge.git
synced 2026-07-22 06:45:03 +00:00
258 lines
8.9 KiB
TypeScript
258 lines
8.9 KiB
TypeScript
import { Notice } from 'obsidian';
|
|
import SpaceforgePlugin from '../main';
|
|
import { IReviewBatchController } from './interfaces';
|
|
import { ReviewSchedule } from '../models/review-schedule';
|
|
import { BatchReviewModal } from '../ui/batch-review-modal';
|
|
import { ConsolidatedMCQModal } from '../ui/consolidated-mcq-modal';
|
|
|
|
/**
|
|
* Controller for batch reviewing multiple notes
|
|
*/
|
|
export class ReviewBatchController implements IReviewBatchController {
|
|
/**
|
|
* Reference to the main plugin
|
|
*/
|
|
plugin: SpaceforgePlugin;
|
|
|
|
/**
|
|
* Initialize batch controller
|
|
*
|
|
* @param plugin Reference to the main plugin
|
|
*/
|
|
constructor(plugin: SpaceforgePlugin) {
|
|
this.plugin = plugin;
|
|
}
|
|
|
|
/**
|
|
* Start reviewing all of today's notes
|
|
*/
|
|
async reviewAllTodaysNotes(): Promise<void> {
|
|
const reviewController = this.plugin.reviewController;
|
|
if (!reviewController) return;
|
|
|
|
// Update notes while preserving existing order
|
|
await reviewController.updateTodayNotes(true);
|
|
|
|
const todayNotes = reviewController.getTodayNotes();
|
|
if (todayNotes.length === 0) {
|
|
new Notice("No notes due for review today!");
|
|
return;
|
|
}
|
|
|
|
|
|
// Always start with first note in current order
|
|
// We need to update the core controller's state
|
|
if (reviewController) {
|
|
// Force update to ensure we start at the first note
|
|
await reviewController.updateTodayNotes(false);
|
|
|
|
const note = todayNotes[0];
|
|
|
|
// Start the review with the selected note
|
|
await reviewController.reviewNote(note.path);
|
|
}
|
|
|
|
new Notice(`Starting review of all ${todayNotes.length} notes due today`);
|
|
}
|
|
|
|
/**
|
|
* Review a specific set of notes
|
|
*
|
|
* @param paths Array of note paths to review
|
|
* @param useMCQ Whether to use MCQs for testing (default: false)
|
|
*/
|
|
async reviewNotes(paths: string[], useMCQ: boolean = false): Promise<void> {
|
|
if (paths.length === 0) {
|
|
new Notice("No notes selected for review.");
|
|
return;
|
|
}
|
|
|
|
// Get the ReviewSchedule objects for the given paths
|
|
const notesToReview = this.plugin.dataStorage.getDueNotesWithCustomOrder().filter(note => paths.includes(note.path));
|
|
|
|
if (notesToReview.length === 0) {
|
|
new Notice("Selected notes are not currently due for review.");
|
|
return;
|
|
}
|
|
|
|
if (useMCQ) {
|
|
new Notice(`Preparing MCQs for ${notesToReview.length} selected notes. This may take a moment...`);
|
|
} else {
|
|
new Notice(`Starting review of ${notesToReview.length} selected notes.`);
|
|
}
|
|
|
|
// Create a new batch review modal with the selected notes
|
|
const modal = new BatchReviewModal(this.plugin.app, this.plugin, notesToReview, useMCQ);
|
|
modal.open();
|
|
}
|
|
|
|
/**
|
|
* Review all notes with MCQs in a batch
|
|
*
|
|
* @param useMCQ Whether to use MCQs for testing
|
|
*/
|
|
async reviewAllNotesWithMCQ(useMCQ: boolean = true): Promise<void> {
|
|
const reviewController = this.plugin.reviewController;
|
|
if (!reviewController) return;
|
|
|
|
// Update today's notes first, preserving custom order if it exists
|
|
const hasCustomOrder = this.plugin.reviewScheduleService.customNoteOrder.length > 0;
|
|
await reviewController.updateTodayNotes(hasCustomOrder);
|
|
|
|
const todayNotes = reviewController.getTodayNotes();
|
|
if (todayNotes.length === 0) {
|
|
new Notice("No notes due for review today!");
|
|
return;
|
|
}
|
|
|
|
if (useMCQ) {
|
|
new Notice("Preparing all MCQs. This may take a moment...");
|
|
}
|
|
|
|
// Use notes directly from todayNotes - they're already in the correct order
|
|
// Custom order is already applied by updateTodayNotes if available
|
|
const orderedNotes = [...todayNotes];
|
|
|
|
// Create a new batch review modal with the ordered notes
|
|
const modal = new BatchReviewModal(this.plugin.app, this.plugin, orderedNotes, useMCQ);
|
|
modal.open();
|
|
}
|
|
|
|
/**
|
|
* Regenerate MCQs for all notes due today
|
|
*/
|
|
async regenerateAllMCQs(): Promise<void> {
|
|
const reviewController = this.plugin.reviewController;
|
|
if (!reviewController) return;
|
|
|
|
// Update notes respecting custom order if it exists
|
|
const hasCustomOrder = this.plugin.reviewScheduleService.customNoteOrder.length > 0;
|
|
await reviewController.updateTodayNotes(hasCustomOrder);
|
|
|
|
const todayNotes = reviewController.getTodayNotes();
|
|
if (todayNotes.length === 0) {
|
|
new Notice("No notes due for review today!");
|
|
return;
|
|
}
|
|
|
|
if (!this.plugin.mcqController) {
|
|
new Notice("MCQ controller not initialized. Please check MCQ settings.");
|
|
return;
|
|
}
|
|
|
|
new Notice(`Regenerating MCQs for ${todayNotes.length} notes...`);
|
|
|
|
let generatedCount = 0;
|
|
for (const note of todayNotes) {
|
|
const success = await this.plugin.mcqController.generateMCQs(note.path);
|
|
if (success) {
|
|
generatedCount++;
|
|
}
|
|
}
|
|
|
|
new Notice(`Generated MCQs for ${generatedCount} out of ${todayNotes.length} notes`);
|
|
|
|
// Start batch review with the newly generated MCQs
|
|
this.reviewAllNotesWithMCQ(true);
|
|
}
|
|
|
|
/**
|
|
* Postpone a specific set of notes
|
|
*
|
|
* @param paths Array of note paths to postpone
|
|
* @param days Number of days to postpone (default: 1)
|
|
*/
|
|
async postponeNotes(paths: string[], days: number = 1): Promise<void> {
|
|
const reviewController = this.plugin.reviewController;
|
|
if (!reviewController) return;
|
|
|
|
if (paths.length === 0) {
|
|
new Notice("No notes selected to postpone.");
|
|
return;
|
|
}
|
|
|
|
new Notice(`Postponing ${paths.length} notes by ${days} day(s)...`);
|
|
for (const path of paths) {
|
|
await this.plugin.dataStorage.postponeNote(path, days);
|
|
// No need to call handleNotePostponed here for each note,
|
|
// as a single refresh of the sidebar after the loop is sufficient.
|
|
}
|
|
await this.plugin.savePluginData(); // Add save call after loop
|
|
|
|
// Refresh the sidebar view if available
|
|
this.plugin.getSidebarView()?.refresh();
|
|
|
|
// Update today's notes after postponing
|
|
await reviewController.updateTodayNotes(true);
|
|
|
|
new Notice(`Postponed ${paths.length} notes by ${days} day(s).`);
|
|
}
|
|
|
|
/**
|
|
* Advance a specific set of notes by one day each, if eligible.
|
|
*
|
|
* @param paths Array of note paths to advance
|
|
*/
|
|
async advanceNotes(paths: string[]): Promise<void> {
|
|
const reviewController = this.plugin.reviewController;
|
|
if (!reviewController) return;
|
|
|
|
if (paths.length === 0) {
|
|
new Notice("No notes selected to advance.");
|
|
return;
|
|
}
|
|
|
|
let advancedCount = 0;
|
|
for (const path of paths) {
|
|
// Directly call the core controller's advanceNote, which handles eligibility and notices
|
|
// The service's advanceNote returns a boolean indicating success
|
|
const advanced = await this.plugin.dataStorage.reviewScheduleService.advanceNote(path);
|
|
if (advanced) {
|
|
advancedCount++;
|
|
}
|
|
}
|
|
|
|
if (advancedCount > 0) {
|
|
await this.plugin.savePluginData(); // Save once after all operations
|
|
|
|
// Refresh the sidebar view if available
|
|
this.plugin.getSidebarView()?.refresh();
|
|
// Update today's notes after advancing
|
|
await reviewController.updateTodayNotes(true);
|
|
new Notice(`Advanced ${advancedCount} note(s).`);
|
|
} else {
|
|
new Notice("No eligible notes were advanced.");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Remove a specific set of notes from the review schedule
|
|
*
|
|
* @param paths Array of note paths to remove
|
|
*/
|
|
async removeNotes(paths: string[]): Promise<void> {
|
|
const reviewController = this.plugin.reviewController;
|
|
if (!reviewController) return;
|
|
|
|
if (paths.length === 0) {
|
|
new Notice("No notes selected to remove.");
|
|
return;
|
|
}
|
|
|
|
new Notice(`Removing ${paths.length} notes from review schedule...`);
|
|
for (const path of paths) {
|
|
await this.plugin.dataStorage.removeFromReview(path);
|
|
// No need to call handleNotePostponed here for each note,
|
|
// as a single refresh of the sidebar after the loop is sufficient.
|
|
}
|
|
await this.plugin.savePluginData(); // Add save call after loop
|
|
|
|
// Refresh the sidebar view if available
|
|
this.plugin.getSidebarView()?.refresh();
|
|
|
|
// Update today's notes after removing
|
|
await reviewController.updateTodayNotes(true);
|
|
|
|
new Notice(`Removed ${paths.length} notes from review schedule.`);
|
|
}
|
|
}
|