Create a component which renders message actions in a dropdown
This commit is contained in:
parent
6ad76c14ef
commit
c2c1db587f
|
@ -90,7 +90,6 @@
|
|||
display: inline-flex;
|
||||
width: 100%;
|
||||
flex-direction: row;
|
||||
overflow: auto; // Ensures that content stays inside
|
||||
padding: 0.125rem 1rem;
|
||||
|
||||
&.onload {
|
||||
|
@ -98,12 +97,7 @@
|
|||
-webkit-animation: colorchange-chatmessage 1s;
|
||||
}
|
||||
&:hover {
|
||||
background-color: rgba(0, 0, 0, 0.035);
|
||||
.chat-msg__actions {
|
||||
.chat-msg__action {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
background-color: var(--list-item-hover-color);
|
||||
}
|
||||
&.correcting {
|
||||
&.groupchat {
|
||||
|
@ -162,9 +156,15 @@
|
|||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
&:hover {
|
||||
.btn--standalone {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.chat-msg__message {
|
||||
line-height: 1.5em;
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
|
@ -235,20 +235,36 @@
|
|||
}
|
||||
}
|
||||
|
||||
converse-message-actions {
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
|
||||
.chat-msg__actions {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
.chat-msg__action {
|
||||
height: var(--message-font-size);
|
||||
font-size: var(--message-font-size);
|
||||
padding: 0;
|
||||
margin-left: 0.75em;
|
||||
.dropdown-menu {
|
||||
min-width: 5rem;
|
||||
}
|
||||
i {
|
||||
color: var(--text-color-lighten-15-percent);
|
||||
font-size: 70%;
|
||||
}
|
||||
button {
|
||||
border: none;
|
||||
opacity: 0;
|
||||
background: transparent;
|
||||
cursor: pointer;
|
||||
&:focus {
|
||||
opacity: 1;
|
||||
color: var(--text-color-lighten-15-percent);
|
||||
padding: 0 0.25em;
|
||||
}
|
||||
.btn--standalone {
|
||||
opacity: 0;
|
||||
margin-top: -0.2em;
|
||||
}
|
||||
.chat-msg__action {
|
||||
width: 100%;
|
||||
padding: 0.5em 1em;
|
||||
text-align: left;
|
||||
white-space: nowrap;
|
||||
&:hover {
|
||||
color: var(--text-color);
|
||||
background-color: var(--list-item-hover-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -178,6 +178,7 @@ $mobile_portrait_length: 480px !default;
|
|||
--list-toggle-color: #818479; // $gray-color
|
||||
--list-toggle-hover-color: #585B51; // $dark-gray-color
|
||||
--list-toggle-font-weight: normal;
|
||||
--list-item-hover-color: rgba(0, 0, 0, 0.035);
|
||||
--list-item-action-color: #e3eef3; // lighten($lightest-blue, 25%)
|
||||
--list-item-link-color: inherit;
|
||||
--list-item-link-hover-color: var(--dark-link-color);
|
||||
|
|
|
@ -107,7 +107,7 @@ describe("Chatboxes", function () {
|
|||
// get the 'chat-msg--followup' class.
|
||||
message = 'This a normal message';
|
||||
await mock.sendMessage(view, message);
|
||||
const msg_txt_sel = 'converse-chat-message:last-child .chat-msg__body';
|
||||
const msg_txt_sel = 'converse-chat-message:last-child .chat-msg__text';
|
||||
await u.waitUntil(() => view.el.querySelector(msg_txt_sel).textContent.trim() === message);
|
||||
let el = view.el.querySelector('converse-chat-message:last-child .chat-msg__body');
|
||||
expect(u.hasClass('chat-msg--followup', el)).toBeFalsy();
|
||||
|
|
|
@ -106,7 +106,7 @@ describe("A Chat Message", function () {
|
|||
const first_msg = view.model.messages.findWhere({'message': 'But soft, what light through yonder airlock breaks?'});
|
||||
expect(view.el.querySelectorAll('.chat-msg .chat-msg__action').length).toBe(2);
|
||||
let action = view.el.querySelector('.chat-msg .chat-msg__action');
|
||||
expect(action.getAttribute('title')).toBe('Edit this message');
|
||||
expect(action.textContent.trim()).toBe('Edit');
|
||||
|
||||
action.style.opacity = 1;
|
||||
action.click();
|
||||
|
|
|
@ -1215,7 +1215,7 @@ describe("A Groupchat Message", function () {
|
|||
`<origin-id id="${msg.nodeTree.querySelector('origin-id').getAttribute("id")}" xmlns="urn:xmpp:sid:0"/>`+
|
||||
`</message>`);
|
||||
|
||||
const action = view.el.querySelector('.chat-msg .chat-msg__action');
|
||||
const action = await u.waitUntil(() => view.el.querySelector('.chat-msg .chat-msg__action'));
|
||||
action.style.opacity = 1;
|
||||
action.click();
|
||||
|
||||
|
|
|
@ -437,7 +437,8 @@ describe("The OMEMO module", function() {
|
|||
_converse.connection._dataRecv(mock.createRequest(carbon));
|
||||
await new Promise(resolve => view.model.messages.once('rendered', resolve));
|
||||
expect(view.model.messages.length).toBe(1);
|
||||
expect(view.el.querySelector('.chat-msg__body').textContent.trim())
|
||||
|
||||
expect(view.el.querySelector('.chat-msg__text').textContent.trim())
|
||||
.toBe('This is an encrypted carbon message from another device of mine');
|
||||
|
||||
expect(devicelist.devices.length).toBe(2);
|
||||
|
|
|
@ -119,8 +119,7 @@ describe("Message Retractions", function () {
|
|||
`);
|
||||
_converse.connection._dataRecv(mock.createRequest(received_stanza));
|
||||
await u.waitUntil(() => view.model.handleRetraction.calls.count() === 2);
|
||||
|
||||
expect(view.el.querySelectorAll('.chat-msg').length).toBe(0);
|
||||
await u.waitUntil(() => view.el.querySelectorAll('.chat-msg').length === 1, 1000);
|
||||
expect(view.model.messages.length).toBe(1);
|
||||
|
||||
const message = view.model.messages.at(0)
|
||||
|
|
|
@ -52,15 +52,17 @@ export class DropdownList extends BaseDropdown {
|
|||
|
||||
static get properties () {
|
||||
return {
|
||||
'icon_classes': { type: String },
|
||||
'items': { type: Array }
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
const icon_classes = this.icon_classes || "fa fa-bars";
|
||||
return html`
|
||||
<div class="dropleft">
|
||||
<button type="button" class="btn btn--transparent btn--standalone" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<i class="fa fa-bars only-icon"></i>
|
||||
<i class="${icon_classes} only-icon"></i>
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
${ this.items.map(b => until(b, '')) }
|
||||
|
|
74
src/components/message-actions.js
Normal file
74
src/components/message-actions.js
Normal file
|
@ -0,0 +1,74 @@
|
|||
import { CustomElement } from './element.js';
|
||||
import { __ } from '@converse/headless/i18n';
|
||||
import { html } from 'lit-element';
|
||||
import { until } from 'lit-html/directives/until.js';
|
||||
|
||||
|
||||
class MessageActions extends CustomElement {
|
||||
|
||||
static get properties () {
|
||||
return {
|
||||
chatview: { type: Object },
|
||||
model: { type: Object },
|
||||
editable: { type: Boolean },
|
||||
correcting: { type: Boolean },
|
||||
message_type: { type: String },
|
||||
is_retracted: { type: Boolean },
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
return html`${ until(this.renderActions(), '') }`;
|
||||
}
|
||||
|
||||
static getActionsDropdownItem (o) {
|
||||
return html`
|
||||
<button class="chat-msg__action ${o.button_class}" @click=${o.handler}>
|
||||
<fa-icon class="${o.icon_class}" path-prefix="/dist" color="var(--text-color-lighten-15-percent)" size="1em"></fa-icon>
|
||||
${o.i18n_text}
|
||||
</button>
|
||||
`;
|
||||
}
|
||||
|
||||
onMessageEditButtonClicked (ev) {
|
||||
ev.preventDefault();
|
||||
this.chatview.onMessageEditButtonClicked(this.model);
|
||||
}
|
||||
|
||||
onMessageRetractButtonClicked (ev) {
|
||||
ev.preventDefault();
|
||||
this.chatview.onMessageRetractButtonClicked(this.model);
|
||||
}
|
||||
|
||||
async renderActions () {
|
||||
const buttons = [];
|
||||
if (this.editable) {
|
||||
buttons.push({
|
||||
'i18n_text': this.correcting ? __('Cancel Editing') : __('Edit'),
|
||||
'handler': ev => this.onMessageEditButtonClicked(ev),
|
||||
'button_class': 'chat-msg__action-edit',
|
||||
'icon_class': 'fa fa-pencil-alt',
|
||||
'name': 'edit'
|
||||
});
|
||||
}
|
||||
const may_be_moderated = this.model.get('type') === 'groupchat' && await this.model.mayBeModerated();
|
||||
const retractable = !this.is_retracted && (this.model.mayBeRetracted() || may_be_moderated);
|
||||
if (retractable) {
|
||||
buttons.push({
|
||||
'i18n_text': __('Retract'),
|
||||
'handler': ev => this.onMessageRetractButtonClicked(ev),
|
||||
'button_class': 'chat-msg__action-retract',
|
||||
'icon_class': 'fas fa-trash-alt',
|
||||
'name': 'retract'
|
||||
});
|
||||
}
|
||||
const items = buttons.map(b => MessageActions.getActionsDropdownItem(b));
|
||||
if (items.length) {
|
||||
return html`<converse-dropdown class="chat-msg__actions" .items=${ items }></converse-dropdown>`;
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('converse-message-actions', MessageActions);
|
|
@ -1,4 +1,6 @@
|
|||
import "./message-body.js";
|
||||
import './dropdown.js';
|
||||
import './message-actions.js';
|
||||
import MessageVersionsModal from '../modals/message-versions.js';
|
||||
import dayjs from 'dayjs';
|
||||
import filesize from "filesize";
|
||||
|
@ -8,12 +10,10 @@ import { __ } from '@converse/headless/i18n';
|
|||
import { _converse, api, converse } from "@converse/headless/converse-core";
|
||||
import { html } from 'lit-element';
|
||||
import { renderAvatar } from './../templates/directives/avatar';
|
||||
import { renderRetractionLink } from './../templates/directives/retraction';
|
||||
|
||||
const { Strophe } = converse.env;
|
||||
const u = converse.env.utils;
|
||||
|
||||
const i18n_edit_message = __('Edit this message');
|
||||
const i18n_edited = __('This message has been edited');
|
||||
const i18n_show = __('Show more');
|
||||
const i18n_show_less = __('Show less');
|
||||
|
@ -150,17 +150,13 @@ class Message extends CustomElement {
|
|||
</div>
|
||||
${ (this.received && !this.is_me_message && !is_groupchat_message) ? html`<span class="fa fa-check chat-msg__receipt"></span>` : '' }
|
||||
${ (this.edited) ? html`<i title="${ i18n_edited }" class="fa fa-edit chat-msg__edit-modal" @click=${this.showMessageVersionsModal}></i>` : '' }
|
||||
<div class="chat-msg__actions">
|
||||
${ this.editable ?
|
||||
html`<button
|
||||
class="chat-msg__action chat-msg__action-edit"
|
||||
title="${i18n_edit_message}"
|
||||
@click=${this.onMessageEditButtonClicked}
|
||||
>
|
||||
<fa-icon class="fas fa-pencil-alt" path-prefix="dist" color="var(--text-color-lighten-15-percent)" size="1em"></fa-icon>
|
||||
</button>` : '' }
|
||||
${ renderRetractionLink(this) }
|
||||
</div>
|
||||
<converse-message-actions
|
||||
.chatview=${this.chatview}
|
||||
.model=${this.model}
|
||||
?correcting="${this.correcting}"
|
||||
?editable="${this.editable}"
|
||||
?is_retracted="${this.is_retracted}"
|
||||
message_type="${this.message_type}"></converse-message-actions>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
|
@ -189,16 +185,6 @@ class Message extends CustomElement {
|
|||
this.parentElement.removeChild(this);
|
||||
}
|
||||
|
||||
onMessageRetractButtonClicked (ev) {
|
||||
ev.preventDefault();
|
||||
this.chatview.onMessageRetractButtonClicked(this.model);
|
||||
}
|
||||
|
||||
onMessageEditButtonClicked (ev) {
|
||||
ev.preventDefault();
|
||||
this.chatview.onMessageEditButtonClicked(this.model);
|
||||
}
|
||||
|
||||
isFollowup () {
|
||||
const messages = this.model.collection.models;
|
||||
const idx = messages.indexOf(this.model);
|
||||
|
|
Loading…
Reference in New Issue
Block a user