diff options
Diffstat (limited to 'main/src')
-rw-r--r-- | main/src/ui/add_conversation/add_conference_dialog.vala | 2 | ||||
-rw-r--r-- | main/src/ui/add_conversation/add_contact_dialog.vala | 23 | ||||
-rw-r--r-- | main/src/ui/add_conversation/add_groupchat_dialog.vala | 38 | ||||
-rw-r--r-- | main/src/ui/add_conversation/conference_details_fragment.vala | 80 | ||||
-rw-r--r-- | main/src/ui/add_conversation/select_jid_fragment.vala | 22 | ||||
-rw-r--r-- | main/src/ui/application.vala | 18 | ||||
-rw-r--r-- | main/src/ui/chat_input_controller.vala | 6 | ||||
-rw-r--r-- | main/src/ui/conversation_summary/content_item_widget_factory.vala | 1 | ||||
-rw-r--r-- | main/src/ui/manage_accounts/add_account_dialog.vala | 117 | ||||
-rw-r--r-- | main/src/ui/util/helper.vala | 6 |
10 files changed, 193 insertions, 120 deletions
diff --git a/main/src/ui/add_conversation/add_conference_dialog.vala b/main/src/ui/add_conversation/add_conference_dialog.vala index ecafc9cc..a03f3db6 100644 --- a/main/src/ui/add_conversation/add_conference_dialog.vala +++ b/main/src/ui/add_conversation/add_conference_dialog.vala @@ -159,7 +159,7 @@ public class AddConferenceDialog : Gtk.Dialog { } private void set_ok_sensitive_from_details() { - ok_button.sensitive = select_fragment.done; + ok_button.sensitive = details_fragment.done; } private void on_next_button_clicked() { diff --git a/main/src/ui/add_conversation/add_contact_dialog.vala b/main/src/ui/add_conversation/add_contact_dialog.vala index 33f6fa72..7e9b5185 100644 --- a/main/src/ui/add_conversation/add_contact_dialog.vala +++ b/main/src/ui/add_conversation/add_contact_dialog.vala @@ -39,17 +39,24 @@ protected class AddContactDialog : Gtk.Dialog { 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); - stream_interactor.get_module(PresenceManager.IDENTITY).request_subscription(account, jid); - close(); + try { + Jid jid = new Jid(jid_entry.text); + stream_interactor.get_module(RosterManager.IDENTITY).add_jid(account, jid, alias); + stream_interactor.get_module(PresenceManager.IDENTITY).request_subscription(account, jid); + close(); + } catch (InvalidJidError e) { + warning("Tried to add contact with invalid Jid: %s", e.message); + } } 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); + try { + Jid parsed_jid = new Jid(jid_entry.text); + ok_button. sensitive = parsed_jid != null && parsed_jid.resourcepart == null && + stream_interactor.get_module(RosterManager.IDENTITY).get_roster_item(account, parsed_jid) == null; + } catch (InvalidJidError e) { + ok_button.sensitive = false; + } } } diff --git a/main/src/ui/add_conversation/add_groupchat_dialog.vala b/main/src/ui/add_conversation/add_groupchat_dialog.vala index 33a3a455..da6b10b2 100644 --- a/main/src/ui/add_conversation/add_groupchat_dialog.vala +++ b/main/src/ui/add_conversation/add_groupchat_dialog.vala @@ -52,29 +52,41 @@ protected class AddGroupchatDialog : Gtk.Dialog { 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; + try { + Jid parsed_jid = new Jid(jid_entry.text); + alias_entry.text = parsed_jid != null && parsed_jid.localpart != null ? parsed_jid.localpart : jid_entry.text; + } catch (InvalidJidError e) { + alias_entry.text = 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; + try { + Jid parsed_jid = new Jid(jid_entry.text); + ok_button.sensitive = parsed_jid != null && parsed_jid.localpart != null && parsed_jid.resourcepart == null; + } catch (InvalidJidError e) { + ok_button.sensitive = false; + } return false; } private void on_ok_button_clicked() { - Conference conference = new Conference(); - conference.jid = Jid.parse(jid_entry.text); - conference.nick = nick_entry.text != "" ? nick_entry.text : null; - conference.name = alias_entry.text; - if (edit_conference == 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_conference, conference); + try { + Conference conference = new Conference(); + conference.jid = new Jid(jid_entry.text); + conference.nick = nick_entry.text != "" ? nick_entry.text : null; + conference.name = alias_entry.text; + if (edit_conference == 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_conference, conference); + } + close(); + } catch (InvalidJidError e) { + warning("Ignoring invalid conference Jid: %s", e.message); } - close(); } } diff --git a/main/src/ui/add_conversation/conference_details_fragment.vala b/main/src/ui/add_conversation/conference_details_fragment.vala index 38232633..d0a4c42e 100644 --- a/main/src/ui/add_conversation/conference_details_fragment.vala +++ b/main/src/ui/add_conversation/conference_details_fragment.vala @@ -14,9 +14,12 @@ 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 != ""; + try { + Jid parsed_jid = new Jid(jid); + return parsed_jid.localpart != null && parsed_jid.resourcepart == null && nick != null; + } catch (InvalidJidError e) { + return false; + } } private set {} } @@ -146,43 +149,46 @@ protected class ConferenceDetailsFragment : Box { ok_button.label = _("Joining…"); ok_button.sensitive = false; - Jid parsed_jid = new Jid(jid); - Muc.JoinResult? join_result = yield stream_interactor.get_module(MucManager.IDENTITY).join(account, parsed_jid, nick, password); - - ok_button.label = _("Join"); - ok_button.sensitive = true; - if (join_result == null || join_result.nick != null) { - Conversation conversation = stream_interactor.get_module(ConversationManager.IDENTITY).create_conversation(parsed_jid, account, Conversation.Type.GROUPCHAT); + string label_text = ""; + try { + Jid parsed_jid = new Jid(jid); + Muc.JoinResult? join_result = yield stream_interactor.get_module(MucManager.IDENTITY).join(account, parsed_jid, nick, password); + + ok_button.label = _("Join"); + ok_button.sensitive = true; + if (join_result == null || join_result.nick != null) { + Conversation conversation = stream_interactor.get_module(ConversationManager.IDENTITY).create_conversation(parsed_jid, account, Conversation.Type.GROUPCHAT); Application app = GLib.Application.get_default() as Application; - app.controller.select_conversation(conversation); - joined(); - return; - } + app.controller.select_conversation(conversation);joined(); + return; + } - string label_text = ""; - if (join_result.muc_error != null) { - switch (join_result.muc_error) { - case Muc.MucEnterError.PASSWORD_REQUIRED: - label_text = _("Password required to enter room"); - password_text_label.visible = true; - password_stack.visible = true; - break; - case Muc.MucEnterError.BANNED: - label_text = _("Banned from joining or creating conference"); break; - case Muc.MucEnterError.ROOM_DOESNT_EXIST: - label_text = _("Room does not exist"); break; - case Muc.MucEnterError.CREATION_RESTRICTED: - label_text = _("Not allowed to create room"); break; - case Muc.MucEnterError.NOT_IN_MEMBER_LIST: - label_text = _("Members-only room"); break; - case Muc.MucEnterError.USE_RESERVED_ROOMNICK: - case Muc.MucEnterError.NICK_CONFLICT: - label_text = _("Choose a different nick"); break; - case Muc.MucEnterError.OCCUPANT_LIMIT_REACHED: - label_text = _("Too many occupants in room"); break; + if (join_result.muc_error != null) { + switch (join_result.muc_error) { + case Muc.MucEnterError.PASSWORD_REQUIRED: + label_text = _("Password required to enter room"); + password_text_label.visible = true; + password_stack.visible = true; + break; + case Muc.MucEnterError.BANNED: + label_text = _("Banned from joining or creating conference"); break; + case Muc.MucEnterError.ROOM_DOESNT_EXIST: + label_text = _("Room does not exist"); break; + case Muc.MucEnterError.CREATION_RESTRICTED: + label_text = _("Not allowed to create room"); break; + case Muc.MucEnterError.NOT_IN_MEMBER_LIST: + label_text = _("Members-only room"); break; + case Muc.MucEnterError.USE_RESERVED_ROOMNICK: + case Muc.MucEnterError.NICK_CONFLICT: + label_text = _("Choose a different nick"); break; + case Muc.MucEnterError.OCCUPANT_LIMIT_REACHED: + label_text = _("Too many occupants in room"); break; + } + } else if (join_result.stanza_error != null) { + label_text = _("Could not connect to %s").printf((new Jid(jid)).domainpart); } - } else if (join_result.stanza_error != null) { - label_text = _("Could not connect to %s").printf((new Jid(jid)).domainpart); + } catch (InvalidJidError e) { + label_text = _("Invalid address"); } notification_label.label = label_text; notification_revealer.set_reveal_child(true); diff --git a/main/src/ui/add_conversation/select_jid_fragment.vala b/main/src/ui/add_conversation/select_jid_fragment.vala index 09792c75..f0170cfb 100644 --- a/main/src/ui/add_conversation/select_jid_fragment.vala +++ b/main/src/ui/add_conversation/select_jid_fragment.vala @@ -53,13 +53,17 @@ public class SelectJidFragment : Gtk.Box { string[] ? values = str == "" ? null : str.split(" "); filterable_list.set_filter_values(values); - Jid? parsed_jid = Jid.parse(str); - if (parsed_jid != null && parsed_jid.localpart != null) { - foreach (Account account in accounts) { - AddListRow row = new AddListRow(stream_interactor, str, account); - filterable_list.add(row); - added_rows.add(row); + try { + Jid parsed_jid = new Jid(str); + if (parsed_jid != null && parsed_jid.localpart != null) { + foreach (Account account in accounts) { + AddListRow row = new AddListRow(stream_interactor, parsed_jid, account); + filterable_list.add(row); + added_rows.add(row); + } } + } catch (InvalidJidError ignored) { + // Ignore } } @@ -82,11 +86,11 @@ public class SelectJidFragment : Gtk.Box { private class AddListRow : ListRow { - public AddListRow(StreamInteractor stream_interactor, string jid, Account account) { + public AddListRow(StreamInteractor stream_interactor, Jid jid, Account account) { this.account = account; - this.jid = new Jid(jid); + this.jid = jid; - name_label.label = jid; + name_label.label = jid.to_string(); if (stream_interactor.get_accounts().size > 1) { via_label.label = account.bare_jid.to_string(); } else { diff --git a/main/src/ui/application.vala b/main/src/ui/application.vala index c8db09da..2b4b2d3e 100644 --- a/main/src/ui/application.vala +++ b/main/src/ui/application.vala @@ -50,12 +50,18 @@ public class Dino.Ui.Application : Gtk.Application, Dino.Application { public void handle_uri(string jid, string query, Gee.Map<string, string> options) { switch (query) { case "join": - show_join_muc_dialog(null, new Jid(jid)); + show_join_muc_dialog(null, jid); break; case "message": Gee.List<Account> accounts = stream_interactor.get_accounts(); - if (accounts.size == 1) { - Conversation conversation = stream_interactor.get_module(ConversationManager.IDENTITY).create_conversation(new Jid(jid), accounts[0], Conversation.Type.CHAT); + Jid parsed_jid = null; + try { + parsed_jid = new Jid(jid); + } catch (InvalidJidError ignored) { + // Ignored + } + if (accounts.size == 1 && parsed_jid != null) { + Conversation conversation = stream_interactor.get_module(ConversationManager.IDENTITY).create_conversation(parsed_jid, accounts[0], Conversation.Type.CHAT); stream_interactor.get_module(ConversationManager.IDENTITY).start_conversation(conversation); controller.select_conversation(conversation); } else { @@ -128,7 +134,7 @@ public class Dino.Ui.Application : Gtk.Application, Dino.Application { accept_muc_invite_action.activate.connect((variant) => { Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation_by_id(variant.get_int32()); if (conversation == null) return; - show_join_muc_dialog(conversation.account, conversation.counterpart); + show_join_muc_dialog(conversation.account, conversation.counterpart.to_string()); }); add_action(accept_muc_invite_action); @@ -182,13 +188,13 @@ public class Dino.Ui.Application : Gtk.Application, Dino.Application { license_type: License.GPL_3_0); } - private void show_join_muc_dialog(Account? account, Jid jid) { + private void show_join_muc_dialog(Account? account, string jid) { Dialog dialog = new Dialog.with_buttons(_("Join Channel"), window, Gtk.DialogFlags.MODAL | Gtk.DialogFlags.USE_HEADER_BAR, _("Join"), ResponseType.OK, _("Cancel"), ResponseType.CANCEL); dialog.modal = true; Button ok_button = dialog.get_widget_for_response(ResponseType.OK) as Button; ok_button.get_style_context().add_class("suggested-action"); ConferenceDetailsFragment conference_fragment = new ConferenceDetailsFragment(stream_interactor) { ok_button=ok_button }; - conference_fragment.jid = jid.to_string(); + conference_fragment.jid = jid; if (account != null) { conference_fragment.account = account; } diff --git a/main/src/ui/chat_input_controller.vala b/main/src/ui/chat_input_controller.vala index c386f55e..aafb40a3 100644 --- a/main/src/ui/chat_input_controller.vala +++ b/main/src/ui/chat_input_controller.vala @@ -108,7 +108,11 @@ public class ChatInputController : Object { return; case "/ping": Xmpp.XmppStream? stream = stream_interactor.get_stream(conversation.account); - stream.get_module(Xmpp.Xep.Ping.Module.IDENTITY).send_ping(stream, conversation.counterpart.with_resource(token[1]), null); + try { + stream.get_module(Xmpp.Xep.Ping.Module.IDENTITY).send_ping(stream, conversation.counterpart.with_resource(token[1]), null); + } catch (Xmpp.InvalidJidError e) { + warning("Could not ping invalid Jid: %s", e.message); + } return; case "/topic": stream_interactor.get_module(MucManager.IDENTITY).change_subject(conversation.account, conversation.counterpart, token[1]); diff --git a/main/src/ui/conversation_summary/content_item_widget_factory.vala b/main/src/ui/conversation_summary/content_item_widget_factory.vala index 00972371..54283e75 100644 --- a/main/src/ui/conversation_summary/content_item_widget_factory.vala +++ b/main/src/ui/conversation_summary/content_item_widget_factory.vala @@ -3,7 +3,6 @@ using Gdk; using Gtk; using Pango; using Xmpp; -using Unicode; using Dino.Entities; diff --git a/main/src/ui/manage_accounts/add_account_dialog.vala b/main/src/ui/manage_accounts/add_account_dialog.vala index 459e91d3..b1727c5d 100644 --- a/main/src/ui/manage_accounts/add_account_dialog.vala +++ b/main/src/ui/manage_accounts/add_account_dialog.vala @@ -73,6 +73,7 @@ public class AddAccountDialog : Gtk.Dialog { private Database db; private HashMap<ListBoxRow, string> list_box_jids = new HashMap<ListBoxRow, string>(); private Jid? server_jid = null; + private Jid? login_jid = null; private Xep.InBandRegistration.Form? form = null; public AddAccountDialog(StreamInteractor stream_interactor, Database db) { @@ -97,8 +98,12 @@ public class AddAccountDialog : Gtk.Dialog { // Select Server server_entry.changed.connect(() => { - Jid? jid = Jid.parse(server_entry.text); - select_server_continue.sensitive = jid != null && jid.localpart == null && jid.resourcepart == null; + try { + Jid jid = new Jid(server_entry.text); + select_server_continue.sensitive = jid != null && jid.localpart == null && jid.resourcepart == null; + } catch (InvalidJidError e) { + select_server_continue.sensitive = false; + } }); select_server_continue.clicked.connect(on_select_server_continue); login_button.clicked.connect(show_sign_in_jid); @@ -131,6 +136,7 @@ public class AddAccountDialog : Gtk.Dialog { set_default(sign_in_jid_continue_button); sign_in_jid_error_label.label = ""; + jid_entry.sensitive = true; animate_window_resize(sign_in_jid_box); } @@ -155,7 +161,7 @@ public class AddAccountDialog : Gtk.Dialog { set_default(sign_in_password_continue_button); sign_in_password_error_label.label = ""; - sign_in_password_title.label = _("Sign in to %s").printf(jid_entry.text); + sign_in_password_title.label = _("Sign in to %s").printf(login_jid.to_string()); animate_window_resize(sign_in_password_box); } @@ -205,46 +211,60 @@ public class AddAccountDialog : Gtk.Dialog { } private void on_jid_entry_changed() { - Jid? jid = Jid.parse(jid_entry.text); - if (jid != null && jid.localpart != null && jid.resourcepart == null) { - sign_in_jid_continue_button.set_sensitive(true); - jid_entry.secondary_icon_name = null; - } else { - sign_in_jid_continue_button.set_sensitive(false); + try { + login_jid = new Jid(jid_entry.text); + if (login_jid.localpart != null && login_jid.resourcepart == null) { + sign_in_jid_continue_button.sensitive = true; + jid_entry.secondary_icon_name = null; + } else { + sign_in_jid_continue_button.sensitive = false; + } + } catch (InvalidJidError e) { + sign_in_jid_continue_button.sensitive = false; } } private async void on_sign_in_jid_continue_button_clicked() { - Jid jid = new Jid(jid_entry.get_text()); - sign_in_jid_continue_stack.visible_child_name = "spinner"; - Register.ServerAvailabilityReturn server_status = yield Register.check_server_availability(jid); - sign_in_jid_continue_stack.visible_child_name = "label"; - if (server_status.available) { - show_sign_in_password(); - } else { - if (server_status.error_flags != null) { - string error_desc = "The server could not prove that it is %s.".printf("<b>" + jid.domainpart + "</b>"); - if (TlsCertificateFlags.UNKNOWN_CA in server_status.error_flags) { - error_desc += " " + "Its security certificate is not trusted by your computer's operating system."; - } else if (TlsCertificateFlags.BAD_IDENTITY in server_status.error_flags) { - error_desc += " " + "Its security certificate is issued to another domain."; - } else if (TlsCertificateFlags.NOT_ACTIVATED in server_status.error_flags) { - error_desc += " " + "Its security certificate will only become valid in the future."; - } else if (TlsCertificateFlags.EXPIRED in server_status.error_flags) { - error_desc += " " + "Its security certificate is expired."; - } - sign_in_tls_label.label = error_desc; - show_tls_error(); + try { + login_jid = new Jid(jid_entry.text); + jid_entry.sensitive = false; + sign_in_tls_label.label = ""; + sign_in_jid_error_label.label = ""; + sign_in_jid_continue_button.sensitive = false; + sign_in_jid_continue_stack.visible_child_name = "spinner"; + Register.ServerAvailabilityReturn server_status = yield Register.check_server_availability(login_jid); + sign_in_jid_continue_stack.visible_child_name = "label"; + sign_in_jid_continue_button.sensitive = true; + if (server_status.available) { + show_sign_in_password(); } else { - sign_in_jid_error_label.label = _("Could not connect to %s").printf(jid.domainpart); + jid_entry.sensitive = true; + if (server_status.error_flags != null) { + string error_desc = "The server could not prove that it is %s.".printf("<b>" + login_jid.domainpart + "</b>"); + if (TlsCertificateFlags.UNKNOWN_CA in server_status.error_flags) { + error_desc += " " + "Its security certificate is not trusted by your computer's operating system."; + } else if (TlsCertificateFlags.BAD_IDENTITY in server_status.error_flags) { + error_desc += " " + "Its security certificate is issued to another domain."; + } else if (TlsCertificateFlags.NOT_ACTIVATED in server_status.error_flags) { + error_desc += " " + "Its security certificate will only become valid in the future."; + } else if (TlsCertificateFlags.EXPIRED in server_status.error_flags) { + error_desc += " " + "Its security certificate is expired."; + } + sign_in_tls_label.label = error_desc; + show_tls_error(); + } else { + sign_in_jid_error_label.label = _("Could not connect to %s").printf(login_jid.domainpart); + } } + } catch (InvalidJidError e) { + warning("Invalid address from interface allowed login: %s", e.message); + sign_in_jid_error_label.label = _("Invalid address"); } } private async void on_sign_in_password_continue_button_clicked() { - Jid jid = new Jid(jid_entry.get_text()); - string password = password_entry.get_text(); - Account account = new Account(jid, null, password, null); + string password = password_entry.text; + Account account = new Account(login_jid, null, password, null); sign_in_password_continue_stack.visible_child_name = "spinner"; ConnectionManager.ConnectionError.Source? error = yield stream_interactor.get_module(Register.IDENTITY).add_check_account(account); @@ -256,7 +276,7 @@ public class AddAccountDialog : Gtk.Dialog { sign_in_password_error_label.label = _("Wrong username or password"); break; default: - sign_in_password_error_label.label = "Something went wrong"; + sign_in_password_error_label.label = _("Something went wrong"); break; } } else { @@ -266,13 +286,23 @@ public class AddAccountDialog : Gtk.Dialog { } private void on_select_server_continue() { - server_jid = new Jid(server_entry.text); - request_show_register_form.begin(); + try { + server_jid = new Jid(server_entry.text); + request_show_register_form.begin(); + } catch (InvalidJidError e) { + warning("Invalid address from interface allowed server: %s", e.message); + display_notification(_("Invalid address")); + } } private void on_server_list_row_selected(ListBox box, ListBoxRow? row) { - server_jid = new Jid(list_box_jids[row]); - request_show_register_form.begin(); + try { + server_jid = new Jid(list_box_jids[row]); + request_show_register_form.begin(); + } catch (InvalidJidError e) { + warning("Invalid address from selected server: %s", e.message); + display_notification(_("Invalid address")); + } } private async void request_show_register_form() { @@ -341,9 +371,14 @@ public class AddAccountDialog : Gtk.Dialog { case "password": password = field.get_value_string(); break; } } - Account account = new Account(new Jid.components(username, server_jid.domainpart, null), null, password, null); - add_activate_account(account); - show_success(account); + try { + Account account = new Account(new Jid.components(username, server_jid.domainpart, null), null, password, null); + add_activate_account(account); + show_success(account); + } catch (InvalidJidError e) { + warning("Invalid address from components of registration: %s", e.message); + display_notification(_("Invalid address")); + } } else { display_notification(error); } diff --git a/main/src/ui/util/helper.vala b/main/src/ui/util/helper.vala index db44103a..b48fdb22 100644 --- a/main/src/ui/util/helper.vala +++ b/main/src/ui/util/helper.vala @@ -331,11 +331,11 @@ public int get_only_emoji_count(string markup_text) { emoji_no--; } - if (last_was_emoji && last_was_modifier_base && Unicode.has_binary_property(curchar, Unicode.Property.EMOJI_MODIFIER)) { + if (last_was_emoji && last_was_modifier_base && ICU.has_binary_property(curchar, ICU.Property.EMOJI_MODIFIER)) { // still an emoji, but no longer a modifier base last_was_modifier_base = false; - } else if (Unicode.has_binary_property(curchar, Unicode.Property.EMOJI_PRESENTATION)) { - if (Unicode.has_binary_property(curchar, Unicode.Property.EMOJI_MODIFIER_BASE)) { + } else if (ICU.has_binary_property(curchar, ICU.Property.EMOJI_PRESENTATION)) { + if (ICU.has_binary_property(curchar, ICU.Property.EMOJI_MODIFIER_BASE)) { last_was_modifier_base = true; } emoji_no++; |