Render matrix.to user links as pills
This commit is contained in:
parent
78d7d556e4
commit
038b101ed7
6 changed files with 70 additions and 2 deletions
|
@ -1,4 +1,5 @@
|
|||
import { linkify } from "./linkify/linkify.js";
|
||||
import { getIdentifierColorNumber, avatarInitials } from "../../../avatar.js";
|
||||
|
||||
/**
|
||||
* Parse text into parts such as newline, links and text.
|
||||
|
@ -92,6 +93,24 @@ export class ImagePart {
|
|||
get type() { return "image"; }
|
||||
};
|
||||
|
||||
export class PillPart {
|
||||
constructor(id, href, children) {
|
||||
this.id = id;
|
||||
this.href = href;
|
||||
this.children = children;
|
||||
}
|
||||
|
||||
get type() { return "pill"; }
|
||||
|
||||
get avatarColorNumber() {
|
||||
return getIdentifierColorNumber(this.id);
|
||||
}
|
||||
|
||||
get avatarInitials() {
|
||||
return avatarInitials(this.id);
|
||||
}
|
||||
}
|
||||
|
||||
export class LinkPart {
|
||||
constructor(url, inlines) {
|
||||
this.url = url;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { MessageBody, HeaderBlock, ListBlock, CodeBlock, FormatPart, NewLinePart, RulePart, TextPart, LinkPart, ImagePart } from "./MessageBody.js"
|
||||
import { MessageBody, HeaderBlock, ListBlock, CodeBlock, PillPart, FormatPart, NewLinePart, RulePart, TextPart, LinkPart, ImagePart } from "./MessageBody.js"
|
||||
import { linkify } from "./linkify/linkify.js";
|
||||
import { parsePillLink } from "./pills.js"
|
||||
|
||||
/* At the time of writing (Jul 1 2021), Matrix Spec recommends
|
||||
* allowing the following HTML tags:
|
||||
|
@ -24,6 +25,10 @@ class Deserializer {
|
|||
// TODO Not equivalent to `node.href`!
|
||||
// Add another HTMLParseResult method?
|
||||
const href = this.result.getAttributeValue(node, "href");
|
||||
const pillData = href && parsePillLink(href);
|
||||
if (pillData && pillData.userId) {
|
||||
return new PillPart(pillData.userId, href, children);
|
||||
}
|
||||
return new LinkPart(href, children);
|
||||
}
|
||||
|
||||
|
|
12
src/domain/session/room/timeline/pills.js
Normal file
12
src/domain/session/room/timeline/pills.js
Normal file
|
@ -0,0 +1,12 @@
|
|||
const baseUrl = 'https://matrix.to';
|
||||
const linkPrefix = `${baseUrl}/#/`;
|
||||
|
||||
export function parsePillLink(link) {
|
||||
if (!link.startsWith(linkPrefix)) {
|
||||
return null;
|
||||
}
|
||||
const contents = link.substring(linkPrefix.length);
|
||||
if (contents[0] === '@') {
|
||||
return { userId: contents }
|
||||
}
|
||||
}
|
|
@ -76,3 +76,11 @@ limitations under the License.
|
|||
line-height: var(--avatar-size);
|
||||
font-size: calc(var(--avatar-size) * 0.6);
|
||||
}
|
||||
|
||||
.hydrogen .avatar.size-12 {
|
||||
--avatar-size: 12px;
|
||||
width: var(--avatar-size);
|
||||
height: var(--avatar-size);
|
||||
line-height: var(--avatar-size);
|
||||
font-size: calc(var(--avatar-size) * 0.6);
|
||||
}
|
||||
|
|
|
@ -216,6 +216,21 @@ only loads when the top comes into view*/
|
|||
padding: 10px;
|
||||
}
|
||||
|
||||
.Timeline_messageBody .pill {
|
||||
padding: 0px 5px 0px 5px;
|
||||
border-radius: 15px;
|
||||
background-color: #f6f6f6;
|
||||
border: 1px solid rgb(206, 206, 206);
|
||||
text-decoration: none;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.Timeline_messageBody .pill div.avatar {
|
||||
display: inline-block;
|
||||
margin-right: 3px;
|
||||
}
|
||||
|
||||
.Timeline_message.unsent .Timeline_messageBody {
|
||||
color: #ccc;
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import {StaticView} from "../../../general/StaticView.js";
|
||||
import {tag, text} from "../../../general/html.js";
|
||||
import {tag, text, classNames} from "../../../general/html.js";
|
||||
import {BaseMessageView} from "./BaseMessageView.js";
|
||||
|
||||
export class TextMessageView extends BaseMessageView {
|
||||
|
@ -53,6 +53,14 @@ function renderImage(imagePart) {
|
|||
return tag.img(attributes, []);
|
||||
}
|
||||
|
||||
function renderPill(pillPart) {
|
||||
const classes = `avatar size-12 usercolor${pillPart.avatarColorNumber}`;
|
||||
const avatar = tag.div({class: classes}, text(pillPart.avatarInitials));
|
||||
const children = renderParts(pillPart.children);
|
||||
children.unshift(avatar);
|
||||
return tag.a({ class: "pill", href: pillPart.href }, children);
|
||||
}
|
||||
|
||||
/**
|
||||
* Map from part to function that outputs DOM for the part
|
||||
*/
|
||||
|
@ -63,6 +71,7 @@ const formatFunction = {
|
|||
code: codePart => tag.code({}, text(codePart.text)),
|
||||
text: textPart => text(textPart.text),
|
||||
link: linkPart => tag.a({ href: linkPart.url, target: "_blank", rel: "noopener" }, renderParts(linkPart.inlines)),
|
||||
pill: renderPill,
|
||||
format: formatPart => tag[formatPart.format]({}, renderParts(formatPart.children)),
|
||||
list: renderList,
|
||||
image: renderImage,
|
||||
|
|
Reference in a new issue