summaryrefslogtreecommitdiff
path: root/src/contacts-main-window.vala
diff options
context:
space:
mode:
authorNiels De Graef <nielsdegraef@gmail.com>2021-01-11 19:22:17 +0100
committerNiels De Graef <nielsdegraef@gmail.com>2022-08-06 12:45:49 +0200
commitfcbc87c40406b322513c209844d3430bc4108b13 (patch)
tree74efc305e00d893a72b3c8fb22017435b6792111 /src/contacts-main-window.vala
parentf37176d5e04b3dc7cc2ad8cb9843d7d7fc35485e (diff)
downloadgnome-contacts-fcbc87c40406b322513c209844d3430bc4108b13.tar.gz
Enable importing & exporting VCards
This commit adds the experimental functionality in Contacts to import VCard (*.vcf) files. Since importing a contact means we have to take in untrusted/unvalidated input, let's give a high-level view of what happens: * Contacts starts a native file chooser dialog so the user can choose which file to import * According to the chosen file, Contacts will launch a subprocess to do the actual parsing using a `Contacts.Io.Parser`. At this point, we only have a single subclass, which allows importing VCards. * The helper process serializes the result to a `GLib.Variant`, and sends it to the main process, which will receive the result and parses it again. * After the parsing operation is done, we can then start up a `ImportOperation`, which will import the contacts using libfolks' API. Exporting contacts is quite a bit easier, since we don't have to deal with untrusted input: we serialize the list of selected contacts and asynchronously write each to the given output stream. In the app, that's a user chosen file; in tests, that can be a string. Fixes: https://gitlab.gnome.org/GNOME/gnome-contacts/-/issues/1 Fixes: https://gitlab.gnome.org/GNOME/gnome-contacts/-/issues/38
Diffstat (limited to 'src/contacts-main-window.vala')
-rw-r--r--src/contacts-main-window.vala56
1 files changed, 56 insertions, 0 deletions
diff --git a/src/contacts-main-window.vala b/src/contacts-main-window.vala
index e395d7e..42d2073 100644
--- a/src/contacts-main-window.vala
+++ b/src/contacts-main-window.vala
@@ -27,6 +27,7 @@ public class Contacts.MainWindow : Adw.ApplicationWindow {
{ "stop-editing-contact", stop_editing_contact, "b" },
{ "link-marked-contacts", link_marked_contacts },
{ "delete-marked-contacts", delete_marked_contacts },
+ { "export-marked-contacts", export_marked_contacts },
// { "share-contact", share_contact },
{ "unlink-contact", unlink_contact },
{ "delete-contact", delete_contact },
@@ -179,6 +180,9 @@ public class Contacts.MainWindow : Adw.ApplicationWindow {
unowned var action = lookup_action ("delete-marked-contacts");
((SimpleAction) action).set_enabled (n_selected > 0);
+ action = lookup_action ("export-marked-contacts");
+ ((SimpleAction) action).set_enabled (n_selected > 0);
+
action = lookup_action ("link-marked-contacts");
((SimpleAction) action).set_enabled (n_selected > 1);
@@ -543,6 +547,58 @@ public class Contacts.MainWindow : Adw.ApplicationWindow {
return toast;
}
+ private void export_marked_contacts (GLib.SimpleAction action, GLib.Variant? parameter) {
+ // Take a copy, since we'll unselect everything later
+ var selection = this.marked_contacts.get_selection ().copy ();
+
+ // Go back to normal state as much as possible
+ this.store.selection.unselect_all ();
+ this.marked_contacts.unselect_all ();
+ this.state = UiState.NORMAL;
+
+ // Open up a file chooser
+ var chooser = new Gtk.FileChooserNative (_("Export to file"),
+ this,
+ Gtk.FileChooserAction.SAVE,
+ _("_Export"),
+ _("_Cancel"));
+ chooser.set_current_name ("contacts.vcf");
+ chooser.modal = true;
+ chooser.response.connect ((response) => {
+ if (response != Gtk.ResponseType.ACCEPT) {
+ chooser.destroy ();
+ return;
+ }
+
+ // Do the actual export
+ var individuals = bitset_to_individuals (this.store.filter_model,
+ selection);
+
+ OutputStream filestream = null;
+ try {
+ filestream = chooser.get_file ().replace (null, false, FileCreateFlags.NONE);
+ } catch (Error err) {
+ warning ("Couldn't create file: %s", err.message);
+ return;
+ }
+
+ var op = new Io.VCardExportOperation (individuals, filestream);
+ this.operations.execute.begin (op, null, (obj, res) => {
+ try {
+ this.operations.execute.end (res);
+ filestream.close ();
+ } catch (Error e) {
+ warning ("ERROR: %s", e.message);
+ }
+ });
+
+ chooser.destroy ();
+ add_toast_for_operation (op);
+ });
+
+ chooser.show ();
+ }
+
// Little helper
private Gee.LinkedList<Individual> bitset_to_individuals (GLib.ListModel model,
Gtk.Bitset bitset) {