experimental: display online/offline status for conversation members
This commit is contained in:
parent
f2096694c6
commit
7976859639
|
@ -60,7 +60,7 @@ public class Dino.CallState : Object {
|
||||||
XmppStream stream = stream_interactor.get_stream(call.account);
|
XmppStream stream = stream_interactor.get_stream(call.account);
|
||||||
if (stream == null) return;
|
if (stream == null) return;
|
||||||
|
|
||||||
Gee.List<Jid> occupants = stream_interactor.get_module(MucManager.IDENTITY).get_other_occupants(muc, call.account);
|
Gee.List<Jid> occupants = stream_interactor.get_module(MucManager.IDENTITY).get_other_members(muc, call.account);
|
||||||
foreach (Jid occupant in occupants) {
|
foreach (Jid occupant in occupants) {
|
||||||
Jid? real_jid = stream_interactor.get_module(MucManager.IDENTITY).get_real_jid(occupant, call.account);
|
Jid? real_jid = stream_interactor.get_module(MucManager.IDENTITY).get_real_jid(occupant, call.account);
|
||||||
if (real_jid == null) continue;
|
if (real_jid == null) continue;
|
||||||
|
|
|
@ -259,27 +259,46 @@ public class MucManager : StreamInteractionModule, Object {
|
||||||
return is_groupchat(jid, account) && !is_private_room(account, jid);
|
return is_groupchat(jid, account) && !is_private_room(account, jid);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Gee.List<Jid>? get_occupants(Jid jid, Account account) {
|
public Gee.List<Jid>? get_all_members(Jid jid, Account account) {
|
||||||
if (is_groupchat(jid, account)) {
|
if (is_groupchat(jid, account)) {
|
||||||
Gee.List<Jid> ret = new ArrayList<Jid>(Jid.equals_func);
|
Gee.List<Jid> ret = new ArrayList<Jid>(Jid.equals_func);
|
||||||
Gee.List<Jid>? full_jids = get_offline_members(jid, account);
|
|
||||||
|
// This should return all members of the chat
|
||||||
|
Gee.List<Jid>? members = get_offline_members(jid, account);
|
||||||
|
if (members != null) {
|
||||||
|
ret.add_all(members);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Gee.List<Jid>? get_members(Jid jid, Account account) {
|
||||||
|
if (is_groupchat(jid, account)) {
|
||||||
|
Gee.List<Jid> ret = new ArrayList<Jid>(Jid.equals_func);
|
||||||
|
Gee.List<Jid>? full_jids = stream_interactor.get_module(PresenceManager.IDENTITY).get_full_jids(jid, account);
|
||||||
if (full_jids != null) {
|
if (full_jids != null) {
|
||||||
ret.add_all(full_jids);
|
ret.add_all(full_jids);
|
||||||
// Remove eventual presence from bare jid
|
// Remove eventual presence from bare jid
|
||||||
ret.remove(jid);
|
ret.remove(jid);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Gee.List<Jid>? get_other_occupants(Jid jid, Account account) {
|
public Gee.List<Jid>? get_other_members(Jid jid, Account account) {
|
||||||
Gee.List<Jid>? occupants = get_occupants(jid, account);
|
Gee.List<Jid>? members = get_members(jid, account);
|
||||||
Jid? own_jid = get_own_jid(jid, account);
|
Jid? own_jid = get_own_jid(jid, account);
|
||||||
if (occupants != null && own_jid != null) {
|
if (members != null && own_jid != null) {
|
||||||
occupants.remove(own_jid);
|
members.remove(own_jid);
|
||||||
}
|
}
|
||||||
return occupants;
|
|
||||||
|
return members;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool is_groupchat(Jid jid, Account account) {
|
public bool is_groupchat(Jid jid, Account account) {
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<property name="margin-start">7</property>
|
<property name="margin-start">7</property>
|
||||||
<property name="margin-bottom">3</property>
|
<property name="margin-bottom">3</property>
|
||||||
<property name="margin-end">7</property>
|
<property name="margin-end">7</property>
|
||||||
<property name="column-spacing">10</property>
|
<property name="column-spacing">5</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="DinoUiAvatarPicture" id="picture">
|
<object class="DinoUiAvatarPicture" id="picture">
|
||||||
<property name="height-request">30</property>
|
<property name="height-request">30</property>
|
||||||
|
@ -25,5 +25,17 @@
|
||||||
</layout>
|
</layout>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="status_label">
|
||||||
|
<property name="max_width_chars">1</property>
|
||||||
|
<property name="ellipsize">end</property>
|
||||||
|
<property name="hexpand">1</property>
|
||||||
|
<property name="xalign">0</property>
|
||||||
|
<layout>
|
||||||
|
<property name="column">2</property>
|
||||||
|
<property name="row">0</property>
|
||||||
|
</layout>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
</object>
|
</object>
|
||||||
</interface>
|
</interface>
|
|
@ -107,7 +107,7 @@ public class OccupantsTabCompletor {
|
||||||
Gee.List<string> ret = generate_completions_from_messages(prefix);
|
Gee.List<string> ret = generate_completions_from_messages(prefix);
|
||||||
|
|
||||||
// Then, suggest other nicks in alphabetical order
|
// Then, suggest other nicks in alphabetical order
|
||||||
Gee.List<Jid>? occupants = stream_interactor.get_module(MucManager.IDENTITY).get_other_occupants(conversation.counterpart, conversation.account);
|
Gee.List<Jid>? occupants = stream_interactor.get_module(MucManager.IDENTITY).get_other_members(conversation.counterpart, conversation.account);
|
||||||
Gee.List<string> filtered_occupants = new ArrayList<string>();
|
Gee.List<string> filtered_occupants = new ArrayList<string>();
|
||||||
if (occupants != null) {
|
if (occupants != null) {
|
||||||
foreach (Jid jid in occupants) {
|
foreach (Jid jid in occupants) {
|
||||||
|
|
|
@ -17,7 +17,9 @@ public class List : Box {
|
||||||
|
|
||||||
private Conversation conversation;
|
private Conversation conversation;
|
||||||
private string[]? filter_values;
|
private string[]? filter_values;
|
||||||
private HashMap<Jid, Widget> rows = new HashMap<Jid, Widget>(Jid.hash_func, Jid.equals_func);
|
|
||||||
|
// List of all chat members with corresponding widgets
|
||||||
|
private HashMap<string, Widget> rows = new HashMap<string, Widget>();
|
||||||
public HashMap<Widget, ListRow> row_wrappers = new HashMap<Widget, ListRow>();
|
public HashMap<Widget, ListRow> row_wrappers = new HashMap<Widget, ListRow>();
|
||||||
|
|
||||||
public List(StreamInteractor stream_interactor, Conversation conversation) {
|
public List(StreamInteractor stream_interactor, Conversation conversation) {
|
||||||
|
@ -27,18 +29,46 @@ public class List : Box {
|
||||||
list_box.set_filter_func(filter);
|
list_box.set_filter_func(filter);
|
||||||
search_entry.search_changed.connect(refilter);
|
search_entry.search_changed.connect(refilter);
|
||||||
|
|
||||||
stream_interactor.get_module(PresenceManager.IDENTITY).show_received.connect(on_show_received);
|
stream_interactor.get_module(PresenceManager.IDENTITY).show_received.connect(on_received_online_presence);
|
||||||
stream_interactor.get_module(PresenceManager.IDENTITY).received_offline_presence.connect(on_received_offline_presence);
|
stream_interactor.get_module(PresenceManager.IDENTITY).received_offline_presence.connect(on_received_offline_presence);
|
||||||
|
|
||||||
initialize_for_conversation(conversation);
|
initialize_for_conversation(conversation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool get_status(Jid jid, Account account) {
|
||||||
|
Gee.List<Jid>? full_jids = stream_interactor.get_module(PresenceManager.IDENTITY).get_full_jids(jid, account);
|
||||||
|
|
||||||
|
debug("Get presence status for %s", jid.bare_jid.to_string());
|
||||||
|
string presence_str = null;
|
||||||
|
if (full_jids != null){
|
||||||
|
// Iterate over all connected devices
|
||||||
|
for (int i = 0; i < full_jids.size; i++) {
|
||||||
|
Jid full_jid = full_jids[i];
|
||||||
|
presence_str = stream_interactor.get_module(PresenceManager.IDENTITY).get_last_show(full_jid, account);
|
||||||
|
switch(presence_str) {
|
||||||
|
case "online": {
|
||||||
|
// Return online status if user is online on at least one device
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public void initialize_for_conversation(Conversation conversation) {
|
public void initialize_for_conversation(Conversation conversation) {
|
||||||
this.conversation = conversation;
|
this.conversation = conversation;
|
||||||
Gee.List<Jid>? occupants = stream_interactor.get_module(MucManager.IDENTITY).get_occupants(conversation.counterpart, conversation.account);
|
|
||||||
if (occupants != null) {
|
var identity = stream_interactor.get_module(MucManager.IDENTITY);
|
||||||
foreach (Jid occupant in occupants) {
|
Gee.List<Jid>? members = identity.get_all_members(conversation.counterpart, conversation.account);
|
||||||
add_occupant(occupant);
|
if (members != null) {
|
||||||
|
// Add all members and their status to the list
|
||||||
|
foreach (Jid member in members) {
|
||||||
|
bool online = get_status(member, conversation.account);
|
||||||
|
add_member(member, online);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
list_box.invalidate_filter();
|
list_box.invalidate_filter();
|
||||||
|
@ -53,34 +83,75 @@ public class List : Box {
|
||||||
list_box.invalidate_filter();
|
list_box.invalidate_filter();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void add_occupant(Jid jid) {
|
public void add_member(Jid jid, bool online) {
|
||||||
|
// HACK:
|
||||||
|
// Here we track members based on their names (not jids)
|
||||||
|
// Sometimes the same member can be referenced with different jids, for example:
|
||||||
|
// When initializing the conversation (see initialize_for_conversation function),
|
||||||
|
// we reference members like this:
|
||||||
|
// test_user@test_domain (using a local part, without a resource)
|
||||||
|
// However when updating status, we get the jid in the following format
|
||||||
|
// local_domain@test_domain/test_user (using a resource)
|
||||||
|
string member_name = null;
|
||||||
|
if (jid.resourcepart != null) {
|
||||||
|
member_name = jid.resourcepart;
|
||||||
|
} else {
|
||||||
|
member_name = jid.localpart;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (member_name == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!rows.has_key(member_name)) {
|
||||||
|
debug("adding new member %s", jid.to_string());
|
||||||
|
debug("local %s", jid.localpart);
|
||||||
|
debug("domain %s", jid.domainpart);
|
||||||
|
debug("resource %s", jid.resourcepart);
|
||||||
|
|
||||||
var row_wrapper = new ListRow(stream_interactor, conversation, jid);
|
var row_wrapper = new ListRow(stream_interactor, conversation, jid);
|
||||||
var widget = row_wrapper.get_widget();
|
var widget = row_wrapper.get_widget();
|
||||||
|
|
||||||
row_wrappers[widget] = row_wrapper;
|
if (online) {
|
||||||
rows[jid] = widget;
|
row_wrapper.set_online();
|
||||||
list_box.append(widget);
|
} else {
|
||||||
|
row_wrapper.set_offline();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void remove_occupant(Jid jid) {
|
row_wrappers[widget] = row_wrapper;
|
||||||
list_box.remove(rows[jid]);
|
rows[member_name] = widget;
|
||||||
rows.unset(jid);
|
list_box.append(widget);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void on_received_offline_presence(Jid jid, Account account) {
|
private void on_received_offline_presence(Jid jid, Account account) {
|
||||||
if (conversation != null && conversation.counterpart.equals_bare(jid) && jid.is_full()) {
|
if (conversation != null && conversation.counterpart.equals_bare(jid) && jid.is_full()) {
|
||||||
if (rows.has_key(jid)) {
|
var member_name = jid.resourcepart;
|
||||||
remove_occupant(jid);
|
if (member_name == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rows.has_key(member_name)) {
|
||||||
|
row_wrappers[rows[member_name]].set_offline();
|
||||||
|
debug("%s is now offline", jid.to_string());
|
||||||
}
|
}
|
||||||
list_box.invalidate_filter();
|
list_box.invalidate_filter();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void on_show_received(Jid jid, Account account) {
|
private void on_received_online_presence(Jid jid, Account account) {
|
||||||
if (conversation != null && conversation.counterpart.equals_bare(jid) && jid.is_full()) {
|
if (conversation != null && conversation.counterpart.equals_bare(jid) && jid.is_full()) {
|
||||||
if (!rows.has_key(jid)) {
|
var member_name = jid.resourcepart;
|
||||||
add_occupant(jid);
|
if (member_name == null) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!rows.has_key(member_name)) {
|
||||||
|
add_member(jid, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
row_wrappers[rows[member_name]].set_online();
|
||||||
|
debug("%s is now online", jid.to_string());
|
||||||
list_box.invalidate_filter();
|
list_box.invalidate_filter();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,9 @@ public class ListRow : Object {
|
||||||
private AvatarPicture picture;
|
private AvatarPicture picture;
|
||||||
public Label name_label;
|
public Label name_label;
|
||||||
|
|
||||||
|
// TODO: use something more visual for status
|
||||||
|
public Label status;
|
||||||
|
|
||||||
public Conversation? conversation;
|
public Conversation? conversation;
|
||||||
public Jid? jid;
|
public Jid? jid;
|
||||||
|
|
||||||
|
@ -19,6 +22,8 @@ public class ListRow : Object {
|
||||||
main_grid = (Grid) builder.get_object("main_grid");
|
main_grid = (Grid) builder.get_object("main_grid");
|
||||||
picture = (AvatarPicture) builder.get_object("picture");
|
picture = (AvatarPicture) builder.get_object("picture");
|
||||||
name_label = (Label) builder.get_object("name_label");
|
name_label = (Label) builder.get_object("name_label");
|
||||||
|
status = (Label) builder.get_object("status_label");
|
||||||
|
status.label = "(unknown)";
|
||||||
}
|
}
|
||||||
|
|
||||||
public ListRow(StreamInteractor stream_interactor, Conversation conversation, Jid jid) {
|
public ListRow(StreamInteractor stream_interactor, Conversation conversation, Jid jid) {
|
||||||
|
@ -37,6 +42,14 @@ public class ListRow : Object {
|
||||||
public Widget get_widget() {
|
public Widget get_widget() {
|
||||||
return main_grid;
|
return main_grid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void set_online() {
|
||||||
|
status.label = ("");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void set_offline() {
|
||||||
|
status.label = ("(offline)");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -154,7 +154,7 @@ public class BadMessagesWidget : Box {
|
||||||
} else if (conversation.type_ == Conversation.Type.GROUPCHAT) {
|
} else if (conversation.type_ == Conversation.Type.GROUPCHAT) {
|
||||||
who = jid.to_string();
|
who = jid.to_string();
|
||||||
// `jid` is a real JID. In MUCs, try to show nicks instead (given that the JID is currently online)
|
// `jid` is a real JID. In MUCs, try to show nicks instead (given that the JID is currently online)
|
||||||
var occupants = plugin.app.stream_interactor.get_module(MucManager.IDENTITY).get_occupants(conversation.counterpart, conversation.account);
|
var occupants = plugin.app.stream_interactor.get_module(MucManager.IDENTITY).get_members(conversation.counterpart, conversation.account);
|
||||||
if (occupants == null) return;
|
if (occupants == null) return;
|
||||||
foreach (Jid occupant in occupants) {
|
foreach (Jid occupant in occupants) {
|
||||||
if (jid.equals_bare(plugin.app.stream_interactor.get_module(MucManager.IDENTITY).get_real_jid(occupant, conversation.account))) {
|
if (jid.equals_bare(plugin.app.stream_interactor.get_module(MucManager.IDENTITY).get_real_jid(occupant, conversation.account))) {
|
||||||
|
|
|
@ -53,7 +53,7 @@ private class EncryptionListEntry : Plugins.EncryptionListEntry, Object {
|
||||||
}
|
}
|
||||||
} else if (conversation.type_ == Conversation.Type.GROUPCHAT) {
|
} else if (conversation.type_ == Conversation.Type.GROUPCHAT) {
|
||||||
Gee.List<Jid> muc_jids = new Gee.ArrayList<Jid>();
|
Gee.List<Jid> muc_jids = new Gee.ArrayList<Jid>();
|
||||||
Gee.List<Jid>? occupants = stream_interactor.get_module(MucManager.IDENTITY).get_occupants(conversation.counterpart, conversation.account);
|
Gee.List<Jid>? occupants = stream_interactor.get_module(MucManager.IDENTITY).get_members(conversation.counterpart, conversation.account);
|
||||||
if (occupants != null) muc_jids.add_all(occupants);
|
if (occupants != null) muc_jids.add_all(occupants);
|
||||||
Gee.List<Jid>? offline_members = stream_interactor.get_module(MucManager.IDENTITY).get_offline_members(conversation.counterpart, conversation.account);
|
Gee.List<Jid>? offline_members = stream_interactor.get_module(MucManager.IDENTITY).get_offline_members(conversation.counterpart, conversation.account);
|
||||||
if (offline_members != null) muc_jids.add_all(offline_members);
|
if (offline_members != null) muc_jids.add_all(offline_members);
|
||||||
|
|
|
@ -36,7 +36,7 @@ public class Manager : StreamInteractionModule, Object {
|
||||||
keys.add(db.get_account_key(conversation.account));
|
keys.add(db.get_account_key(conversation.account));
|
||||||
if (conversation.type_ == Conversation.Type.GROUPCHAT) {
|
if (conversation.type_ == Conversation.Type.GROUPCHAT) {
|
||||||
Gee.List<Jid> muc_jids = new Gee.ArrayList<Jid>();
|
Gee.List<Jid> muc_jids = new Gee.ArrayList<Jid>();
|
||||||
Gee.List<Jid>? occupants = stream_interactor.get_module(MucManager.IDENTITY).get_occupants(conversation.counterpart, conversation.account);
|
Gee.List<Jid>? occupants = stream_interactor.get_module(MucManager.IDENTITY).get_members(conversation.counterpart, conversation.account);
|
||||||
if (occupants != null) muc_jids.add_all(occupants);
|
if (occupants != null) muc_jids.add_all(occupants);
|
||||||
Gee.List<Jid>? offline_members = stream_interactor.get_module(MucManager.IDENTITY).get_offline_members(conversation.counterpart, conversation.account);
|
Gee.List<Jid>? offline_members = stream_interactor.get_module(MucManager.IDENTITY).get_offline_members(conversation.counterpart, conversation.account);
|
||||||
if (occupants != null) muc_jids.add_all(offline_members);
|
if (occupants != null) muc_jids.add_all(offline_members);
|
||||||
|
|
Loading…
Reference in a new issue