Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 64 additions & 30 deletions src/bootstrap/pluginBootstrap.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
import { Editor, MarkdownView, Notice, Platform, addIcon } from "obsidian";
import { EditorView } from "@codemirror/view";
import type TaskNotesPlugin from "../main";
import { EVENT_DATA_CHANGED, EVENT_TASK_UPDATED, POMODORO_STATS_VIEW_TYPE, POMODORO_VIEW_TYPE, STATS_VIEW_TYPE, TaskInfo } from "../types";
import {
EVENT_DATA_CHANGED,
EVENT_TASK_UPDATED,
POMODORO_STATS_VIEW_TYPE,
POMODORO_VIEW_TYPE,
STATS_VIEW_TYPE,
TaskInfo,
} from "../types";
import { RequestDeduplicator, PredictivePrefetcher } from "../utils/RequestDeduplicator";
import { DOMReconciler, UIStateManager } from "../utils/DOMReconciler";
import { FieldMapper } from "../services/FieldMapper";
Expand Down Expand Up @@ -43,6 +50,10 @@ import { MicrosoftCalendarService } from "../services/MicrosoftCalendarService";
import { CalendarProviderRegistry } from "../services/CalendarProvider";
import { PomodoroService } from "../services/PomodoroService";
import { AutoExportService } from "../services/AutoExportService";
import { TaskSelectionService } from "../services/TaskSelectionService";
import { BasesFilterConverter } from "../services/BasesFilterConverter";
import { MdbaseSpecService } from "../services/MdbaseSpecService";
import { registerBasesTaskList } from "../bases/registration";

type FileDeletedEventData = { path: string; prevCache?: unknown };

Expand Down Expand Up @@ -100,17 +111,14 @@ export async function initializeCoreServices(plugin: TaskNotesPlugin): Promise<v
plugin.expandedProjectsService = new ExpandedProjectsService(plugin);
plugin.autoArchiveService = new AutoArchiveService(plugin);

const { TaskSelectionService } = await import("../services/TaskSelectionService");
plugin.taskSelectionService = new TaskSelectionService(plugin);
plugin.dragDropManager = new DragDropManager(plugin);
plugin.statusBarService = new StatusBarService(plugin);
plugin.notificationService = new NotificationService(plugin);
plugin.viewPerformanceService = new ViewPerformanceService(plugin);

const { BasesFilterConverter } = await import("../services/BasesFilterConverter");
plugin.basesFilterConverter = new BasesFilterConverter(plugin);

const { MdbaseSpecService } = await import("../services/MdbaseSpecService");
plugin.mdbaseSpecService = new MdbaseSpecService(plugin);

plugin.icsSubscriptionService = new ICSSubscriptionService(plugin);
Expand All @@ -119,25 +127,41 @@ export async function initializeCoreServices(plugin: TaskNotesPlugin): Promise<v
}

export function registerRibbonIcons(plugin: TaskNotesPlugin): void {
plugin.addRibbonIcon("calendar-days", plugin.i18n.translate("commands.openCalendarView"), async () => {
await plugin.activateCalendarView();
});
plugin.addRibbonIcon(
"calendar-days",
plugin.i18n.translate("commands.openCalendarView"),
async () => {
await plugin.activateCalendarView();
}
);

plugin.addRibbonIcon("calendar", plugin.i18n.translate("commands.openAdvancedCalendarView"), async () => {
await plugin.openBasesFileForCommand("open-advanced-calendar-view");
});
plugin.addRibbonIcon(
"calendar",
plugin.i18n.translate("commands.openAdvancedCalendarView"),
async () => {
await plugin.openBasesFileForCommand("open-advanced-calendar-view");
}
);

plugin.addRibbonIcon("check-square", plugin.i18n.translate("commands.openTasksView"), async () => {
await plugin.openBasesFileForCommand("open-tasks-view");
});
plugin.addRibbonIcon(
"check-square",
plugin.i18n.translate("commands.openTasksView"),
async () => {
await plugin.openBasesFileForCommand("open-tasks-view");
}
);

plugin.addRibbonIcon("list", plugin.i18n.translate("commands.openAgendaView"), async () => {
await plugin.openBasesFileForCommand("open-agenda-view");
});

plugin.addRibbonIcon("columns-3", plugin.i18n.translate("commands.openKanbanView"), async () => {
await plugin.openBasesFileForCommand("open-kanban-view");
});
plugin.addRibbonIcon(
"columns-3",
plugin.i18n.translate("commands.openKanbanView"),
async () => {
await plugin.openBasesFileForCommand("open-kanban-view");
}
);

plugin.addRibbonIcon("timer", plugin.i18n.translate("commands.openPomodoroView"), async () => {
await plugin.activatePomodoroView();
Expand All @@ -151,9 +175,13 @@ export function registerRibbonIcons(plugin: TaskNotesPlugin): void {
}
);

plugin.addRibbonIcon("tasknotes-simple", plugin.i18n.translate("commands.createNewTask"), () => {
plugin.openTaskCreationModal();
});
plugin.addRibbonIcon(
"tasknotes-simple",
plugin.i18n.translate("commands.createNewTask"),
() => {
plugin.openTaskCreationModal();
}
);
}

export function initializeCalendarProviders(plugin: TaskNotesPlugin): void {
Expand All @@ -165,14 +193,14 @@ export function initializeCalendarProviders(plugin: TaskNotesPlugin): void {
plugin.calendarProviderRegistry.register(plugin.microsoftCalendarService);
}

export async function registerBasesIntegration(plugin: TaskNotesPlugin): Promise<void> {
export function registerBasesIntegration(plugin: TaskNotesPlugin): void {
if (!plugin.settings?.enableBases || plugin.basesRegistered) {
return;
}

try {
const { registerBasesTaskList } = await import("../bases/registration");
await registerBasesTaskList(plugin);
// Call without await to prevent blocking if it enters its retry loop
registerBasesTaskList(plugin).catch((e) => console.debug(e));
plugin.basesRegistered = true;
} catch (error) {
console.debug("[TaskNotes][Bases] Registration failed:", error);
Expand Down Expand Up @@ -268,8 +296,9 @@ export function initializeServicesLazily(plugin: TaskNotesPlugin): void {
});
await plugin.googleCalendarService.initialize();

plugin.taskCalendarSyncService = new (await import("../services/TaskCalendarSyncService"))
.TaskCalendarSyncService(plugin, plugin.googleCalendarService);
plugin.taskCalendarSyncService = new (
await import("../services/TaskCalendarSyncService")
).TaskCalendarSyncService(plugin, plugin.googleCalendarService);

plugin.registerEvent(
plugin.emitter.on("file-deleted", (data: FileDeletedEventData) => {
Expand All @@ -278,7 +307,9 @@ export function initializeServicesLazily(plugin: TaskNotesPlugin): void {
}

const eventIdKey = plugin.fieldMapper.toUserField("googleCalendarEventId");
const prevCache = data.prevCache as { frontmatter?: Record<string, unknown> } | undefined;
const prevCache = data.prevCache as
| { frontmatter?: Record<string, unknown> }
| undefined;
const eventId = prevCache?.frontmatter?.[eventIdKey];

if (typeof eventId === "string" && eventId.length > 0) {
Expand All @@ -301,12 +332,12 @@ export function initializeServicesLazily(plugin: TaskNotesPlugin): void {

await initializeHTTPAPI(plugin);

const { TaskLinkDetectionService } = await import("../services/TaskLinkDetectionService");
const { TaskLinkDetectionService } =
await import("../services/TaskLinkDetectionService");
plugin.taskLinkDetectionService = new TaskLinkDetectionService(plugin);

const { InstantTaskConvertService } = await import(
"../services/InstantTaskConvertService"
);
const { InstantTaskConvertService } =
await import("../services/InstantTaskConvertService");
plugin.instantTaskConvertService = new InstantTaskConvertService(
plugin,
plugin.statusManager,
Expand All @@ -324,7 +355,10 @@ export function initializeServicesLazily(plugin: TaskNotesPlugin): void {
const editor = (leaf.view as MarkdownView).editor;
if (editor && (editor as Editor & { cm?: EditorView }).cm) {
const taskPath = data?.path || data?.updatedTask?.path;
dispatchTaskUpdate((editor as Editor & { cm: EditorView }).cm, taskPath);
dispatchTaskUpdate(
(editor as Editor & { cm: EditorView }).cm,
taskPath
);
}
}
});
Expand Down
53 changes: 29 additions & 24 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,7 @@ import {
initializeCalendarProviders,
registerBasesIntegration,
} from "./bootstrap/pluginBootstrap";
import {
cleanupPluginRuntime,
initializePluginRuntime,
} from "./bootstrap/pluginRuntime";
import { cleanupPluginRuntime, initializePluginRuntime } from "./bootstrap/pluginRuntime";

export default class TaskNotesPlugin extends Plugin {
settings: TaskNotesSettings;
Expand Down Expand Up @@ -240,7 +237,8 @@ export default class TaskNotesPlugin extends Plugin {
getSystemLocale: () => this.getSystemUILocale(),
});

this.i18n.on("locale-changed", ({ current }) => {
this.i18n.on("locale-changed", (event: any) => {
const current: string = event.current;
if (!this.initializationComplete) {
return;
}
Expand All @@ -256,7 +254,7 @@ export default class TaskNotesPlugin extends Plugin {
this.migrationPromise = this.performEarlyMigrationCheck();

initializeCalendarProviders(this);
await registerBasesIntegration(this);
registerBasesIntegration(this);

// Defer expensive initialization until layout is ready
this.app.workspace.onLayoutReady(() => {
Expand Down Expand Up @@ -574,7 +572,11 @@ export default class TaskNotesPlugin extends Plugin {
}

// Migration: Migrate statusSuggestionTrigger to nlpTriggers if needed
if (loadedData && !loadedData.nlpTriggers && loadedData.statusSuggestionTrigger !== undefined) {
if (
loadedData &&
!loadedData.nlpTriggers &&
loadedData.statusSuggestionTrigger !== undefined
) {
// eslint-disable-next-line @typescript-eslint/no-require-imports
const { DEFAULT_NLP_TRIGGERS } = require("./settings/defaults");
loadedData.nlpTriggers = {
Expand All @@ -594,10 +596,7 @@ export default class TaskNotesPlugin extends Plugin {
if (loadedData && !loadedData.modalFieldsConfig) {
// eslint-disable-next-line @typescript-eslint/no-require-imports
const { initializeFieldConfig } = require("./utils/fieldConfigDefaults");
loadedData.modalFieldsConfig = initializeFieldConfig(
undefined,
loadedData.userFields
);
loadedData.modalFieldsConfig = initializeFieldConfig(undefined, loadedData.userFields);
}

// Migration: Force enableBases to true (issue #1187)
Expand Down Expand Up @@ -641,7 +640,8 @@ export default class TaskNotesPlugin extends Plugin {
nlpTriggers: {
...DEFAULT_SETTINGS.nlpTriggers,
...(loadedData?.nlpTriggers || {}),
triggers: loadedData?.nlpTriggers?.triggers || DEFAULT_SETTINGS.nlpTriggers.triggers,
triggers:
loadedData?.nlpTriggers?.triggers || DEFAULT_SETTINGS.nlpTriggers.triggers,
},
// Modal fields configuration (already migrated above if needed)
modalFieldsConfig: loadedData?.modalFieldsConfig,
Expand Down Expand Up @@ -757,16 +757,13 @@ export default class TaskNotesPlugin extends Plugin {

if (created.length > 0) {
new Notice(
`Created ${created.length} default Bases file(s):\n${created.join('\n')}`,
`Created ${created.length} default Bases file(s):\n${created.join("\n")}`,
8000
);
}

if (skipped.length > 0 && created.length === 0) {
new Notice(
`Default Bases files already exist:\n${skipped.join('\n')}`,
8000
);
new Notice(`Default Bases files already exist:\n${skipped.join("\n")}`, 8000);
}
}

Expand Down Expand Up @@ -838,7 +835,8 @@ export default class TaskNotesPlugin extends Plugin {

// Only create folder hierarchy if we're actually creating the file
const lastSlashIndex = normalizedPath.lastIndexOf("/");
const directory = lastSlashIndex >= 0 ? normalizedPath.substring(0, lastSlashIndex) : "";
const directory =
lastSlashIndex >= 0 ? normalizedPath.substring(0, lastSlashIndex) : "";

if (directory) {
// eslint-disable-next-line no-await-in-loop
Expand Down Expand Up @@ -1162,13 +1160,19 @@ export default class TaskNotesPlugin extends Plugin {
due: frontmatter.due || undefined,
scheduled: frontmatter.scheduled || undefined,
contexts: frontmatter.contexts
? (Array.isArray(frontmatter.contexts) ? frontmatter.contexts : [frontmatter.contexts])
? Array.isArray(frontmatter.contexts)
? frontmatter.contexts
: [frontmatter.contexts]
: undefined,
projects: frontmatter.projects
? (Array.isArray(frontmatter.projects) ? frontmatter.projects : [frontmatter.projects])
? Array.isArray(frontmatter.projects)
? frontmatter.projects
: [frontmatter.projects]
: undefined,
tags: frontmatter.tags
? (Array.isArray(frontmatter.tags) ? frontmatter.tags : [frontmatter.tags])
? Array.isArray(frontmatter.tags)
? frontmatter.tags
: [frontmatter.tags]
: [],
timeEstimate: frontmatter.timeEstimate || undefined,
recurrence: frontmatter.recurrence || undefined,
Expand Down Expand Up @@ -1362,7 +1366,8 @@ export default class TaskNotesPlugin extends Plugin {
private async openTaskDatePicker(task: TaskInfo, field: "due" | "scheduled") {
try {
const { DateTimePickerModal } = await import("./modals/DateTimePickerModal");
const { getDatePart, getTimePart, combineDateAndTime } = await import("./utils/dateUtils");
const { getDatePart, getTimePart, combineDateAndTime } =
await import("./utils/dateUtils");
const currentValue = (field === "due" ? task.due : task.scheduled) || "";
const modal = new DateTimePickerModal(this.app, {
currentDate: getDatePart(currentValue) || null,
Expand Down Expand Up @@ -1645,7 +1650,8 @@ export default class TaskNotesPlugin extends Plugin {
// Open task creation modal with callback to insert link
// Use modal-inline-creation context for inline folder behavior (Issue #1424)
const modal = new TaskCreationModal(this.app, this, {
prePopulatedValues: Object.keys(prePopulatedValues).length > 0 ? prePopulatedValues : undefined,
prePopulatedValues:
Object.keys(prePopulatedValues).length > 0 ? prePopulatedValues : undefined,
onTaskCreated: (task: TaskInfo) => {
this.handleInlineTaskCreated(task, insertionContext);
},
Expand Down Expand Up @@ -1704,5 +1710,4 @@ export default class TaskNotesPlugin extends Plugin {
new Notice("Failed to insert task link");
}
}

}