feat: full Synology bidirectional sync and fix someday list delete button
- Add createSynologyTask: new tasks in synced lists push to Synology CalDAV - Wire Synology create into POST /api/tasks when somedayList is synced - Fix someday list title input using width:100% instead of flex:1, which pushed the delete button off-screen making lists undeletable - Slightly improved provider icon styling v1.16.1 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
fe41c153f7
commit
2ec09fdcca
@ -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": {
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
*/
|
||||
|
||||
Loading…
Reference in New Issue
Block a user