From d9b91206c0291fa8aa58df572292784a4f8ff878 Mon Sep 17 00:00:00 2001 From: fiaxh Date: Sat, 28 Oct 2017 22:02:32 +0200 Subject: Keep MUC join dialog open until joined, show errors --- .../ui/add_conversation/accounts_combo_box.vala | 57 ------ .../ui/add_conversation/add_conference_dialog.vala | 172 ++++++++++++++++++ .../ui/add_conversation/add_contact_dialog.vala | 58 ++++++ .../ui/add_conversation/add_groupchat_dialog.vala | 79 +++++++++ .../add_conversation/chat/add_contact_dialog.vala | 58 ------ main/src/ui/add_conversation/chat/dialog.vala | 78 --------- main/src/ui/add_conversation/chat/roster_list.vala | 94 ---------- .../conference/add_groupchat_dialog.vala | 82 --------- .../conference/conference_details_fragment.vala | 144 --------------- .../conference/conference_list.vala | 103 ----------- .../src/ui/add_conversation/conference/dialog.vala | 162 ----------------- .../conference_details_fragment.vala | 195 +++++++++++++++++++++ main/src/ui/add_conversation/conference_list.vala | 103 +++++++++++ main/src/ui/add_conversation/list_row.vala | 2 +- main/src/ui/add_conversation/roster_list.vala | 94 ++++++++++ .../ui/add_conversation/select_contact_dialog.vala | 94 ++++++++++ .../ui/add_conversation/select_jid_fragment.vala | 2 +- main/src/ui/application.vala | 12 +- main/src/ui/conversation_list_titlebar.vala | 10 +- main/src/ui/conversation_selector/list.vala | 2 + main/src/ui/notifications.vala | 2 +- main/src/ui/occupant_menu/view.vala | 2 +- main/src/ui/util/accounts_combo_box.vala | 57 ++++++ 23 files changed, 865 insertions(+), 797 deletions(-) delete mode 100644 main/src/ui/add_conversation/accounts_combo_box.vala create mode 100644 main/src/ui/add_conversation/add_conference_dialog.vala create mode 100644 main/src/ui/add_conversation/add_contact_dialog.vala create mode 100644 main/src/ui/add_conversation/add_groupchat_dialog.vala delete mode 100644 main/src/ui/add_conversation/chat/add_contact_dialog.vala delete mode 100644 main/src/ui/add_conversation/chat/dialog.vala delete mode 100644 main/src/ui/add_conversation/chat/roster_list.vala delete mode 100644 main/src/ui/add_conversation/conference/add_groupchat_dialog.vala delete mode 100644 main/src/ui/add_conversation/conference/conference_details_fragment.vala delete mode 100644 main/src/ui/add_conversation/conference/conference_list.vala delete mode 100644 main/src/ui/add_conversation/conference/dialog.vala create mode 100644 main/src/ui/add_conversation/conference_details_fragment.vala create mode 100644 main/src/ui/add_conversation/conference_list.vala create mode 100644 main/src/ui/add_conversation/roster_list.vala create mode 100644 main/src/ui/add_conversation/select_contact_dialog.vala create mode 100644 main/src/ui/util/accounts_combo_box.vala (limited to 'main/src') diff --git a/main/src/ui/add_conversation/accounts_combo_box.vala b/main/src/ui/add_conversation/accounts_combo_box.vala deleted file mode 100644 index 5fdd18e6..00000000 --- a/main/src/ui/add_conversation/accounts_combo_box.vala +++ /dev/null @@ -1,57 +0,0 @@ -using Gee; -using Gtk; - -using Dino.Entities; - -namespace Dino.Ui { - -class AccountComboBox : ComboBox { - - public Account? selected { - get { - TreeIter selected; - if (get_active_iter(out selected)) { - Value value; - list_store.get_value(selected, 1, out value); - return value as Account; - } - return null; - } - set { - TreeIter iter; - if (list_store.get_iter_first(out iter)) { - int i = 0; - do { - Value val; - list_store.get_value(iter, 1, out val); - if ((val as Account).equals(value)) { - active = i; - break; - } - i++; - } while (list_store.iter_next(ref iter)); - } - } - } - - private StreamInteractor? stream_interactor; - private Gtk.ListStore list_store = new Gtk.ListStore(2, typeof(string), typeof(Account)); - - public void initialize(StreamInteractor stream_interactor) { - this.stream_interactor = stream_interactor; - - CellRendererText renderer = new Gtk.CellRendererText(); - pack_start(renderer, true); - add_attribute(renderer, "text", 0); - - TreeIter iter; - foreach (Account account in stream_interactor.get_accounts()) { - list_store.append(out iter); - list_store.set(iter, 0, account.bare_jid.to_string(), 1, account); - } - set_model(list_store); - active = 0; - } -} - -} diff --git a/main/src/ui/add_conversation/add_conference_dialog.vala b/main/src/ui/add_conversation/add_conference_dialog.vala new file mode 100644 index 00000000..5e5698fb --- /dev/null +++ b/main/src/ui/add_conversation/add_conference_dialog.vala @@ -0,0 +1,172 @@ +using Gee; +using Gtk; + +using Dino.Entities; + +namespace Dino.Ui { + +public class AddConferenceDialog : Gtk.Dialog { + + public signal void conversation_opened(Conversation conversation); + + private Stack stack = new Stack(); + private Button cancel_button; + private Button ok_button; + private Label cancel_label = new Label(_("Cancel")) {visible=true}; + private Image cancel_image = new Image.from_icon_name("go-previous-symbolic", IconSize.MENU) {visible=true}; + + private SelectJidFragment select_fragment; + private ConferenceDetailsFragment details_fragment; + private ConferenceList conference_list; + + private StreamInteractor stream_interactor; + + public AddConferenceDialog(StreamInteractor stream_interactor) { + Object(use_header_bar : 1); + this.title = _("Join Conference"); + this.modal = true; + this.stream_interactor = stream_interactor; + + stack.visible = true; + stack.vhomogeneous = false; + get_content_area().add(stack); + + setup_headerbar(); + setup_jid_add_view(); + setup_conference_details_view(); + show_jid_add_view(); + + stream_interactor.get_module(MucManager.IDENTITY).joined.connect((account, jid, nick) => { Idle.add(() => { on_joined(account, jid, nick); return false; } ); }); + } + + private void show_jid_add_view() { + if (cancel_image.get_parent() != null) cancel_button.remove(cancel_image); + cancel_button.add(cancel_label); + cancel_button.clicked.disconnect(show_jid_add_view); + cancel_button.clicked.connect(on_cancel); + ok_button.label = _("Next"); + ok_button.sensitive = select_fragment.done; + ok_button.clicked.disconnect(on_ok_button_clicked); + ok_button.clicked.connect(on_next_button_clicked); + details_fragment.notify["done"].disconnect(set_ok_sensitive_from_details); + select_fragment.notify["done"].connect(set_ok_sensitive_from_select); + stack.transition_type = StackTransitionType.SLIDE_RIGHT; + stack.set_visible_child_name("select"); + } + + private void show_conference_details_view() { + if (cancel_label.get_parent() != null) cancel_button.remove(cancel_label); + cancel_button.add(cancel_image); + cancel_button.clicked.disconnect(on_cancel); + cancel_button.clicked.connect(show_jid_add_view); + ok_button.label = _("Join"); + ok_button.sensitive = details_fragment.done; + ok_button.clicked.disconnect(on_next_button_clicked); + ok_button.clicked.connect(on_ok_button_clicked); + select_fragment.notify["done"].disconnect(set_ok_sensitive_from_select); + details_fragment.notify["done"].connect(set_ok_sensitive_from_details); + stack.transition_type = StackTransitionType.SLIDE_LEFT; + stack.set_visible_child_name("details"); + animate_window_resize(); + } + + private void setup_headerbar() { + HeaderBar header_bar = get_header_bar() as HeaderBar; + header_bar.show_close_button = false; + + cancel_button = new Button(); + header_bar.pack_start(cancel_button); + cancel_button.visible = true; + + ok_button = new Button(); + header_bar.pack_end(ok_button); + ok_button.get_style_context().add_class("suggested-action"); + ok_button.visible = true; + ok_button.can_focus = true; + ok_button.can_default = true; + ok_button.has_default = true; + } + + private void setup_jid_add_view() { + conference_list = new ConferenceList(stream_interactor); + conference_list.row_activated.connect(() => { ok_button.clicked(); }); + select_fragment = new SelectJidFragment(stream_interactor, conference_list, stream_interactor.get_accounts()); + select_fragment.add_jid.connect((row) => { + AddGroupchatDialog dialog = new AddGroupchatDialog(stream_interactor); + dialog.set_transient_for(this); + dialog.present(); + }); + select_fragment.remove_jid.connect((row) => { + ConferenceListRow conference_row = row as ConferenceListRow; + stream_interactor.get_module(MucManager.IDENTITY).remove_bookmark(conference_row.account, conference_row.bookmark); + }); + stack.add_named(select_fragment, "select"); + } + + private void setup_conference_details_view() { + details_fragment = new ConferenceDetailsFragment(stream_interactor, ok_button); + stack.add_named(details_fragment, "details"); + } + + private void set_ok_sensitive_from_select() { + ok_button.sensitive = select_fragment.done; + } + + private void set_ok_sensitive_from_details() { + ok_button.sensitive = select_fragment.done; + } + + private void on_next_button_clicked() { + details_fragment.clear(); + ListRow? row = conference_list.get_selected_row() as ListRow; + ConferenceListRow? conference_row = conference_list.get_selected_row() as ConferenceListRow; + if (conference_row != null) { + details_fragment.account = conference_row.account; + details_fragment.jid = conference_row.bookmark.jid; + details_fragment.nick = conference_row.bookmark.nick; + if (conference_row.bookmark.password != null) details_fragment.password = conference_row.bookmark.password; + ok_button.grab_focus(); + } else if (row != null) { + details_fragment.account = row.account; + details_fragment.jid = row.jid.to_string(); + details_fragment.set_editable(); + } + show_conference_details_view(); + } + + private void on_ok_button_clicked() { + stream_interactor.get_module(MucManager.IDENTITY).join(details_fragment.account, new Jid(details_fragment.jid), details_fragment.nick, details_fragment.password); + } + + private void on_joined(Account account, Jid jid, string nick) { + if (account.equals(details_fragment.account) && jid.equals_bare(new Jid(details_fragment.jid))) { + Conversation conversation = stream_interactor.get_module(ConversationManager.IDENTITY).create_conversation(jid, account, Conversation.Type.GROUPCHAT); + stream_interactor.get_module(ConversationManager.IDENTITY).start_conversation(conversation, true); + conversation_opened(conversation); + close(); + } + } + + private void on_cancel() { + close(); + } + + private void animate_window_resize() { + int def_height, curr_width, curr_height; + get_size(out curr_width, out curr_height); + stack.get_preferred_height(null, out def_height); + int difference = def_height - curr_height; + Timer timer = new Timer(); + Timeout.add((int) (stack.transition_duration / 30), + () => { + ulong microsec; + timer.elapsed(out microsec); + ulong millisec = microsec / 1000; + double partial = double.min(1, (double) millisec / stack.transition_duration); + resize(curr_width, (int) (curr_height + difference * partial)); + return millisec < stack.transition_duration; + }); + } +} + +} diff --git a/main/src/ui/add_conversation/add_contact_dialog.vala b/main/src/ui/add_conversation/add_contact_dialog.vala new file mode 100644 index 00000000..102cf18a --- /dev/null +++ b/main/src/ui/add_conversation/add_contact_dialog.vala @@ -0,0 +1,58 @@ +using Gee; +using Gtk; + +using Dino.Entities; + +namespace Dino.Ui { + +[GtkTemplate (ui = "/im/dino/add_conversation/add_contact_dialog.ui")] +protected class AddContactDialog : Gtk.Dialog { + + public Account? account { + get { return account_combobox.selected; } + set { account_combobox.selected = value; } + } + + public string jid { + get { return jid_entry.text; } + set { jid_entry.text = value; } + } + + [GtkChild] private AccountComboBox account_combobox; + [GtkChild] private Button ok_button; + [GtkChild] private Button cancel_button; + [GtkChild] private Entry jid_entry; + [GtkChild] private Entry alias_entry; + [GtkChild] private CheckButton subscribe_checkbutton; + + private StreamInteractor stream_interactor; + + public AddContactDialog(StreamInteractor stream_interactor) { + Object(use_header_bar : 1); + this.stream_interactor = stream_interactor; + account_combobox.initialize(stream_interactor); + + cancel_button.clicked.connect(() => { close(); }); + ok_button.clicked.connect(on_ok_button_clicked); + jid_entry.changed.connect(on_jid_entry_changed); + } + + private void on_ok_button_clicked() { + string? alias = alias_entry.text == "" ? null : alias_entry.text; + Jid jid = new Jid(jid_entry.text); + stream_interactor.get_module(RosterManager.IDENTITY).add_jid(account, jid, alias); + if (subscribe_checkbutton.active) { + stream_interactor.get_module(PresenceManager.IDENTITY).request_subscription(account, jid); + } + close(); + } + + private void on_jid_entry_changed() { + Jid parsed_jid = Jid.parse(jid_entry.text); + bool sensitive = parsed_jid != null && parsed_jid.resourcepart == null && + stream_interactor.get_module(RosterManager.IDENTITY).get_roster_item(account, parsed_jid) == null; + ok_button.set_sensitive(sensitive); + } +} + +} diff --git a/main/src/ui/add_conversation/add_groupchat_dialog.vala b/main/src/ui/add_conversation/add_groupchat_dialog.vala new file mode 100644 index 00000000..274ab6f9 --- /dev/null +++ b/main/src/ui/add_conversation/add_groupchat_dialog.vala @@ -0,0 +1,79 @@ +using Gee; +using Gtk; + +using Dino.Entities; + +namespace Dino.Ui { + +[GtkTemplate (ui = "/im/dino/add_conversation/add_groupchat_dialog.ui")] +protected class AddGroupchatDialog : Gtk.Dialog { + + [GtkChild] private Stack accounts_stack; + [GtkChild] private AccountComboBox account_combobox; + [GtkChild] private Label account_label; + [GtkChild] private Button ok_button; + [GtkChild] private Button cancel_button; + [GtkChild] private Entry jid_entry; + [GtkChild] private Entry alias_entry; + [GtkChild] private Entry nick_entry; + + private StreamInteractor stream_interactor; + private Xmpp.Xep.Bookmarks.Conference? edit_confrence = null; + private bool alias_entry_changed = false; + + public AddGroupchatDialog(StreamInteractor stream_interactor) { + Object(use_header_bar : 1); + this.stream_interactor = stream_interactor; + ok_button.label = _("Add"); + ok_button.get_style_context().add_class("suggested-action"); // TODO why doesn't it work in XML + accounts_stack.set_visible_child_name("combobox"); + account_combobox.initialize(stream_interactor); + + cancel_button.clicked.connect(() => { close(); }); + ok_button.clicked.connect(on_ok_button_clicked); + jid_entry.key_release_event.connect(on_jid_key_release); + nick_entry.key_release_event.connect(check_ok); + } + + public AddGroupchatDialog.for_conference(StreamInteractor stream_interactor, Account account, Xmpp.Xep.Bookmarks.Conference conference) { + this(stream_interactor); + edit_confrence = conference; + ok_button.label = _("Save"); + ok_button.sensitive = true; + accounts_stack.set_visible_child_name("label"); + account_label.label = account.bare_jid.to_string(); + account_combobox.selected = account; + jid_entry.text = conference.jid; + nick_entry.text = conference.nick ?? ""; + alias_entry.text = conference.name; + } + + private bool on_jid_key_release() { + check_ok(); + if (!alias_entry_changed) { + Jid? parsed_jid = Jid.parse(jid_entry.text); + alias_entry.text = parsed_jid != null && parsed_jid.localpart != null ? parsed_jid.localpart : jid_entry.text; + } + return false; + } + + private bool check_ok() { + Jid? parsed_jid = Jid.parse(jid_entry.text); + ok_button.sensitive = parsed_jid != null && parsed_jid.localpart != null && parsed_jid.resourcepart == null; + return false; + } + + private void on_ok_button_clicked() { + Xmpp.Xep.Bookmarks.Conference conference = new Xmpp.Xep.Bookmarks.Conference(jid_entry.text); + conference.nick = nick_entry.text != "" ? nick_entry.text : null; + conference.name = alias_entry.text; + if (edit_confrence == null) { + stream_interactor.get_module(MucManager.IDENTITY).add_bookmark(account_combobox.selected, conference); + } else { + stream_interactor.get_module(MucManager.IDENTITY).replace_bookmark(account_combobox.selected, edit_confrence, conference); + } + close(); + } +} + +} diff --git a/main/src/ui/add_conversation/chat/add_contact_dialog.vala b/main/src/ui/add_conversation/chat/add_contact_dialog.vala deleted file mode 100644 index 9c14883a..00000000 --- a/main/src/ui/add_conversation/chat/add_contact_dialog.vala +++ /dev/null @@ -1,58 +0,0 @@ -using Gee; -using Gtk; - -using Dino.Entities; - -namespace Dino.Ui.AddConversation.Chat { - -[GtkTemplate (ui = "/im/dino/add_conversation/add_contact_dialog.ui")] -protected class AddContactDialog : Gtk.Dialog { - - public Account? account { - get { return account_combobox.selected; } - set { account_combobox.selected = value; } - } - - public string jid { - get { return jid_entry.text; } - set { jid_entry.text = value; } - } - - [GtkChild] private AccountComboBox account_combobox; - [GtkChild] private Button ok_button; - [GtkChild] private Button cancel_button; - [GtkChild] private Entry jid_entry; - [GtkChild] private Entry alias_entry; - [GtkChild] private CheckButton subscribe_checkbutton; - - private StreamInteractor stream_interactor; - - public AddContactDialog(StreamInteractor stream_interactor) { - Object(use_header_bar : 1); - this.stream_interactor = stream_interactor; - account_combobox.initialize(stream_interactor); - - cancel_button.clicked.connect(() => { close(); }); - ok_button.clicked.connect(on_ok_button_clicked); - jid_entry.changed.connect(on_jid_entry_changed); - } - - private void on_ok_button_clicked() { - string? alias = alias_entry.text == "" ? null : alias_entry.text; - Jid jid = new Jid(jid_entry.text); - stream_interactor.get_module(RosterManager.IDENTITY).add_jid(account, jid, alias); - if (subscribe_checkbutton.active) { - stream_interactor.get_module(PresenceManager.IDENTITY).request_subscription(account, jid); - } - close(); - } - - private void on_jid_entry_changed() { - Jid parsed_jid = Jid.parse(jid_entry.text); - bool sensitive = parsed_jid != null && parsed_jid.resourcepart == null && - stream_interactor.get_module(RosterManager.IDENTITY).get_roster_item(account, parsed_jid) == null; - ok_button.set_sensitive(sensitive); - } -} - -} diff --git a/main/src/ui/add_conversation/chat/dialog.vala b/main/src/ui/add_conversation/chat/dialog.vala deleted file mode 100644 index 361f70ba..00000000 --- a/main/src/ui/add_conversation/chat/dialog.vala +++ /dev/null @@ -1,78 +0,0 @@ -using Gee; -using Gdk; -using Gtk; - -using Dino.Entities; - -namespace Dino.Ui.AddConversation.Chat { - -public class Dialog : Gtk.Dialog { - - public signal void selected(Account account, Jid jid); - - public Button ok_button; - - private RosterList roster_list; - private SelectJidFragment select_jid_fragment; - private StreamInteractor stream_interactor; - private Gee.List accounts; - - public Dialog(StreamInteractor stream_interactor, Gee.List accounts) { - Object(use_header_bar : 1); - modal = true; - - this.stream_interactor = stream_interactor; - this.accounts = accounts; - - setup_headerbar(); - setup_view(); - } - - public void set_filter(string str) { - select_jid_fragment.set_filter(str); - } - - private void setup_headerbar() { - HeaderBar header_bar = get_header_bar() as HeaderBar; - header_bar.show_close_button = false; - - Button cancel_button = new Button(); - cancel_button.set_label(_("Cancel")); - cancel_button.visible = true; - header_bar.pack_start(cancel_button); - - ok_button = new Button(); - ok_button.get_style_context().add_class("suggested-action"); - ok_button.sensitive = false; - ok_button.visible = true; - header_bar.pack_end(ok_button); - - cancel_button.clicked.connect(() => { close(); }); - ok_button.clicked.connect(() => { - ListRow? selected_row = roster_list.get_selected_row() as ListRow; - if (selected_row != null) selected(selected_row.account, selected_row.jid); - close(); - }); - } - - private void setup_view() { - roster_list = new RosterList(stream_interactor, accounts); - roster_list.row_activated.connect(() => { ok_button.clicked(); }); - select_jid_fragment = new SelectJidFragment(stream_interactor, roster_list, accounts); - select_jid_fragment.add_jid.connect((row) => { - AddContactDialog add_contact_dialog = new AddContactDialog(stream_interactor); - add_contact_dialog.set_transient_for(this); - add_contact_dialog.present(); - }); - select_jid_fragment.remove_jid.connect((row) => { - ListRow list_row = roster_list.get_selected_row() as ListRow; - stream_interactor.get_module(RosterManager.IDENTITY).remove_jid(list_row.account, list_row.jid); - }); - select_jid_fragment.notify["done"].connect(() => { - ok_button.sensitive = select_jid_fragment.done; - }); - get_content_area().add(select_jid_fragment); - } -} - -} diff --git a/main/src/ui/add_conversation/chat/roster_list.vala b/main/src/ui/add_conversation/chat/roster_list.vala deleted file mode 100644 index f4b42cc4..00000000 --- a/main/src/ui/add_conversation/chat/roster_list.vala +++ /dev/null @@ -1,94 +0,0 @@ -using Gee; -using Gtk; - -using Dino.Entities; -using Xmpp; - -namespace Dino.Ui.AddConversation.Chat { - -protected class RosterList : FilterableList { - - public signal void conversation_selected(Conversation? conversation); - private StreamInteractor stream_interactor; - private Gee.List accounts; - private ulong[] handler_ids = new ulong[0]; - - private HashMap> rows = new HashMap>(Account.hash_func, Account.equals_func); - - public RosterList(StreamInteractor stream_interactor, Gee.List accounts) { - this.stream_interactor = stream_interactor; - this.accounts = accounts; - - set_filter_func(filter); - set_header_func(header); - set_sort_func(sort); - - handler_ids += stream_interactor.get_module(RosterManager.IDENTITY).removed_roster_item.connect( (account, jid, roster_item) => { - if (accounts.contains(account)) { - Idle.add(() => { on_removed_roster_item(account, jid, roster_item); return false;}); - } - }); - handler_ids += stream_interactor.get_module(RosterManager.IDENTITY).updated_roster_item.connect( (account, jid, roster_item) => { - if (accounts.contains(account)) { - Idle.add(() => { on_updated_roster_item(account, jid, roster_item); return false;}); - } - }); - destroy.connect(() => { - foreach (ulong handler_id in handler_ids) stream_interactor.get_module(RosterManager.IDENTITY).disconnect(handler_id); - }); - - foreach (Account a in accounts) fetch_roster_items(a); - } - - private void on_removed_roster_item(Account account, Jid jid, Roster.Item roster_item) { - if (rows.has_key(account) && rows[account].has_key(jid)) { - remove(rows[account][jid]); - rows[account].unset(jid); - } - } - - private void on_updated_roster_item(Account account, Jid jid, Roster.Item roster_item) { - on_removed_roster_item(account, jid, roster_item); - ListRow row = new ListRow.from_jid(stream_interactor, new Jid(roster_item.jid), account, accounts.size > 1); - rows[account][jid] = row; - add(row); - invalidate_sort(); - invalidate_filter(); - } - - private void fetch_roster_items(Account account) { - rows[account] = new HashMap(Jid.hash_func, Jid.equals_func); - foreach (Roster.Item roster_item in stream_interactor.get_module(RosterManager.IDENTITY).get_roster(account)) { - on_updated_roster_item(account, new Jid(roster_item.jid), roster_item); - } - } - - private void header(ListBoxRow row, ListBoxRow? before_row) { - if (row.get_header() == null && before_row != null) { - row.set_header(new Separator(Orientation.HORIZONTAL)); - } - } - - private bool filter(ListBoxRow r) { - if (r.get_type().is_a(typeof(ListRow))) { - ListRow row = r as ListRow; - if (filter_values != null) { - foreach (string filter in filter_values) { - if (!(row.name_label.label.down().contains(filter.down()) || - row.jid.to_string().down().contains(filter.down()))) { - return false; - } - } - } - } - return true; - } - - public override int sort(ListBoxRow row1, ListBoxRow row2) { - ListRow c1 = (row1 as ListRow); - ListRow c2 = (row2 as ListRow); - return c1.name_label.label.collate(c2.name_label.label); - } -} - -} diff --git a/main/src/ui/add_conversation/conference/add_groupchat_dialog.vala b/main/src/ui/add_conversation/conference/add_groupchat_dialog.vala deleted file mode 100644 index dc0cfd6d..00000000 --- a/main/src/ui/add_conversation/conference/add_groupchat_dialog.vala +++ /dev/null @@ -1,82 +0,0 @@ -using Gee; -using Gtk; - -using Dino.Entities; - -namespace Dino.Ui.AddConversation.Conference { - -[GtkTemplate (ui = "/im/dino/add_conversation/add_groupchat_dialog.ui")] -protected class AddGroupchatDialog : Gtk.Dialog { - - [GtkChild] private Stack accounts_stack; - [GtkChild] private AccountComboBox account_combobox; - [GtkChild] private Label account_label; - [GtkChild] private Button ok_button; - [GtkChild] private Button cancel_button; - [GtkChild] private Entry jid_entry; - [GtkChild] private Entry alias_entry; - [GtkChild] private Entry nick_entry; - [GtkChild] private CheckButton autojoin_checkbutton; - - private StreamInteractor stream_interactor; - private Xmpp.Xep.Bookmarks.Conference? edit_confrence = null; - private bool alias_entry_changed = false; - - public AddGroupchatDialog(StreamInteractor stream_interactor) { - Object(use_header_bar : 1); - this.stream_interactor = stream_interactor; - ok_button.label = _("Add"); - ok_button.get_style_context().add_class("suggested-action"); // TODO why doesn't it work in XML - accounts_stack.set_visible_child_name("combobox"); - account_combobox.initialize(stream_interactor); - - cancel_button.clicked.connect(() => { close(); }); - ok_button.clicked.connect(on_ok_button_clicked); - jid_entry.key_release_event.connect(on_jid_key_release); - nick_entry.key_release_event.connect(check_ok); - } - - public AddGroupchatDialog.for_conference(StreamInteractor stream_interactor, Account account, Xmpp.Xep.Bookmarks.Conference conference) { - this(stream_interactor); - edit_confrence = conference; - ok_button.label = _("Save"); - ok_button.sensitive = true; - accounts_stack.set_visible_child_name("label"); - account_label.label = account.bare_jid.to_string(); - account_combobox.selected = account; - jid_entry.text = conference.jid; - nick_entry.text = conference.nick ?? ""; - autojoin_checkbutton.active = conference.autojoin; - alias_entry.text = conference.name; - } - - private bool on_jid_key_release() { - check_ok(); - if (!alias_entry_changed) { - Jid? parsed_jid = Jid.parse(jid_entry.text); - alias_entry.text = parsed_jid != null && parsed_jid.localpart != null ? parsed_jid.localpart : jid_entry.text; - } - return false; - } - - private bool check_ok() { - Jid? parsed_jid = Jid.parse(jid_entry.text); - ok_button.sensitive = parsed_jid != null && parsed_jid.localpart != null && parsed_jid.resourcepart == null; - return false; - } - - private void on_ok_button_clicked() { - Xmpp.Xep.Bookmarks.Conference conference = new Xmpp.Xep.Bookmarks.Conference(jid_entry.text); - conference.nick = nick_entry.text != "" ? nick_entry.text : null; - conference.name = alias_entry.text; - conference.autojoin = autojoin_checkbutton.active; - if (edit_confrence == null) { - stream_interactor.get_module(MucManager.IDENTITY).add_bookmark(account_combobox.selected, conference); - } else { - stream_interactor.get_module(MucManager.IDENTITY).replace_bookmark(account_combobox.selected, edit_confrence, conference); - } - close(); - } -} - -} diff --git a/main/src/ui/add_conversation/conference/conference_details_fragment.vala b/main/src/ui/add_conversation/conference/conference_details_fragment.vala deleted file mode 100644 index 9f9ffe9c..00000000 --- a/main/src/ui/add_conversation/conference/conference_details_fragment.vala +++ /dev/null @@ -1,144 +0,0 @@ -using Gdk; -using Gtk; - -using Dino.Entities; - -namespace Dino.Ui.AddConversation.Conference { - -[GtkTemplate (ui = "/im/dino/add_conversation/conference_details_fragment.ui")] -protected class ConferenceDetailsFragment : Box { - - public bool done { - get { - Jid? parsed_jid = Jid.parse(jid); - return parsed_jid != null && parsed_jid.localpart != null && - parsed_jid.resourcepart == null && nick != ""; - } - private set {} - } - - public Account account { - owned get { return account_combobox.selected; } - set { - accounts_label.label = value.bare_jid.to_string(); - account_combobox.selected = value; - } - } - public string jid { - get { return jid_entry.text; } - set { - jid_label.label = value; - jid_entry.text = value; - } - } - public string? nick { - get { return nick_entry.text != "" ? nick_entry.text : null; } - set { - nick_label.label = value ?? ""; - nick_entry.text = value ?? ""; - } - } - public string? password { - get { return password_entry.text == "" ? null : password_entry.text; } - set { - password_label.label = value; - password_entry.text = value; - } - } - - [GtkChild] private Stack accounts_stack; - [GtkChild] private Button accounts_button; - [GtkChild] private Label accounts_label; - [GtkChild] private AccountComboBox account_combobox; - - [GtkChild] private Stack jid_stack; - [GtkChild] private Button jid_button; - [GtkChild] private Label jid_label; - [GtkChild] private Entry jid_entry; - - [GtkChild] private Stack nick_stack; - [GtkChild] private Button nick_button; - [GtkChild] private Label nick_label; - [GtkChild] private Entry nick_entry; - - [GtkChild] private Stack password_stack; - [GtkChild] private Button password_button; - [GtkChild] private Label password_label; - [GtkChild] private Entry password_entry; - - private StreamInteractor stream_interactor; - - public ConferenceDetailsFragment(StreamInteractor stream_interactor) { - this.stream_interactor = stream_interactor; - account_combobox.initialize(stream_interactor); - - accounts_stack.set_visible_child_name("label"); - jid_stack.set_visible_child_name("label"); - nick_stack.set_visible_child_name("label"); - password_stack.set_visible_child_name("label"); - - accounts_button.clicked.connect(() => { set_active_stack(accounts_stack); }); - jid_button.clicked.connect(() => { set_active_stack(jid_stack); }); - nick_button.clicked.connect(() => { set_active_stack(nick_stack); }); - password_button.clicked.connect(() => { set_active_stack(password_stack); }); - - account_combobox.changed.connect(() => { accounts_label.label = account_combobox.selected.bare_jid.to_string(); }); - accounts_label.label = account_combobox.selected.bare_jid.to_string(); - jid_entry.key_release_event.connect(on_jid_key_release_event); - nick_entry.key_release_event.connect(on_nick_key_release_event); - password_entry.key_release_event.connect(on_password_key_release_event); - - jid_entry.key_release_event.connect(() => { done = true; return false; }); // just for notifying - nick_entry.key_release_event.connect(() => { done = true; return false; }); - } - - public void set_editable() { - nick_stack.set_visible_child_name("entry"); - password_stack.set_visible_child_name("entry"); - } - - public void reset_editable() { - jid_stack.set_visible_child_name("label"); - accounts_stack.set_visible_child_name("label"); - nick_stack.set_visible_child_name("label"); - password_stack.set_visible_child_name("label"); - } - - public void clear() { - jid = ""; - nick = ""; - password = ""; - reset_editable(); - } - - private bool on_jid_key_release_event(EventKey event) { - jid_label.label = jid_entry.text; - if (event.keyval == Key.Return) jid_stack.set_visible_child_name("label"); - return false; - } - - private bool on_nick_key_release_event(EventKey event) { - nick_label.label = nick_entry.text; - if (event.keyval == Key.Return) nick_stack.set_visible_child_name("label"); - return false; - } - - private bool on_password_key_release_event(EventKey event) { - string filler = ""; - for (int i = 0; i < password_entry.text.length; i++) filler += password_entry.get_invisible_char().to_string(); - password_label.label = filler; - if (event.keyval == Key.Return) password_stack.set_visible_child_name("label"); - return false; - } - - private void set_active_stack(Stack stack) { - stack.set_visible_child_name("entry"); - if (stack != accounts_stack) accounts_stack.set_visible_child_name("label"); - if (stack != jid_stack) jid_stack.set_visible_child_name("label"); - if (stack != nick_stack) nick_stack.set_visible_child_name("label"); - if (stack != password_stack) password_stack.set_visible_child_name("label"); - } - -} - -} diff --git a/main/src/ui/add_conversation/conference/conference_list.vala b/main/src/ui/add_conversation/conference/conference_list.vala deleted file mode 100644 index 1f4abfa3..00000000 --- a/main/src/ui/add_conversation/conference/conference_list.vala +++ /dev/null @@ -1,103 +0,0 @@ -using Gee; -using Gtk; - -using Xmpp; -using Dino.Entities; - -namespace Dino.Ui.AddConversation.Conference { - -protected class ConferenceList : FilterableList { - - public signal void conversation_selected(Conversation? conversation); - - private StreamInteractor stream_interactor; - private HashMap> lists = new HashMap>(); - - public ConferenceList(StreamInteractor stream_interactor) { - this.stream_interactor = stream_interactor; - - set_filter_func(filter); - set_header_func(header); - set_sort_func(sort); - - stream_interactor.get_module(MucManager.IDENTITY).bookmarks_updated.connect((account, conferences) => { - Idle.add(() => { - lists[account] = conferences; - refresh_conferences(); - return false; - }); - }); - - foreach (Account account in stream_interactor.get_accounts()) { - stream_interactor.get_module(MucManager.IDENTITY).get_bookmarks(account, (stream, conferences) => { on_conference_bookmarks_received(stream, account, conferences); }); - } - } - - public void refresh_conferences() { - @foreach((widget) => { remove(widget); }); - foreach (Account account in lists.keys) { - foreach (Xep.Bookmarks.Conference conference in lists[account]) { - add(new ConferenceListRow(stream_interactor, conference, account)); - } - } - } - - private void on_conference_bookmarks_received(Core.XmppStream stream, Account account, Gee.List conferences) { - Idle.add(() => { - lists[account] = conferences; - refresh_conferences(); - return false; - }); - } - - private void header(ListBoxRow row, ListBoxRow? before_row) { - if (row.get_header() == null && before_row != null) { - row.set_header(new Separator(Orientation.HORIZONTAL)); - } - } - - private bool filter(ListBoxRow r) { - if (r.get_type().is_a(typeof(ListRow))) { - ListRow row = r as ListRow; - if (filter_values != null) { - foreach (string filter in filter_values) { - if (!(row.name_label.label.down().contains(filter.down()) || - row.jid.to_string().down().contains(filter.down()))) { - return false; - } - } - } - } - return true; - } - - public override int sort(ListBoxRow row1, ListBoxRow row2) { - ListRow c1 = (row1 as ListRow); - ListRow c2 = (row2 as ListRow); - return c1.name_label.label.collate(c2.name_label.label); - } -} - -internal class ConferenceListRow : ListRow { - - public Xep.Bookmarks.Conference bookmark; - - public ConferenceListRow(StreamInteractor stream_interactor, Xep.Bookmarks.Conference bookmark, Account account) { - this.jid = new Jid(bookmark.jid); - this.account = account; - this.bookmark = bookmark; - - name_label.label = bookmark.name ?? bookmark.jid; - if (stream_interactor.get_accounts().size > 1) { - via_label.label = "via " + account.bare_jid.to_string(); - } else if (bookmark.name != null && bookmark.name != bookmark.jid) { - via_label.label = bookmark.jid; - } else { - via_label.visible = false; - } - - image.set_from_pixbuf((new AvatarGenerator(35, 35)).set_stateless(true).draw_jid(stream_interactor, jid, account)); - } -} - -} diff --git a/main/src/ui/add_conversation/conference/dialog.vala b/main/src/ui/add_conversation/conference/dialog.vala deleted file mode 100644 index 10d6e535..00000000 --- a/main/src/ui/add_conversation/conference/dialog.vala +++ /dev/null @@ -1,162 +0,0 @@ -using Gee; -using Gtk; - -using Dino.Entities; - -namespace Dino.Ui.AddConversation.Conference { - -public class Dialog : Gtk.Dialog { - - public signal void conversation_opened(Conversation conversation); - - private Stack stack = new Stack(); - private Button cancel_button; - private Button ok_button; - private Label cancel_label = new Label(_("Cancel")) {visible=true}; - private Image cancel_image = new Image.from_icon_name("go-previous-symbolic", IconSize.MENU) {visible=true}; - - private SelectJidFragment select_fragment; - private ConferenceDetailsFragment details_fragment; - private ConferenceList conference_list; - - private StreamInteractor stream_interactor; - - public Dialog(StreamInteractor stream_interactor) { - Object(use_header_bar : 1); - this.title = _("Join Conference"); - this.modal = true; - this.stream_interactor = stream_interactor; - - stack.visible = true; - stack.vhomogeneous = false; - get_content_area().add(stack); - - setup_headerbar(); - setup_jid_add_view(); - setup_conference_details_view(); - show_jid_add_view(); - } - - private void show_jid_add_view() { - if (cancel_image.get_parent() != null) cancel_button.remove(cancel_image); - cancel_button.add(cancel_label); - cancel_button.clicked.disconnect(show_jid_add_view); - cancel_button.clicked.connect(on_cancel); - ok_button.label = _("Next"); - ok_button.sensitive = select_fragment.done; - ok_button.clicked.disconnect(on_ok_button_clicked); - ok_button.clicked.connect(on_next_button_clicked); - details_fragment.notify["done"].disconnect(set_ok_sensitive_from_details); - select_fragment.notify["done"].connect(set_ok_sensitive_from_select); - stack.transition_type = StackTransitionType.SLIDE_RIGHT; - stack.set_visible_child_name("select"); - } - - private void show_conference_details_view() { - if (cancel_label.get_parent() != null) cancel_button.remove(cancel_label); - cancel_button.add(cancel_image); - cancel_button.clicked.disconnect(on_cancel); - cancel_button.clicked.connect(show_jid_add_view); - ok_button.label = _("Join"); - ok_button.sensitive = details_fragment.done; - ok_button.clicked.disconnect(on_next_button_clicked); - ok_button.clicked.connect(on_ok_button_clicked); - select_fragment.notify["done"].disconnect(set_ok_sensitive_from_select); - details_fragment.notify["done"].connect(set_ok_sensitive_from_details); - stack.transition_type = StackTransitionType.SLIDE_LEFT; - stack.set_visible_child_name("details"); - animate_window_resize(); - } - - private void setup_headerbar() { - HeaderBar header_bar = get_header_bar() as HeaderBar; - header_bar.show_close_button = false; - - cancel_button = new Button(); - header_bar.pack_start(cancel_button); - cancel_button.visible = true; - - ok_button = new Button(); - header_bar.pack_end(ok_button); - ok_button.get_style_context().add_class("suggested-action"); - ok_button.visible = true; - ok_button.can_focus = true; - ok_button.can_default = true; - ok_button.has_default = true; - } - - private void setup_jid_add_view() { - conference_list = new ConferenceList(stream_interactor); - conference_list.row_activated.connect(() => { ok_button.clicked(); }); - select_fragment = new SelectJidFragment(stream_interactor, conference_list, stream_interactor.get_accounts()); - select_fragment.add_jid.connect((row) => { - AddGroupchatDialog dialog = new AddGroupchatDialog(stream_interactor); - dialog.set_transient_for(this); - dialog.present(); - }); - select_fragment.remove_jid.connect((row) => { - ConferenceListRow conference_row = row as ConferenceListRow; - stream_interactor.get_module(MucManager.IDENTITY).remove_bookmark(conference_row.account, conference_row.bookmark); - }); - stack.add_named(select_fragment, "select"); - } - - private void setup_conference_details_view() { - details_fragment = new ConferenceDetailsFragment(stream_interactor); - stack.add_named(details_fragment, "details"); - } - - private void set_ok_sensitive_from_select() { - ok_button.sensitive = select_fragment.done; - } - - private void set_ok_sensitive_from_details() { - ok_button.sensitive = select_fragment.done; - } - - private void on_next_button_clicked() { - details_fragment.clear(); - ListRow? row = conference_list.get_selected_row() as ListRow; - ConferenceListRow? conference_row = conference_list.get_selected_row() as ConferenceListRow; - if (conference_row != null) { - details_fragment.account = conference_row.account; - details_fragment.jid = conference_row.bookmark.jid; - details_fragment.nick = conference_row.bookmark.nick; - if (conference_row.bookmark.password != null) details_fragment.password = conference_row.bookmark.password; - ok_button.grab_focus(); - } else if (row != null) { - details_fragment.account = row.account; - details_fragment.jid = row.jid.to_string(); - details_fragment.set_editable(); - } - show_conference_details_view(); - } - - private void on_ok_button_clicked() { - stream_interactor.get_module(MucManager.IDENTITY).join(details_fragment.account, new Jid(details_fragment.jid), details_fragment.nick, details_fragment.password); - close(); - } - - private void on_cancel() { - close(); - } - - private void animate_window_resize() { - int def_height, curr_width, curr_height; - get_size(out curr_width, out curr_height); - stack.get_preferred_height(null, out def_height); - int difference = def_height - curr_height; - Timer timer = new Timer(); - Timeout.add((int) (stack.transition_duration / 30), - () => { - ulong microsec; - timer.elapsed(out microsec); - ulong millisec = microsec / 1000; - double partial = double.min(1, (double) millisec / stack.transition_duration); - resize(curr_width, (int) (curr_height + difference * partial)); - return millisec < stack.transition_duration; - }); - } -} - -} diff --git a/main/src/ui/add_conversation/conference_details_fragment.vala b/main/src/ui/add_conversation/conference_details_fragment.vala new file mode 100644 index 00000000..064d1053 --- /dev/null +++ b/main/src/ui/add_conversation/conference_details_fragment.vala @@ -0,0 +1,195 @@ +using Gdk; +using Gtk; + +using Dino.Entities; + +namespace Dino.Ui { + +[GtkTemplate (ui = "/im/dino/add_conversation/conference_details_fragment.ui")] +protected class ConferenceDetailsFragment : Box { + + public bool done { + get { + Jid? parsed_jid = Jid.parse(jid); + return parsed_jid != null && parsed_jid.localpart != null && + parsed_jid.resourcepart == null && nick != ""; + } + private set {} + } + + public Account account { + owned get { return account_combobox.selected; } + set { + accounts_label.label = value.bare_jid.to_string(); + account_combobox.selected = value; + } + } + public string jid { + get { return jid_entry.text; } + set { + jid_label.label = value; + jid_entry.text = value; + } + } + public string? nick { + get { return nick_entry.text != "" ? nick_entry.text : null; } + set { + nick_label.label = value ?? ""; + nick_entry.text = value ?? ""; + } + } + public string? password { + get { return password_entry.text == "" ? null : password_entry.text; } + set { + password_label.label = value; + password_entry.text = value; + } + } + + [GtkChild] private Stack accounts_stack; + [GtkChild] private Button accounts_button; + [GtkChild] private Label accounts_label; + [GtkChild] private AccountComboBox account_combobox; + + [GtkChild] private Stack jid_stack; + [GtkChild] private Button jid_button; + [GtkChild] private Label jid_label; + [GtkChild] private Entry jid_entry; + + [GtkChild] private Stack nick_stack; + [GtkChild] private Button nick_button; + [GtkChild] private Label nick_label; + [GtkChild] private Entry nick_entry; + + [GtkChild] private Stack password_stack; + [GtkChild] private Button password_button; + [GtkChild] private Label password_label; + [GtkChild] private Label password_text_label; + [GtkChild] private Entry password_entry; + + [GtkChild] private Revealer notification_revealer; + [GtkChild] private Button notification_button; + [GtkChild] private Label notification_label; + + private StreamInteractor stream_interactor; + private Button ok_button; + + public ConferenceDetailsFragment(StreamInteractor stream_interactor, Button ok_button) { + this.stream_interactor = stream_interactor; + this.ok_button = ok_button; + + account_combobox.initialize(stream_interactor); + + accounts_stack.set_visible_child_name("label"); + jid_stack.set_visible_child_name("label"); + nick_stack.set_visible_child_name("label"); + password_stack.set_visible_child_name("label"); + + accounts_button.clicked.connect(() => { set_active_stack(accounts_stack); }); + jid_button.clicked.connect(() => { set_active_stack(jid_stack); }); + nick_button.clicked.connect(() => { set_active_stack(nick_stack); }); + password_button.clicked.connect(() => { set_active_stack(password_stack); }); + + account_combobox.changed.connect(() => { accounts_label.label = account_combobox.selected.bare_jid.to_string(); }); + accounts_label.label = account_combobox.selected.bare_jid.to_string(); + jid_entry.key_release_event.connect(on_jid_key_release_event); + nick_entry.key_release_event.connect(on_nick_key_release_event); + password_entry.key_release_event.connect(on_password_key_release_event); + + jid_entry.key_release_event.connect(() => { done = true; return false; }); // just for notifying + nick_entry.key_release_event.connect(() => { done = true; return false; }); + + stream_interactor.get_module(MucManager.IDENTITY).enter_error.connect((account, jid, error) => { + Idle.add(() => { + on_enter_error(account, jid, error); + return false; + }); + }); + notification_button.clicked.connect(() => { notification_revealer.set_reveal_child(false); }); + ok_button.clicked.connect(() => { + ok_button.label = _("Joining..."); + ok_button.sensitive = false; + }); + } + + public void set_editable() { + nick_stack.set_visible_child_name("entry"); + password_stack.set_visible_child_name("entry"); + } + + public void reset_editable() { + jid_stack.set_visible_child_name("label"); + accounts_stack.set_visible_child_name("label"); + nick_stack.set_visible_child_name("label"); + password_stack.set_visible_child_name("label"); + } + + public void clear() { + jid = ""; + nick = ""; + password = ""; + password_text_label.visible = false; + password_stack.visible = false; + notification_revealer.set_reveal_child(false); + reset_editable(); + } + + private void on_enter_error(Account account, Jid jid, Xmpp.Xep.Muc.MucEnterError error) { + ok_button.label = _("Join"); + ok_button.sensitive = true; + string label_text = ""; + switch (error) { + case Xmpp.Xep.Muc.MucEnterError.PASSWORD_REQUIRED: + label_text = _("Password required to enter room"); + password_text_label.visible = true; + password_stack.visible = true; + break; + case Xmpp.Xep.Muc.MucEnterError.BANNED: + label_text = _("Banned from joining or creating conference"); break; + case Xmpp.Xep.Muc.MucEnterError.ROOM_DOESNT_EXIST: + label_text = _("Room does not exist"); break; + case Xmpp.Xep.Muc.MucEnterError.CREATION_RESTRICTED: + label_text = _("Not allowed to create room"); break; + case Xmpp.Xep.Muc.MucEnterError.NOT_IN_MEMBER_LIST: + label_text = _("Room is members only"); break; + case Xmpp.Xep.Muc.MucEnterError.USE_RESERVED_ROOMNICK: + case Xmpp.Xep.Muc.MucEnterError.NICK_CONFLICT: + label_text = _("Choose a different nick"); break; + case Xmpp.Xep.Muc.MucEnterError.OCCUPANT_LIMIT_REACHED: + label_text = _("Room has too many occupants"); break; + } + notification_label.label = label_text; + notification_revealer.set_reveal_child(true); + } + + private bool on_jid_key_release_event(EventKey event) { + jid_label.label = jid_entry.text; + if (event.keyval == Key.Return) jid_stack.set_visible_child_name("label"); + return false; + } + + private bool on_nick_key_release_event(EventKey event) { + nick_label.label = nick_entry.text; + if (event.keyval == Key.Return) nick_stack.set_visible_child_name("label"); + return false; + } + + private bool on_password_key_release_event(EventKey event) { + string filler = ""; + for (int i = 0; i < password_entry.text.length; i++) filler += password_entry.get_invisible_char().to_string(); + password_label.label = filler; + if (event.keyval == Key.Return) password_stack.set_visible_child_name("label"); + return false; + } + + private void set_active_stack(Stack stack) { + stack.set_visible_child_name("entry"); + if (stack != accounts_stack) accounts_stack.set_visible_child_name("label"); + if (stack != jid_stack) jid_stack.set_visible_child_name("label"); + if (stack != nick_stack) nick_stack.set_visible_child_name("label"); + if (stack != password_stack) password_stack.set_visible_child_name("label"); + } + +} + +} diff --git a/main/src/ui/add_conversation/conference_list.vala b/main/src/ui/add_conversation/conference_list.vala new file mode 100644 index 00000000..570166b1 --- /dev/null +++ b/main/src/ui/add_conversation/conference_list.vala @@ -0,0 +1,103 @@ +using Gee; +using Gtk; + +using Xmpp; +using Dino.Entities; + +namespace Dino.Ui { + +protected class ConferenceList : FilterableList { + + public signal void conversation_selected(Conversation? conversation); + + private StreamInteractor stream_interactor; + private HashMap> lists = new HashMap>(); + + public ConferenceList(StreamInteractor stream_interactor) { + this.stream_interactor = stream_interactor; + + set_filter_func(filter); + set_header_func(header); + set_sort_func(sort); + + stream_interactor.get_module(MucManager.IDENTITY).bookmarks_updated.connect((account, conferences) => { + Idle.add(() => { + lists[account] = conferences; + refresh_conferences(); + return false; + }); + }); + + foreach (Account account in stream_interactor.get_accounts()) { + stream_interactor.get_module(MucManager.IDENTITY).get_bookmarks(account, (stream, conferences) => { on_conference_bookmarks_received(stream, account, conferences); }); + } + } + + public void refresh_conferences() { + @foreach((widget) => { remove(widget); }); + foreach (Account account in lists.keys) { + foreach (Xep.Bookmarks.Conference conference in lists[account]) { + add(new ConferenceListRow(stream_interactor, conference, account)); + } + } + } + + private void on_conference_bookmarks_received(Core.XmppStream stream, Account account, Gee.List conferences) { + Idle.add(() => { + lists[account] = conferences; + refresh_conferences(); + return false; + }); + } + + private void header(ListBoxRow row, ListBoxRow? before_row) { + if (row.get_header() == null && before_row != null) { + row.set_header(new Separator(Orientation.HORIZONTAL)); + } + } + + private bool filter(ListBoxRow r) { + if (r.get_type().is_a(typeof(ListRow))) { + ListRow row = r as ListRow; + if (filter_values != null) { + foreach (string filter in filter_values) { + if (!(row.name_label.label.down().contains(filter.down()) || + row.jid.to_string().down().contains(filter.down()))) { + return false; + } + } + } + } + return true; + } + + public override int sort(ListBoxRow row1, ListBoxRow row2) { + ListRow c1 = (row1 as ListRow); + ListRow c2 = (row2 as ListRow); + return c1.name_label.label.collate(c2.name_label.label); + } +} + +internal class ConferenceListRow : ListRow { + + public Xep.Bookmarks.Conference bookmark; + + public ConferenceListRow(StreamInteractor stream_interactor, Xep.Bookmarks.Conference bookmark, Account account) { + this.jid = new Jid(bookmark.jid); + this.account = account; + this.bookmark = bookmark; + + name_label.label = bookmark.name ?? bookmark.jid; + if (stream_interactor.get_accounts().size > 1) { + via_label.label = "via " + account.bare_jid.to_string(); + } else if (bookmark.name != null && bookmark.name != bookmark.jid) { + via_label.label = bookmark.jid; + } else { + via_label.visible = false; + } + + image.set_from_pixbuf((new AvatarGenerator(35, 35)).set_stateless(true).draw_jid(stream_interactor, jid, account)); + } +} + +} diff --git a/main/src/ui/add_conversation/list_row.vala b/main/src/ui/add_conversation/list_row.vala index 5c33095f..fa607841 100644 --- a/main/src/ui/add_conversation/list_row.vala +++ b/main/src/ui/add_conversation/list_row.vala @@ -3,7 +3,7 @@ using Gtk; using Dino.Entities; -namespace Dino.Ui.AddConversation { +namespace Dino.Ui { [GtkTemplate (ui = "/im/dino/add_conversation/list_row.ui")] public class ListRow : ListBoxRow { diff --git a/main/src/ui/add_conversation/roster_list.vala b/main/src/ui/add_conversation/roster_list.vala new file mode 100644 index 00000000..70e4bc14 --- /dev/null +++ b/main/src/ui/add_conversation/roster_list.vala @@ -0,0 +1,94 @@ +using Gee; +using Gtk; + +using Dino.Entities; +using Xmpp; + +namespace Dino.Ui { + +protected class RosterList : FilterableList { + + public signal void conversation_selected(Conversation? conversation); + private StreamInteractor stream_interactor; + private Gee.List accounts; + private ulong[] handler_ids = new ulong[0]; + + private HashMap> rows = new HashMap>(Account.hash_func, Account.equals_func); + + public RosterList(StreamInteractor stream_interactor, Gee.List accounts) { + this.stream_interactor = stream_interactor; + this.accounts = accounts; + + set_filter_func(filter); + set_header_func(header); + set_sort_func(sort); + + handler_ids += stream_interactor.get_module(RosterManager.IDENTITY).removed_roster_item.connect( (account, jid, roster_item) => { + if (accounts.contains(account)) { + Idle.add(() => { on_removed_roster_item(account, jid, roster_item); return false;}); + } + }); + handler_ids += stream_interactor.get_module(RosterManager.IDENTITY).updated_roster_item.connect( (account, jid, roster_item) => { + if (accounts.contains(account)) { + Idle.add(() => { on_updated_roster_item(account, jid, roster_item); return false;}); + } + }); + destroy.connect(() => { + foreach (ulong handler_id in handler_ids) stream_interactor.get_module(RosterManager.IDENTITY).disconnect(handler_id); + }); + + foreach (Account a in accounts) fetch_roster_items(a); + } + + private void on_removed_roster_item(Account account, Jid jid, Roster.Item roster_item) { + if (rows.has_key(account) && rows[account].has_key(jid)) { + remove(rows[account][jid]); + rows[account].unset(jid); + } + } + + private void on_updated_roster_item(Account account, Jid jid, Roster.Item roster_item) { + on_removed_roster_item(account, jid, roster_item); + ListRow row = new ListRow.from_jid(stream_interactor, new Jid(roster_item.jid), account, accounts.size > 1); + rows[account][jid] = row; + add(row); + invalidate_sort(); + invalidate_filter(); + } + + private void fetch_roster_items(Account account) { + rows[account] = new HashMap(Jid.hash_func, Jid.equals_func); + foreach (Roster.Item roster_item in stream_interactor.get_module(RosterManager.IDENTITY).get_roster(account)) { + on_updated_roster_item(account, new Jid(roster_item.jid), roster_item); + } + } + + private void header(ListBoxRow row, ListBoxRow? before_row) { + if (row.get_header() == null && before_row != null) { + row.set_header(new Separator(Orientation.HORIZONTAL)); + } + } + + private bool filter(ListBoxRow r) { + if (r.get_type().is_a(typeof(ListRow))) { + ListRow row = r as ListRow; + if (filter_values != null) { + foreach (string filter in filter_values) { + if (!(row.name_label.label.down().contains(filter.down()) || + row.jid.to_string().down().contains(filter.down()))) { + return false; + } + } + } + } + return true; + } + + public override int sort(ListBoxRow row1, ListBoxRow row2) { + ListRow c1 = (row1 as ListRow); + ListRow c2 = (row2 as ListRow); + return c1.name_label.label.collate(c2.name_label.label); + } +} + +} diff --git a/main/src/ui/add_conversation/select_contact_dialog.vala b/main/src/ui/add_conversation/select_contact_dialog.vala new file mode 100644 index 00000000..b05762ec --- /dev/null +++ b/main/src/ui/add_conversation/select_contact_dialog.vala @@ -0,0 +1,94 @@ +using Gee; +using Gdk; +using Gtk; + +using Dino.Entities; + +namespace Dino.Ui { + +public class SelectContactDialog : Gtk.Dialog { + + public signal void selected(Account account, Jid jid); + + public Button ok_button; + + private RosterList roster_list; + private SelectJidFragment select_jid_fragment; + private StreamInteractor stream_interactor; + private Gee.List accounts; + + public SelectContactDialog(StreamInteractor stream_interactor, Gee.List accounts) { + Object(use_header_bar : 1); + modal = true; + + this.stream_interactor = stream_interactor; + this.accounts = accounts; + + setup_headerbar(); + setup_view(); + } + + public void set_filter(string str) { + select_jid_fragment.set_filter(str); + } + + private void setup_headerbar() { + HeaderBar header_bar = get_header_bar() as HeaderBar; + header_bar.show_close_button = false; + + Button cancel_button = new Button(); + cancel_button.set_label(_("Cancel")); + cancel_button.visible = true; + header_bar.pack_start(cancel_button); + + ok_button = new Button(); + ok_button.get_style_context().add_class("suggested-action"); + ok_button.sensitive = false; + ok_button.visible = true; + header_bar.pack_end(ok_button); + + cancel_button.clicked.connect(() => { close(); }); + ok_button.clicked.connect(() => { + ListRow? selected_row = roster_list.get_selected_row() as ListRow; + if (selected_row != null) selected(selected_row.account, selected_row.jid); + close(); + }); + } + + private void setup_view() { + roster_list = new RosterList(stream_interactor, accounts); + roster_list.row_activated.connect(() => { ok_button.clicked(); }); + select_jid_fragment = new SelectJidFragment(stream_interactor, roster_list, accounts); + select_jid_fragment.add_jid.connect((row) => { + AddContactDialog add_contact_dialog = new AddContactDialog(stream_interactor); + add_contact_dialog.set_transient_for(this); + add_contact_dialog.present(); + }); + select_jid_fragment.remove_jid.connect((row) => { + ListRow list_row = roster_list.get_selected_row() as ListRow; + stream_interactor.get_module(RosterManager.IDENTITY).remove_jid(list_row.account, list_row.jid); + }); + select_jid_fragment.notify["done"].connect(() => { + ok_button.sensitive = select_jid_fragment.done; + }); + get_content_area().add(select_jid_fragment); + } +} + +public class AddChatDialog : SelectContactDialog { + + public signal void added(Conversation conversation); + + public AddChatDialog(StreamInteractor stream_interactor, Gee.List accounts) { + base(stream_interactor, accounts); + title = _("Start Chat"); + ok_button.label = _("Start"); + selected.connect((account, jid) => { + Conversation conversation = stream_interactor.get_module(ConversationManager.IDENTITY).create_conversation(jid, account, Conversation.Type.CHAT); + stream_interactor.get_module(ConversationManager.IDENTITY).start_conversation(conversation, true); + added(conversation); + }); + } +} + +} diff --git a/main/src/ui/add_conversation/select_jid_fragment.vala b/main/src/ui/add_conversation/select_jid_fragment.vala index 8e975d2d..d34a22d7 100644 --- a/main/src/ui/add_conversation/select_jid_fragment.vala +++ b/main/src/ui/add_conversation/select_jid_fragment.vala @@ -3,7 +3,7 @@ using Gtk; using Dino.Entities; -namespace Dino.Ui.AddConversation { +namespace Dino.Ui { [GtkTemplate (ui = "/im/dino/add_conversation/select_jid_fragment.ui")] public class SelectJidFragment : Gtk.Box { diff --git a/main/src/ui/application.vala b/main/src/ui/application.vala index ff5f44ac..625673fb 100644 --- a/main/src/ui/application.vala +++ b/main/src/ui/application.vala @@ -40,9 +40,9 @@ public class Dino.Ui.Application : Gtk.Application, Dino.Application { case "join": Dialog dialog = new Dialog.with_buttons(_("Join Conference"), window, Gtk.DialogFlags.MODAL | Gtk.DialogFlags.USE_HEADER_BAR, _("Join"), ResponseType.OK, _("Cancel"), ResponseType.CANCEL); dialog.modal = true; - Widget ok_button = dialog.get_widget_for_response(ResponseType.OK); + Button ok_button = dialog.get_widget_for_response(ResponseType.OK) as Button; ok_button.get_style_context().add_class("suggested-action"); - AddConversation.Conference.ConferenceDetailsFragment conference_fragment = new AddConversation.Conference.ConferenceDetailsFragment(stream_interactor); + ConferenceDetailsFragment conference_fragment = new ConferenceDetailsFragment(stream_interactor, ok_button); conference_fragment.jid = jid; conference_fragment.set_editable(); Box content_area = dialog.get_content_area(); @@ -64,14 +64,10 @@ public class Dino.Ui.Application : Gtk.Application, Dino.Application { stream_interactor.get_module(ConversationManager.IDENTITY).start_conversation(conversation, true); window.on_conversation_selected(conversation); } else { - AddConversation.Chat.Dialog dialog = new AddConversation.Chat.Dialog(stream_interactor, stream_interactor.get_accounts()); + AddChatDialog dialog = new AddChatDialog(stream_interactor, stream_interactor.get_accounts()); dialog.set_filter(jid); dialog.set_transient_for(window); - dialog.title = _("Start Chat"); - dialog.ok_button.label = _("Start"); - dialog.selected.connect((account, jid) => { - Conversation conversation = stream_interactor.get_module(ConversationManager.IDENTITY).create_conversation(jid, account, Conversation.Type.CHAT); - stream_interactor.get_module(ConversationManager.IDENTITY).start_conversation(conversation, true); + dialog.added.connect((conversation) => { window.on_conversation_selected(conversation); }); dialog.present(); diff --git a/main/src/ui/conversation_list_titlebar.vala b/main/src/ui/conversation_list_titlebar.vala index 815807f2..e499c6d5 100644 --- a/main/src/ui/conversation_list_titlebar.vala +++ b/main/src/ui/conversation_list_titlebar.vala @@ -26,13 +26,9 @@ public class ConversationListTitlebar : Gtk.HeaderBar { private void create_add_menu(Window window) { SimpleAction contacts_action = new SimpleAction("add_chat", null); contacts_action.activate.connect(() => { - AddConversation.Chat.Dialog add_chat_dialog = new AddConversation.Chat.Dialog(stream_interactor, stream_interactor.get_accounts()); + AddChatDialog add_chat_dialog = new AddChatDialog(stream_interactor, stream_interactor.get_accounts()); add_chat_dialog.set_transient_for(window); - add_chat_dialog.title = _("Start Chat"); - add_chat_dialog.ok_button.label = _("Start"); - add_chat_dialog.selected.connect((account, jid) => { - Conversation conversation = stream_interactor.get_module(ConversationManager.IDENTITY).create_conversation(jid, account, Conversation.Type.CHAT); - stream_interactor.get_module(ConversationManager.IDENTITY).start_conversation(conversation, true); + add_chat_dialog.added.connect((conversation) => { conversation_opened(conversation); }); add_chat_dialog.present(); @@ -41,7 +37,7 @@ public class ConversationListTitlebar : Gtk.HeaderBar { SimpleAction conference_action = new SimpleAction("add_conference", null); conference_action.activate.connect(() => { - AddConversation.Conference.Dialog add_conference_dialog = new AddConversation.Conference.Dialog(stream_interactor); + AddConferenceDialog add_conference_dialog = new AddConferenceDialog(stream_interactor); add_conference_dialog.set_transient_for(window); add_conference_dialog.conversation_opened.connect((conversation) => conversation_opened(conversation)); add_conference_dialog.present(); diff --git a/main/src/ui/conversation_selector/list.vala b/main/src/ui/conversation_selector/list.vala index 6e9a0ec2..dbad72a8 100644 --- a/main/src/ui/conversation_selector/list.vala +++ b/main/src/ui/conversation_selector/list.vala @@ -144,6 +144,8 @@ public class List : ListBox { private void header(ListBoxRow row, ListBoxRow? before_row) { if (row.get_header() == null && before_row != null) { row.set_header(new Separator(Orientation.HORIZONTAL)); + } else if (row.get_header() != null && before_row == null) { + row.set_header(null); } } diff --git a/main/src/ui/notifications.vala b/main/src/ui/notifications.vala index b3a373be..cfbec8dc 100644 --- a/main/src/ui/notifications.vala +++ b/main/src/ui/notifications.vala @@ -54,7 +54,7 @@ public class Notifications : Object { if (conversation == null) return; stream_interactor.get_module(PresenceManager.IDENTITY).approve_subscription(conversation.account, conversation.counterpart); if (stream_interactor.get_module(RosterManager.IDENTITY).get_roster_item(conversation.account, conversation.counterpart) == null) { - AddConversation.Chat.AddContactDialog dialog = new AddConversation.Chat.AddContactDialog(stream_interactor); + AddContactDialog dialog = new AddContactDialog(stream_interactor); dialog.jid = conversation.counterpart.bare_jid.to_string(); dialog.account = conversation.account; dialog.present(); diff --git a/main/src/ui/occupant_menu/view.vala b/main/src/ui/occupant_menu/view.vala index b93e38e6..fb63a06a 100644 --- a/main/src/ui/occupant_menu/view.vala +++ b/main/src/ui/occupant_menu/view.vala @@ -29,7 +29,7 @@ public class View : Popover { hide(); Gee.List acc_list = new ArrayList(Account.equals_func); acc_list.add(conversation.account); - AddConversation.Chat.Dialog add_chat_dialog = new AddConversation.Chat.Dialog(stream_interactor, acc_list); + SelectContactDialog add_chat_dialog = new SelectContactDialog(stream_interactor, acc_list); add_chat_dialog.set_transient_for(window); add_chat_dialog.title = _("Invite to Conference"); add_chat_dialog.ok_button.label = _("Invite"); diff --git a/main/src/ui/util/accounts_combo_box.vala b/main/src/ui/util/accounts_combo_box.vala new file mode 100644 index 00000000..5fdd18e6 --- /dev/null +++ b/main/src/ui/util/accounts_combo_box.vala @@ -0,0 +1,57 @@ +using Gee; +using Gtk; + +using Dino.Entities; + +namespace Dino.Ui { + +class AccountComboBox : ComboBox { + + public Account? selected { + get { + TreeIter selected; + if (get_active_iter(out selected)) { + Value value; + list_store.get_value(selected, 1, out value); + return value as Account; + } + return null; + } + set { + TreeIter iter; + if (list_store.get_iter_first(out iter)) { + int i = 0; + do { + Value val; + list_store.get_value(iter, 1, out val); + if ((val as Account).equals(value)) { + active = i; + break; + } + i++; + } while (list_store.iter_next(ref iter)); + } + } + } + + private StreamInteractor? stream_interactor; + private Gtk.ListStore list_store = new Gtk.ListStore(2, typeof(string), typeof(Account)); + + public void initialize(StreamInteractor stream_interactor) { + this.stream_interactor = stream_interactor; + + CellRendererText renderer = new Gtk.CellRendererText(); + pack_start(renderer, true); + add_attribute(renderer, "text", 0); + + TreeIter iter; + foreach (Account account in stream_interactor.get_accounts()) { + list_store.append(out iter); + list_store.set(iter, 0, account.bare_jid.to_string(), 1, account); + } + set_model(list_store); + active = 0; + } +} + +} -- cgit v1.2.3-54-g00ecf