diff --git a/data/gresource.xml b/data/gresource.xml index 650e28c..1aacf16 100644 --- a/data/gresource.xml +++ b/data/gresource.xml @@ -44,6 +44,8 @@ icons/scalable/actions/tooth-contact-new-symbolic.svg icons/scalable/actions/tooth-dock-left-symbolic.svg icons/scalable/actions/tooth-about-symbolic.svg + icons/scalable/actions/tooth-error-symbolic.svg + icons/scalable/actions/tooth-minus-large-symbolic.svg gtk/dropdown/icon.ui gtk/dropdown/full.ui @@ -59,14 +61,12 @@ ui/widgets/profile_field_row.ui ui/widgets/timeline_menu.ui ui/widgets/list_item.ui - ui/widgets/list_editor_item.ui ui/widgets/compose_attachment.ui ui/widgets/votebox.ui ui/dialogs/new_account.ui ui/dialogs/compose.ui ui/dialogs/main.ui ui/dialogs/preferences.ui - ui/dialogs/list_editor.ui ui/menus.ui diff --git a/data/icons/scalable/actions/tooth-error-symbolic.svg b/data/icons/scalable/actions/tooth-error-symbolic.svg new file mode 100644 index 0000000..10ac209 --- /dev/null +++ b/data/icons/scalable/actions/tooth-error-symbolic.svg @@ -0,0 +1,2 @@ + + diff --git a/data/icons/scalable/actions/tooth-minus-large-symbolic.svg b/data/icons/scalable/actions/tooth-minus-large-symbolic.svg new file mode 100644 index 0000000..09943ae --- /dev/null +++ b/data/icons/scalable/actions/tooth-minus-large-symbolic.svg @@ -0,0 +1,2 @@ + + diff --git a/data/style.css b/data/style.css index a19a50e..d4866de 100644 --- a/data/style.css +++ b/data/style.css @@ -144,3 +144,7 @@ .ttl-profile-stat-button:last-child { border-bottom-right-radius: 12px; } + +.ttl-box-no-shadow > revealer > box { + box-shadow: none; +} diff --git a/data/ui/dialogs/list_editor.ui b/data/ui/dialogs/list_editor.ui deleted file mode 100644 index 81093c6..0000000 --- a/data/ui/dialogs/list_editor.ui +++ /dev/null @@ -1,241 +0,0 @@ - - - - - - diff --git a/data/ui/menus.ui b/data/ui/menus.ui index cbb3a7c..3c8c767 100644 --- a/data/ui/menus.ui +++ b/data/ui/menus.ui @@ -106,6 +106,11 @@ --> + + Lists + view.ar_list + action-disabled + Refresh app.refresh diff --git a/data/ui/widgets/list_editor_item.ui b/data/ui/widgets/list_editor_item.ui deleted file mode 100644 index 054959b..0000000 --- a/data/ui/widgets/list_editor_item.ui +++ /dev/null @@ -1,72 +0,0 @@ - - - - - diff --git a/install.sh b/install.sh index 0777dc1..9636cc8 100755 --- a/install.sh +++ b/install.sh @@ -1,7 +1,7 @@ #! /bin/sh set -e -meson build --prefix=/usr +meson setup build --prefix=/usr cd build ninja sudo ninja install diff --git a/meson.build b/meson.build index 6b8084f..a99df01 100644 --- a/meson.build +++ b/meson.build @@ -65,7 +65,6 @@ sources = files( 'src/Dialogs/Composer/EditorPage.vala', 'src/Dialogs/Composer/Page.vala', 'src/Dialogs/Composer/PollPage.vala', - 'src/Dialogs/ListEditor.vala', 'src/Dialogs/MainWindow.vala', 'src/Dialogs/NewAccount.vala', 'src/Dialogs/Preferences.vala', diff --git a/src/API/List.vala b/src/API/List.vala index 8d25609..e42caaf 100644 --- a/src/API/List.vala +++ b/src/API/List.vala @@ -4,6 +4,7 @@ public class Tooth.API.List : Entity, Widgetizable { public string id { get; set; } public string title { get; set; } + public string? replies_policy { get; set; default = null; } public static List from (Json.Node node) throws Error { return Entity.from_json (typeof (API.List), node) as API.List; diff --git a/src/Dialogs/ListEditor.vala b/src/Dialogs/ListEditor.vala deleted file mode 100644 index 4505b31..0000000 --- a/src/Dialogs/ListEditor.vala +++ /dev/null @@ -1,280 +0,0 @@ -using Gtk; - -[GtkTemplate (ui = "/dev/geopjr/tooth/ui/dialogs/list_editor.ui")] -public class Tooth.Dialogs.ListEditor: Adw.Window { - - [GtkTemplate (ui = "/dev/geopjr/tooth/ui/widgets/list_editor_item.ui")] - class Item : ListBoxRow { - - public ListEditor editor { get; construct set; } - public API.Account acc { get; construct set; } - public bool committed { get; construct set; } - - [GtkChild] unowned Widgets.RichLabel label; - [GtkChild] unowned Widgets.RichLabel handle; - [GtkChild] unowned ToggleButton status; - - public Item (ListEditor editor, API.Account acc, bool committed) { - this.editor = editor; - this.acc = acc; - this.committed = committed; - acc.bind_property ("display-name", label, "text", BindingFlags.SYNC_CREATE); - acc.bind_property ("handle", handle, "text", BindingFlags.SYNC_CREATE); - status.active = committed; - status.sensitive = true; - } - - [GtkCallback] - void on_toggled () { - if (!status.sensitive) - return; - - if (status.active) { - debug (@"To add: $(acc.id)"); - editor.to_add.add (acc.id); - editor.to_remove.remove (acc.id); - } - else { - debug (@"To remove: $(acc.id)"); - editor.to_add.remove (acc.id); - editor.to_remove.add (acc.id); - } - committed = status.active; - if (!editor.working) - editor.dirty = true; - } - - } - - public API.List list { get; set; } - public bool working { get; set; default = false; } - public bool exists { get; set; default = false; } - public bool dirty { get; set; default = false; } - - Soup.Message? search_req = null; - - public Gee.ArrayList to_add = new Gee.ArrayList (); - public Gee.ArrayList to_remove = new Gee.ArrayList (); - - [GtkChild] unowned Button save_btn; - [GtkChild] unowned Stack save_btn_stack; - [GtkChild] unowned Entry name_entry; - [GtkChild] unowned SearchEntry search_entry; - [GtkChild] unowned ListBox listbox; - - [GtkChild] unowned InfoBar infobar; - [GtkChild] unowned Label infobar_label; - - public signal void done (); - - construct { - transient_for = app.main_window; - show (); - } - - public ListEditor.empty () { - var obj = new API.List () { - title = _("Untitled") - }; - Object (list: obj); - init (); - } - - public ListEditor (API.List list) { - Object (list: list, working: true, exists: true); - init (); - - new Request.GET (@"/api/v1/lists/$(list.id)/accounts") - .with_account (accounts.active) - .with_ctx (this) - .on_error (on_error) - .then ((sess, msg) => { - Network.parse_array (msg, node => { - var acc = API.Account.from (node); - add_account (acc, true); - }); - working = false; - }) - .exec (); - } - - void init () { - notify["working"].connect (on_state_changed); - list.bind_property ("title", name_entry, "text", BindingFlags.SYNC_CREATE); - - ulong dirty_sigid = 0; - dirty_sigid = name_entry.changed.connect (() => { - dirty = true; - name_entry.disconnect (dirty_sigid); - }); - - on_state_changed (null); - } - - void on_state_changed (ParamSpec? p) { - save_btn_stack.visible_child_name = working ? "working" : "done"; - save_btn.sensitive = search_entry.sensitive = name_entry.sensitive = !working; - } - - void on_error (int32 code, string msg) { - warning (msg); - infobar_label.label = msg; - infobar.revealed = true; - } - - [GtkCallback] - void infobar_response (int i) { - infobar.revealed = false; - } - - void request_search (string q) { - debug (@"Searching for: \"$q\"..."); - if (search_req != null) { - network.cancel (search_req); - search_req = null; - } - - search_req = new Request.GET ("/api/v1/accounts/search") - .with_account (accounts.active) - .with_ctx (this) - .with_param ("resolve", "false") - .with_param ("limit", "8") - .with_param ("following", "true") - .with_param ("q", q) - .then ((sess, msg) => { - Network.parse_array (msg, node => { - var acc = API.Account.from (node); - add_account (acc, false, 0); - }); - }) - .on_error (on_error) - .exec (); - } - - void add_account (API.Account acc, bool added, int order = -1) { - var exists = false; - // listbox.@foreach (w => { - // var i = w as Item; - // if (i != null) { - // if (i.acc.id == acc.id) - // exists = true; - // } - // }); - - if (!exists) { - var item = new Item (this, acc, added); - listbox.insert (item, order); - } - } - - void invalidate () { - // listbox.@foreach (w => { - // var i = w as Item; - // if (i != null) { - // if (!i.committed) - // i.destroy (); - // } - // }); - } - - - [GtkCallback] - void validate () { - var has_title = name_entry.text.replace (" ", "") != ""; - save_btn.sensitive = has_title; - } - - [GtkCallback] - void on_cancel_clicked () { - if (dirty) { - var dlg = app.question ( - _("Discard changes?"), - _("You need to save the list if you want to keep them."), - this, - _("Discard"), - Adw.ResponseAppearance.DESTRUCTIVE - ); - - dlg.response.connect(res => { - if (res == "yes") { - destroy (); - } - dlg.destroy(); - }); - - dlg.present (); - } - else - destroy (); - } - - [GtkCallback] - void on_search_changed () { - var q = search_entry.text.chug ().chomp (); - - if (q.char_count () < 3) - invalidate (); - else if (q != "") { - invalidate (); - request_search (q); - } - } - - [GtkCallback] - void on_save_clicked () { - working = true; - transaction.begin ((obj, res) => { - try { - transaction.end (res); - - done (); - destroy (); - } - catch (Error e) { - working = false; - on_error (0, e.message); - } - }); - } - - async void transaction () throws Error { - if (!exists) { - message ("Creating list..."); - var req = new Request.POST ("/api/v1/lists") - .with_account (accounts.active) - .with_param ("title", name_entry.text); - yield req.await (); - - message ("Received new List entity"); - var node = network.parse_node (req); - list = API.List.from (node); - } - else { - message ("Updating list title..."); - yield new Request.PUT (@"/api/v1/lists/$(list.id)") - .with_account (accounts.active) - .with_param ("title", name_entry.text) - .await (); - } - - if (!to_add.is_empty) { - message ("Adding accounts to list..."); - var id_array = Request.array2string (to_add, "account_ids"); - yield new Request.POST (@"/api/v1/lists/$(list.id)/accounts/?$id_array") - .with_account (accounts.active) - .await (); - } - - if (!to_remove.is_empty) { - message ("Removing accounts from list..."); - var id_array = Request.array2string (to_remove, "account_ids"); - yield new Request.DELETE (@"/api/v1/lists/$(list.id)/accounts/?$id_array") - .with_account (accounts.active) - .await (); - } - - message ("OK: List updated"); - list.title = name_entry.text; - } - -} diff --git a/src/Views/Lists.vala b/src/Views/Lists.vala index 24bf144..692bb3c 100644 --- a/src/Views/Lists.vala +++ b/src/Views/Lists.vala @@ -3,32 +3,58 @@ using Gtk; // TODO: Lists is borken public class Tooth.Views.Lists : Views.Timeline { - [GtkTemplate (ui = "/dev/geopjr/tooth/ui/widgets/list_item.ui")] - public class Row : ListBoxRow { + public class Row : Adw.ActionRow { + public API.List? list; + Button delete_button; + Button edit_button; - API.List? list; + construct { + var action_box = new Box(Orientation.HORIZONTAL, 6); - [GtkChild] unowned Stack stack; - [GtkChild] unowned Label title; + edit_button = new Button() { + icon_name = "tooth-edit-symbolic", + valign = Align.CENTER, + halign = Align.CENTER + }; + edit_button.add_css_class("flat"); + edit_button.add_css_class("circular"); + + delete_button = new Button() { + icon_name = "tooth-trash-symbolic", + valign = Align.CENTER, + halign = Align.CENTER + }; + delete_button.add_css_class("flat"); + delete_button.add_css_class("circular"); + delete_button.add_css_class("error"); + delete_button.clicked.connect(on_remove_clicked); + + // this.apply.connect(on_apply); + action_box.append(edit_button); + action_box.append(delete_button); + + this.activated.connect(() => open()); + this.activatable = true; + + this.add_suffix(action_box); + } public Row (API.List? list) { this.list = list; - if (list == null) - stack.visible_child_name = "add"; - else - list.bind_property ("title", title, "label", BindingFlags.SYNC_CREATE); + if (list != null) { + this.list.bind_property ("title", this, "title", BindingFlags.SYNC_CREATE); + edit_button.clicked.connect(() => { + create_edit_preferences_window(this.list).show(); + }); + } } - [GtkCallback] - void on_edit_clicked () { - new Dialogs.ListEditor (this.list); - } + public virtual signal void remove_from_model () {} - [GtkCallback] void on_remove_clicked () { var remove = app.question ( - _("Delete \"%s\"?").printf (list.title), + _("Delete \"%s\"?").printf (this.list.title), _("This action cannot be reverted."), app.main_window, _("Delete"), @@ -38,9 +64,12 @@ public class Tooth.Views.Lists : Views.Timeline { remove.response.connect(res => { if (res == "yes") { new Request.DELETE (@"/api/v1/lists/$(list.id)") - .with_account (accounts.active) - .then (() => { this.destroy (); }) - .exec (); + .with_account (accounts.active) + .then (() => { + remove_from_model(); + this.destroy (); + }) + .exec (); } remove.destroy(); }); @@ -48,6 +77,171 @@ public class Tooth.Views.Lists : Views.Timeline { remove.present (); } + public Adw.PreferencesWindow create_edit_preferences_window(API.List t_list) { + var edit_preferences_window = new Adw.PreferencesWindow() { + modal = true, + title = _("Edit \"%s\"").printf (t_list.title), + transient_for = app.main_window + }; + var list_settings_page_general = new Adw.PreferencesPage() { + icon_name = "tooth-gear-symbolic", + title = _("General") + }; + var info_group = new Adw.PreferencesGroup() { + title = _("Info") + }; + var title_row = new Adw.EntryRow() { + input_purpose = InputPurpose.FREE_FORM, + title = _("List Name"), + text = t_list.title + }; + info_group.add(title_row); + list_settings_page_general.add(info_group); + + string? replies_policy_active = null; + if (t_list.replies_policy != null) { + var replies_group = new Adw.PreferencesGroup() { + title = _("Replies Policy"), + description = _("Show member replies to") + }; + var none_radio = new CheckButton(); + var none_row = new Adw.ActionRow() { + title = _("Nobody"), + activatable_widget = none_radio + }; + none_row.add_prefix(none_radio); + none_radio.toggled.connect(() => { + if (none_radio.active) + replies_policy_active = "none"; + }); + + var list_radio = new CheckButton(); + list_radio.group = none_radio; + var list_row = new Adw.ActionRow() { + title = _("Other members of the list"), + activatable_widget = list_radio + }; + list_row.add_prefix(list_radio); + list_radio.toggled.connect(() => { + if (list_radio.active) + replies_policy_active = "list"; + }); + + var followed_radio = new CheckButton(); + followed_radio.group = none_radio; + var followed_row = new Adw.ActionRow() { + title = _("Any followed user"), + activatable_widget = followed_radio + }; + followed_row.add_prefix(followed_radio); + followed_radio.toggled.connect(() => { + if (followed_radio.active) + replies_policy_active = "followed"; + }); + + switch (t_list.replies_policy) { + case "none": + none_radio.active = true; + break; + case "followed": + followed_radio.active = true; + break; + default: + list_radio.active = true; + break; + } + + replies_group.add(none_row); + replies_group.add(list_row); + replies_group.add(followed_row); + + list_settings_page_general.add(replies_group); + } + + var to_remove = new Gee.ArrayList(); + new Request.GET (@"/api/v1/lists/$(t_list.id)/accounts") + .with_account (accounts.active) + .then ((sess, msg) => { + if (Network.get_array_size(msg) > 0) { + var list_settings_page_members = new Adw.PreferencesPage() { + icon_name = "tooth-people-symbolic", + title = _("Members") + }; + + var rm_group = new Adw.PreferencesGroup() { + title = _("Remove Members") + }; + + Network.parse_array (msg, node => { + var member = API.Account.from (node); + var avi = new Widgets.Avatar() { + account = member, + size = 32 + }; + var m_switch = new Switch() { + active = true, + state = true, + valign = Align.CENTER, + halign = Align.CENTER + }; + m_switch.state_set.connect((x) => { + if (!x) { + to_remove.add(member.id); + } else if (to_remove.contains(member.id)) { + to_remove.remove(member.id); + } + + return x; + }); + + var member_row = new Adw.ActionRow() { + title = member.full_handle + }; + member_row.add_prefix(avi); + member_row.add_suffix(m_switch); + + rm_group.add(member_row); + }); + + list_settings_page_members.add(rm_group); + edit_preferences_window.add(list_settings_page_members); + } + }) + .exec(); + + edit_preferences_window.add(list_settings_page_general); + + edit_preferences_window.close_request.connect(() => { + on_apply(t_list, title_row.text, replies_policy_active, to_remove); + edit_preferences_window.hide(); + edit_preferences_window.destroy(); + return false; + }); + + return edit_preferences_window; + } + + public void on_apply(API.List t_list, string title, string? replies_policy, Gee.ArrayList to_remove) { + if (t_list.title != title || t_list.replies_policy != replies_policy) { + this.list.title = title; + this.list.replies_policy = replies_policy; + new Request.PUT (@"/api/v1/lists/$(t_list.id)") + .with_account (accounts.active) + .with_param ("title", title) + .with_param ("replies_policy", replies_policy) + .then(() => {}) + .exec (); + } + + if (to_remove.size > 0) { + var id_array = Request.array2string (to_remove, "account_ids"); + new Request.DELETE (@"/api/v1/lists/$(t_list.id)/accounts/?$id_array") + .with_account (accounts.active) + .then(() => {}) + .exec (); + } + } + public virtual signal void open () { if (this.list == null) return; @@ -61,6 +255,25 @@ public class Tooth.Views.Lists : Views.Timeline { get { return false; } } + public override Widget on_create_model_widget(Object obj) { + var widget = base.on_create_model_widget(obj); + var widget_row = widget as Row; + + if (widget_row != null) + widget_row.remove_from_model.connect(() => remove_list(widget_row.list)); + + return widget; + } + + public void remove_list(API.List? list) { + if (list == null) return; + + uint indx; + var found = model.find (list, out indx); + if (found) + model.remove(indx); + } + public Lists () { Object ( url: @"/api/v1/lists", @@ -70,14 +283,58 @@ public class Tooth.Views.Lists : Views.Timeline { accepts = typeof (API.List); } + public void create_list(string list_name) { + new Request.POST ("/api/v1/lists") + .with_account (accounts.active) + .with_param ("title", list_name) + .then ((sess, msg) => { + var node = network.parse_node (msg); + var list = API.List.from (node); + model.insert (0, list); + }) + .exec (); + } + + public void on_action_bar_activate(EntryBuffer buffer) { + if (buffer.length > 0) + create_list(buffer.text); + buffer.set_text("".data); + } + + construct { + var add_action_bar = new ActionBar (); + add_action_bar.add_css_class("ttl-box-no-shadow"); + + var child_box = new Box(Orientation.HORIZONTAL, 6); + var child_entry = new Entry() { + input_purpose = InputPurpose.FREE_FORM, + placeholder_text = _("New list title") + }; + var add_button = new Button.with_label (_("Add list")) { + sensitive = false + }; + + add_button.clicked.connect(() => { + on_action_bar_activate(child_entry.buffer); + }); + child_entry.activate.connect(() => { + on_action_bar_activate(child_entry.buffer); + }); + + child_entry.buffer.bind_property("length", add_button, "sensitive", BindingFlags.SYNC_CREATE, (b, src, ref target) => { + target.set_boolean ((uint) src > 0); + return true; + }); + + child_box.append(child_entry); + child_box.append(add_button); + + add_action_bar.set_center_widget(child_box); + insert_child_after (add_action_bar, header); + } + public override void on_request_finish () { - var add_row = new Row (null); - add_row.open.connect (() => { - var dlg = new Dialogs.ListEditor.empty (); - dlg.done.connect (on_refresh); - }); - append (add_row); - on_content_changed (); + on_content_changed (); } } diff --git a/src/Views/Profile.vala b/src/Views/Profile.vala index 1598030..a0c98be 100644 --- a/src/Views/Profile.vala +++ b/src/Views/Profile.vala @@ -18,6 +18,7 @@ public class Tooth.Views.Profile : Views.Timeline { protected SimpleAction hiding_reblogs_action; protected SimpleAction blocking_action; protected SimpleAction domain_blocking_action; + protected SimpleAction ar_list_action; // protected SimpleAction source_action; construct { @@ -35,6 +36,7 @@ public class Tooth.Views.Profile : Views.Timeline { ); cover.bind (profile); build_profile_stats(cover.info); + rs.invalidated.connect (() => invalidate_actions(false)); } [GtkTemplate (ui = "/dev/geopjr/tooth/ui/views/profile_header.ui")] @@ -164,6 +166,11 @@ public class Tooth.Views.Profile : Views.Timeline { // invalidate_actions (true); // }); // actions.add_action (source_action); + ar_list_action = new SimpleAction ("ar_list", null); + ar_list_action.activate.connect (v => { + create_ar_list_dialog().show(); + }); + actions.add_action (ar_list_action); var mention_action = new SimpleAction ("mention", VariantType.STRING); mention_action.activate.connect (v => { @@ -268,6 +275,7 @@ public class Tooth.Views.Profile : Views.Timeline { blocking_action.set_state (rs.blocking); domain_blocking_action.set_state (rs.domain_blocking); domain_blocking_action.set_enabled (accounts.active.domain != profile.domain); + ar_list_action.set_enabled(profile.id != accounts.active.id && rs.following); if (refresh) { page_next = null; @@ -320,4 +328,142 @@ public class Tooth.Views.Profile : Views.Timeline { network.on_error); } + public class RowButton : Button { + public bool remove { get; set; default = false; } + } + + public Adw.Window create_ar_list_dialog() { + var spinner = new Spinner() { + spinning = true, + halign = Align.CENTER, + valign = Align.CENTER, + vexpand = true, + hexpand = true, + width_request = 32, + height_request = 32 + }; + var box = new Box(Orientation.VERTICAL, 6); + var headerbar = new Adw.HeaderBar(); + var toast_overlay = new Adw.ToastOverlay() { + vexpand = true, + valign = Align.CENTER + }; + toast_overlay.child = spinner; + + box.append(headerbar); + box.append(toast_overlay); + var dialog = new Adw.Window() { + title = _("Add or remove \"%s\" to or from a list").printf (profile.handle), + modal = true, + transient_for = app.main_window, + content = box, + default_width = 600, + default_height = 550 + }; + spinner.start(); + + var preferences_page = new Adw.PreferencesPage(); + var preferences_group = new Adw.PreferencesGroup() { + title = _("Select the list to add or remove \"%s\" to or from:").printf (profile.handle) + }; + + var no_lists_page = new Adw.StatusPage() { + icon_name = "tooth-error-symbolic", + vexpand = true, + title = _("You don't have any lists") + }; + + new Request.GET (@"/api/v1/lists/") + .with_account (accounts.active) + .with_ctx (this) + .on_error (on_error) + .then ((sess, msg) => { + if (Network.get_array_size(msg) > 0) { + new Request.GET (@"/api/v1/accounts/$(profile.id)/lists") + .with_account (accounts.active) + .with_ctx (this) + .on_error (on_error) + .then ((sess2, msg2) => { + var added = false; + var in_list = new Gee.ArrayList(); + + Network.parse_array (msg2, node => { + var list = API.List.from (node); + in_list.add(list.id); + }); + Network.parse_array (msg, node => { + var list = API.List.from (node); + var is_already = in_list.contains(list.id); + + var add_button = new RowButton() { + icon_name = is_already ? "tooth-minus-large-symbolic" : "tooth-plus-large-symbolic", + tooltip_text = is_already ? _("Remove \"%s\" from \"%s\"").printf (profile.handle, list.title) : _("Add \"%s\" to \"%s\"").printf (profile.handle, list.title), + halign = Align.CENTER, + valign = Align.CENTER + }; + add_button.add_css_class("flat"); + add_button.add_css_class("circular"); + add_button.remove = is_already; + + var row = new Adw.ActionRow() { + title = list.title + }; + row.add_suffix(add_button); + + add_button.clicked.connect(() => { + handle_list_edit(list, row, toast_overlay, add_button); + }); + + preferences_group.add(row); + added = true; + }); + + if (added) { + preferences_page.add(preferences_group); + + toast_overlay.child = preferences_page; + toast_overlay.valign = Align.FILL; + } else { + toast_overlay.child = no_lists_page; + } + }) + .exec(); + } else { + toast_overlay.child = no_lists_page; + } + }) + .exec (); + + return dialog; + } + + public void handle_list_edit(API.List list, Adw.ActionRow row, Adw.ToastOverlay toast_overlay, RowButton button) { + row.sensitive = false; + + var endpoint = @"/api/v1/lists/$(list.id)/accounts/?account_ids[]=$(profile.id)"; + var req = button.remove ? new Request.DELETE (endpoint) : new Request.POST (endpoint); + req + .with_account (accounts.active) + .with_ctx (this) + .on_error (on_error) + .then ((sess, msg) => { + var toast_msg = ""; + if (button.remove) { + toast_msg = _("User \"%s\" got removed from \"%s\"").printf (profile.handle, list.title); + button.icon_name = "tooth-plus-large-symbolic"; + button.tooltip_text = _("Add \"%s\" to \"%s\"").printf (profile.handle, list.title); + } else { + toast_msg = _("User \"%s\" got added to \"%s\"").printf (profile.handle, list.title); + button.icon_name = "tooth-minus-large-symbolic"; + button.tooltip_text = _("Remove \"%s\" from \"%s\"").printf (profile.handle, list.title); + } + + button.remove = !button.remove; + row.sensitive = true; + + var toast = new Adw.Toast(toast_msg); + toast_overlay.add_toast(toast); + }) + .exec(); + } }