diff --git a/core/Objects/Item.vala b/core/Objects/Item.vala index 16380f346..c293a3d71 100644 --- a/core/Objects/Item.vala +++ b/core/Objects/Item.vala @@ -348,16 +348,15 @@ public class Objects.Item : Objects.BaseObject { due.date = Util.ical_to_date_time_local (ical.get_due ()).to_string (); } - string _parent_id = Util.find_string_value ("RELATED-TO", data); - if (_parent_id != "") { - parent_id = _parent_id; - } + parent_id = Util.find_string_value ("RELATED-TO", data); if (ical.get_status () == ICal.PropertyStatus.COMPLETED) { checked = true; - var completed_datetime = ical.get_first_property (ICal.PropertyKind.COMPLETED_PROPERTY); - if (completed_datetime != null) { - completed_at = Util.ical_to_date_time_local (completed_datetime.get_completed ()).to_string (); + string completed = Util.find_string_value ("COMPLETED", data); + if (completed != "") { + completed_at = Util.get_default ().get_format_date ( + Util.ical_to_date_time_local (new ICal.Time.from_string (completed)) + ).to_string (); } else { completed_at = Util.get_default ().get_format_date (new GLib.DateTime.now_local ()).to_string (); } diff --git a/core/Services/CalDAV.vala b/core/Services/CalDAV.vala index 379c09db9..6a9a187f7 100644 --- a/core/Services/CalDAV.vala +++ b/core/Services/CalDAV.vala @@ -333,7 +333,7 @@ public class Services.CalDAV : GLib.Object { GXml.DomDocument doc = new GXml.Document.from_string ((string) stream.get_data ()); GXml.DomHTMLCollection response = doc.get_elements_by_tag_name ("d:response"); foreach (GXml.DomElement element in response) { - if (is_calendar (element)) { + if (is_vtodo_calendar (element)) { var project = new Objects.Project.from_caldav_xml (element); Services.Database.get_default ().insert_project (project); yield get_all_tasks_by_tasklist (project); @@ -394,15 +394,19 @@ public class Services.CalDAV : GLib.Object { if (item != null) { string old_project_id = item.project_id; - string old_section_id = item.section_id; string old_parent_id = item.parent_id; item.update_from_caldav_xml (element); + item.project_id = project.id; Services.Database.get_default ().update_item (item); - // TODO: Fix moved - if (old_parent_id != item.parent_id) { - Services.EventBus.get_default ().item_moved (item, old_project_id, old_section_id, old_parent_id); + if (old_project_id != item.project_id || old_parent_id != item.parent_id) { + print ("old_project_id: %s\n".printf (old_project_id)); + print ("old_parent_id: %s\n".printf (old_parent_id)); + print ("project_id: %s\n".printf (item.project_id)); + print ("parent_id: %s\n".printf (item.parent_id)); + + Services.EventBus.get_default ().item_moved (item, old_project_id, "", old_parent_id); } bool old_checked = item.checked; @@ -462,7 +466,7 @@ public class Services.CalDAV : GLib.Object { GXml.DomDocument doc = new GXml.Document.from_string ((string) stream.get_data ()); GXml.DomHTMLCollection response = doc.get_elements_by_tag_name ("d:response"); foreach (GXml.DomElement element in response) { - if (is_calendar (element)) { + if (is_vtodo_calendar (element)) { Objects.Project? project = Services.Database.get_default ().get_project (get_id_from_url (element)); if (project == null) { project = new Objects.Project.from_caldav_xml (element); @@ -652,6 +656,33 @@ public class Services.CalDAV : GLib.Object { return response; } + public async HttpResponse move_task (Objects.Item item, string project_id) { + var server_url = Services.Settings.get_default ().settings.get_string ("caldav-server-url"); + var username = Services.Settings.get_default ().settings.get_string ("caldav-username"); + var credential = Services.Settings.get_default ().settings.get_string ("caldav-credential"); + + var url = "%s/remote.php/dav/calendars/%s/%s/%s".printf (server_url, username, item.project.id, item.ics); + var destination = "/remote.php/dav/calendars/%s/%s/%s".printf (username, project_id, item.ics); + + var message = new Soup.Message ("MOVE", url); + message.request_headers.append ("Authorization", "Basic %s".printf (credential)); + message.request_headers.append ("Destination", destination); + + HttpResponse response = new HttpResponse (); + + try { + yield session.send_and_read_async (message, GLib.Priority.HIGH, null); + + if (message.status_code == 201) { + response.status = true; + } + } catch (Error e) { + debug (e.message); + } + + return response; + } + public void remove_items () { Services.Settings.get_default ().settings.set_string ("caldav-server-url", ""); Services.Settings.get_default ().settings.set_string ("caldav-username", ""); @@ -699,11 +730,20 @@ public class Services.CalDAV : GLib.Object { return server_url != "" && username != "" && credential != ""; } - public bool is_calendar (GXml.DomElement element) { + public bool is_vtodo_calendar (GXml.DomElement element) { GXml.DomElement propstat = element.get_elements_by_tag_name ("d:propstat").get_element (0); GXml.DomElement prop = propstat.get_elements_by_tag_name ("d:prop").get_element (0); GXml.DomElement resourcetype = prop.get_elements_by_tag_name ("d:resourcetype").get_element (0); - return resourcetype.get_elements_by_tag_name ("cal:calendar").length > 0; + + bool is_calendar = resourcetype.get_elements_by_tag_name ("cal:calendar").length > 0; + bool is_vtodo = false; + if (is_calendar) { + GXml.DomElement supported_calendar = prop.get_elements_by_tag_name ("cal:supported-calendar-component-set").get_element (0); + GXml.DomElement calendar_comp = supported_calendar.get_elements_by_tag_name ("cal:comp").get_element (0); + is_vtodo = calendar_comp.get_attribute ("name") == "VTODO"; + } + + return is_vtodo; } public bool is_deleted_calendar (GXml.DomElement element) { diff --git a/core/Widgets/ProjectPicker/ProjectPickerButton.vala b/core/Widgets/ProjectPicker/ProjectPickerButton.vala index daef1c9e6..8b8722069 100644 --- a/core/Widgets/ProjectPicker/ProjectPickerButton.vala +++ b/core/Widgets/ProjectPicker/ProjectPickerButton.vala @@ -25,6 +25,7 @@ public class Widgets.ProjectPicker.ProjectPickerButton : Adw.Bin { private Gtk.Label section_label; private Layouts.HeaderItem sections_group; private Gtk.Popover sections_popover; + private Gtk.Revealer section_box_revealer; public signal void project_change (Objects.Project project); public signal void section_change (Objects.Section? section); @@ -61,16 +62,28 @@ public class Widgets.ProjectPicker.ProjectPickerButton : Adw.Bin { }; sections_popover = build_sections_popover (); + + var arrow_label = new Gtk.Label ("→"); + var section_button = new Gtk.MenuButton () { popover = sections_popover, child = section_label, css_classes = { Granite.STYLE_CLASS_FLAT } }; + var section_box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 6); + section_box.append (arrow_label); + section_box.append (section_button); + + section_box_revealer = new Gtk.Revealer () { + transition_type = Gtk.RevealerTransitionType.SLIDE_RIGHT, + reveal_child = true, + child = section_box + }; + var box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 6); box.append (project_button); - box.append (new Gtk.Label ("→")); - box.append (section_button); + box.append (section_box_revealer); child = box; @@ -86,6 +99,7 @@ public class Widgets.ProjectPicker.ProjectPickerButton : Adw.Bin { name_label.label = project.is_inbox_project ? _("Inbox") : project.name; icon_project.project = project; icon_project.update_request (); + section_box_revealer.reveal_child = project.backend_type != BackendType.CALDAV; } private Gtk.Popover build_sections_popover () { diff --git a/core/Widgets/ProjectPicker/ProjectPickerPopover.vala b/core/Widgets/ProjectPicker/ProjectPickerPopover.vala index c873a2f53..1b2357406 100644 --- a/core/Widgets/ProjectPicker/ProjectPickerPopover.vala +++ b/core/Widgets/ProjectPicker/ProjectPickerPopover.vala @@ -32,10 +32,16 @@ public class Widgets.ProjectPicker.ProjectPickerPopover : Gtk.Popover { }; todoist_group.show_action = false; + var nextcloud_group = new Layouts.HeaderItem (_("Nextcloud")) { + reveal_child = Services.Database.get_default ().get_projects_by_backend_type (BackendType.CALDAV).size > 0 + }; + nextcloud_group.show_action = false; + var scrolled_box = new Gtk.Box (Gtk.Orientation.VERTICAL, 0); scrolled_box.append (inbox_group); scrolled_box.append (local_group); scrolled_box.append (todoist_group); + scrolled_box.append (nextcloud_group); var listbox = new Gtk.ListBox () { hexpand = true, @@ -92,6 +98,8 @@ public class Widgets.ProjectPicker.ProjectPickerPopover : Gtk.Popover { local_group.add_child (row); } else if (project.backend_type == BackendType.TODOIST) { todoist_group.add_child (row); + } else if (project.backend_type == BackendType.CALDAV) { + nextcloud_group.add_child (row); } } } diff --git a/src/Dialogs/Preferences/PreferencesWindow.vala b/src/Dialogs/Preferences/PreferencesWindow.vala index ccb19b5a6..5db94ed17 100644 --- a/src/Dialogs/Preferences/PreferencesWindow.vala +++ b/src/Dialogs/Preferences/PreferencesWindow.vala @@ -1293,15 +1293,12 @@ public class Dialogs.Preferences.PreferencesWindow : Adw.PreferencesWindow { var settings_header = new Dialogs.Preferences.SettingsHeader (_("CalDAV Setup")); var username_entry = new Adw.EntryRow (); - username_entry.text = "estratertux@gmail.com"; username_entry.title = _("User Name"); var password_entry = new Adw.PasswordEntryRow (); - password_entry.text = "Estrater@200"; password_entry.title = _("Password"); var server_entry = new Adw.EntryRow (); - server_entry.text = "https://evi.nl.tab.digital/"; server_entry.title = _("Server URL"); var providers_model = new Gtk.StringList (null); diff --git a/src/Dialogs/ProjectPicker/ProjectPicker.vala b/src/Dialogs/ProjectPicker/ProjectPicker.vala index 4b231ee9a..3270d4487 100644 --- a/src/Dialogs/ProjectPicker/ProjectPicker.vala +++ b/src/Dialogs/ProjectPicker/ProjectPicker.vala @@ -29,6 +29,7 @@ public class Dialogs.ProjectPicker.ProjectPicker : Adw.Window { private Layouts.HeaderItem inbox_group; private Layouts.HeaderItem local_group; private Layouts.HeaderItem todoist_group; + private Layouts.HeaderItem caldav_group; public Gee.HashMap projects_hashmap; @@ -126,6 +127,7 @@ public class Dialogs.ProjectPicker.ProjectPicker : Adw.Window { search_entry.search_changed.connect (() => { local_group.invalidate_filter (); todoist_group.invalidate_filter (); + caldav_group.invalidate_filter (); }); Services.EventBus.get_default ().project_picker_changed.connect ((id) => { @@ -164,10 +166,14 @@ public class Dialogs.ProjectPicker.ProjectPicker : Adw.Window { todoist_group = new Layouts.HeaderItem (_("Todoist")); todoist_group.show_action = false; + caldav_group = new Layouts.HeaderItem (_("Nextcloud")); + caldav_group.show_action = false; + if (backend_type == BackendType.ALL) { inbox_group.reveal = true; local_group.reveal = true; todoist_group.reveal = true; + caldav_group.reveal = true; } else if (backend_type == BackendType.LOCAL) { inbox_group.reveal = Services.Settings.get_default ().settings.get_enum ("default-inbox") == 0; local_group.reveal = true; @@ -176,10 +182,16 @@ public class Dialogs.ProjectPicker.ProjectPicker : Adw.Window { inbox_group.reveal = Services.Settings.get_default ().settings.get_enum ("default-inbox") == 1; local_group.reveal = false; todoist_group.reveal = true; + } else if (backend_type == BackendType.CALDAV) { + inbox_group.reveal = false; + local_group.reveal = false; + todoist_group.reveal = false; + caldav_group.reveal = true; } local_group.set_filter_func (filter_func); todoist_group.set_filter_func (filter_func); + caldav_group.set_filter_func (filter_func); var scrolled_box = new Gtk.Box (Gtk.Orientation.VERTICAL, 0) { margin_start = 12, @@ -188,6 +200,7 @@ public class Dialogs.ProjectPicker.ProjectPicker : Adw.Window { scrolled_box.append (inbox_group); scrolled_box.append (local_group); scrolled_box.append (todoist_group); + scrolled_box.append (caldav_group); var scrolled = new Gtk.ScrolledWindow () { hexpand = true, @@ -234,15 +247,17 @@ public class Dialogs.ProjectPicker.ProjectPicker : Adw.Window { private void add_projects () { foreach (Objects.Project project in Services.Database.get_default ().projects) { - projects_hashmap [project.id_string] = new Dialogs.ProjectPicker.ProjectPickerRow (project); + projects_hashmap [project.id] = new Dialogs.ProjectPicker.ProjectPickerRow (project); if (project.is_inbox_project) { - inbox_group.add_child (projects_hashmap [project.id_string]); + inbox_group.add_child (projects_hashmap [project.id]); } else { if (project.backend_type == BackendType.LOCAL) { - local_group.add_child (projects_hashmap [project.id_string]); + local_group.add_child (projects_hashmap [project.id]); } else if (project.backend_type == BackendType.TODOIST) { - todoist_group.add_child (projects_hashmap [project.id_string]); + todoist_group.add_child (projects_hashmap [project.id]); + } else if (project.backend_type == BackendType.CALDAV) { + caldav_group.add_child (projects_hashmap [project.id]); } } } diff --git a/src/Layouts/ItemRow.vala b/src/Layouts/ItemRow.vala index aef64a8ac..8997da1d4 100644 --- a/src/Layouts/ItemRow.vala +++ b/src/Layouts/ItemRow.vala @@ -1361,7 +1361,9 @@ public class Layouts.ItemRow : Layouts.ItemBase { } if (item.project_id != project_id || item.section_id != section_id) { - if (item.project.backend_type == BackendType.TODOIST) { + if (item.project.backend_type == BackendType.LOCAL) { + move_item (project_id, section_id); + } else if (item.project.backend_type == BackendType.TODOIST) { is_loading = true; string move_id = project_id; @@ -1376,11 +1378,20 @@ public class Layouts.ItemRow : Layouts.ItemBase { move_item (project_id, section_id); is_loading = false; } else { - main_revealer.reveal_child = true; + is_loading = false; + } + }); + } else if (item.project.backend_type == BackendType.CALDAV) { + is_loading = true; + + Services.CalDAV.get_default ().move_task.begin (item, project_id, (obj, res) => { + if (Services.CalDAV.get_default ().move_task.end (res).status) { + move_item (project_id, section_id); + is_loading = false; + } else { + is_loading = false; } }); - } else if (item.project.backend_type == BackendType.LOCAL) { - move_item (project_id, section_id); } } } diff --git a/src/Layouts/SectionRow.vala b/src/Layouts/SectionRow.vala index 6b421d34f..30031327f 100644 --- a/src/Layouts/SectionRow.vala +++ b/src/Layouts/SectionRow.vala @@ -271,65 +271,65 @@ public class Layouts.SectionRow : Gtk.ListBoxRow { if (item.project_id == section.project_id && item.section_id == section.id && item.parent_id == "") { if (!old_checked) { - if (items.has_key (item.id_string)) { - items [item.id_string].hide_destroy (); - items.unset (item.id_string); + if (items.has_key (item.id)) { + items [item.id].hide_destroy (); + items.unset (item.id); } - if (!items_checked.has_key (item.id_string)) { - items_checked [item.id_string] = new Layouts.ItemRow (item); - checked_listbox.insert (items_checked [item.id_string], 0); + if (!items_checked.has_key (item.id)) { + items_checked [item.id] = new Layouts.ItemRow (item); + checked_listbox.insert (items_checked [item.id], 0); } } else { - if (items_checked.has_key (item.id_string)) { - items_checked [item.id_string].hide_destroy (); - items_checked.unset (item.id_string); + if (items_checked.has_key (item.id)) { + items_checked [item.id].hide_destroy (); + items_checked.unset (item.id); } - if (!items.has_key (item.id_string)) { - items [item.id_string] = new Layouts.ItemRow (item); - listbox.append (items [item.id_string]); + if (!items.has_key (item.id)) { + items [item.id] = new Layouts.ItemRow (item); + listbox.append (items [item.id]); } } } }); Services.Database.get_default ().item_updated.connect ((item, update_id) => { - if (items.has_key (item.id_string)) { - if (items [item.id_string].update_id != update_id) { - items [item.id_string].update_request (); + if (items.has_key (item.id)) { + if (items [item.id].update_id != update_id) { + items [item.id].update_request (); update_sort (); } } - if (items_checked.has_key (item.id_string)) { - items_checked [item.id_string].update_request (); + if (items_checked.has_key (item.id)) { + items_checked [item.id].update_request (); } }); Services.Database.get_default ().item_deleted.connect ((item) => { - if (items.has_key (item.id_string)) { - items [item.id_string].hide_destroy (); - items.unset (item.id_string); + if (items.has_key (item.id)) { + items [item.id].hide_destroy (); + items.unset (item.id); } - if (items_checked.has_key (item.id_string)) { - items_checked [item.id_string].hide_destroy (); - items_checked.unset (item.id_string); + if (items_checked.has_key (item.id)) { + items_checked [item.id].hide_destroy (); + items_checked.unset (item.id); } }); Services.EventBus.get_default ().item_moved.connect ((item, old_project_id, old_section_id, old_parent_id) => { if (old_project_id == section.project_id && old_section_id == section.id) { - if (items.has_key (item.id_string)) { - items [item.id_string].hide_destroy (); - items.unset (item.id_string); + if (items.has_key (item.id)) { + items [item.id].hide_destroy (); + items.unset (item.id); } - if (items_checked.has_key (item.id_string)) { - items_checked [item.id_string].hide_destroy (); - items_checked.unset (item.id_string); + if (items_checked.has_key (item.id)) { + items_checked [item.id].hide_destroy (); + items_checked.unset (item.id); } } @@ -448,9 +448,9 @@ public class Layouts.SectionRow : Gtk.ListBoxRow { public void add_complete_item (Objects.Item item) { if (section.project.show_completed && item.checked) { - if (!items_checked.has_key (item.id_string)) { - items_checked [item.id_string] = new Layouts.ItemRow (item); - checked_listbox.append (items_checked [item.id_string]); + if (!items_checked.has_key (item.id)) { + items_checked [item.id] = new Layouts.ItemRow (item); + checked_listbox.append (items_checked [item.id]); } } } @@ -479,13 +479,13 @@ public class Layouts.SectionRow : Gtk.ListBoxRow { } public void add_item (Objects.Item item) { - if (!item.checked && !items.has_key (item.id_string)) { - items [item.id_string] = new Layouts.ItemRow (item); + if (!item.checked && !items.has_key (item.id)) { + items [item.id] = new Layouts.ItemRow (item); if (item.custom_order) { - listbox.insert (items [item.id_string], item.child_order); + listbox.insert (items [item.id], item.child_order); } else { - listbox.append (items [item.id_string]); + listbox.append (items [item.id]); } } }