8.9 KiB
Issue #859 Analysis: Multiple Days Task Glitches in Calendar View
Problem Understanding
Issue Description
When creating a multi-day task by dragging in the calendar view, the task duration behaves unexpectedly. Users report that when they drag to create a 2-day task, it spreads to an entire week, several weeks, or any random amount of days. This happens in both the base calendar view (Bases integration) and the old calendar view (AdvancedCalendarView).
Root Cause
The bug is only in the Bases calendar view (src/bases/calendar-view.ts), not in AdvancedCalendarView. The issue occurs because the handleDateSelect function in the Bases calendar view doesn't calculate the timeEstimate property for multi-day all-day task selections, unlike AdvancedCalendarView which does.
When a user drags across multiple days:
- The Bases calendar view only sets the
scheduleddate to the start date - It does NOT calculate or set the
timeEstimatebased on the number of days dragged - The TaskCreationModal then either uses a default time estimate or shows an incorrect duration
- When the task is displayed back on the calendar, the end date is calculated from
start + timeEstimate, which doesn't match the user's intended selection
The AdvancedCalendarView (old calendar view) already has the fix implemented (see issue #564), but this fix was never ported to the Bases calendar view.
Evidence
Comparing the two implementations:
AdvancedCalendarView (src/views/AdvancedCalendarView.ts:1182-1225):
- ✅ Calculates
daysDurationfor multi-day all-day selections - ✅ Sets
timeEstimate = daysDuration * minutesPerDaywhendaysDuration > 1 - ✅ Handles single-day selections correctly (no time estimate)
- ✅ Handles timed selections correctly
Bases Calendar View (src/bases/calendar-view.ts:108-139):
- ❌ Does NOT calculate duration for multi-day selections
- ❌ Only sets
scheduleddate, ignoring the dragged duration - ❌ Missing the entire multi-day calculation logic
Test File Location
Test File: tests/unit/issues/issue-859-bases-calendar-multi-day-task.test.ts
How to Run:
npm test -- tests/unit/issues/issue-859-bases-calendar-multi-day-task.test.ts
The test demonstrates:
- The current buggy behavior (timeEstimate is undefined for multi-day selections)
- The expected fixed behavior (timeEstimate should be set to
daysDuration * 1440minutes)
Relevant Code Locations
Primary Bug Location
- File:
src/bases/calendar-view.ts - Function:
handleDateSelect(lines 108-139) - Issue: Missing multi-day duration calculation logic
Reference Implementation (Working)
- File:
src/views/AdvancedCalendarView.ts - Function:
handleTaskCreation(lines 1182-1225) - Status: Already fixed for issue #564
Related Test Files
- Existing test:
tests/unit/issues/issue-564-multi-day-task-creation.test.ts(shows correct behavior) - New test:
tests/unit/issues/issue-859-bases-calendar-multi-day-task.test.ts(demonstrates bug)
Shared Calendar Core
- File:
src/bases/calendar-core.ts - Relevance: Contains shared event generation logic but not task creation logic
Proposed Solutions
Solution 1: Port the Fix from AdvancedCalendarView (Recommended)
Approach: Copy the multi-day duration calculation logic from AdvancedCalendarView.handleTaskCreation to calendar-view.ts:handleDateSelect.
Implementation:
// In calendar-view.ts, replace lines 125-133 with:
const scheduledDate = allDay
? format(start, "yyyy-MM-dd")
: format(start, "yyyy-MM-dd'T'HH:mm");
const durationMinutes = Math.round((end.getTime() - start.getTime()) / (1000 * 60));
// Convert slot duration setting to minutes for comparison
const slotDurationMinutes = 30; // Default from settings
// Determine if this was a drag (intentional time selection) or just a click
const isDragOperation = !allDay && durationMinutes > slotDurationMinutes;
const prePopulatedValues: any = {
scheduled: scheduledDate
};
// Calculate duration for multi-day all-day selections
if (allDay) {
const dayDurationMillis = 24 * 60 * 60 * 1000;
const daysDuration = Math.round((end.getTime() - start.getTime()) / dayDurationMillis);
if (daysDuration > 1) {
const minutesPerDay = 60 * 24;
prePopulatedValues.timeEstimate = daysDuration * minutesPerDay;
}
} else if (isDragOperation) {
prePopulatedValues.timeEstimate = durationMinutes;
}
const { TaskCreationModal } = require("../modals/TaskCreationModal");
const modal = new TaskCreationModal(plugin.app, plugin, {
prePopulatedValues
});
modal.open();
Pros:
- ✅ Direct fix that matches the working implementation
- ✅ Consistent behavior across both calendar views
- ✅ Minimal code changes (only modifying
handleDateSelect) - ✅ Already proven to work in AdvancedCalendarView
- ✅ Easy to test and verify
Cons:
- ⚠️ Requires accessing
plugin.settings.calendarViewSettings.slotDurationwhich may need to be passed to the factory function - ⚠️ Minor code duplication between two calendar views
Risk: Low - This is a proven fix already working in production
Solution 2: Extract to Shared Function
Approach: Create a shared function in calendar-core.ts for task creation logic, then use it in both calendar views.
Implementation:
- Create
calculateTaskCreationValues(start: Date, end: Date, allDay: boolean, slotDurationMinutes: number)incalendar-core.ts - Use this function in both
AdvancedCalendarView.handleTaskCreationandcalendar-view.ts:handleDateSelect
Pros:
- ✅ DRY principle - single source of truth
- ✅ Easier to maintain and test
- ✅ Any future fixes apply to both views automatically
- ✅ Better code organization
Cons:
- ⚠️ Requires refactoring both calendar views
- ⚠️ More extensive testing needed
- ⚠️ Larger changeset with higher risk of regression
Risk: Medium - More extensive changes but better long-term solution
Solution 3: Minimal Patch (Quick Fix)
Approach: Add just the multi-day calculation without the full drag detection logic.
Implementation:
// In calendar-view.ts, after line 128:
const scheduledDate = allDay
? format(start, "yyyy-MM-dd")
: format(start, "yyyy-MM-dd'T'HH:mm");
const prePopulatedValues: any = {
scheduled: scheduledDate
};
// Quick fix: add multi-day duration calculation
if (allDay) {
const daysDuration = Math.round((end.getTime() - start.getTime()) / (24 * 60 * 60 * 1000));
if (daysDuration > 1) {
prePopulatedValues.timeEstimate = daysDuration * 60 * 24;
}
}
Pros:
- ✅ Minimal code change
- ✅ Very low risk
- ✅ Fast to implement and test
Cons:
- ❌ Doesn't handle timed task duration (week view drags)
- ❌ Missing drag vs. click detection logic
- ❌ Incomplete fix - only solves all-day multi-day tasks
- ❌ Still inconsistent with AdvancedCalendarView
Risk: Low, but incomplete solution
Recommended Approach
Solution 1: Port the Fix from AdvancedCalendarView
This is the recommended approach because:
- Proven Solution: The fix already works in AdvancedCalendarView since issue #564
- Complete Fix: Handles all cases (multi-day, single-day, timed, clicks)
- Low Risk: Minimal changes to a well-understood bug
- Fast Implementation: Can be done in a single commit
- Easy to Test: Can verify against existing test patterns
Implementation Steps
- Locate the
handleDateSelectfunction insrc/bases/calendar-view.ts:108 - Replace lines 126-133 with the duration calculation logic from AdvancedCalendarView
- Access the slot duration setting from plugin settings (may need to pass it to the factory)
- Run the test suite to verify the fix
- Manually test both calendar views to ensure consistency
Testing Strategy
- Run unit test:
npm test -- tests/unit/issues/issue-859-bases-calendar-multi-day-task.test.ts - Run existing test:
npm test -- tests/unit/issues/issue-564-multi-day-task-creation.test.ts - Manual testing in Bases calendar view:
- Drag to create 2-day task → should have 2880 minutes time estimate
- Drag to create 7-day task → should have 10080 minutes time estimate
- Click single day → should use default time estimate
- Drag timed selection → should calculate duration in minutes
- Verify AdvancedCalendarView still works correctly
Edge Cases to Consider
- User drags across daylight saving time boundaries
- User drags across month/year boundaries
- Very long selections (30+ days)
- Selections that start/end at non-midnight times but are marked allDay
Alternative Considerations
If Solution 2 (Extract to Shared Function) is preferred for long-term maintainability:
- Implement Solution 1 first as an immediate fix
- Create a follow-up task to refactor both views to use shared logic
- This allows the bug to be fixed quickly while planning for better architecture
This two-phase approach balances speed of fix with code quality goals.