mirror of
https://github.com/vector-im/element-web.git
synced 2024-11-16 05:04:57 +08:00
Use styled mxids in member list (#6328)
* Add DisambiguatedProfile Signed-off-by: Šimon Brandner <simon.bra.ag@gmail.com> * Give DisambiguatedProfile some nice options Signed-off-by: Šimon Brandner <simon.bra.ag@gmail.com> * Delint Signed-off-by: Šimon Brandner <simon.bra.ag@gmail.com> * Use DisambiguatedProfile in member tile Signed-off-by: Šimon Brandner <simon.bra.ag@gmail.com> * Basic handling of text overflow Signed-off-by: Šimon Brandner <simon.bra.ag@gmail.com> * Use name instead of rawDisplayName Signed-off-by: Šimon Brandner <simon.bra.ag@gmail.com> * This seems to make more sense Signed-off-by: Šimon Brandner <simon.bra.ag@gmail.com> * Reodred Signed-off-by: Šimon Brandner <simon.bra.ag@gmail.com> * rethemedex Signed-off-by: Šimon Brandner <simon.bra.ag@gmail.com> * Fix import Signed-off-by: Šimon Brandner <simon.bra.ag@gmail.com> * Fix color Signed-off-by: Šimon Brandner <simon.bra.ag@gmail.com> * Appease the linter * Re-apply UserIdentifier patch Co-authored-by: Matthew Hodgson <matthew@matrix.org> Co-authored-by: Travis Ralston <travisr@matrix.org>
This commit is contained in:
parent
e55136cede
commit
5d28e0533d
@ -192,6 +192,7 @@
|
|||||||
@import "./views/messages/_CallEvent.scss";
|
@import "./views/messages/_CallEvent.scss";
|
||||||
@import "./views/messages/_CreateEvent.scss";
|
@import "./views/messages/_CreateEvent.scss";
|
||||||
@import "./views/messages/_DateSeparator.scss";
|
@import "./views/messages/_DateSeparator.scss";
|
||||||
|
@import "./views/messages/_DisambiguatedProfile.scss";
|
||||||
@import "./views/messages/_EventTileBubble.scss";
|
@import "./views/messages/_EventTileBubble.scss";
|
||||||
@import "./views/messages/_HiddenBody.scss";
|
@import "./views/messages/_HiddenBody.scss";
|
||||||
@import "./views/messages/_JumpToDatePicker.scss";
|
@import "./views/messages/_JumpToDatePicker.scss";
|
||||||
@ -214,7 +215,6 @@
|
|||||||
@import "./views/messages/_ReactionsRowButton.scss";
|
@import "./views/messages/_ReactionsRowButton.scss";
|
||||||
@import "./views/messages/_RedactedBody.scss";
|
@import "./views/messages/_RedactedBody.scss";
|
||||||
@import "./views/messages/_RoomAvatarEvent.scss";
|
@import "./views/messages/_RoomAvatarEvent.scss";
|
||||||
@import "./views/messages/_SenderProfile.scss";
|
|
||||||
@import "./views/messages/_TextualEvent.scss";
|
@import "./views/messages/_TextualEvent.scss";
|
||||||
@import "./views/messages/_UnknownBody.scss";
|
@import "./views/messages/_UnknownBody.scss";
|
||||||
@import "./views/messages/_ViewSourceEvent.scss";
|
@import "./views/messages/_ViewSourceEvent.scss";
|
||||||
|
@ -93,7 +93,7 @@ limitations under the License.
|
|||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_FilePanel .mx_EventTile .mx_SenderProfile {
|
.mx_FilePanel .mx_EventTile .mx_DisambiguatedProfile {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
line-height: initial;
|
line-height: initial;
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
|
@ -77,7 +77,7 @@ limitations under the License.
|
|||||||
display: none; // we don't need this in this view
|
display: none; // we don't need this in this view
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_NotificationPanel .mx_EventTile .mx_SenderProfile,
|
.mx_NotificationPanel .mx_EventTile .mx_DisambiguatedProfile,
|
||||||
.mx_NotificationPanel .mx_EventTile .mx_MessageTimestamp {
|
.mx_NotificationPanel .mx_EventTile .mx_MessageTimestamp {
|
||||||
color: $primary-content;
|
color: $primary-content;
|
||||||
font-size: $font-12px;
|
font-size: $font-12px;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2015, 2016 OpenMarket Ltd
|
Copyright 2015, 2016 OpenMarket Ltd
|
||||||
|
Copyright 2021 Šimon Brandner <simon.bra.ag@gmail.com>
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@ -14,13 +15,18 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.mx_SenderProfile_displayName {
|
.mx_DisambiguatedProfile {
|
||||||
font-weight: 600;
|
overflow: hidden;
|
||||||
}
|
text-overflow: ellipsis;
|
||||||
|
|
||||||
.mx_SenderProfile_mxid {
|
.mx_DisambiguatedProfile_displayName {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
font-size: 1.1rem;
|
}
|
||||||
margin-left: 5px;
|
|
||||||
opacity: 0.5; // Match mx_TextualEvent
|
.mx_DisambiguatedProfile_mxid {
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
margin-left: 5px;
|
||||||
|
opacity: 0.5; // Match mx_TextualEvent
|
||||||
|
}
|
||||||
}
|
}
|
@ -126,7 +126,7 @@ $left-gutter: 64px;
|
|||||||
max-width: calc(100% - $left-gutter);
|
max-width: calc(100% - $left-gutter);
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_SenderProfile .mx_Flair {
|
.mx_DisambiguatedProfile .mx_Flair {
|
||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
@ -141,6 +141,21 @@ $left-gutter: 64px;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_DisambiguatedProfile {
|
||||||
|
color: $primary-content;
|
||||||
|
font-size: $font-14px;
|
||||||
|
display: inline-block; /* anti-zalgo, with overflow hidden */
|
||||||
|
overflow: hidden;
|
||||||
|
cursor: pointer;
|
||||||
|
padding-bottom: 0px;
|
||||||
|
padding-top: 0px;
|
||||||
|
margin: 0px;
|
||||||
|
/* the next three lines, along with overflow hidden, truncate long display names */
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
max-width: calc(100% - $left-gutter);
|
||||||
|
}
|
||||||
|
|
||||||
&.mx_EventTile_isEditing .mx_MessageTimestamp {
|
&.mx_EventTile_isEditing .mx_MessageTimestamp {
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ $left-gutter: 64px;
|
|||||||
|
|
||||||
.mx_GroupLayout {
|
.mx_GroupLayout {
|
||||||
.mx_EventTile {
|
.mx_EventTile {
|
||||||
> .mx_SenderProfile {
|
> .mx_DisambiguatedProfile {
|
||||||
line-height: $font-20px;
|
line-height: $font-20px;
|
||||||
margin-left: $left-gutter;
|
margin-left: $left-gutter;
|
||||||
}
|
}
|
||||||
@ -65,7 +65,7 @@ $left-gutter: 64px;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_SenderProfile {
|
.mx_DisambiguatedProfile {
|
||||||
font-size: $font-13px;
|
font-size: $font-13px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,7 +173,7 @@ $irc-line-height: $font-18px;
|
|||||||
border-left: 0;
|
border-left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_SenderProfile {
|
.mx_DisambiguatedProfile {
|
||||||
width: var(--name-width);
|
width: var(--name-width);
|
||||||
display: flex;
|
display: flex;
|
||||||
order: 2;
|
order: 2;
|
||||||
@ -181,14 +181,14 @@ $irc-line-height: $font-18px;
|
|||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
> .mx_SenderProfile_displayName {
|
> .mx_DisambiguatedProfile_displayName {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
text-align: end;
|
text-align: end;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
> .mx_SenderProfile_mxid {
|
> .mx_DisambiguatedProfile_mxid {
|
||||||
visibility: collapse;
|
visibility: collapse;
|
||||||
// Override the inherited margin.
|
// Override the inherited margin.
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
@ -196,11 +196,11 @@ $irc-line-height: $font-18px;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_SenderProfile:hover {
|
.mx_DisambiguatedProfile:hover {
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
|
|
||||||
> .mx_SenderProfile_displayName {
|
> .mx_DisambiguatedProfile_displayName {
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
display: inline;
|
display: inline;
|
||||||
background-color: $event-selected-color;
|
background-color: $event-selected-color;
|
||||||
@ -208,7 +208,7 @@ $irc-line-height: $font-18px;
|
|||||||
padding-right: 8px;
|
padding-right: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
> .mx_SenderProfile_mxid {
|
> .mx_DisambiguatedProfile_mxid {
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
background-color: $event-selected-color;
|
background-color: $event-selected-color;
|
||||||
@ -217,7 +217,7 @@ $irc-line-height: $font-18px;
|
|||||||
|
|
||||||
.mx_ReplyChain {
|
.mx_ReplyChain {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
.mx_SenderProfile {
|
.mx_DisambiguatedProfile {
|
||||||
order: unset;
|
order: unset;
|
||||||
max-width: unset;
|
max-width: unset;
|
||||||
width: unset;
|
width: unset;
|
||||||
|
71
src/components/views/messages/DisambiguatedProfile.tsx
Normal file
71
src/components/views/messages/DisambiguatedProfile.tsx
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2021 Šimon Brandner <simon.bra.ag@gmail.com>
|
||||||
|
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import { RoomMember } from 'matrix-js-sdk/src/models/room-member';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
|
import { getUserNameColorClass } from '../../../utils/FormattingUtils';
|
||||||
|
import UserIdentifier from "../../../customisations/UserIdentifier";
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
member?: RoomMember;
|
||||||
|
fallbackName: string;
|
||||||
|
flair?: JSX.Element;
|
||||||
|
onClick?(): void;
|
||||||
|
colored?: boolean;
|
||||||
|
emphasizeDisplayName?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class DisambiguatedProfile extends React.Component<IProps> {
|
||||||
|
render() {
|
||||||
|
const { fallbackName, member, flair, colored, emphasizeDisplayName, onClick } = this.props;
|
||||||
|
const rawDisplayName = member?.rawDisplayName || fallbackName;
|
||||||
|
const mxid = member?.userId;
|
||||||
|
|
||||||
|
let colorClass;
|
||||||
|
if (colored) {
|
||||||
|
colorClass = getUserNameColorClass(fallbackName);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mxidElement;
|
||||||
|
if (member?.disambiguate && mxid) {
|
||||||
|
mxidElement = (
|
||||||
|
<span className="mx_DisambiguatedProfile_mxid">
|
||||||
|
{ UserIdentifier.getDisplayUserIdentifier(
|
||||||
|
mxid, { withDisplayName: true, roomId: member.roomId },
|
||||||
|
) }
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const displayNameClasses = classNames({
|
||||||
|
"mx_DisambiguatedProfile_displayName": emphasizeDisplayName,
|
||||||
|
[colorClass]: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="mx_DisambiguatedProfile" dir="auto" onClick={onClick}>
|
||||||
|
<span className={displayNameClasses}>
|
||||||
|
{ rawDisplayName }
|
||||||
|
</span>
|
||||||
|
{ mxidElement }
|
||||||
|
{ flair }
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -21,10 +21,9 @@ import { RoomStateEvent } from "matrix-js-sdk/src/models/room-state";
|
|||||||
|
|
||||||
import Flair from '../elements/Flair';
|
import Flair from '../elements/Flair';
|
||||||
import FlairStore from '../../../stores/FlairStore';
|
import FlairStore from '../../../stores/FlairStore';
|
||||||
import { getUserNameColorClass } from '../../../utils/FormattingUtils';
|
|
||||||
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
||||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||||
import UserIdentifier from '../../../customisations/UserIdentifier';
|
import DisambiguatedProfile from "./DisambiguatedProfile";
|
||||||
import RoomContext, { TimelineRenderingType } from '../../../contexts/RoomContext';
|
import RoomContext, { TimelineRenderingType } from '../../../contexts/RoomContext';
|
||||||
import SettingsStore from "../../../settings/SettingsStore";
|
import SettingsStore from "../../../settings/SettingsStore";
|
||||||
import { MatrixClientPeg } from "../../../MatrixClientPeg";
|
import { MatrixClientPeg } from "../../../MatrixClientPeg";
|
||||||
@ -106,9 +105,8 @@ export default class SenderProfile extends React.Component<IProps, IState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { mxEvent } = this.props;
|
const { mxEvent, onClick } = this.props;
|
||||||
const colorClass = getUserNameColorClass(mxEvent.getSender());
|
const msgtype = mxEvent.getContent().msgtype;
|
||||||
const { msgtype } = mxEvent.getContent();
|
|
||||||
|
|
||||||
let member = mxEvent.sender;
|
let member = mxEvent.sender;
|
||||||
if (SettingsStore.getValue("feature_use_only_current_profiles")) {
|
if (SettingsStore.getValue("feature_use_only_current_profiles")) {
|
||||||
@ -118,10 +116,6 @@ export default class SenderProfile extends React.Component<IProps, IState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const disambiguate = member?.disambiguate || mxEvent.sender?.disambiguate;
|
|
||||||
const displayName = member?.rawDisplayName || mxEvent.getSender() || "";
|
|
||||||
const mxid = member?.userId || mxEvent.getSender() || "";
|
|
||||||
|
|
||||||
return <RoomContext.Consumer>
|
return <RoomContext.Consumer>
|
||||||
{ roomContext => {
|
{ roomContext => {
|
||||||
if (msgtype === MsgType.Emote &&
|
if (msgtype === MsgType.Emote &&
|
||||||
@ -130,17 +124,6 @@ export default class SenderProfile extends React.Component<IProps, IState> {
|
|||||||
return null; // emote message must include the name so don't duplicate it
|
return null; // emote message must include the name so don't duplicate it
|
||||||
}
|
}
|
||||||
|
|
||||||
let mxidElement;
|
|
||||||
if (disambiguate) {
|
|
||||||
mxidElement = (
|
|
||||||
<span className="mx_SenderProfile_mxid">
|
|
||||||
{ UserIdentifier.getDisplayUserIdentifier(
|
|
||||||
mxid, { withDisplayName: true, roomId: mxEvent.getRoomId() },
|
|
||||||
) }
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let flair;
|
let flair;
|
||||||
if (this.props.enableFlair) {
|
if (this.props.enableFlair) {
|
||||||
const displayedGroups = this.getDisplayedGroups(
|
const displayedGroups = this.getDisplayedGroups(
|
||||||
@ -151,13 +134,14 @@ export default class SenderProfile extends React.Component<IProps, IState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx_SenderProfile" dir="auto" onClick={this.props.onClick}>
|
<DisambiguatedProfile
|
||||||
<span className={`mx_SenderProfile_displayName ${colorClass}`}>
|
fallbackName={mxEvent.getSender() || ""}
|
||||||
{ displayName }
|
flair={flair}
|
||||||
</span>
|
onClick={onClick}
|
||||||
{ mxidElement }
|
member={member}
|
||||||
{ flair }
|
colored={true}
|
||||||
</div>
|
emphasizeDisplayName={true}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
} }
|
} }
|
||||||
</RoomContext.Consumer>;
|
</RoomContext.Consumer>;
|
||||||
|
@ -64,6 +64,7 @@ function presenceClassForMember(presenceState: string, lastActiveAgo: number, sh
|
|||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
name?: string;
|
name?: string;
|
||||||
|
nameJSX?: JSX.Element;
|
||||||
title?: string;
|
title?: string;
|
||||||
avatarJsx?: JSX.Element; // <BaseAvatar />
|
avatarJsx?: JSX.Element; // <BaseAvatar />
|
||||||
className?: string;
|
className?: string;
|
||||||
@ -117,7 +118,7 @@ export default class EntityTile extends React.PureComponent<IProps, IState> {
|
|||||||
mainClassNames[presenceClass] = true;
|
mainClassNames[presenceClass] = true;
|
||||||
|
|
||||||
let nameEl;
|
let nameEl;
|
||||||
const { name } = this.props;
|
const name = this.props.nameJSX || this.props.name;
|
||||||
|
|
||||||
if (!this.props.suppressOnHover) {
|
if (!this.props.suppressOnHover) {
|
||||||
const activeAgo = this.props.presenceLastActiveAgo ?
|
const activeAgo = this.props.presenceLastActiveAgo ?
|
||||||
|
@ -33,6 +33,7 @@ import { Action } from "../../../dispatcher/actions";
|
|||||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||||
import EntityTile, { PowerStatus } from "./EntityTile";
|
import EntityTile, { PowerStatus } from "./EntityTile";
|
||||||
import MemberAvatar from "./../avatars/MemberAvatar";
|
import MemberAvatar from "./../avatars/MemberAvatar";
|
||||||
|
import DisambiguatedProfile from "../messages/DisambiguatedProfile";
|
||||||
import UserIdentifierCustomisations from '../../../customisations/UserIdentifier';
|
import UserIdentifierCustomisations from '../../../customisations/UserIdentifier';
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
@ -258,6 +259,13 @@ export default class MemberTile extends React.Component<IProps, IState> {
|
|||||||
e2eStatus = this.state.e2eStatus;
|
e2eStatus = this.state.e2eStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const nameJSX = (
|
||||||
|
<DisambiguatedProfile
|
||||||
|
member={member}
|
||||||
|
fallbackName={name || ""}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<EntityTile
|
<EntityTile
|
||||||
{...this.props}
|
{...this.props}
|
||||||
@ -268,6 +276,7 @@ export default class MemberTile extends React.Component<IProps, IState> {
|
|||||||
avatarJsx={av}
|
avatarJsx={av}
|
||||||
title={this.getPowerLabel()}
|
title={this.getPowerLabel()}
|
||||||
name={name}
|
name={name}
|
||||||
|
nameJSX={nameJSX}
|
||||||
powerStatus={powerStatus}
|
powerStatus={powerStatus}
|
||||||
showPresence={this.props.showPresence}
|
showPresence={this.props.showPresence}
|
||||||
subtextLabel={statusMessage}
|
subtextLabel={statusMessage}
|
||||||
|
@ -132,7 +132,7 @@ function getAllEventTiles(session: ElementSession): Promise<ElementHandle[]> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function getMessageFromEventTile(eventTile: ElementHandle): Promise<Message> {
|
async function getMessageFromEventTile(eventTile: ElementHandle): Promise<Message> {
|
||||||
const senderElement = await eventTile.$(".mx_SenderProfile_displayName");
|
const senderElement = await eventTile.$(".mx_DisambiguatedProfile_displayName");
|
||||||
const className: string = await (await eventTile.getProperty("className")).jsonValue();
|
const className: string = await (await eventTile.getProperty("className")).jsonValue();
|
||||||
const classNames = className.split(" ");
|
const classNames = className.split(" ");
|
||||||
const bodyElement = await eventTile.$(".mx_EventTile_body");
|
const bodyElement = await eventTile.$(".mx_EventTile_body");
|
||||||
|
Loading…
Reference in New Issue
Block a user