Merge pull request #1564 from matrix-org/dbkr/pillify_at_room
Pillify room notifs in the timeline
This commit is contained in:
commit
8895ab7e5b
2 changed files with 101 additions and 10 deletions
|
@ -37,11 +37,20 @@ const Pill = React.createClass({
|
||||||
isMessagePillUrl: (url) => {
|
isMessagePillUrl: (url) => {
|
||||||
return !!REGEX_LOCAL_MATRIXTO.exec(url);
|
return !!REGEX_LOCAL_MATRIXTO.exec(url);
|
||||||
},
|
},
|
||||||
|
roomNotifPos: (text) => {
|
||||||
|
return text.indexOf("@room");
|
||||||
|
},
|
||||||
|
roomNotifLen: () => {
|
||||||
|
return "@room".length;
|
||||||
|
},
|
||||||
TYPE_USER_MENTION: 'TYPE_USER_MENTION',
|
TYPE_USER_MENTION: 'TYPE_USER_MENTION',
|
||||||
TYPE_ROOM_MENTION: 'TYPE_ROOM_MENTION',
|
TYPE_ROOM_MENTION: 'TYPE_ROOM_MENTION',
|
||||||
|
TYPE_AT_ROOM_MENTION: 'TYPE_AT_ROOM_MENTION', // '@room' mention
|
||||||
},
|
},
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
|
// The Type of this Pill. If url is given, this is auto-detected.
|
||||||
|
type: PropTypes.string,
|
||||||
// The URL to pillify (no validation is done, see isPillUrl and isMessagePillUrl)
|
// The URL to pillify (no validation is done, see isPillUrl and isMessagePillUrl)
|
||||||
url: PropTypes.string,
|
url: PropTypes.string,
|
||||||
// Whether the pill is in a message
|
// Whether the pill is in a message
|
||||||
|
@ -72,14 +81,20 @@ const Pill = React.createClass({
|
||||||
regex = REGEX_LOCAL_MATRIXTO;
|
regex = REGEX_LOCAL_MATRIXTO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let matrixToMatch;
|
||||||
|
let resourceId;
|
||||||
|
let prefix;
|
||||||
|
|
||||||
|
if (nextProps.url) {
|
||||||
// Default to the empty array if no match for simplicity
|
// Default to the empty array if no match for simplicity
|
||||||
// resource and prefix will be undefined instead of throwing
|
// resource and prefix will be undefined instead of throwing
|
||||||
const matrixToMatch = regex.exec(nextProps.url) || [];
|
matrixToMatch = regex.exec(nextProps.url) || [];
|
||||||
|
|
||||||
const resourceId = matrixToMatch[1]; // The room/user ID
|
resourceId = matrixToMatch[1]; // The room/user ID
|
||||||
const prefix = matrixToMatch[2]; // The first character of prefix
|
prefix = matrixToMatch[2]; // The first character of prefix
|
||||||
|
}
|
||||||
|
|
||||||
const pillType = {
|
const pillType = this.props.type || {
|
||||||
'@': Pill.TYPE_USER_MENTION,
|
'@': Pill.TYPE_USER_MENTION,
|
||||||
'#': Pill.TYPE_ROOM_MENTION,
|
'#': Pill.TYPE_ROOM_MENTION,
|
||||||
'!': Pill.TYPE_ROOM_MENTION,
|
'!': Pill.TYPE_ROOM_MENTION,
|
||||||
|
@ -88,6 +103,10 @@ const Pill = React.createClass({
|
||||||
let member;
|
let member;
|
||||||
let room;
|
let room;
|
||||||
switch (pillType) {
|
switch (pillType) {
|
||||||
|
case Pill.TYPE_AT_ROOM_MENTION: {
|
||||||
|
room = nextProps.room;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case Pill.TYPE_USER_MENTION: {
|
case Pill.TYPE_USER_MENTION: {
|
||||||
const localMember = nextProps.room.getMember(resourceId);
|
const localMember = nextProps.room.getMember(resourceId);
|
||||||
member = localMember;
|
member = localMember;
|
||||||
|
@ -160,6 +179,17 @@ const Pill = React.createClass({
|
||||||
let href = this.props.url;
|
let href = this.props.url;
|
||||||
let onClick;
|
let onClick;
|
||||||
switch (this.state.pillType) {
|
switch (this.state.pillType) {
|
||||||
|
case Pill.TYPE_AT_ROOM_MENTION: {
|
||||||
|
const room = this.props.room;
|
||||||
|
if (room) {
|
||||||
|
linkText = "@room";
|
||||||
|
if (this.props.shouldShowPillAvatar) {
|
||||||
|
avatar = <RoomAvatar room={room} width={16} height={16} />;
|
||||||
|
}
|
||||||
|
pillClass = 'mx_AtRoomPill';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
case Pill.TYPE_USER_MENTION: {
|
case Pill.TYPE_USER_MENTION: {
|
||||||
// If this user is not a member of this room, default to the empty member
|
// If this user is not a member of this room, default to the empty member
|
||||||
const member = this.state.member;
|
const member = this.state.member;
|
||||||
|
|
|
@ -34,6 +34,7 @@ import MatrixClientPeg from '../../../MatrixClientPeg';
|
||||||
import ContextualMenu from '../../structures/ContextualMenu';
|
import ContextualMenu from '../../structures/ContextualMenu';
|
||||||
import {RoomMember} from 'matrix-js-sdk';
|
import {RoomMember} from 'matrix-js-sdk';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
import PushProcessor from 'matrix-js-sdk/lib/pushprocessor';
|
||||||
|
|
||||||
linkifyMatrix(linkify);
|
linkifyMatrix(linkify);
|
||||||
|
|
||||||
|
@ -169,8 +170,10 @@ module.exports = React.createClass({
|
||||||
|
|
||||||
pillifyLinks: function(nodes) {
|
pillifyLinks: function(nodes) {
|
||||||
const shouldShowPillAvatar = !UserSettingsStore.getSyncedSetting("Pill.shouldHidePillAvatar", false);
|
const shouldShowPillAvatar = !UserSettingsStore.getSyncedSetting("Pill.shouldHidePillAvatar", false);
|
||||||
for (let i = 0; i < nodes.length; i++) {
|
let node = nodes[0];
|
||||||
const node = nodes[i];
|
while (node) {
|
||||||
|
let pillified = false;
|
||||||
|
|
||||||
if (node.tagName === "A" && node.getAttribute("href")) {
|
if (node.tagName === "A" && node.getAttribute("href")) {
|
||||||
const href = node.getAttribute("href");
|
const href = node.getAttribute("href");
|
||||||
|
|
||||||
|
@ -189,10 +192,68 @@ module.exports = React.createClass({
|
||||||
|
|
||||||
ReactDOM.render(pill, pillContainer);
|
ReactDOM.render(pill, pillContainer);
|
||||||
node.parentNode.replaceChild(pillContainer, node);
|
node.parentNode.replaceChild(pillContainer, node);
|
||||||
|
// Pills within pills aren't going to go well, so move on
|
||||||
|
pillified = true;
|
||||||
}
|
}
|
||||||
} else if (node.children && node.children.length) {
|
} else if (node.nodeType == Node.TEXT_NODE) {
|
||||||
this.pillifyLinks(node.children);
|
const Pill = sdk.getComponent('elements.Pill');
|
||||||
|
|
||||||
|
let currentTextNode = node;
|
||||||
|
const roomNotifTextNodes = [];
|
||||||
|
|
||||||
|
// Take a textNode and break it up to make all the instances of @room their
|
||||||
|
// own textNode, adding those nodes to roomNotifTextNodes
|
||||||
|
while (currentTextNode !== null) {
|
||||||
|
const roomNotifPos = Pill.roomNotifPos(currentTextNode.textContent);
|
||||||
|
let nextTextNode = null;
|
||||||
|
if (roomNotifPos > -1) {
|
||||||
|
let roomTextNode = currentTextNode;
|
||||||
|
|
||||||
|
if (roomNotifPos > 0) roomTextNode = roomTextNode.splitText(roomNotifPos);
|
||||||
|
if (roomTextNode.textContent.length > Pill.roomNotifLen()) {
|
||||||
|
nextTextNode = roomTextNode.splitText(Pill.roomNotifLen());
|
||||||
}
|
}
|
||||||
|
roomNotifTextNodes.push(roomTextNode);
|
||||||
|
}
|
||||||
|
currentTextNode = nextTextNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (roomNotifTextNodes.length > 0) {
|
||||||
|
const pushProcessor = new PushProcessor(MatrixClientPeg.get());
|
||||||
|
const atRoomRule = pushProcessor.getPushRuleById(".m.rule.roomnotif");
|
||||||
|
if (pushProcessor.ruleMatchesEvent(atRoomRule, this.props.mxEvent)) {
|
||||||
|
// Now replace all those nodes with Pills
|
||||||
|
for (const roomNotifTextNode of roomNotifTextNodes) {
|
||||||
|
const pillContainer = document.createElement('span');
|
||||||
|
const room = MatrixClientPeg.get().getRoom(this.props.mxEvent.getRoomId());
|
||||||
|
const pill = <Pill
|
||||||
|
type={Pill.TYPE_AT_ROOM_MENTION}
|
||||||
|
inMessage={true}
|
||||||
|
room={room}
|
||||||
|
shouldShowPillAvatar={true}
|
||||||
|
/>;
|
||||||
|
|
||||||
|
ReactDOM.render(pill, pillContainer);
|
||||||
|
roomNotifTextNode.parentNode.replaceChild(pillContainer, roomNotifTextNode);
|
||||||
|
|
||||||
|
// Set the next node to be processed to the one after the node
|
||||||
|
// we're adding now, since we've just inserted nodes into the structure
|
||||||
|
// we're iterating over.
|
||||||
|
// Note we've checked roomNotifTextNodes.length > 0 so we'll do this at least once
|
||||||
|
node = roomNotifTextNode.nextSibling;
|
||||||
|
}
|
||||||
|
// Nothing else to do for a text node (and we don't need to advance
|
||||||
|
// the loop pointer because we did it above)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.childNodes && node.childNodes.length && !pillified) {
|
||||||
|
this.pillifyLinks(node.childNodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
node = node.nextSibling;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue