1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-08-06 13:38:28 -04:00

calendar: support tasks/VTODO from dank calendar

This commit is contained in:
bbedward
2026-07-06 12:40:41 -04:00
parent c175f4823a
commit 240adfa28f
4 changed files with 339 additions and 226 deletions
@@ -941,12 +941,16 @@ Item {
section: topBarContent.getWidgetSection(parent) section: topBarContent.getWidgetSection(parent)
parentScreen: barWindow.screen parentScreen: barWindow.screen
onClicked: { onClicked: {
if (powerMenuModalLoader) { if (!powerMenuModalLoader)
powerMenuModalLoader.active = true; return;
if (powerMenuModalLoader.item) { powerMenuModalLoader.active = true;
powerMenuModalLoader.item.openCentered(); if (!powerMenuModalLoader.item)
} return;
if (powerMenuModalLoader.item.shouldBeVisible) {
powerMenuModalLoader.item.close();
return;
} }
powerMenuModalLoader.item.openCentered();
} }
} }
} }
@@ -86,12 +86,10 @@ Rectangle {
} }
function updateSelectedDateEvents() { function updateSelectedDateEvents() {
if (CalendarService && CalendarService.calendarAvailable) { const events = (CalendarService && CalendarService.calendarAvailable) ? CalendarService.getEventsForDate(selectedDate) : [];
const events = CalendarService.getEventsForDate(selectedDate); if (JSON.stringify(events) === JSON.stringify(selectedDateEvents))
selectedDateEvents = events; return;
} else { selectedDateEvents = events;
selectedDateEvents = [];
}
} }
function loadEventsForMonth() { function loadEventsForMonth() {
@@ -276,57 +274,29 @@ Rectangle {
height: 40 height: 40
visible: showEventDetails visible: showEventDetails
Rectangle { DankActionButton {
width: 32 buttonSize: 32
height: 32 iconSize: 14
iconName: "arrow_back"
iconColor: Theme.primary
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left anchors.left: parent.left
anchors.leftMargin: Theme.spacingS anchors.leftMargin: Theme.spacingS
radius: Theme.cornerRadius onClicked: root.showEventDetails = false
color: backButtonArea.containsMouse ? Theme.primaryHover : Theme.withAlpha(Theme.primaryHover, 0)
DankIcon {
anchors.centerIn: parent
name: "arrow_back"
size: 14
color: Theme.primary
}
MouseArea {
id: backButtonArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: root.showEventDetails = false
}
} }
Rectangle { DankActionButton {
width: 32 buttonSize: 32
height: 32 iconSize: 16
iconName: "event"
iconColor: Theme.primary
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right anchors.right: parent.right
anchors.rightMargin: Theme.spacingS anchors.rightMargin: Theme.spacingS
radius: Theme.cornerRadius
visible: CalendarService && CalendarService.canCreateEvents visible: CalendarService && CalendarService.canCreateEvents
color: addEventArea.containsMouse ? Theme.primaryHover : Theme.withAlpha(Theme.primaryHover, 0) onClicked: {
root.editorEvent = null;
DankIcon { root.showEditor = true;
anchors.centerIn: parent
name: "event"
size: 16
color: Theme.primary
}
MouseArea {
id: addEventArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
root.editorEvent = null;
root.showEditor = true;
}
} }
} }
@@ -358,31 +328,12 @@ Rectangle {
height: 28 height: 28
visible: !showEventDetails visible: !showEventDetails
Rectangle { DankActionButton {
width: 28 buttonSize: 28
height: 28 iconSize: 14
radius: Theme.cornerRadius iconName: "chevron_left"
color: prevMonthArea.containsMouse ? Theme.primaryHover : Theme.withAlpha(Theme.primaryHover, 0) iconColor: Theme.primary
onClicked: root.shiftMonth(-1)
DankIcon {
anchors.centerIn: parent
name: "chevron_left"
size: 14
color: Theme.primary
}
MouseArea {
id: prevMonthArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
let newDate = new Date(calendarGrid.displayDate);
newDate.setMonth(newDate.getMonth() - 1);
calendarGrid.displayDate = newDate;
loadEventsForMonth();
}
}
} }
StyledText { StyledText {
@@ -396,53 +347,20 @@ Rectangle {
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
} }
Rectangle { DankActionButton {
width: 28 buttonSize: 28
height: 28 iconSize: 14
radius: Theme.cornerRadius iconName: "today"
color: todayArea.containsMouse ? Theme.primaryHover : Theme.withAlpha(Theme.primaryHover, 0) iconColor: Theme.primary
onClicked: root.goToToday()
DankIcon {
anchors.centerIn: parent
name: "today"
size: 14
color: Theme.primary
}
MouseArea {
id: todayArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: root.goToToday()
}
} }
Rectangle { DankActionButton {
width: 28 buttonSize: 28
height: 28 iconSize: 14
radius: Theme.cornerRadius iconName: "chevron_right"
color: nextMonthArea.containsMouse ? Theme.primaryHover : Theme.withAlpha(Theme.primaryHover, 0) iconColor: Theme.primary
onClicked: root.shiftMonth(1)
DankIcon {
anchors.centerIn: parent
name: "chevron_right"
size: 14
color: Theme.primary
}
MouseArea {
id: nextMonthArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
let newDate = new Date(calendarGrid.displayDate);
newDate.setMonth(newDate.getMonth() + 1);
calendarGrid.displayDate = newDate;
loadEventsForMonth();
}
}
} }
} }
@@ -814,12 +732,16 @@ Rectangle {
} }
} }
readonly property bool isTask: modelData && modelData.id && modelData.id.startsWith("task_") readonly property bool isLocalTask: taskId.startsWith("task_")
readonly property color accentColor: { readonly property bool isDankTask: taskId.startsWith("vtodo_")
if (isTask) readonly property bool isTask: isLocalTask || isDankTask
return modelData.completed ? Theme.withAlpha(Theme.primary, 0.4) : Theme.primary; readonly property bool canModify: isLocalTask || (isDankTask && modelData && !modelData.readOnly)
return (modelData && modelData.color && modelData.color.length) ? modelData.color : Theme.primary; readonly property color baseAccent: {
if (isLocalTask || !modelData || !modelData.color || !modelData.color.length)
return Theme.primary;
return modelData.color;
} }
readonly property color accentColor: (isTask && modelData && modelData.completed) ? Theme.withAlpha(baseAccent, 0.4) : baseAccent
readonly property color surfaceColor: isDragging ? Theme.primaryPressed : (eventMouseArea.containsMouse ? Theme.primaryBackground : Theme.nestedSurface) readonly property color surfaceColor: isDragging ? Theme.primaryPressed : (eventMouseArea.containsMouse ? Theme.primaryBackground : Theme.nestedSurface)
color: surfaceColor color: surfaceColor
@@ -888,13 +810,13 @@ Rectangle {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: "transparent" color: "transparent"
visible: modelData && modelData.id && modelData.id.startsWith("task_") && !taskItem.isEditing visible: taskItem.isLocalTask && !taskItem.isEditing
DankIcon { DankIcon {
anchors.centerIn: parent anchors.centerIn: parent
name: "drag_indicator" name: "drag_indicator"
size: 14 size: 14
color: dragMouseArea.containsMouse ? Theme.primary : Theme.surfaceTextAlpha color: dragMouseArea.containsMouse ? Theme.primary : Theme.surfaceTextMedium
} }
MouseArea { MouseArea {
@@ -937,34 +859,14 @@ Rectangle {
} }
} }
// Checkbox status icon
Rectangle {
id: checkboxContainer
width: 24
height: 24
anchors.left: parent.left
anchors.leftMargin: (modelData && modelData.id && modelData.id.startsWith("task_")) ? (taskItem.isEditing ? 8 : 32) : 8
anchors.verticalCenter: parent.verticalCenter
radius: Theme.cornerRadius
color: "transparent"
visible: modelData && modelData.id && modelData.id.startsWith("task_")
DankIcon {
anchors.centerIn: parent
name: (modelData && modelData.completed) ? "check_box" : "check_box_outline_blank"
size: 16
color: (modelData && modelData.completed) ? Theme.primary : Theme.onSurface_38
}
}
Column { Column {
id: eventContent id: eventContent
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
anchors.leftMargin: (modelData && modelData.id && modelData.id.startsWith("task_")) ? 60 : (Theme.spacingS + 6) anchors.leftMargin: taskItem.isLocalTask ? 60 : taskItem.isDankTask ? 36 : (Theme.spacingS + 6)
anchors.rightMargin: (modelData && modelData.id && modelData.id.startsWith("task_")) ? 64 : Theme.spacingXS anchors.rightMargin: taskItem.canModify ? 64 : Theme.spacingXS
spacing: Theme.spacingXXS spacing: Theme.spacingXXS
visible: !taskItem.isEditing visible: !taskItem.isEditing
@@ -972,7 +874,7 @@ Rectangle {
width: parent.width width: parent.width
text: modelData ? modelData.title : "" text: modelData ? modelData.title : ""
font.pixelSize: Theme.fontSizeSmall font.pixelSize: Theme.fontSizeSmall
color: (modelData && modelData.id && modelData.id.startsWith("task_") && modelData.completed) ? Theme.surfaceTextSecondary : Theme.surfaceText color: (taskItem.isTask && modelData && modelData.completed) ? Theme.surfaceTextSecondary : Theme.surfaceText
font.weight: Font.Medium font.weight: Font.Medium
horizontalAlignment: Text.AlignLeft horizontalAlignment: Text.AlignLeft
elide: Text.ElideRight elide: Text.ElideRight
@@ -1000,7 +902,7 @@ Rectangle {
color: Theme.surfaceTextMedium color: Theme.surfaceTextMedium
font.weight: Font.Normal font.weight: Font.Normal
horizontalAlignment: Text.AlignLeft horizontalAlignment: Text.AlignLeft
visible: text !== "" && modelData && modelData.id && !modelData.id.startsWith("task_") visible: text !== "" && !taskItem.isLocalTask
} }
} }
@@ -1047,90 +949,73 @@ Rectangle {
id: eventMouseArea id: eventMouseArea
anchors.fill: parent anchors.fill: parent
anchors.leftMargin: (modelData && modelData.id && modelData.id.startsWith("task_")) ? 32 : 6 anchors.leftMargin: taskItem.isLocalTask ? 32 : 6
anchors.rightMargin: (modelData && modelData.id && modelData.id.startsWith("task_")) ? 64 : 0 anchors.rightMargin: taskItem.canModify ? 64 : 0
hoverEnabled: true hoverEnabled: true
cursorShape: modelData ? Qt.PointingHandCursor : Qt.ArrowCursor cursorShape: modelData ? Qt.PointingHandCursor : Qt.ArrowCursor
enabled: modelData && !taskItem.isEditing enabled: modelData && !taskItem.isEditing
onClicked: { onClicked: {
if (modelData && modelData.id && modelData.id.startsWith("task_")) { if (!modelData)
return;
if (taskItem.isTask && taskItem.canModify) {
CalendarService.toggleTask(modelData.id); CalendarService.toggleTask(modelData.id);
return; return;
} }
if (modelData) root.detailEvent = modelData;
root.detailEvent = modelData;
} }
} }
// Delete / Cancel Button DankActionButton {
Rectangle { buttonSize: 24
iconSize: 16
iconName: (modelData && modelData.completed) ? "check_box" : "check_box_outline_blank"
iconColor: (modelData && modelData.completed) ? Theme.primary : Theme.surfaceText
anchors.left: parent.left
anchors.leftMargin: taskItem.isLocalTask ? (taskItem.isEditing ? 8 : 32) : 8
anchors.verticalCenter: parent.verticalCenter
visible: taskItem.isTask
enabled: taskItem.canModify && !taskItem.isEditing
onClicked: CalendarService.toggleTask(modelData.id)
}
DankActionButton {
id: deleteButton id: deleteButton
width: 24 buttonSize: 24
height: 24 iconSize: 14
iconName: taskItem.isEditing ? "close" : "delete"
iconColor: taskItem.isEditing ? Theme.surfaceText : Theme.error
anchors.right: parent.right anchors.right: parent.right
anchors.rightMargin: Theme.spacingS anchors.rightMargin: Theme.spacingS
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
radius: Theme.cornerRadius visible: taskItem.canModify
color: deleteMouseArea.containsMouse ? (taskItem.isEditing ? Theme.primaryHover : Qt.rgba(0.9, 0.2, 0.2, 0.15)) : Theme.withAlpha(Qt.rgba(0.9, 0.2, 0.2, 0.15), 0) onClicked: {
visible: modelData && modelData.id && modelData.id.startsWith("task_") if (taskItem.isEditing) {
taskItem.isEditing = false;
DankIcon { return;
anchors.centerIn: parent
name: taskItem.isEditing ? "close" : "delete"
size: 14
color: deleteMouseArea.containsMouse ? (taskItem.isEditing ? Theme.primary : Qt.rgba(0.9, 0.2, 0.2, 1.0)) : Theme.onSurface_38
}
MouseArea {
id: deleteMouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
if (taskItem.isEditing) {
taskItem.isEditing = false;
} else if (modelData && modelData.id) {
CalendarService.removeTask(modelData.id);
}
} }
if (modelData && modelData.id)
CalendarService.removeTask(modelData.id);
} }
} }
// Edit / Save Button DankActionButton {
Rectangle { buttonSize: 24
id: editButton iconSize: 14
width: 24 iconName: taskItem.isEditing ? "check" : "edit"
height: 24 iconColor: taskItem.isEditing ? Theme.primary : Theme.surfaceText
anchors.right: deleteButton.left anchors.right: deleteButton.left
anchors.rightMargin: Theme.spacingXS anchors.rightMargin: Theme.spacingXS
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
radius: Theme.cornerRadius visible: taskItem.canModify
color: editMouseArea.containsMouse ? Theme.primaryHover : Theme.withAlpha(Theme.primaryHover, 0) onClicked: {
visible: modelData && modelData.id && modelData.id.startsWith("task_") if (!taskItem.isEditing) {
taskItem.isEditing = true;
DankIcon { return;
anchors.centerIn: parent
name: taskItem.isEditing ? "check" : "edit"
size: 14
color: editMouseArea.containsMouse ? Theme.primary : Theme.onSurface_38
}
MouseArea {
id: editMouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
if (taskItem.isEditing) {
let txt = editInput.text.trim();
if (txt !== "" && modelData && modelData.id) {
CalendarService.editTask(modelData.id, txt);
}
taskItem.isEditing = false;
} else {
taskItem.isEditing = true;
}
} }
let txt = editInput.text.trim();
if (txt !== "" && modelData && modelData.id)
CalendarService.editTask(modelData.id, txt);
taskItem.isEditing = false;
} }
} }
} }
+191 -2
View File
@@ -21,6 +21,9 @@ Item {
property var calendars: [] property var calendars: []
property var events: [] property var events: []
property var eventsByDate: ({}) property var eventsByDate: ({})
property var tasks: []
property var tasksByDate: ({})
property var _pendingTaskState: ({})
property string lastError: "" property string lastError: ""
property date focusDate: new Date() property date focusDate: new Date()
property var _loadedFrom: null property var _loadedFrom: null
@@ -130,6 +133,7 @@ Item {
root.log.info("connected to dankcal:", root.socketPath); root.log.info("connected to dankcal:", root.socketPath);
root.refreshCalendars(); root.refreshCalendars();
root.reloadEvents(); root.reloadEvents();
root.reloadTasks();
return; return;
} }
if (!root.connected && !root.socketFound) if (!root.connected && !root.socketFound)
@@ -193,12 +197,19 @@ Item {
} }
} }
Timer {
id: tasksDebounce
interval: 400
repeat: false
onTriggered: root.reloadTasks()
}
function _sendSubscribe() { function _sendSubscribe() {
subscribeSocket.send({ subscribeSocket.send({
"id": _nextId(), "id": _nextId(),
"method": "subscribe", "method": "subscribe",
"params": { "params": {
"topics": ["accounts", "calendars", "events", "sync"] "topics": ["accounts", "calendars", "events", "tasks", "sync"]
} }
}); });
} }
@@ -243,8 +254,14 @@ Item {
refreshDebounce.restart(); refreshDebounce.restart();
break; break;
case "events": case "events":
refreshDebounce.restart();
break;
case "tasks":
tasksDebounce.restart();
break;
case "sync": case "sync":
refreshDebounce.restart(); refreshDebounce.restart();
tasksDebounce.restart();
break; break;
} }
} }
@@ -282,6 +299,7 @@ Item {
} }
calendars = list; calendars = list;
_rebuildEventsByDate(); _rebuildEventsByDate();
_rebuildTasksByDate();
}); });
} }
@@ -353,7 +371,7 @@ Item {
function _normalizeEvent(e) { function _normalizeEvent(e) {
const allDay = !!e.allDay; const allDay = !!e.allDay;
const id = e.id || ""; const id = e.id || "";
if (id.startsWith("task_")) if (id.startsWith("task_") || id.startsWith("vtodo_"))
log.warn("daemon event id collides with task prefix:", id); log.warn("daemon event id collides with task prefix:", id);
return { return {
"id": id, "id": id,
@@ -478,4 +496,175 @@ Item {
callback(response); callback(response);
}); });
} }
function reloadTasks() {
if (!connected)
return;
sendRequest("tasks.list", {
"includeCompleted": true,
"limit": 5000
}, response => {
if (response.error) {
lastError = response.error;
return;
}
const raw = (response.result || {}).tasks || [];
tasks = raw.map(t => _applyPendingState(_normalizeTask(t)));
_rebuildTasksByDate();
});
}
// A completion toggle is applied optimistically; slow providers can serve a
// reload that predates the write, so the desired state wins over a
// disagreeing reload until the daemon confirms it or the hold expires.
function _applyPendingState(t) {
const pending = _pendingTaskState[t.id];
if (!pending)
return t;
if (t.completed === pending.completed || Date.now() > pending.expires) {
delete _pendingTaskState[t.id];
return t;
}
return Object.assign({}, t, {
"completed": pending.completed,
"status": pending.completed ? "completed" : "needs_action"
});
}
function _normalizeTask(t) {
const allDay = !!t.allDay;
return {
"id": t.id || "",
"calendarId": t.calendarId || "",
"title": t.summary || "(untitled)",
"description": t.description || "",
"location": t.location || "",
"status": t.status || "needs_action",
"completed": t.status === "completed",
"priority": t.priority || 0,
"due": t.due ? (allDay ? _dayBoundary(t.due) : new Date(t.due)) : null,
"allDay": allDay
};
}
function taskById(id) {
for (let i = 0; i < tasks.length; i++) {
if (tasks[i].id === id)
return tasks[i];
}
return null;
}
function taskCalendars() {
return calendars.filter(c => c.holdsTasks && !c.readOnly);
}
function defaultTaskCalendar() {
const writable = taskCalendars().filter(c => !c.hidden);
return writable.length > 0 ? writable[0] : null;
}
function _taskAsEvent(t) {
const cal = calendarById(t.calendarId);
return {
"id": "vtodo_" + t.id,
"title": t.title,
"description": t.description,
"location": t.location,
"url": "",
"calendar": cal ? cal.name : "",
"color": cal ? cal.color : fallbackPalette[0],
"readOnly": cal ? !!cal.readOnly : false,
"start": t.due,
"end": t.due,
"allDay": t.allDay,
"completed": t.completed,
"status": t.status,
"isMultiDay": false
};
}
function _rebuildTasksByDate() {
const hidden = _hiddenCalendarIds();
const map = {};
for (const t of tasks) {
if (!t.due || t.status === "cancelled" || hidden[t.calendarId])
continue;
const key = Qt.formatDate(t.due, "yyyy-MM-dd");
if (!map[key])
map[key] = [];
map[key].push(_taskAsEvent(t));
}
tasksByDate = map;
}
function _patchTask(id, changes) {
const next = tasks.slice();
for (let i = 0; i < next.length; i++) {
if (next[i].id !== id)
continue;
next[i] = Object.assign({}, next[i], changes);
break;
}
tasks = next;
_rebuildTasksByDate();
}
function createTask(fields, callback) {
sendRequest("tasks.create", fields, response => {
if (response.error)
lastError = response.error;
reloadTasks();
if (callback)
callback(response);
});
}
function updateTask(id, fields, callback) {
const params = Object.assign({
"id": id
}, fields);
sendRequest("tasks.update", params, response => {
if (response.error)
lastError = response.error;
reloadTasks();
if (callback)
callback(response);
});
}
function completeTask(id, completed, callback) {
_pendingTaskState[id] = {
"completed": completed,
"expires": Date.now() + 15000
};
_patchTask(id, {
"completed": completed,
"status": completed ? "completed" : "needs_action"
});
sendRequest("tasks.complete", {
"id": id,
"completed": completed
}, response => {
if (response.error) {
lastError = response.error;
delete _pendingTaskState[id];
reloadTasks();
}
if (callback)
callback(response);
});
}
function deleteTask(id, callback) {
sendRequest("tasks.delete", {
"id": id
}, response => {
if (response.error)
lastError = response.error;
reloadTasks();
if (callback)
callback(response);
});
}
} }
+43 -8
View File
@@ -63,6 +63,7 @@ Singleton {
id: dankBackend id: dankBackend
enabled: root.backendPref === "dankcal" || root.backendPref === "auto" enabled: root.backendPref === "dankcal" || root.backendPref === "auto"
onEventsByDateChanged: root.mergeEvents() onEventsByDateChanged: root.mergeEvents()
onTasksByDateChanged: root.mergeEvents()
onConnectedChanged: { onConnectedChanged: {
if (connected && root._rangeSet) if (connected && root._rangeSet)
root.loadEvents(root.lastStartDate, root.lastEndDate); root.loadEvents(root.lastStartDate, root.lastEndDate);
@@ -198,6 +199,17 @@ Singleton {
} }
function addTaskForDate(date, text) { function addTaskForDate(date, text) {
const taskCal = isDankActive && dankBackend.connected ? dankBackend.defaultTaskCalendar() : null;
if (taskCal) {
const due = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()));
dankBackend.createTask({
"calendarId": taskCal.id,
"summary": text,
"allDay": true,
"due": due.toISOString()
});
return;
}
let dateKey = Qt.formatDate(date, "yyyy-MM-dd"); let dateKey = Qt.formatDate(date, "yyyy-MM-dd");
let tasks = Object.assign({}, root.localTasks); let tasks = Object.assign({}, root.localTasks);
if (!tasks[dateKey]) if (!tasks[dateKey])
@@ -214,6 +226,13 @@ Singleton {
} }
function toggleTask(taskId) { function toggleTask(taskId) {
if (taskId.startsWith("vtodo_")) {
const id = taskId.slice(6);
const task = dankBackend.taskById(id);
if (task)
dankBackend.completeTask(id, !task.completed);
return;
}
let cleanId = taskId.replace("task_", ""); let cleanId = taskId.replace("task_", "");
let tasks = Object.assign({}, root.localTasks); let tasks = Object.assign({}, root.localTasks);
let updated = false; let updated = false;
@@ -237,6 +256,10 @@ Singleton {
} }
function removeTask(taskId) { function removeTask(taskId) {
if (taskId.startsWith("vtodo_")) {
dankBackend.deleteTask(taskId.slice(6));
return;
}
let cleanId = taskId.replace("task_", ""); let cleanId = taskId.replace("task_", "");
let tasks = Object.assign({}, root.localTasks); let tasks = Object.assign({}, root.localTasks);
let updated = false; let updated = false;
@@ -283,6 +306,12 @@ Singleton {
} }
function editTask(taskId, newText) { function editTask(taskId, newText) {
if (taskId.startsWith("vtodo_")) {
dankBackend.updateTask(taskId.slice(6), {
"summary": newText
});
return;
}
let cleanId = taskId.replace("task_", ""); let cleanId = taskId.replace("task_", "");
let tasks = Object.assign({}, root.localTasks); let tasks = Object.assign({}, root.localTasks);
let updated = false; let updated = false;
@@ -305,6 +334,17 @@ Singleton {
} }
} }
function _mergeInto(merged, byDate) {
for (let dateKey in byDate) {
if (!merged[dateKey])
merged[dateKey] = [];
for (let event of byDate[dateKey]) {
if (!merged[dateKey].some(e => e.id === event.id))
merged[dateKey].push(event);
}
}
}
function mergeEvents() { function mergeEvents() {
let merged = {}; let merged = {};
let backendEvents = _activeBackendEventsByDate(); let backendEvents = _activeBackendEventsByDate();
@@ -312,14 +352,9 @@ Singleton {
for (let dateKey in backendEvents) for (let dateKey in backendEvents)
merged[dateKey] = [].concat(backendEvents[dateKey]); merged[dateKey] = [].concat(backendEvents[dateKey]);
for (let dateKey in root.taskEventsByDate) { _mergeInto(merged, root.taskEventsByDate);
if (!merged[dateKey]) if (isDankActive)
merged[dateKey] = []; _mergeInto(merged, dankBackend.tasksByDate);
for (let event of root.taskEventsByDate[dateKey]) {
if (!merged[dateKey].some(e => e.id === event.id))
merged[dateKey].push(event);
}
}
for (let dateKey in merged) { for (let dateKey in merged) {
let list = merged[dateKey]; let list = merged[dateKey];