226 lines
No EOL
6.1 KiB
JavaScript
226 lines
No EOL
6.1 KiB
JavaScript
import QuickReactions from './QuickReactions.vue';
|
|
var linkify = require('linkifyjs');
|
|
var linkifyHtml = require('linkifyjs/html');
|
|
linkify.options.defaults.className = "link";
|
|
linkify.options.defaults.target = { url: '_blank' };
|
|
|
|
export default {
|
|
components: {
|
|
QuickReactions
|
|
},
|
|
props: {
|
|
room: {
|
|
type: Object,
|
|
default: function () {
|
|
return null
|
|
}
|
|
},
|
|
event: {
|
|
type: Object,
|
|
default: function () {
|
|
return {}
|
|
}
|
|
},
|
|
nextEvent: {
|
|
type: Object,
|
|
default: function () {
|
|
return null
|
|
}
|
|
},
|
|
timelineSet: {
|
|
type: Object,
|
|
default: function () {
|
|
return null
|
|
}
|
|
},
|
|
},
|
|
data() {
|
|
return {
|
|
inReplyToEvent: null,
|
|
inReplyToSender: null
|
|
}
|
|
},
|
|
mounted() {
|
|
const relatesTo = this.event.getWireContent()['m.relates_to'];
|
|
if (relatesTo && relatesTo['m.in_reply_to'])
|
|
{
|
|
// Can we find the original message?
|
|
const originalEventId = relatesTo['m.in_reply_to'].event_id;
|
|
if (originalEventId && this.timelineSet) {
|
|
const originalEvent = this.timelineSet.findEventById(originalEventId);
|
|
if (originalEvent) {
|
|
this.inReplyToEvent = originalEvent;
|
|
this.inReplyToSender = this.messageEventDisplayName(originalEvent);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
computed: {
|
|
incoming() {
|
|
return this.event && this.event.getSender() != this.$matrix.currentUserId;
|
|
},
|
|
|
|
/**
|
|
* Don't show sender and time if the next event is within 2 minutes and also from us (= back to back messages)
|
|
*/
|
|
showSenderAndTime() {
|
|
if (this.nextEvent && this.nextEvent.getSender() == this.event.getSender()) {
|
|
const ts1 = this.nextEvent.event.origin_server_ts;
|
|
const ts2 = this.event.event.origin_server_ts;
|
|
return (ts1 - ts2) < (2 * 60 * 1000); // less than 2 minutes
|
|
}
|
|
return true;
|
|
},
|
|
|
|
inReplyToText() {
|
|
const relatesTo = this.event.getWireContent()['m.relates_to'];
|
|
if (relatesTo && relatesTo['m.in_reply_to'])
|
|
{
|
|
const content = this.event.getContent();
|
|
const lines = content.body.split('\n').reverse();
|
|
while (lines.length && !lines[0].startsWith('> ')) lines.shift();
|
|
// Reply fallback has a blank line after it, so remove it to prevent leading newline
|
|
if (lines[0] === '') lines.shift();
|
|
const text = lines[0] && lines[0].replace(/^> (<.*> )?/g, '');
|
|
if (text) {
|
|
return text;
|
|
}
|
|
|
|
if (this.inReplyToEvent) {
|
|
var c = this.inReplyToEvent.getContent();
|
|
return c.body;
|
|
}
|
|
|
|
// We don't have the original text (at the moment at least)
|
|
return this.$t('fallbacks.original_text');
|
|
}
|
|
return null;
|
|
},
|
|
|
|
messageText() {
|
|
const relatesTo = this.event.getWireContent()['m.relates_to'];
|
|
if (relatesTo && relatesTo['m.in_reply_to'])
|
|
{
|
|
const content = this.event.getContent();
|
|
|
|
// Remove the new text and strip "> " from the old original text
|
|
const lines = content.body.split('\n');
|
|
while (lines.length && lines[0].startsWith('> ')) lines.shift();
|
|
// Reply fallback has a blank line after it, so remove it to prevent leading newline
|
|
if (lines[0] === '') lines.shift();
|
|
return lines.join('\n');
|
|
}
|
|
return this.event.getContent().body;
|
|
},
|
|
|
|
/**
|
|
* Classes to set for the message. Currently only for "messageIn", TODO: - detect messageIn or messageOut.
|
|
*/
|
|
|
|
messageClasses() {
|
|
return {'messageIn':true,'from-admin':this.senderIsAdminOrModerator(this.event)}
|
|
},
|
|
|
|
userAvatar() {
|
|
if (!this.$matrix.userAvatar) {
|
|
return null;
|
|
}
|
|
return this.$matrix.matrixClient.mxcUrlToHttp(
|
|
this.$matrix.userAvatar,
|
|
80,
|
|
80,
|
|
"scale",
|
|
true
|
|
);
|
|
},
|
|
|
|
userAvatarLetter() {
|
|
if (!this.$matrix.currentUser) {
|
|
return null;
|
|
}
|
|
return (this.$matrix.currentUserDisplayName || this.$matrix.currentUserId.substring(1)).substring(0, 1).toUpperCase();
|
|
}
|
|
},
|
|
methods: {
|
|
ownAvatarClicked() {
|
|
this.$emit("own-avatar-clicked", {event: this.event});
|
|
},
|
|
|
|
otherAvatarClicked(avatarRef) {
|
|
this.$emit("other-avatar-clicked", {event: this.event, anchor: avatarRef});
|
|
},
|
|
|
|
showContextMenu(buttonRef) {
|
|
this.$emit("context-menu", {event: this.event,anchor: buttonRef});
|
|
},
|
|
|
|
/**
|
|
* Get a display name given an event.
|
|
*/
|
|
stateEventDisplayName(event) {
|
|
if (event.getSender() == this.$matrix.currentUserId) {
|
|
return this.$t('message.you');
|
|
}
|
|
if (this.room) {
|
|
const member = this.room.getMember(event.getSender());
|
|
if (member) {
|
|
return member.name;
|
|
}
|
|
}
|
|
return event.getContent().displayname || event.event.state_key;
|
|
},
|
|
|
|
messageEventDisplayName(event) {
|
|
return this.stateEventDisplayName(event);
|
|
},
|
|
|
|
messageEventAvatar(event) {
|
|
if (this.room) {
|
|
const member = this.room.getMember(event.getSender());
|
|
if (member) {
|
|
return member.getAvatarUrl(
|
|
this.$matrix.matrixClient.getHomeserverUrl(),
|
|
40,
|
|
40,
|
|
"scale",
|
|
true
|
|
);
|
|
}
|
|
}
|
|
return null;
|
|
},
|
|
|
|
/**
|
|
* Return true if the event sender has a powel level > 0, e.g. is moderator or admin of some sort.
|
|
*/
|
|
senderIsAdminOrModerator(event) {
|
|
if (this.room) {
|
|
const member = this.room.getMember(event.getSender());
|
|
if (member) {
|
|
return member.powerLevel > 0;
|
|
}
|
|
}
|
|
return false;
|
|
},
|
|
|
|
formatTime(time) {
|
|
const date = new Date();
|
|
date.setTime(time);
|
|
|
|
const today = new Date();
|
|
if (
|
|
date.getDate() == today.getDate() &&
|
|
date.getMonth() == today.getMonth() &&
|
|
date.getFullYear() == today.getFullYear()
|
|
) {
|
|
// For today, skip the date part
|
|
return date.toLocaleTimeString();
|
|
}
|
|
return date.toLocaleString();
|
|
},
|
|
|
|
linkify(text) {
|
|
return linkifyHtml(text);
|
|
}
|
|
},
|
|
} |