diff --git a/package.json b/package.json index 89339c5..62334e4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "my-weekly-todo-list", - "version": "1.16.0", + "version": "1.16.1", "description": "A web-based weekly task management application that organizes to-dos and calendar events in a single, intuitive weekly view", "main": "index.js", "scripts": { diff --git a/src/app/api/tasks/route.ts b/src/app/api/tasks/route.ts index 7768e09..383cfe4 100644 --- a/src/app/api/tasks/route.ts +++ b/src/app/api/tasks/route.ts @@ -287,6 +287,21 @@ export async function POST(request: NextRequest) { externalProvider = 'outlook'; externalListId = somedayList.externalId; } + } else if (somedayList.externalProvider === 'synology') { + const { createSynologyTask } = await import('@/lib/synology-tasks'); + const synoConnection = await prisma.calendarConnection.findFirst({ + where: { userId, provider: 'synology' } + }); + if (synoConnection?.accessToken && synoConnection?.refreshToken) { + const [synoUsername, synoPassword] = synoConnection.accessToken.split(':'); + const synoServerUrl = synoConnection.refreshToken; + if (synoUsername && synoPassword && synoServerUrl) { + const synoTask = await createSynologyTask(synoServerUrl, synoUsername, synoPassword, somedayList.externalId, { title }); + externalId = synoTask.id; + externalProvider = 'synology'; + externalListId = somedayList.externalId; + } + } } } catch (syncError) { console.error('Failed to sync new task to external provider:', syncError); diff --git a/src/app/globals.css b/src/app/globals.css index 8800a50..ddbc0fe 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -1458,7 +1458,8 @@ h3 { color: var(--weekly-text, #222); background: transparent; border: none; - width: 100%; + flex: 1; + min-width: 0; padding: 4px 0; outline: none; transition: opacity 0.15s ease; diff --git a/src/lib/synology-tasks.ts b/src/lib/synology-tasks.ts index 886cfde..f62b7eb 100644 --- a/src/lib/synology-tasks.ts +++ b/src/lib/synology-tasks.ts @@ -104,6 +104,59 @@ export const createSynologyReminderList = async (serverUrl: string, username: st } }; +/** + * Create a new task (VTODO) in a Synology list via CalDAV + */ +export const createSynologyTask = async ( + serverUrl: string, username: string, password: string, + listId: string, task: { title: string; notes?: string; due?: string | null } +): Promise<{ id: string }> => { + const client = createClient(serverUrl, username, password); + await client.login(); + + const calendars = await client.fetchCalendars(); + const getPath = (url: string) => { + try { return new URL(url).pathname; } catch { return url; } + }; + const targetCalendar = calendars.find(c => getPath(c.url) === getPath(listId)); + if (!targetCalendar) throw new Error(`List not found: ${listId}`); + + const uid = crypto.randomUUID(); + const now = ICAL.Time.now(); + + const vcalendar = new ICAL.Component(['vcalendar', [], []]); + vcalendar.updatePropertyWithValue('prodid', '-//My Weekly ToDo List//EN'); + vcalendar.updatePropertyWithValue('version', '2.0'); + + const vtodo = new ICAL.Component('vtodo'); + vtodo.updatePropertyWithValue('uid', uid); + vtodo.updatePropertyWithValue('summary', task.title); + vtodo.updatePropertyWithValue('created', now); + vtodo.updatePropertyWithValue('dtstamp', now); + vtodo.updatePropertyWithValue('status', 'NEEDS-ACTION'); + + if (task.notes) { + vtodo.updatePropertyWithValue('description', task.notes); + } + if (task.due) { + const dueTime = ICAL.Time.fromJSDate(new Date(task.due), false); + vtodo.updatePropertyWithValue('due', dueTime); + } + + vcalendar.addSubcomponent(vtodo); + + const calendarUrl = targetCalendar.url.replace(/\/$/, ''); + const objectUrl = `${calendarUrl}/${uid}.ics`; + + await client.createCalendarObject({ + calendar: targetCalendar, + filename: `${uid}.ics`, + iCalString: vcalendar.toString(), + }); + + return { id: `synology::${uid}` }; +}; + /** * Fetch tasks (VTODOs) from a specific Synology list */