Skip to content

Commit 132d0b9

Browse files
committed
Automatically syncing timeblocks to external calendar
1 parent 7b17a26 commit 132d0b9

14 files changed

Lines changed: 494 additions & 3 deletions

docs/releases/unreleased.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,7 @@ When a change has user-facing documentation, include a canonical tasknotes.dev l
3131
```
3232
3333
-->
34+
35+
## Added
36+
37+
- Added a dedicated Google Calendar integration setting to enable or disable timeblock synchronization independently from task synchronization

src/bases/calendar-core.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1833,6 +1833,20 @@ export async function handleTimeblockDrop(
18331833
newEndTime
18341834
);
18351835

1836+
if (plugin.timeblockCalendarSyncService?.isEnabled()) {
1837+
const updatedTimeblock: TimeBlock = {
1838+
...timeblock,
1839+
startTime: newStartTime,
1840+
endTime: newEndTime,
1841+
};
1842+
1843+
plugin.timeblockCalendarSyncService
1844+
.updateTimeblockInCalendar(updatedTimeblock, newDate, originalDate)
1845+
.catch((error) => {
1846+
console.warn("Failed to sync moved timeblock to Google Calendar:", error);
1847+
});
1848+
}
1849+
18361850
new Notice("Timeblock moved successfully");
18371851
} catch (error: unknown) {
18381852
tasknotesLogger.error("Error moving timeblock:", {
@@ -1877,6 +1891,20 @@ export async function handleTimeblockResize(
18771891
newEndTime
18781892
);
18791893

1894+
if (plugin.timeblockCalendarSyncService?.isEnabled()) {
1895+
const updatedTimeblock: TimeBlock = {
1896+
...timeblock,
1897+
startTime: newStartTime,
1898+
endTime: newEndTime,
1899+
};
1900+
1901+
plugin.timeblockCalendarSyncService
1902+
.updateTimeblockInCalendar(updatedTimeblock, originalDate)
1903+
.catch((error) => {
1904+
console.warn("Failed to sync resized timeblock to Google Calendar:", error);
1905+
});
1906+
}
1907+
18801908
new Notice("Timeblock duration updated");
18811909
} catch (error: unknown) {
18821910
tasknotesLogger.error("Error resizing timeblock:", {

src/bootstrap/pluginBootstrap.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,11 @@ export function initializeServicesLazily(plugin: TaskNotesPlugin): void {
355355
await plugin.taskCalendarSyncService.initializeExternalFileReconciliation();
356356
plugin.taskCalendarSyncService.startRecoveryQueueProcessor();
357357

358+
// Initialize Timeblock Calendar Sync service for pushing timeblocks to Google Calendar
359+
plugin.timeblockCalendarSyncService = new (
360+
await import("../services/TimeblockCalendarSyncService")
361+
).TimeblockCalendarSyncService(plugin, plugin.googleCalendarService);
362+
358363
plugin.registerEvent(
359364
plugin.emitter.on("file-updated", (data: FileUpdatedEventData) => {
360365
if (!plugin.taskCalendarSyncService || !data?.path) {

src/bootstrap/pluginRuntime.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ export async function cleanupPluginRuntime(plugin: TaskNotesPlugin): Promise<voi
7575
void plugin.oauthService?.destroy();
7676
plugin.taskFileLifecycleReconciliationService?.destroy();
7777
plugin.taskCalendarSyncService?.destroy();
78+
plugin.timeblockCalendarSyncService?.destroy();
7879
plugin.googleCalendarService?.destroy();
7980
plugin.microsoftCalendarService?.destroy();
8081
plugin.calendarProviderRegistry?.destroyAll();

src/main.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ import { GoogleCalendarService } from "./services/GoogleCalendarService";
6565
import { MicrosoftCalendarService } from "./services/MicrosoftCalendarService";
6666
import { CalendarProviderRegistry } from "./services/CalendarProvider";
6767
import { TaskCalendarSyncService } from "./services/TaskCalendarSyncService";
68+
import { TimeblockCalendarSyncService } from "./services/TimeblockCalendarSyncService";
6869
import { addTaskToProject, assignTaskAsSubtask } from "./services/taskRelationshipActions";
6970
import {
7071
initializeAfterLayoutReady,
@@ -211,6 +212,8 @@ export default class TaskNotesPlugin extends Plugin {
211212

212213
// Task-to-Google Calendar sync service
213214
taskCalendarSyncService: TaskCalendarSyncService;
215+
// Timeblock-to-Google Calendar sync service
216+
timeblockCalendarSyncService: TimeblockCalendarSyncService;
214217
taskFileLifecycleReconciliationService?: import("./services/TaskFileLifecycleReconciliationService").TaskFileLifecycleReconciliationService;
215218

216219
// mdbase-spec generation service

src/modals/TimeblockCreationModal.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,18 @@ export class TimeblockCreationModal extends Modal {
357357
// Save to daily note
358358
const dailyNote = await this.saveTimeblockToDailyNote(timeblock);
359359

360+
// Sync to Google Calendar if enabled (fire-and-forget to avoid blocking modal)
361+
if (
362+
this.plugin.timeblockCalendarSyncService?.isEnabled() &&
363+
this.plugin.settings.googleCalendarExport.syncOnTaskCreate
364+
) {
365+
this.plugin.timeblockCalendarSyncService
366+
.syncTimeblockToCalendar(timeblock, this.options.date)
367+
.catch((error) => {
368+
console.warn("Failed to sync timeblock to Google Calendar:", error);
369+
});
370+
}
371+
360372
// Refresh calendar views
361373
this.plugin.emitter.trigger("data-changed");
362374
void this.options.onCreated?.({

src/modals/TimeblockInfoModal.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@ export interface TimeBlock {
5151
description?: string;
5252
attachments?: string[];
5353
color?: string;
54-
id?: string;
54+
id: string;
55+
googleCalendarEventId?: string;
5556
}
5657

5758
/**
@@ -469,6 +470,18 @@ export class TimeblockInfoModal extends Modal {
469470
// Save to daily note
470471
await this.updateTimeblockInDailyNote();
471472

473+
// Sync to Google Calendar if enabled (fire-and-forget to avoid blocking modal)
474+
if (
475+
this.plugin.timeblockCalendarSyncService?.isEnabled() &&
476+
this.plugin.settings.googleCalendarExport.syncOnTaskUpdate
477+
) {
478+
this.plugin.timeblockCalendarSyncService
479+
.updateTimeblockInCalendar(this.timeblock, this.timeblockDate)
480+
.catch((error) => {
481+
console.warn("Failed to sync timeblock update to Google Calendar:", error);
482+
});
483+
}
484+
472485
// Signal immediate update before triggering data change
473486
this.onChange?.();
474487

@@ -563,6 +576,15 @@ export class TimeblockInfoModal extends Modal {
563576
if (!confirmed) return;
564577

565578
try {
579+
// Delete from Google Calendar before deleting from daily note so we still have ID linkage
580+
if (this.plugin.timeblockCalendarSyncService?.isEnabled()) {
581+
this.plugin.timeblockCalendarSyncService
582+
.deleteTimeblockFromCalendar(this.timeblock, this.timeblockDate)
583+
.catch((error) => {
584+
console.warn("Failed to delete timeblock from Google Calendar:", error);
585+
});
586+
}
587+
566588
await this.deleteTimeblockFromDailyNote();
567589

568590
// Signal immediate update before triggering data change

0 commit comments

Comments
 (0)