Use SVG icons and tweak message padding

This commit is contained in:
JC Brand 2022-01-31 21:51:51 +01:00
parent 125f45c447
commit bef2cbb462
21 changed files with 135 additions and 89 deletions

View File

@ -1,6 +1,5 @@
import BootstrapModal from "plugins/modal/base.js";
import tpl_muc_details from "./templates/muc-details.js";
import { __ } from 'i18n';
export default BootstrapModal.extend({
@ -15,13 +14,6 @@ export default BootstrapModal.extend({
},
toHTML () {
return tpl_muc_details(Object.assign(
this.model.toJSON(), {
'config': this.model.config.toJSON(),
'display_name': __('Groupchat info for %1$s', this.model.getDisplayName()),
'features': this.model.features.toJSON(),
'num_occupants': this.model.occupants.length,
})
);
return tpl_muc_details(this.model);
}
});

View File

@ -13,7 +13,13 @@ const subject = (o) => {
}
export default (o) => {
export default (model) => {
const o = model.toJSON();
const config = model.config.toJSON();
const display_name = __('Groupchat info for %1$s', model.getDisplayName());
const features = model.features.toJSON();
const num_occupants = model.occupants.filter(o => o.get('show') !== 'offline').length;
const i18n_address = __('Groupchat XMPP address');
const i18n_archiving = __('Message archiving');
const i18n_archiving_help = __('Messages are archived on the server');
@ -48,7 +54,7 @@ export default (o) => {
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="muc-details-modal-label">${o.display_name}</h5>
<h5 class="modal-title" id="muc-details-modal-label">${display_name}</h5>
${modal_header_close_button}
</div>
<div class="modal-body">
@ -56,25 +62,25 @@ export default (o) => {
<div class="room-info">
<p class="room-info"><strong>${i18n_name}</strong>: ${o.name}</p>
<p class="room-info"><strong>${i18n_address}</strong>: ${o.jid}</p>
<p class="room-info"><strong>${i18n_desc}</strong>: ${o.config.description}</p>
<p class="room-info"><strong>${i18n_desc}</strong>: ${config.description}</p>
${ (o.subject) ? subject(o) : '' }
<p class="room-info"><strong>${i18n_online_users}</strong>: ${o.num_occupants}</p>
<p class="room-info"><strong>${i18n_online_users}</strong>: ${num_occupants}</p>
<p class="room-info"><strong>${i18n_features}</strong>:
<div class="chatroom-features">
<ul class="features-list">
${ o.features.passwordprotected ? html`<li class="feature" ><span class="fa fa-lock"></span>${i18n_password_protected} - <em>${i18n_password_help}</em></li>` : '' }
${ o.features.unsecured ? html`<li class="feature" ><span class="fa fa-unlock"></span>${i18n_no_password_required} - <em>${i18n_no_pass_help}</em></li>` : '' }
${ o.features.hidden ? html`<li class="feature" ><span class="fa fa-eye-slash"></span>${i18n_hidden} - <em>${i18n_hidden_help}</em></li>` : '' }
${ o.features.public_room ? html`<li class="feature" ><span class="fa fa-eye"></span>${i18n_public} - <em>${o.__('This groupchat is publicly searchable') }</em></li>` : '' }
${ o.features.membersonly ? html`<li class="feature" ><span class="fa fa-address-book"></span>${i18n_members_only} - <em>${i18n_members_help}</em></li>` : '' }
${ o.features.open ? html`<li class="feature" ><span class="fa fa-globe"></span>${i18n_open} - <em>${i18n_open_help}</em></li>` : '' }
${ o.features.persistent ? html`<li class="feature" ><span class="fa fa-save"></span>${i18n_persistent} - <em>${i18n_persistent_help}</em></li>` : '' }
${ o.features.temporary ? html`<li class="feature" ><span class="fa fa-snowflake-o"></span>${i18n_temporary} - <em>${i18n_temporary_help}</em></li>` : '' }
${ o.features.nonanonymous ? html`<li class="feature" ><span class="fa fa-id-card"></span>${i18n_not_anonymous} - <em>${i18n_not_anonymous_help}</em></li>` : '' }
${ o.features.semianonymous ? html`<li class="feature" ><span class="fa fa-user-secret"></span>${i18n_semi_anon} - <em>${i18n_semi_anon_help}</em></li>` : '' }
${ o.features.moderated ? html`<li class="feature" ><span class="fa fa-gavel"></span>${i18n_moderated} - <em>${i18n_moderated_help}</em></li>` : '' }
${ o.features.unmoderated ? html`<li class="feature" ><span class="fa fa-info-circle"></span>${i18n_not_moderated} - <em>${i18n_not_moderated_help}</em></li>` : '' }
${ o.features.mam_enabled ? html`<li class="feature" ><span class="fa fa-database"></span>${i18n_archiving} - <em>${i18n_archiving_help}</em></li>` : '' }
${ features.passwordprotected ? html`<li class="feature" ><converse-icon size="1em" class="fa fa-lock"></converse-icon size="1em">${i18n_password_protected} - <em>${i18n_password_help}</em></li>` : '' }
${ features.unsecured ? html`<li class="feature" ><converse-icon size="1em" class="fa fa-unlock"></converse-icon size="1em">${i18n_no_password_required} - <em>${i18n_no_pass_help}</em></li>` : '' }
${ features.hidden ? html`<li class="feature" ><converse-icon size="1em" class="fa fa-eye-slash"></converse-icon size="1em">${i18n_hidden} - <em>${i18n_hidden_help}</em></li>` : '' }
${ features.public_room ? html`<li class="feature" ><converse-icon size="1em" class="fa fa-eye"></converse-icon size="1em">${i18n_public} - <em>${o.__('This groupchat is publicly searchable') }</em></li>` : '' }
${ features.membersonly ? html`<li class="feature" ><converse-icon size="1em" class="fa fa-address-book"></converse-icon size="1em">${i18n_members_only} - <em>${i18n_members_help}</em></li>` : '' }
${ features.open ? html`<li class="feature" ><converse-icon size="1em" class="fa fa-globe"></converse-icon size="1em">${i18n_open} - <em>${i18n_open_help}</em></li>` : '' }
${ features.persistent ? html`<li class="feature" ><converse-icon size="1em" class="fa fa-save"></converse-icon size="1em">${i18n_persistent} - <em>${i18n_persistent_help}</em></li>` : '' }
${ features.temporary ? html`<li class="feature" ><converse-icon size="1em" class="fa fa-snowflake-o"></converse-icon size="1em">${i18n_temporary} - <em>${i18n_temporary_help}</em></li>` : '' }
${ features.nonanonymous ? html`<li class="feature" ><converse-icon size="1em" class="fa fa-id-card"></converse-icon size="1em">${i18n_not_anonymous} - <em>${i18n_not_anonymous_help}</em></li>` : '' }
${ features.semianonymous ? html`<li class="feature" ><converse-icon size="1em" class="fa fa-user-secret"></converse-icon size="1em">${i18n_semi_anon} - <em>${i18n_semi_anon_help}</em></li>` : '' }
${ features.moderated ? html`<li class="feature" ><converse-icon size="1em" class="fa fa-gavel"></converse-icon size="1em">${i18n_moderated} - <em>${i18n_moderated_help}</em></li>` : '' }
${ features.unmoderated ? html`<li class="feature" ><converse-icon size="1em" class="fa fa-info-circle"></converse-icon size="1em">${i18n_not_moderated} - <em>${i18n_not_moderated_help}</em></li>` : '' }
${ features.mam_enabled ? html`<li class="feature" ><converse-icon size="1em" class="fa fa-database"></converse-icon size="1em">${i18n_archiving} - <em>${i18n_archiving_help}</em></li>` : '' }
</ul>
</div>
</p>

View File

@ -8,7 +8,12 @@ const remove_button = (o) => {
const i18n_remove_contact = __('Remove as contact');
return html`
<button type="button" @click="${o.removeContact}" class="btn btn-danger remove-contact">
<i class="far fa-trash-alt"></i>${i18n_remove_contact}
<converse-icon
class="fas fa-trash-alt"
color="var(--text-color-lighten-15-percent)"
size="1em"
></converse-icon>
${i18n_remove_contact}
</button>
`;
}

View File

@ -1,7 +1,7 @@
import BootstrapModal from "plugins/modal/base.js";
import head from "lodash-es/head";
import log from "@converse/headless/log";
import tpl_list_chatrooms_modal from "../templates/muc-list.js";
import tpl_muc_list from "../templates/muc-list.js";
import tpl_muc_description from "../templates/muc-description.js";
import tpl_spinner from "templates/spinner.js";
import { __ } from 'i18n';
@ -87,7 +87,7 @@ export default BootstrapModal.extend({
toHTML () {
const muc_domain = this.model.get('muc_domain') || api.settings.get('muc_domain');
return tpl_list_chatrooms_modal(
return tpl_muc_list(
Object.assign(this.model.toJSON(), {
'show_form': !api.settings.get('locked_muc_domain'),
'server_placeholder': muc_domain ? muc_domain : __('conference.example.org'),

View File

@ -17,32 +17,32 @@ export default (o) => html`
<input ?checked=${o.status === 'online'}
type="radio" id="radio-online" value="online" name="chat_status" class="custom-control-input"/>
<label class="custom-control-label" for="radio-online">
<span class="fa fa-circle chat-status chat-status--online"></span>${o.label_online}</label>
<converse-icon size="1em" class="fa fa-circle chat-status chat-status--online"></converse-icon>${o.label_online}</label>
</div>
<div class="custom-control custom-radio">
<input ?checked=${o.status === 'busy'}
type="radio" id="radio-busy" value="dnd" name="chat_status" class="custom-control-input"/>
<label class="custom-control-label" for="radio-busy">
<span class="fa fa-minus-circle chat-status chat-status--busy"></span>${o.label_busy}</label>
<converse-icon size="1em" class="fa fa-minus-circle chat-status chat-status--busy"></converse-icon>${o.label_busy}</label>
</div>
<div class="custom-control custom-radio">
<input ?checked=${o.status === 'away'}
type="radio" id="radio-away" value="away" name="chat_status" class="custom-control-input"/>
<label class="custom-control-label" for="radio-away">
<span class="fa fa-circle chat-status chat-status--away"></span>${o.label_away}</label>
<converse-icon size="1em" class="fa fa-circle chat-status chat-status--away"></converse-icon>${o.label_away}</label>
</div>
<div class="custom-control custom-radio">
<input ?checked=${o.status === 'xa'}
type="radio" id="radio-xa" value="xa" name="chat_status" class="custom-control-input"/>
<label class="custom-control-label" for="radio-xa">
<span class="far fa-circle chat-status chat-status--xa"></span>${o.label_xa}</label>
<converse-icon size="1em" class="far fa-circle chat-status chat-status--xa"></converse-icon>${o.label_xa}</label>
</div>
</div>
<div class="form-group">
<div class="btn-group w-100">
<input name="status_message" type="text" class="form-control"
value="${o.status_message || ''}" placeholder="${o.placeholder_status_message}"/>
<span class="clear-input fa fa-times ${o.status_message ? '' : 'hidden'}"></span>
<converse-icon size="1em" class="fa fa-times clear-input ${o.status_message ? '' : 'hidden'}"></converse-icon>
</div>
</div>
<button type="submit" class="btn btn-primary">${o.label_save}</button>

View File

@ -3,6 +3,7 @@ import tpl_roster_filter from "./templates/roster_filter.js";
import { CustomElement } from 'shared/components/element.js';
import { Model } from '@converse/skeletor/src/model.js';
import { _converse, api } from "@converse/headless/core";
import { ancestor } from 'utils/html.js';
import { initStorage } from '@converse/headless/utils/storage.js';
export const RosterFilter = Model.extend({
@ -62,7 +63,7 @@ export class RosterFilterView extends CustomElement {
changeTypeFilter (ev) {
ev && ev.preventDefault();
const type = ev.target.dataset.type;
const type = ancestor(ev.target, 'converse-icon')?.dataset.type || 'contacts';
if (type === 'state') {
this.model.save({
'filter_type': type,

View File

@ -30,10 +30,8 @@
padding: 0.2em;
}
span {
padding: 0.3em;
min-width: 25px;
text-align: center;
converse-icon {
padding: 0.25em;
}
.roster-filter {
@ -56,6 +54,16 @@
overflow-y: auto;
color: var(--text-color);
.roster-group-contacts {
.list-item {
&:hover {
.list-item-action {
opacity: 1;
}
}
}
}
converse-roster-contact {
width: 100%;
overflow: hidden;
@ -63,6 +71,16 @@
text-overflow: ellipsis;
display: flex;
justify-content: space-between;
.list-item-action {
line-height: 2em;
}
&:hover {
.list-item-action {
opacity: 1;
}
}
}
.group-toggle {

View File

@ -21,18 +21,18 @@ export default (o) => {
@submit=${o.submitFilter}>
<div class="form-inline flex-nowrap">
<div class="filter-by d-flex flex-nowrap">
<span @click=${o.changeTypeFilter} class="clickable fa fa-user ${ (o.filter_type === 'contacts') ? 'selected' : '' }" data-type="contacts" title="${title_contact_filter}"></span>
<span @click=${o.changeTypeFilter} class="clickable fa fa-users ${ (o.filter_type === 'groups') ? 'selected' : '' }" data-type="groups" title="${title_group_filter}"></span>
<span @click=${o.changeTypeFilter} class="clickable fa fa-circle ${ (o.filter_type === 'state') ? 'selected' : '' }" data-type="state" title="${title_status_filter}"></span>
<converse-icon size="1em" @click=${o.changeTypeFilter} class="fa fa-user clickable ${ (o.filter_type === 'contacts') ? 'selected' : '' }" data-type="contacts" title="${title_contact_filter}"></converse-icon>
<converse-icon size="1em" @click=${o.changeTypeFilter} class="fa fa-users clickable ${ (o.filter_type === 'groups') ? 'selected' : '' }" data-type="groups" title="${title_group_filter}"></converse-icon>
<converse-icon size="1em" @click=${o.changeTypeFilter} class="fa fa-circle clickable ${ (o.filter_type === 'state') ? 'selected' : '' }" data-type="state" title="${title_status_filter}"></converse-icon>
</div>
<div class="btn-group">
<input .value="${o.filter_text || ''}"
@keydown=${o.liveFilter}
class="roster-filter form-control ${ (o.filter_type === 'state') ? 'hidden' : '' }"
placeholder="${i18n_placeholder}"/>
<span class="clear-input fa fa-times ${ (!o.filter_text || o.filter_type === 'state') ? 'hidden' : '' }"
<converse-icon size="1em" class="fa fa-times clear-input ${ (!o.filter_text || o.filter_type === 'state') ? 'hidden' : '' }"
@click=${o.clearFilter}>
</span>
</converse-icon>
</div>
<select class="form-control state-type ${ (o.filter_type !== 'state') ? 'hidden' : '' }"
@change=${o.changeChatStateFilter}>

View File

@ -1,8 +1,17 @@
import { __ } from 'i18n';
import { api } from "@converse/headless/core";
import { api } from "@converse/headless/core.js";
import { html } from "lit";
import { STATUSES } from '../constants.js';
const tpl_remove_link = (el, item) => {
const display_name = item.getDisplayName();
const i18n_remove = __('Click to remove %1$s as a contact', display_name);
return html`
<a class="list-item-action remove-xmpp-contact" @click=${el.removeContact} title="${i18n_remove}" href="#">
<converse-icon class="fa fa-trash-alt" size="1.5em"></converse-icon>
</a>
`;
}
export default (el, item) => {
const show = item.presence.get('show') || 'offline';
@ -16,11 +25,10 @@ export default (el, item) => {
} 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 display_name = item.getDisplayName();
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`
<a class="list-item-link cbox-list-item open-chat ${ num_unread ? 'unread-msgs' : '' }" title="${i18n_chat}" href="#" @click=${el.openChat}>
<span>
@ -38,5 +46,5 @@ export default (el, item) => {
${ num_unread ? html`<span class="msgs-indicator">${ num_unread }</span>` : '' }
<span class="contact-name contact-name--${el.show} ${ num_unread ? 'unread-msgs' : ''}">${display_name}</span>
</a>
${ api.settings.get('allow_contact_removal') ? html`<a class="list-item-action remove-xmpp-contact far fa-trash-alt" @click=${el.removeContact} title="${i18n_remove}" href="#"></a>` : '' }`;
${ api.settings.get('allow_contact_removal') ? tpl_remove_link(el, item) : '' }`;
}

View File

@ -1,4 +1,5 @@
import './message-history';
import tpl_spinner from "templates/spinner.js";
import { CustomElement } from 'shared/components/element.js';
import { api } from '@converse/headless/core';
import { html } from 'lit';
@ -56,7 +57,7 @@ export default class ChatContent extends CustomElement {
.model=${this.model}
.messages=${[...this.model.messages.models]}>
</converse-message-history>
${ this.model.ui?.get('chat-content-spinner-top') ? html`<span class="spinner fa fa-spinner centered"></span>` : '' }
${ this.model.ui?.get('chat-content-spinner-top') ? tpl_spinner() : '' }
`;
}

View File

@ -57,7 +57,6 @@ class MessageActions extends CustomElement {
<button class="chat-msg__action ${o.button_class}" @click=${o.handler}>
<converse-icon
class="${o.icon_class}"
path-prefix="${api.settings.get('assets_path')}"
color="var(--text-color-lighten-15-percent)"
size="1em"
></converse-icon>

View File

@ -60,7 +60,7 @@ export class ChatToolbar extends CustomElement {
const i18n_start_call = __('Start a call');
buttons.push(html`
<button class="toggle-call" @click=${this.toggleCall} title="${i18n_start_call}">
<converse-icon color="var(${color})" class="fa fa-phone" path-prefix="/dist" size="1em"></converse-icon>
<converse-icon color="var(${color})" class="fa fa-phone" size="1em"></converse-icon>
</button>`
);
}
@ -90,7 +90,7 @@ export class ChatToolbar extends CustomElement {
<converse-icon
color="var(--muc-toolbar-btn-color)"
class="fa ${this.hidden_occupants ? `fa-angle-double-left` : `fa-angle-double-right`}"
path-prefix="${api.settings.get('assets_path')}" size="1em"></converse-icon>
size="1em"></converse-icon>
</button>`
);
}
@ -118,7 +118,6 @@ export class ChatToolbar extends CustomElement {
<converse-icon
color="var(${color})"
class="fa fa-paperclip"
path-prefix="${api.settings.get('assets_path')}"
size="1em"></converse-icon>
</button>
<input type="file" @change=${this.onFileSelection} class="fileupload" multiple="" style="display:none"/>`;
@ -147,7 +146,6 @@ export class ChatToolbar extends CustomElement {
<converse-icon
color="var(${color})"
class="fa ${this.composing_spoiler ? 'fa-eye-slash' : 'fa-eye'}"
path-prefix="${api.settings.get('assets_path')}"
size="1em"></converse-icon>
</button>`;

View File

@ -5,12 +5,19 @@ converse-icon {
svg {
fill: var(--subdued-color);
}
}
a, .clickable {
converse-icon {
svg {
&:hover {
&.clickable {
&:hover {
svg {
fill: var(--icon-hover-color);
}
}
}
}
a, .clickable {
converse-icon {
&:hover {
svg {
fill: var(--icon-hover-color);
}
}

View File

@ -1,5 +1,4 @@
import { __ } from '../i18n';
import { api } from "@converse/headless/core";
import { directive, html } from "lit";
@ -8,7 +7,6 @@ const tpl_retract = (o) => html`
<button class="chat-msg__action chat-msg__action-retract" title="${i18n_retract_message}" @click=${o.onMessageRetractButtonClicked}>
<converse-icon
class="fas fa-trash-alt"
path-prefix="${api.settings.get("assets_path")}"
color="var(--text-color-lighten-15-percent)"
size="1em"
></converse-icon>

View File

@ -264,6 +264,9 @@
.selected {
color: var(--link-color) !important;
svg {
fill: var(--link-color);
}
}
.circle {
@ -365,17 +368,6 @@
}
}
.spinner__container {
width: 100%;
}
.spinner {
animation: spin 2s infinite, linear;
width: 1em;
display: block;
text-align: center;
padding: 0.5em 0;
font-size: 24px;
}
.left {
float: left;
}

View File

@ -32,6 +32,7 @@
@import "themes/dracula";
@import "core";
@import "spinner";
@import "buttons";
@import "badges";
@import "forms";

View File

@ -108,11 +108,7 @@
display: inline-flex;
width: 100%;
flex-direction: row;
padding: 0 1rem;
&.chat-msg--with-avatar {
padding-top: 0.25rem;
}
padding: 0.25em 1rem;
&.onload {
animation: colorchange-chatmessage 1s;
@ -144,7 +140,7 @@
}
&:before {
padding-right: 0.25em;
whitespace: nowrap;
white-space: nowrap;
}
}
@ -170,7 +166,7 @@
}
.chat-msg__message {
line-height: 1.5em;
line-height: 1.65em;
display: inline-flex;
flex-direction: column;
width: 100%;

View File

@ -0,0 +1,19 @@
.conversejs {
.spinner__container {
width: 100%;
}
.spinner {
animation: spin 2s infinite, linear;
width: 1em;
display: block;
text-align: center;
padding: 0.5em 0;
font-size: 24px;
svg {
fill: var(--primary-color);
}
}
}

View File

@ -17,18 +17,23 @@
}
.chat-status--online {
color: var(--chat-status-online);
svg {
fill: var(--chat-status-online);
}
}
.chat-status--busy {
color: var(--chat-status-busy);
svg {
fill: var(--chat-status-busy);
}
}
.chat-status--away {
color: var(--chat-status-away);
svg {
fill: var(--chat-status-away);
}
}
.chat-status--offline {
display: none;
}
.far.fa-circle,
.fa-times-circle {
color: var(--subdued-color);
}
}

View File

@ -2,8 +2,8 @@ import { html } from "lit";
export default (o={}) => {
if (o.classes?.includes('hor_centered')) {
return html`<div class="spinner__container"><span class="spinner fa fa-spinner centered ${o.classes || ''}"/></div>`
return html`<div class="spinner__container"><converse-icon size="1em" class="fa fa-spinner spinner centered ${o.classes || ''}"></converse-icon></div>`
} else {
return html`<span class="spinner fa fa-spinner centered ${o.classes || ''}"/>`
return html`<converse-icon size="1em" class="fa fa-spinner spinner centered ${o.classes || ''}"></converse-icon>`
}
}

View File

@ -200,13 +200,13 @@ u.hideElement = function (el) {
return el;
};
u.ancestor = function (el, selector) {
export function ancestor (el, selector) {
let parent = el;
while (parent !== null && !sizzle.matchesSelector(parent, selector)) {
parent = parent.parentElement;
}
return parent;
};
}
/**
* Return the element's siblings until one matches the selector.
@ -513,6 +513,6 @@ u.xForm2TemplateResult = function (field, stanza, options) {
}
};
Object.assign(u, { getOOBURLMarkup });
Object.assign(u, { getOOBURLMarkup, ancestor });
export default u;