From 35947e3d62d5d3251d9813071a2ba17d9d34b9fe Mon Sep 17 00:00:00 2001 From: JC Brand Date: Fri, 19 Nov 2021 13:43:14 +0100 Subject: [PATCH] Show avatars in MUC occupants sidebar Fixes #1322 (Also clean up some loose threads) --- CHANGES.md | 1 + src/headless/plugins/vcard/utils.js | 12 ++-- src/modals/templates/occupant.js | 2 +- .../controlbox/styles/_controlbox.scss | 5 ++ src/plugins/muc-views/constants.js | 9 +++ src/plugins/muc-views/sidebar.js | 3 + src/plugins/muc-views/styles/index.scss | 3 - .../muc-views/styles/muc-occupants.scss | 35 +++------ .../muc-views/templates/muc-sidebar.js | 23 +----- src/plugins/muc-views/templates/occupant.js | 71 ++++++++++++++----- src/plugins/muc-views/utils.js | 6 ++ src/plugins/profile/statusview.js | 4 +- src/plugins/profile/templates/profile.js | 2 +- src/plugins/rosterview/index.js | 1 + src/plugins/rosterview/styles/roster.scss | 29 -------- .../rosterview/templates/roster_item.js | 42 ++++++----- src/shared/avatar/avatar.scss | 6 ++ src/shared/avatar/templates/avatar.js | 6 +- src/shared/styles/messages.scss | 1 - src/shared/styles/status.scss | 34 +++++++++ 20 files changed, 167 insertions(+), 128 deletions(-) create mode 100644 src/plugins/muc-views/constants.js create mode 100644 src/shared/styles/status.scss diff --git a/CHANGES.md b/CHANGES.md index 2014edcf9..7bcfb3745 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -8,6 +8,7 @@ - Fix trimming of chats in overlayed view mode - OMEMO bugfix: Always create device session based on real JID. - If `auto_register_muc_nickname` is set, make sure to register when the user changes current nick. +- #1322: Display occupants’ avatars in the occupants list - #1419: Clicking on avatar should show bigger version - #2647: Singleton mode doesn't work - #2704: Send button doesn't work in a multi-user chat diff --git a/src/headless/plugins/vcard/utils.js b/src/headless/plugins/vcard/utils.js index 6eb4f5685..8c287f97c 100644 --- a/src/headless/plugins/vcard/utils.js +++ b/src/headless/plugins/vcard/utils.js @@ -82,8 +82,10 @@ function getVCardForChatroomOccupant (message) { export async function setVCardOnOccupant (occupant) { await api.waitUntil('VCardsInitialized'); occupant.vcard = getVCardForChatroomOccupant(occupant); - occupant.vcard.on('change', () => occupant.trigger('vcard:change')); - occupant.trigger('vcard:add'); + if (occupant.vcard) { + occupant.vcard.on('change', () => occupant.trigger('vcard:change')); + occupant.trigger('vcard:add'); + } } export async function setVCardOnMUCMessage (message) { @@ -92,8 +94,10 @@ export async function setVCardOnMUCMessage (message) { } else { await api.waitUntil('VCardsInitialized'); message.vcard = getVCardForChatroomOccupant(message); - message.vcard.on('change', () => message.trigger('vcard:change')); - message.trigger('vcard:add'); + if (message.vcard) { + message.vcard.on('change', () => message.trigger('vcard:change')); + message.trigger('vcard:add'); + } } } diff --git a/src/modals/templates/occupant.js b/src/modals/templates/occupant.js index 96fb4c30b..97ce5e86d 100644 --- a/src/modals/templates/occupant.js +++ b/src/modals/templates/occupant.js @@ -16,7 +16,7 @@ export default (o) => {
diff --git a/src/plugins/controlbox/styles/_controlbox.scss b/src/plugins/controlbox/styles/_controlbox.scss index e866c59c8..5fb687ee6 100644 --- a/src/plugins/controlbox/styles/_controlbox.scss +++ b/src/plugins/controlbox/styles/_controlbox.scss @@ -75,6 +75,11 @@ order: -1; color: var(--controlbox-text-color); + .chat-status--avatar { + border: 1px solid var(--controlbox-pane-background-color); + background: var(--controlbox-pane-background-color); + } + converse-brand-logo { width: 100%; display: block; diff --git a/src/plugins/muc-views/constants.js b/src/plugins/muc-views/constants.js new file mode 100644 index 000000000..2d7b2b9f3 --- /dev/null +++ b/src/plugins/muc-views/constants.js @@ -0,0 +1,9 @@ +export const PRETTY_CHAT_STATUS = { + 'offline': 'Offline', + 'unavailable': 'Unavailable', + 'xa': 'Extended Away', + 'away': 'Away', + 'dnd': 'Do not disturb', + 'chat': 'Chattty', + 'online': 'Online' +}; diff --git a/src/plugins/muc-views/sidebar.js b/src/plugins/muc-views/sidebar.js index 4a5e114ec..1cac50ebb 100644 --- a/src/plugins/muc-views/sidebar.js +++ b/src/plugins/muc-views/sidebar.js @@ -3,6 +3,7 @@ import tpl_muc_sidebar from "./templates/muc-sidebar.js"; import { CustomElement } from 'shared/components/element.js'; import { _converse, api, converse } from "@converse/headless/core"; +import 'shared/styles/status.scss'; import './styles/muc-occupants.scss'; const { u } = converse.env; @@ -21,6 +22,8 @@ export default class MUCSidebar extends CustomElement { this.listenTo(this.model.occupants, 'add', this.requestUpdate); this.listenTo(this.model.occupants, 'remove', this.requestUpdate); this.listenTo(this.model.occupants, 'change', this.requestUpdate); + this.listenTo(this.model.occupants, 'vcard:change', this.requestUpdate); + this.listenTo(this.model.occupants, 'vcard:add', this.requestUpdate); this.model.initialized.then(() => this.requestUpdate()); } diff --git a/src/plugins/muc-views/styles/index.scss b/src/plugins/muc-views/styles/index.scss index 1d1d10015..859f44495 100644 --- a/src/plugins/muc-views/styles/index.scss +++ b/src/plugins/muc-views/styles/index.scss @@ -67,9 +67,6 @@ converse-muc-destroyed { display: none; } } - .occupant-status { - margin-top: 6px; - } } } } diff --git a/src/plugins/muc-views/styles/muc-occupants.scss b/src/plugins/muc-views/styles/muc-occupants.scss index d4e9e3ae4..5b9719db5 100644 --- a/src/plugins/muc-views/styles/muc-occupants.scss +++ b/src/plugins/muc-views/styles/muc-occupants.scss @@ -1,5 +1,15 @@ .conversejs { converse-muc.chatroom { + + .chat-status--avatar { + background: var(--occupants-background-color); + border: 1px solid var(--occupants-background-color); + } + + .badge-groupchat { + background-color: var(--groupchats-header-color); + } + .box-flyout { .occupants { display: flex; @@ -90,6 +100,7 @@ flex-direction: row; span { + height: 1.6em; margin-right: 0.25rem; } } @@ -102,30 +113,6 @@ .badge { margin-bottom: 0.125rem; } - - .occupant-status { - display: inline-block; - margin: 0 0.5em 0.125em 0; - width: 0.5em; - height: 0.5em; - - &.occupant-online, - &.occupant-chat { - background-color: #1A9707; - } - &.occupant-dnd { - background-color: red; - } - &.occupant-away { - background-color: darkorange; - } - &.occupant-xa { - background-color: orange; - } - &.occupant-offline { - background-color: darkgrey; - } - } } } } diff --git a/src/plugins/muc-views/templates/muc-sidebar.js b/src/plugins/muc-views/templates/muc-sidebar.js index d76250aec..fd687d36b 100644 --- a/src/plugins/muc-views/templates/muc-sidebar.js +++ b/src/plugins/muc-views/templates/muc-sidebar.js @@ -3,29 +3,8 @@ import { __ } from 'i18n'; import tpl_occupant from "./occupant.js"; -const PRETTY_CHAT_STATUS = { - 'offline': 'Offline', - 'unavailable': 'Unavailable', - 'xa': 'Extended Away', - 'away': 'Away', - 'dnd': 'Do not disturb', - 'chat': 'Chattty', - 'online': 'Online' -}; - - export default (o) => { - const i18n_occupant_hint = (occupant) => __('Click to mention %1$s in your message.', occupant.get('nick')) const i18n_participants = __('Participants'); - const occupant_tpls = o.occupants.map(occupant => { - return tpl_occupant(Object.assign({ - 'jid': '', - 'hint_show': PRETTY_CHAT_STATUS[occupant.get('show')], - 'hint_occupant': i18n_occupant_hint(occupant), - 'onOccupantClicked': o.onOccupantClicked - }, occupant.toJSON())); - }); - return html`
@@ -36,6 +15,6 @@ export default (o) => {
-
    ${occupant_tpls}
+
    ${o.occupants.map(occ => tpl_occupant(occ, o))}
`; } diff --git a/src/plugins/muc-views/templates/occupant.js b/src/plugins/muc-views/templates/occupant.js index 4fa7bd45b..c3facf865 100644 --- a/src/plugins/muc-views/templates/occupant.js +++ b/src/plugins/muc-views/templates/occupant.js @@ -1,44 +1,77 @@ -import { html } from "lit"; +import { PRETTY_CHAT_STATUS } from '../constants.js'; import { __ } from 'i18n'; +import { html } from "lit"; +import { showOccupantModal } from '../utils.js'; +const i18n_occupant_hint = (o) => __('Click to mention %1$s in your message.', o.get('nick')) const occupant_title = (o) => { + const role = o.get('role'); + const hint_occupant = i18n_occupant_hint(o); const i18n_moderator_hint = __('This user is a moderator.'); const i18n_participant_hint = __('This user can send messages in this groupchat.'); const i18n_visitor_hint = __('This user can NOT send messages in this groupchat.') - const spaced_jid = `${o.jid} ` || ''; - if (o.role === "moderator") { - return `${spaced_jid}${i18n_moderator_hint} ${o.hint_occupant}`; - } else if (o.role === "participant") { - return `${spaced_jid}${i18n_participant_hint} ${o.hint_occupant}`; - } else if (o.role === "visitor") { - return `${spaced_jid}${i18n_visitor_hint} ${o.hint_occupant}`; - } else if (!["visitor", "participant", "moderator"].includes(o.role)) { - return `${spaced_jid}${o.hint_occupant}`; + const spaced_jid = o.get('jid') ? `${o.get('jid')} ` : ''; + if (role === "moderator") { + return `${spaced_jid}${i18n_moderator_hint} ${hint_occupant}`; + } else if (role === "participant") { + return `${spaced_jid}${i18n_participant_hint} ${hint_occupant}`; + } else if (role === "visitor") { + return `${spaced_jid}${i18n_visitor_hint} ${hint_occupant}`; + } else if (!["visitor", "participant", "moderator"].includes(role)) { + return `${spaced_jid}${hint_occupant}`; } } -export default (o) => { - const i18n_owner = __('Owner'); +export default (o, chat) => { + const affiliation = o.get('affiliation'); + const hint_show = PRETTY_CHAT_STATUS[o.get('show')]; const i18n_admin = __('Admin'); const i18n_member = __('Member'); const i18n_moderator = __('Moderator'); + const i18n_owner = __('Owner'); const i18n_visitor = __('Visitor'); + const role = o.get('role'); + + const show = o.get('show'); + let classes, color; + if (show === 'online') { + [classes, color] = ['fa fa-circle', 'chat-status-online']; + } else if (show === 'dnd') { + [classes, color] = ['fa fa-minus-circle', 'chat-status-busy']; + } else if (show === 'away') { + [classes, color] = ['fa fa-circle', 'chat-status-away']; + } else { + [classes, color] = ['fa fa-circle', 'subdued-color']; + } + return html`
  • - ${o.nick || o.jid} + ${o.getDisplayName()} - ${ (o.affiliation === "owner") ? html`${i18n_owner}` : '' } - ${ (o.affiliation === "admin") ? html`${i18n_admin}` : '' } - ${ (o.affiliation === "member") ? html`${i18n_member}` : '' } - ${ (o.role === "moderator") ? html`${i18n_moderator}` : '' } - ${ (o.role === "visitor") ? html`${i18n_visitor}` : '' } + ${ (affiliation === "owner") ? html`${i18n_owner}` : '' } + ${ (affiliation === "admin") ? html`${i18n_admin}` : '' } + ${ (affiliation === "member") ? html`${i18n_member}` : '' } + ${ (role === "moderator") ? html`${i18n_moderator}` : '' } + ${ (role === "visitor") ? html`${i18n_visitor}` : '' }
    diff --git a/src/plugins/muc-views/utils.js b/src/plugins/muc-views/utils.js index 240db7c19..121cf76f8 100644 --- a/src/plugins/muc-views/utils.js +++ b/src/plugins/muc-views/utils.js @@ -1,4 +1,5 @@ import ModeratorToolsModal from './modals/moderator-tools.js'; +import OccupantModal from 'modals/occupant.js'; import log from "@converse/headless/log"; import tpl_spinner from 'templates/spinner.js'; import { __ } from 'i18n'; @@ -292,6 +293,11 @@ export function showModeratorToolsModal (muc, affiliation) { } +export function showOccupantModal (ev, occupant) { + api.modal.show(OccupantModal, { 'model': occupant }, ev); +} + + export function parseMessageForMUCCommands (muc, text) { if ( api.settings.get('muc_disable_slash_commands') && diff --git a/src/plugins/profile/statusview.js b/src/plugins/profile/statusview.js index b167b1617..24c9e65e6 100644 --- a/src/plugins/profile/statusview.js +++ b/src/plugins/profile/statusview.js @@ -4,7 +4,7 @@ import { CustomElement } from 'shared/components/element.js'; import { __ } from 'i18n'; import { _converse, api } from '@converse/headless/core'; -class ProfileView extends CustomElement { +class Profile extends CustomElement { initialize () { this.model = _converse.xmppstatus; @@ -41,4 +41,4 @@ class ProfileView extends CustomElement { } } -api.elements.define('converse-user-profile', ProfileView); +api.elements.define('converse-user-profile', Profile); diff --git a/src/plugins/profile/templates/profile.js b/src/plugins/profile/templates/profile.js index 1ec568148..e96f71e72 100644 --- a/src/plugins/profile/templates/profile.js +++ b/src/plugins/profile/templates/profile.js @@ -51,7 +51,7 @@ export default (el) => {
  • ` diff --git a/src/plugins/rosterview/index.js b/src/plugins/rosterview/index.js index 925976c00..6b68fd9d4 100644 --- a/src/plugins/rosterview/index.js +++ b/src/plugins/rosterview/index.js @@ -12,6 +12,7 @@ import { RosterFilter, RosterFilterView } from './filterview.js'; import { _converse, api, converse } from "@converse/headless/core"; import { highlightRosterItem } from './utils.js'; +import 'shared/styles/status.scss'; import './styles/roster.scss'; diff --git a/src/plugins/rosterview/styles/roster.scss b/src/plugins/rosterview/styles/roster.scss index 18c961528..8eb334602 100644 --- a/src/plugins/rosterview/styles/roster.scss +++ b/src/plugins/rosterview/styles/roster.scss @@ -81,35 +81,6 @@ .current-xmpp-contact { margin: 0.25em 0; - - .chat-status { - vertical-align: middle; - font-size: 0.6em; - margin-right: 0; - margin-left: -0.7em; - margin-bottom: -1.5em; - border-radius: 50%; - border: 2px solid var(--occupants-background-color); - } - .chat-status--offline { - margin-right: 0.8em; - } - .chat-status--online { - color: var(--chat-status-online); - } - .chat-status--busy { - color: var(--chat-status-busy); - } - .chat-status--away { - color: var(--chat-status-away); - } - .chat-status--offline { - display: none; - } - .far.fa-circle, - .fa-times-circle { - color: var(--subdued-color); - } } li { diff --git a/src/plugins/rosterview/templates/roster_item.js b/src/plugins/rosterview/templates/roster_item.js index cb678b6e8..0167442d8 100644 --- a/src/plugins/rosterview/templates/roster_item.js +++ b/src/plugins/rosterview/templates/roster_item.js @@ -6,31 +6,35 @@ import { STATUSES } from '../constants.js'; export default (el, item) => { const show = item.presence.get('show') || 'offline'; - let status_icon; - if (show === 'online') { - status_icon = 'fa fa-circle chat-status chat-status--online'; - } else if (show === 'away') { - status_icon = 'fa fa-circle chat-status chat-status--away'; - } else if (show === 'xa') { - status_icon = 'far fa-circle chat-status chat-status-xa'; - } else if (show === 'dnd') { - status_icon = 'fa fa-minus-circle chat-status chat-status--busy'; - } else { - status_icon = 'fa fa-times-circle chat-status chat-status--offline'; - } + let classes, color; + if (show === 'online') { + [classes, color] = ['fa fa-circle', 'chat-status-online']; + } else if (show === 'dnd') { + [classes, color] = ['fa fa-minus-circle', 'chat-status-busy']; + } else if (show === 'away') { + [classes, color] = ['fa fa-circle', 'chat-status-away']; + } else { + [classes, color] = ['fa fa-circle', 'subdued-color']; + } const display_name = item.getDisplayName(); const desc_status = STATUSES[show]; const num_unread = item.get('num_unread') || 0; - const i18n_chat = __('Click to chat with %1$s (XMPP address: %2$s)', display_name, el.jid); + const i18n_chat = __('Click to chat with %1$s (XMPP address: %2$s)', display_name, el.model.get('jid')); const i18n_remove = __('Click to remove %1$s as a contact', display_name); return html` - - + + + + ${ num_unread ? html`${ num_unread }` : '' } ${display_name} diff --git a/src/shared/avatar/avatar.scss b/src/shared/avatar/avatar.scss index 0a1d801ff..6043f2767 100644 --- a/src/shared/avatar/avatar.scss +++ b/src/shared/avatar/avatar.scss @@ -1,6 +1,12 @@ converse-avatar { border: 0; background: transparent; + + &.modal-avatar { + display: block; + margin-bottom: 1em; + } + .avatar { border-radius: var(--avatar-border-radius); } diff --git a/src/shared/avatar/templates/avatar.js b/src/shared/avatar/templates/avatar.js index 5ffb85e32..5d0defe8c 100644 --- a/src/shared/avatar/templates/avatar.js +++ b/src/shared/avatar/templates/avatar.js @@ -7,9 +7,9 @@ const getImgHref = (image, image_type) => { export default (o) => { if (o.image) { return html` - - - `; + + + `; } else { return ''; } diff --git a/src/shared/styles/messages.scss b/src/shared/styles/messages.scss index f8d0229bc..a6d76bca6 100644 --- a/src/shared/styles/messages.scss +++ b/src/shared/styles/messages.scss @@ -252,7 +252,6 @@ .chat-msg__heading { width: 100%; - margin-top: 0.5em; padding-right: 0.25rem; padding-bottom: 0.25rem; diff --git a/src/shared/styles/status.scss b/src/shared/styles/status.scss new file mode 100644 index 000000000..e2dbd79b1 --- /dev/null +++ b/src/shared/styles/status.scss @@ -0,0 +1,34 @@ +.conversejs { + .chat-status { + vertical-align: middle; + margin-right: 0; + border-radius: 50%; + font-size: 1em; + + &.chat-status--avatar { + font-size: 0.6rem; + margin-left: -0.7em; + margin-bottom: -1.9em; + border-radius: 50%; + } + } + .chat-status--offline { + margin-right: 0.8em; + } + .chat-status--online { + color: var(--chat-status-online); + } + .chat-status--busy { + color: var(--chat-status-busy); + } + .chat-status--away { + color: var(--chat-status-away); + } + .chat-status--offline { + display: none; + } + .far.fa-circle, + .fa-times-circle { + color: var(--subdued-color); + } +}