mirror of https://github.com/vector-im/riot-web
Show invite reasons
Displays the reason for invitation in the invitation dialog, requiring a click to reveal the message. Signed-off-by: Robin Townsend <robin@robin.town>pull/21833/head
parent
667c94b387
commit
f0333b5b1c
|
@ -40,6 +40,35 @@ limitations under the License.
|
|||
word-break: break-word;
|
||||
}
|
||||
|
||||
.mx_RoomPreviewBar_reason {
|
||||
text-align: left;
|
||||
background-color: $primary-bg-color;
|
||||
border: 1px solid $invite-reason-border-color;
|
||||
border-radius: 10px;
|
||||
padding: 0 16px 12px 16px;
|
||||
margin: 5px 0 20px 0;
|
||||
|
||||
div {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.mx_EventTile_msgOption {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.mx_MatrixChat_useCompactLayout & {
|
||||
padding-top: 9px;
|
||||
}
|
||||
|
||||
&.mx_EventTilePreview_faded {
|
||||
cursor: pointer;
|
||||
|
||||
.mx_SenderProfile, .mx_EventTile_avatar {
|
||||
opacity: 0.3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mx_Spinner {
|
||||
width: auto;
|
||||
height: auto;
|
||||
|
|
|
@ -205,6 +205,8 @@ $user-tile-hover-bg-color: $header-panel-bg-color;
|
|||
// Appearance tab colors
|
||||
$appearance-tab-border-color: $room-highlight-color;
|
||||
|
||||
$invite-reason-border-color: $room-highlight-color;
|
||||
|
||||
// blur amounts for left left panel (only for element theme, used in _mods.scss)
|
||||
$roomlist-background-blur-amount: 60px;
|
||||
$groupFilterPanel-background-blur-amount: 30px;
|
||||
|
|
|
@ -200,6 +200,8 @@ $user-tile-hover-bg-color: $header-panel-bg-color;
|
|||
// Appearance tab colors
|
||||
$appearance-tab-border-color: $room-highlight-color;
|
||||
|
||||
$invite-reason-border-color: $room-highlight-color;
|
||||
|
||||
$composer-shadow-color: tranparent;
|
||||
|
||||
// ***** Mixins! *****
|
||||
|
|
|
@ -324,6 +324,8 @@ $user-tile-hover-bg-color: $header-panel-bg-color;
|
|||
// FontSlider colors
|
||||
$appearance-tab-border-color: $input-darker-bg-color;
|
||||
|
||||
$invite-reason-border-color: $input-darker-bg-color;
|
||||
|
||||
$composer-shadow-color: tranparent;
|
||||
|
||||
// ***** Mixins! *****
|
||||
|
|
|
@ -325,6 +325,8 @@ $user-tile-hover-bg-color: $header-panel-bg-color;
|
|||
// FontSlider colors
|
||||
$appearance-tab-border-color: $input-darker-bg-color;
|
||||
|
||||
$invite-reason-border-color: $input-darker-bg-color;
|
||||
|
||||
// blur amounts for left left panel (only for element theme, used in _mods.scss)
|
||||
$roomlist-background-blur-amount: 40px;
|
||||
$groupFilterPanel-background-blur-amount: 20px;
|
||||
|
|
|
@ -19,7 +19,6 @@ import classnames from 'classnames';
|
|||
import { MatrixEvent } from 'matrix-js-sdk/src/models/event';
|
||||
|
||||
import * as Avatar from '../../../Avatar';
|
||||
import { MatrixClientPeg } from '../../../MatrixClientPeg';
|
||||
import EventTile from '../rooms/EventTile';
|
||||
import SettingsStore from "../../../settings/SettingsStore";
|
||||
import {Layout} from "../../../settings/Layout";
|
||||
|
@ -40,61 +39,84 @@ interface IProps {
|
|||
* classnames to apply to the wrapper of the preview
|
||||
*/
|
||||
className: string;
|
||||
|
||||
/**
|
||||
* The ID of the displayed user
|
||||
*/
|
||||
userId: string;
|
||||
|
||||
/**
|
||||
* The display name of the displayed user
|
||||
*/
|
||||
displayName?: string;
|
||||
|
||||
/**
|
||||
* The mxc:// avatar URL of the displayed user
|
||||
*/
|
||||
avatarUrl?: string;
|
||||
|
||||
/**
|
||||
* Whether the EventTile should appear faded
|
||||
*/
|
||||
faded?: boolean;
|
||||
|
||||
/**
|
||||
* Callback for when the component is clicked
|
||||
*/
|
||||
onClick?: () => void;
|
||||
}
|
||||
|
||||
/* eslint-disable camelcase */
|
||||
interface IState {
|
||||
userId: string;
|
||||
displayname: string;
|
||||
avatar_url: string;
|
||||
message: string;
|
||||
faded: boolean;
|
||||
eventTileKey: number;
|
||||
}
|
||||
/* eslint-enable camelcase */
|
||||
|
||||
const AVATAR_SIZE = 32;
|
||||
|
||||
export default class EventTilePreview extends React.Component<IProps, IState> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
userId: "@erim:fink.fink",
|
||||
displayname: "Erimayas Fink",
|
||||
avatar_url: null,
|
||||
message: props.message,
|
||||
faded: !!props.faded,
|
||||
eventTileKey: 0,
|
||||
};
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
// Fetch current user data
|
||||
const client = MatrixClientPeg.get();
|
||||
const userId = client.getUserId();
|
||||
const profileInfo = await client.getProfileInfo(userId);
|
||||
const avatarUrl = Avatar.avatarUrlForUser(
|
||||
{avatarUrl: profileInfo.avatar_url},
|
||||
AVATAR_SIZE, AVATAR_SIZE, "crop");
|
||||
|
||||
changeMessage(message: string) {
|
||||
this.setState({
|
||||
userId,
|
||||
displayname: profileInfo.displayname,
|
||||
avatar_url: avatarUrl,
|
||||
message,
|
||||
// Change the EventTile key to force React to create a new instance
|
||||
eventTileKey: this.state.eventTileKey + 1,
|
||||
});
|
||||
}
|
||||
|
||||
private fakeEvent({userId, displayname, avatar_url: avatarUrl}: IState) {
|
||||
unfade() {
|
||||
this.setState({ faded: false });
|
||||
}
|
||||
|
||||
private fakeEvent({message}: IState) {
|
||||
const avatarUrl = Avatar.avatarUrlForUser(
|
||||
{ avatarUrl: this.props.avatarUrl },
|
||||
AVATAR_SIZE, AVATAR_SIZE, "crop",
|
||||
);
|
||||
|
||||
// Fake it till we make it
|
||||
/* eslint-disable quote-props */
|
||||
const rawEvent = {
|
||||
type: "m.room.message",
|
||||
sender: userId,
|
||||
sender: this.props.userId,
|
||||
content: {
|
||||
"m.new_content": {
|
||||
msgtype: "m.text",
|
||||
body: this.props.message,
|
||||
displayname: displayname,
|
||||
body: message,
|
||||
displayname: this.props.displayName,
|
||||
avatar_url: avatarUrl,
|
||||
},
|
||||
msgtype: "m.text",
|
||||
body: this.props.message,
|
||||
displayname: displayname,
|
||||
body: message,
|
||||
displayname: this.props.displayName,
|
||||
avatar_url: avatarUrl,
|
||||
},
|
||||
unsigned: {
|
||||
|
@ -108,8 +130,8 @@ export default class EventTilePreview extends React.Component<IProps, IState> {
|
|||
|
||||
// Fake it more
|
||||
event.sender = {
|
||||
name: displayname,
|
||||
userId: userId,
|
||||
name: this.props.displayName,
|
||||
userId: this.props.userId,
|
||||
getAvatarUrl: (..._) => {
|
||||
return avatarUrl;
|
||||
},
|
||||
|
@ -124,10 +146,12 @@ export default class EventTilePreview extends React.Component<IProps, IState> {
|
|||
const className = classnames(this.props.className, {
|
||||
"mx_IRCLayout": this.props.layout == Layout.IRC,
|
||||
"mx_GroupLayout": this.props.layout == Layout.Group,
|
||||
"mx_EventTilePreview_faded": this.state.faded,
|
||||
});
|
||||
|
||||
return <div className={className}>
|
||||
return <div className={className} onClick={this.props.onClick}>
|
||||
<EventTile
|
||||
key={this.state.eventTileKey}
|
||||
mxEvent={event}
|
||||
layout={this.props.layout}
|
||||
enableFlair={SettingsStore.getValue(UIFeature.Flair)}
|
||||
|
|
|
@ -25,6 +25,7 @@ import classNames from 'classnames';
|
|||
import { _t } from '../../../languageHandler';
|
||||
import SdkConfig from "../../../SdkConfig";
|
||||
import IdentityAuthClient from '../../../IdentityAuthClient';
|
||||
import SettingsStore from "../../../settings/SettingsStore";
|
||||
import {CommunityPrototypeStore} from "../../../stores/CommunityPrototypeStore";
|
||||
import {UPDATE_EVENT} from "../../../stores/AsyncStore";
|
||||
|
||||
|
@ -300,10 +301,12 @@ export default class RoomPreviewBar extends React.Component {
|
|||
const brand = SdkConfig.get().brand;
|
||||
const Spinner = sdk.getComponent('elements.Spinner');
|
||||
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
||||
const EventTilePreview = sdk.getComponent('elements.EventTilePreview');
|
||||
|
||||
let showSpinner = false;
|
||||
let title;
|
||||
let subTitle;
|
||||
let reasonElement;
|
||||
let primaryActionHandler;
|
||||
let primaryActionLabel;
|
||||
let secondaryActionHandler;
|
||||
|
@ -489,6 +492,29 @@ export default class RoomPreviewBar extends React.Component {
|
|||
primaryActionLabel = _t("Accept");
|
||||
}
|
||||
|
||||
const myUserId = MatrixClientPeg.get().getUserId();
|
||||
const reason = this.props.room.currentState.getMember(myUserId).events.member.event.content.reason;
|
||||
if (reason) {
|
||||
this.reasonElement = React.createRef();
|
||||
// We hide the reason for invitation by default, since it can be a
|
||||
// vector for spam/harassment.
|
||||
const showReason = () => {
|
||||
this.reasonElement.current.unfade();
|
||||
this.reasonElement.current.changeMessage(reason);
|
||||
};
|
||||
reasonElement = <EventTilePreview
|
||||
ref={this.reasonElement}
|
||||
onClick={showReason}
|
||||
className="mx_RoomPreviewBar_reason"
|
||||
message={_t("Invite messages are hidden by default. Click to show the message.")}
|
||||
layout={SettingsStore.getValue("layout")}
|
||||
userId={inviteMember.userId}
|
||||
displayName={inviteMember.rawDisplayName}
|
||||
avatarUrl={inviteMember.events.member.event.content.avatar_url}
|
||||
faded={true}
|
||||
/>;
|
||||
}
|
||||
|
||||
primaryActionHandler = this.props.onJoinClick;
|
||||
secondaryActionLabel = _t("Reject");
|
||||
secondaryActionHandler = this.props.onRejectClick;
|
||||
|
@ -580,6 +606,7 @@ export default class RoomPreviewBar extends React.Component {
|
|||
{ titleElement }
|
||||
{ subTitleElements }
|
||||
</div>
|
||||
{ reasonElement }
|
||||
<div className="mx_RoomPreviewBar_actions">
|
||||
{ secondaryButton }
|
||||
{ extraComponents }
|
||||
|
|
|
@ -18,6 +18,7 @@ limitations under the License.
|
|||
import React from 'react';
|
||||
import {_t} from "../../../../../languageHandler";
|
||||
import SdkConfig from "../../../../../SdkConfig";
|
||||
import { MatrixClientPeg } from '../../../../../MatrixClientPeg';
|
||||
import SettingsStore from "../../../../../settings/SettingsStore";
|
||||
import { enumerateThemes } from "../../../../../theme";
|
||||
import ThemeWatcher from "../../../../../settings/watchers/ThemeWatcher";
|
||||
|
@ -62,6 +63,10 @@ interface IState extends IThemeState {
|
|||
systemFont: string;
|
||||
showAdvanced: boolean;
|
||||
layout: Layout;
|
||||
// User profile data for the message preview
|
||||
userId: string;
|
||||
displayName: string;
|
||||
avatarUrl: string;
|
||||
}
|
||||
|
||||
|
||||
|
@ -83,9 +88,25 @@ export default class AppearanceUserSettingsTab extends React.Component<IProps, I
|
|||
systemFont: SettingsStore.getValue("systemFont"),
|
||||
showAdvanced: false,
|
||||
layout: SettingsStore.getValue("layout"),
|
||||
userId: "@erim:fink.fink",
|
||||
displayName: "Erimayas Fink",
|
||||
avatarUrl: null,
|
||||
};
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
// Fetch the current user profile for the message preview
|
||||
const client = MatrixClientPeg.get();
|
||||
const userId = client.getUserId();
|
||||
const profileInfo = await client.getProfileInfo(userId);
|
||||
|
||||
this.setState({
|
||||
userId,
|
||||
displayName: profileInfo.displayname,
|
||||
avatarUrl: profileInfo.avatar_url,
|
||||
});
|
||||
}
|
||||
|
||||
private calculateThemeState(): IThemeState {
|
||||
// We have to mirror the logic from ThemeWatcher.getEffectiveTheme so we
|
||||
// show the right values for things.
|
||||
|
@ -306,6 +327,9 @@ export default class AppearanceUserSettingsTab extends React.Component<IProps, I
|
|||
className="mx_AppearanceUserSettingsTab_fontSlider_preview"
|
||||
message={this.MESSAGE_PREVIEW_TEXT}
|
||||
layout={this.state.layout}
|
||||
userId={this.state.userId}
|
||||
displayName={this.state.displayName}
|
||||
avatarUrl={this.state.avatarUrl}
|
||||
/>
|
||||
<div className="mx_AppearanceUserSettingsTab_fontSlider">
|
||||
<div className="mx_AppearanceUserSettingsTab_fontSlider_smallText">Aa</div>
|
||||
|
|
|
@ -1525,6 +1525,7 @@
|
|||
"Start chatting": "Start chatting",
|
||||
"Do you want to join %(roomName)s?": "Do you want to join %(roomName)s?",
|
||||
"<userName/> invited you": "<userName/> invited you",
|
||||
"Invite messages are hidden by default. Click to show the message.": "Invite messages are hidden by default. Click to show the message.",
|
||||
"Reject": "Reject",
|
||||
"Reject & Ignore user": "Reject & Ignore user",
|
||||
"You're previewing %(roomName)s. Want to join it?": "You're previewing %(roomName)s. Want to join it?",
|
||||
|
|
Loading…
Reference in New Issue