diff --git a/res/css/views/elements/_RichText.scss b/res/css/views/elements/_RichText.scss index 474a123455..eda9f6a4de 100644 --- a/res/css/views/elements/_RichText.scss +++ b/res/css/views/elements/_RichText.scss @@ -4,6 +4,7 @@ .mx_UserPill, .mx_RoomPill, +.mx_GroupPill, .mx_AtRoomPill { border-radius: 16px; display: inline-block; @@ -13,7 +14,8 @@ } .mx_EventTile_body .mx_UserPill, -.mx_EventTile_body .mx_RoomPill { +.mx_EventTile_body .mx_RoomPill, +.mx_EventTile_body .mx_GroupPill { cursor: pointer; } @@ -43,6 +45,7 @@ .mx_UserPill .mx_BaseAvatar, .mx_RoomPill .mx_BaseAvatar, +.mx_GroupPill .mx_BaseAvatar, .mx_AtRoomPill .mx_BaseAvatar { position: relative; left: -3px; diff --git a/src/components/views/elements/Pill.js b/src/components/views/elements/Pill.js index 7e5ad379de..70e1d0659a 100644 --- a/src/components/views/elements/Pill.js +++ b/src/components/views/elements/Pill.js @@ -1,5 +1,6 @@ /* Copyright 2017 Vector Creations Ltd +Copyright 2018 New Vector Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -22,12 +23,13 @@ import PropTypes from 'prop-types'; import MatrixClientPeg from '../../../MatrixClientPeg'; import { MATRIXTO_URL_PATTERN } from '../../../linkify-matrix'; import { getDisplayAliasForRoom } from '../../../Rooms'; +import FlairStore from "../../../stores/FlairStore"; const REGEX_MATRIXTO = new RegExp(MATRIXTO_URL_PATTERN); // For URLs of matrix.to links in the timeline which have been reformatted by // HttpUtils transformTags to relative links. This excludes event URLs (with `[^\/]*`) -const REGEX_LOCAL_MATRIXTO = /^#\/(?:user|room)\/(([\#\!\@\+])[^\/]*)$/; +const REGEX_LOCAL_MATRIXTO = /^#\/(?:user|room|group)\/(([#!@+])[^\/]*)$/; const Pill = React.createClass({ statics: { @@ -45,6 +47,7 @@ const Pill = React.createClass({ }, TYPE_USER_MENTION: 'TYPE_USER_MENTION', TYPE_ROOM_MENTION: 'TYPE_ROOM_MENTION', + TYPE_GROUP_MENTION: 'TYPE_GROUP_MENTION', TYPE_AT_ROOM_MENTION: 'TYPE_AT_ROOM_MENTION', // '@room' mention }, @@ -81,12 +84,14 @@ const Pill = React.createClass({ // The member related to the user pill member: null, + // The group related to the group pill + group: null, // The room related to the room pill room: null, }; }, - componentWillReceiveProps(nextProps) { + async componentWillReceiveProps(nextProps) { let regex = REGEX_MATRIXTO; if (nextProps.inMessage) { regex = REGEX_LOCAL_MATRIXTO; @@ -109,9 +114,11 @@ const Pill = React.createClass({ '@': Pill.TYPE_USER_MENTION, '#': Pill.TYPE_ROOM_MENTION, '!': Pill.TYPE_ROOM_MENTION, + '+': Pill.TYPE_GROUP_MENTION, }[prefix]; let member; + let group; let room; switch (pillType) { case Pill.TYPE_AT_ROOM_MENTION: { @@ -140,8 +147,17 @@ const Pill = React.createClass({ } } break; + case Pill.TYPE_GROUP_MENTION: { + const cli = MatrixClientPeg.get(); + + try { + group = await FlairStore.getGroupProfileCached(cli, resourceId); + } catch (e) { // if FlairStore failed, rely on js-sdk's store which lacks info + group = cli.getGroup(resourceId); + } + } } - this.setState({resourceId, pillType, member, room}); + this.setState({resourceId, pillType, member, group, room}); }, componentWillMount() { @@ -179,6 +195,7 @@ const Pill = React.createClass({ }); }, render: function() { + const BaseAvatar = sdk.getComponent('views.avatars.BaseAvatar'); const MemberAvatar = sdk.getComponent('avatars.MemberAvatar'); const RoomAvatar = sdk.getComponent('avatars.RoomAvatar'); @@ -229,6 +246,20 @@ const Pill = React.createClass({ } } break; + case Pill.TYPE_GROUP_MENTION: { + if (this.state.group) { + const {avatarUrl, groupId, name} = this.state.group; + const cli = MatrixClientPeg.get(); + + linkText = groupId; + if (this.props.shouldShowPillAvatar) { + avatar = ; + } + pillClass = 'mx_RoomPill' || 'mx_GroupPill'; + } + } + break; } const classes = classNames(pillClass, {