feat: add CalDAV synchronization with automatic sync
- Add CaldavService with tsdav/ical.js for CalDAV server communication - Add CaldavController, CaldavRepository, and caldav routes - Add client-side CaldavConfigService with sync(), config CRUD - Add CalDAV settings UI with config load/save in settings screen - Sync on login, auto-login (AuthGuard), periodic timer (calendar), and sync button - Push single events to CalDAV on server-side create/update/delete - Push all events to CalDAV after chat event confirmation - Refactor ChatService to use EventService instead of direct EventRepository - Rename CalDav/calDav to Caldav/caldav for consistent naming - Add Radicale Docker setup for local CalDAV testing - Update PlantUML diagrams and CLAUDE.md with CalDAV architecture
This commit is contained in:
@@ -6,7 +6,7 @@ import {
|
||||
MONTH_TO_GERMAN,
|
||||
ExpandedEvent,
|
||||
} from "@calchat/shared";
|
||||
import { EventRepository } from "../services/interfaces";
|
||||
import { EventService } from "../services/EventService";
|
||||
import { expandRecurringEvents } from "./recurrenceExpander";
|
||||
|
||||
// Private formatting helpers
|
||||
@@ -107,13 +107,13 @@ function formatMonthText(events: ExpandedEvent[], monthName: string): string {
|
||||
* Recurring events are expanded to show all occurrences within the range.
|
||||
*/
|
||||
export async function getWeeksOverview(
|
||||
eventRepo: EventRepository,
|
||||
eventService: EventService,
|
||||
userId: string,
|
||||
weeks: number,
|
||||
): Promise<string> {
|
||||
const now = new Date();
|
||||
const endDate = new Date(now.getTime() + weeks * 7 * 24 * 60 * 60 * 1000);
|
||||
const events = await eventRepo.findByUserId(userId);
|
||||
const events = await eventService.getAll(userId);
|
||||
const expanded = expandRecurringEvents(events, now, endDate);
|
||||
return formatWeeksText(expanded, weeks);
|
||||
}
|
||||
@@ -123,14 +123,14 @@ export async function getWeeksOverview(
|
||||
* Recurring events are expanded to show all occurrences within the month.
|
||||
*/
|
||||
export async function getMonthOverview(
|
||||
eventRepo: EventRepository,
|
||||
eventService: EventService,
|
||||
userId: string,
|
||||
year: number,
|
||||
month: number,
|
||||
): Promise<string> {
|
||||
const startOfMonth = new Date(year, month, 1);
|
||||
const endOfMonth = new Date(year, month + 1, 0, 23, 59, 59);
|
||||
const events = await eventRepo.findByUserId(userId);
|
||||
const events = await eventService.getAll(userId);
|
||||
const expanded = expandRecurringEvents(events, startOfMonth, endOfMonth);
|
||||
const monthName = MONTH_TO_GERMAN[MONTHS[month]];
|
||||
return formatMonthText(expanded, monthName);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { RRule, rrulestr } from "rrule";
|
||||
import { CalendarEvent, ExpandedEvent } from "@calchat/shared";
|
||||
import { CalendarEvent, ExpandedEvent, formatDateKey } from "@calchat/shared";
|
||||
|
||||
// Convert local time to "fake UTC" for rrule
|
||||
// rrule interprets all dates as UTC internally, so we need to trick it
|
||||
@@ -133,11 +133,3 @@ function formatRRuleDateString(date: Date): string {
|
||||
const seconds = String(date.getSeconds()).padStart(2, "0");
|
||||
return `${year}${month}${day}T${hours}${minutes}${seconds}`;
|
||||
}
|
||||
|
||||
// Format date as YYYY-MM-DD for exception date comparison
|
||||
function formatDateKey(date: Date): string {
|
||||
const year = date.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, "0");
|
||||
const day = String(date.getDate()).padStart(2, "0");
|
||||
return `${year}-${month}-${day}`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user