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`
-
+
${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`