From a22146fd72860817907795012257777a32539097 Mon Sep 17 00:00:00 2001 From: Marvin W Date: Sat, 12 Sep 2020 12:48:17 +0200 Subject: Display placeholder avatar while loading --- libdino/src/service/avatar_manager.vala | 46 +++++++++++++- main/src/ui/avatar_image.vala | 106 +++++++++++++++++++++++++------- 2 files changed, 127 insertions(+), 25 deletions(-) diff --git a/libdino/src/service/avatar_manager.vala b/libdino/src/service/avatar_manager.vala index ba6efd31..4d86588f 100644 --- a/libdino/src/service/avatar_manager.vala +++ b/libdino/src/service/avatar_manager.vala @@ -67,6 +67,48 @@ public class AvatarManager : StreamInteractionModule, Object { return image; } + private string? get_avatar_hash(Account account, Jid jid_) { + Jid jid = jid_; + if (!stream_interactor.get_module(MucManager.IDENTITY).is_groupchat_occupant(jid_, account)) { + jid = jid_.bare_jid; + } + if (user_avatars.has_key(jid)) { + return user_avatars[jid]; + } else if (vcard_avatars.has_key(jid)) { + return vcard_avatars[jid]; + } else { + return null; + } + } + + public bool has_avatar_cached(Account account, Jid jid) { + string? hash = get_avatar_hash(account, jid); + return hash != null && cached_pixbuf.has_key(hash); + } + + public async bool has_avatar_stored(Account account, Jid jid) { + string? hash = get_avatar_hash(account, jid); + if (hash == null) return false; + if (cached_pixbuf.has_key(hash)) return true; + try { + if ((yield File.new_for_path(Path.build_filename(folder, hash)).query_info_async(FileAttribute.STANDARD_NAME, FileQueryInfoFlags.NONE)) != null) return true; + } catch (IOError ignored) { + return false; + } + return false; + } + + public bool has_avatar(Account account, Jid jid) { + return get_avatar_hash(account, jid) != null; + } + + public Pixbuf? get_cached_avatar(Account account, Jid jid_) { + string? hash = get_avatar_hash(account, jid_); + if (hash == null) return null; + if (cached_pixbuf.has_key(hash)) return cached_pixbuf[hash]; + return null; + } + public async Pixbuf? get_avatar(Account account, Jid jid_) { Jid jid = jid_; if (!stream_interactor.get_module(MucManager.IDENTITY).is_groupchat_occupant(jid_, account)) { @@ -229,13 +271,13 @@ public class AvatarManager : StreamInteractionModule, Object { public async Pixbuf? get_image(string id) { try { File file = File.new_for_path(Path.build_filename(folder, id)); - FileInputStream stream = yield file.read_async(); + FileInputStream stream = yield file.read_async(Priority.LOW); uint8 fbuf[1024]; size_t size; Checksum checksum = new Checksum (ChecksumType.SHA1); - while ((size = yield stream.read_async(fbuf)) > 0) { + while ((size = yield stream.read_async(fbuf, Priority.LOW)) > 0) { checksum.update(fbuf, size); } diff --git a/main/src/ui/avatar_image.vala b/main/src/ui/avatar_image.vala index 107e421a..b2f44ae2 100644 --- a/main/src/ui/avatar_image.vala +++ b/main/src/ui/avatar_image.vala @@ -44,8 +44,6 @@ public class AvatarImage : Misc { } public override bool draw(Cairo.Context ctx_in) { - if (drawer == null) return false; - Cairo.Context ctx = ctx_in; int width = this.width, height = this.height, base_factor = 1; if (use_image_surface == -1) { @@ -53,7 +51,7 @@ public class AvatarImage : Misc { use_image_surface = 1; } if (use_image_surface == 1) { - ctx_in.scale(1f/scale_factor, 1f/scale_factor); + ctx_in.scale(1f / scale_factor, 1f / scale_factor); if (cached_surface != null) { ctx_in.set_source_surface(cached_surface, 0, 0); ctx_in.paint(); @@ -67,10 +65,66 @@ public class AvatarImage : Misc { ctx = new Cairo.Context(cached_surface); } + AvatarDrawer drawer = this.drawer; + Jid[] jids = this.jids; + if (drawer == null && jids.length == 0) { + switch (conversation.type_) { + case CHAT: + case GROUPCHAT_PM: + // In direct chats or group chats, conversation avatar is same as counterpart avatar + jids = { conversation.counterpart }; + break; + case GROUPCHAT: + string user_color = Util.get_avatar_hex_color(stream_interactor, account, conversation.counterpart, conversation); + if (avatar_manager.has_avatar_cached(account, conversation.counterpart)) { + drawer = new AvatarDrawer().tile(avatar_manager.get_cached_avatar(account, conversation.counterpart), "#", user_color); + if (allow_gray && (!is_self_online() || !is_counterpart_online())) drawer.grayscale(); + } else { + Gee.List? occupants = muc_manager.get_other_offline_members(conversation.counterpart, account); + if (muc_manager.is_private_room(account, conversation.counterpart) && occupants != null && occupants.size > 0) { + jids = occupants.to_array(); + } else { + drawer = new AvatarDrawer().tile(null, "#", user_color); + if (allow_gray && (!is_self_online() || !is_counterpart_online())) drawer.grayscale(); + } + try_load_avatar_async(conversation.counterpart); + } + break; + } + } + if (drawer == null && jids.length > 0) { + drawer = new AvatarDrawer(); + for (int i = 0; i < (jids.length <= 4 ? jids.length : 3); i++) { + Jid avatar_jid = jids[i]; + Jid? real_avatar_jid = null; + if (conversation.type_ != Conversation.Type.CHAT && avatar_jid.equals_bare(conversation.counterpart) && muc_manager.is_private_room(account, conversation.counterpart.bare_jid)) { + // In private room, consider real jid + real_avatar_jid = muc_manager.get_real_jid(avatar_jid, account) ?? avatar_jid; + } + string display_name = Util.get_participant_display_name(stream_interactor, conversation, jids[i]); + string user_color = Util.get_avatar_hex_color(stream_interactor, account, jids[i], conversation); + if (avatar_manager.has_avatar_cached(account, avatar_jid)) { + drawer.tile(avatar_manager.get_cached_avatar(account, avatar_jid), display_name, user_color); + } else if (real_avatar_jid != null && avatar_manager.has_avatar_cached(account, real_avatar_jid)) { + drawer.tile(avatar_manager.get_cached_avatar(account, avatar_jid), display_name, user_color); + } else { + drawer.tile(null, display_name, user_color); + try_load_avatar_async(avatar_jid); + if (real_avatar_jid != null) try_load_avatar_async(real_avatar_jid); + } + } + if (jids.length > 4) { + drawer.plus(); + } + if (allow_gray && (!is_self_online() || !is_counterpart_online())) drawer.grayscale(); + } + + + if (drawer == null) return false; drawer.size(height, width) - .scale(base_factor) - .font(get_pango_context().get_font_description().get_family()) - .draw_on_context(ctx); + .scale(base_factor) + .font(get_pango_context().get_font_description().get_family()) + .draw_on_context(ctx); if (use_image_surface == 1) { ctx_in.set_source_surface(ctx.get_target(), 0, 0); @@ -81,6 +135,20 @@ public class AvatarImage : Misc { return true; } + private void try_load_avatar_async(Jid jid) { + if (avatar_manager.has_avatar(account, jid)) { + avatar_manager.get_avatar.begin(account, jid, (_, res) => { + var avatar = avatar_manager.get_avatar.end(res); + if (avatar != null) force_redraw(); + }); + } + } + + private void force_redraw() { + this.cached_surface = null; + queue_draw(); + } + private void disconnect_stream_interactor() { if (stream_interactor != null) { presence_manager.show_received.disconnect(on_show_received); @@ -105,12 +173,12 @@ public class AvatarImage : Misc { private void update_avatar_if_jid(Jid jid) { if (jid.equals_bare(this.conversation.counterpart)) { - update_avatar_async.begin(); + force_redraw(); return; } foreach (Jid ours in this.jids) { if (jid.equals_bare(ours)) { - update_avatar_async.begin(); + force_redraw(); return; } } @@ -118,7 +186,7 @@ public class AvatarImage : Misc { private void on_connection_changed(Account account, ConnectionManager.ConnectionState state) { if (!account.equals(this.account)) return; - update_avatar_async.begin(); + force_redraw(); } private void on_roster_updated(Account account, Jid jid, Roster.Item roster_item) { @@ -148,26 +216,18 @@ public class AvatarImage : Misc { } public void set_conversation(StreamInteractor stream_interactor, Conversation conversation) { - set_avatar_async.begin(stream_interactor, conversation, new Jid[0]); + set_avatar(stream_interactor, conversation, new Jid[0]); } public void set_conversation_participant(StreamInteractor stream_interactor, Conversation conversation, Jid sub_jid) { - set_avatar_async.begin(stream_interactor, conversation, new Jid[] {sub_jid}); + set_avatar(stream_interactor, conversation, new Jid[] {sub_jid}); } public void set_conversation_participants(StreamInteractor stream_interactor, Conversation conversation, Jid[] sub_jids) { - set_avatar_async.begin(stream_interactor, conversation, sub_jids); + set_avatar(stream_interactor, conversation, sub_jids); } - private async void update_avatar_async() { - this.cached_surface = null; - this.drawer = yield Util.get_conversation_participants_avatar_drawer(stream_interactor, conversation, jids); - if (allow_gray && (!is_self_online() || !is_counterpart_online())) drawer.grayscale(); - - queue_draw(); - } - - private async void set_avatar_async(StreamInteractor stream_interactor, Conversation conversation, Jid[] jids) { + private void set_avatar(StreamInteractor stream_interactor, Conversation conversation, Jid[] jids) { if (this.stream_interactor != null && stream_interactor != this.stream_interactor) { disconnect_stream_interactor(); } @@ -184,14 +244,14 @@ public class AvatarImage : Misc { this.conversation = conversation; this.jids = jids; - yield update_avatar_async(); + force_redraw(); } public void set_text(string text, bool gray = true) { disconnect_stream_interactor(); this.drawer = new AvatarDrawer().tile(null, text, null); if (gray) drawer.grayscale(); - queue_draw(); + force_redraw(); } } -- cgit v1.2.3-54-g00ecf