Merge branch 'develop' into travis/msc-send-widget-events
This commit is contained in:
commit
94550546eb
107 changed files with 3225 additions and 1480 deletions
|
@ -48,8 +48,8 @@ export default (props) => {
|
|||
title: _t('Feedback sent'),
|
||||
description: _t('Thank you!'),
|
||||
});
|
||||
props.onFinished();
|
||||
}
|
||||
props.onFinished();
|
||||
};
|
||||
|
||||
const brand = SdkConfig.get().brand;
|
||||
|
|
|
@ -280,11 +280,17 @@ class DMRoomTile extends React.PureComponent {
|
|||
</span>
|
||||
);
|
||||
|
||||
const caption = this.props.member.isEmail
|
||||
? _t("Invite by email")
|
||||
: this._highlightName(this.props.member.userId);
|
||||
|
||||
return (
|
||||
<div className='mx_InviteDialog_roomTile' onClick={this._onClick}>
|
||||
{stackedAvatar}
|
||||
<span className='mx_InviteDialog_roomTile_name'>{this._highlightName(this.props.member.name)}</span>
|
||||
<span className='mx_InviteDialog_roomTile_userId'>{this._highlightName(this.props.member.userId)}</span>
|
||||
<span className="mx_InviteDialog_roomTile_nameStack">
|
||||
<div className='mx_InviteDialog_roomTile_name'>{this._highlightName(this.props.member.name)}</div>
|
||||
<div className='mx_InviteDialog_roomTile_userId'>{caption}</div>
|
||||
</span>
|
||||
{timestamp}
|
||||
</div>
|
||||
);
|
||||
|
@ -663,12 +669,21 @@ export default class InviteDialog extends React.PureComponent {
|
|||
};
|
||||
|
||||
_onKeyDown = (e) => {
|
||||
// when the field is empty and the user hits backspace remove the right-most target
|
||||
if (!e.target.value && !this.state.busy && this.state.targets.length > 0 && e.key === Key.BACKSPACE &&
|
||||
!e.ctrlKey && !e.shiftKey && !e.metaKey
|
||||
) {
|
||||
if (this.state.busy) return;
|
||||
const value = e.target.value.trim();
|
||||
const hasModifiers = e.ctrlKey || e.shiftKey || e.metaKey;
|
||||
if (!value && this.state.targets.length > 0 && e.key === Key.BACKSPACE && !hasModifiers) {
|
||||
// when the field is empty and the user hits backspace remove the right-most target
|
||||
e.preventDefault();
|
||||
this._removeMember(this.state.targets[this.state.targets.length - 1]);
|
||||
} else if (value && e.key === Key.ENTER && !hasModifiers) {
|
||||
// when the user hits enter with something in their field try to convert it
|
||||
e.preventDefault();
|
||||
this._convertFilter();
|
||||
} else if (value && e.key === Key.SPACE && !hasModifiers && value.includes("@") && !value.includes(" ")) {
|
||||
// when the user hits space and their input looks like an e-mail/MXID then try to convert it
|
||||
e.preventDefault();
|
||||
this._convertFilter();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -811,6 +826,10 @@ export default class InviteDialog extends React.PureComponent {
|
|||
filterText = ""; // clear the filter when the user accepts a suggestion
|
||||
}
|
||||
this.setState({targets, filterText});
|
||||
|
||||
if (this._editorRef && this._editorRef.current) {
|
||||
this._editorRef.current.focus();
|
||||
}
|
||||
};
|
||||
|
||||
_removeMember = (member: Member) => {
|
||||
|
@ -820,6 +839,10 @@ export default class InviteDialog extends React.PureComponent {
|
|||
targets.splice(idx, 1);
|
||||
this.setState({targets});
|
||||
}
|
||||
|
||||
if (this._editorRef && this._editorRef.current) {
|
||||
this._editorRef.current.focus();
|
||||
}
|
||||
};
|
||||
|
||||
_onPaste = async (e) => {
|
||||
|
@ -829,7 +852,7 @@ export default class InviteDialog extends React.PureComponent {
|
|||
return;
|
||||
}
|
||||
|
||||
// Prevent the text being pasted into the textarea
|
||||
// Prevent the text being pasted into the input
|
||||
e.preventDefault();
|
||||
|
||||
// Process it as a list of addresses to add instead
|
||||
|
@ -1024,8 +1047,8 @@ export default class InviteDialog extends React.PureComponent {
|
|||
<DMUserTile member={t} onRemove={!this.state.busy && this._removeMember} key={t.userId} />
|
||||
));
|
||||
const input = (
|
||||
<textarea
|
||||
rows={1}
|
||||
<input
|
||||
type="text"
|
||||
onKeyDown={this._onKeyDown}
|
||||
onChange={this._updateFilter}
|
||||
value={this.state.filterText}
|
||||
|
@ -1033,6 +1056,7 @@ export default class InviteDialog extends React.PureComponent {
|
|||
onPaste={this._onPaste}
|
||||
autoFocus={true}
|
||||
disabled={this.state.busy}
|
||||
autoComplete="off"
|
||||
/>
|
||||
);
|
||||
return (
|
||||
|
@ -1103,7 +1127,7 @@ export default class InviteDialog extends React.PureComponent {
|
|||
|
||||
if (identityServersEnabled) {
|
||||
helpText = _t(
|
||||
"Start a conversation with someone using their name, username (like <userId/>) or email address.",
|
||||
"Start a conversation with someone using their name, email address or username (like <userId/>).",
|
||||
{},
|
||||
{userId: () => {
|
||||
return (
|
||||
|
@ -1158,7 +1182,7 @@ export default class InviteDialog extends React.PureComponent {
|
|||
|
||||
if (identityServersEnabled) {
|
||||
helpText = _t(
|
||||
"Invite someone using their name, username (like <userId/>), email address or " +
|
||||
"Invite someone using their name, email address, username (like <userId/>) or " +
|
||||
"<a>share this room</a>.",
|
||||
{},
|
||||
{
|
||||
|
|
|
@ -23,6 +23,11 @@ import {
|
|||
IModalWidgetCloseRequest,
|
||||
IModalWidgetOpenRequestData,
|
||||
IModalWidgetReturnData,
|
||||
ISetModalButtonEnabledActionRequest,
|
||||
IWidgetApiAcknowledgeResponseData,
|
||||
IWidgetApiErrorResponseData,
|
||||
BuiltInModalButtonID,
|
||||
ModalButtonID,
|
||||
ModalButtonKind,
|
||||
Widget,
|
||||
WidgetApiFromWidgetAction,
|
||||
|
@ -31,6 +36,7 @@ import {StopGapWidgetDriver} from "../../../stores/widgets/StopGapWidgetDriver";
|
|||
import {MatrixClientPeg} from "../../../MatrixClientPeg";
|
||||
import RoomViewStore from "../../../stores/RoomViewStore";
|
||||
import {OwnProfileStore} from "../../../stores/OwnProfileStore";
|
||||
import { arrayFastClone } from "../../../utils/arrays";
|
||||
|
||||
interface IProps {
|
||||
widgetDefinition: IModalWidgetOpenRequestData;
|
||||
|
@ -40,15 +46,19 @@ interface IProps {
|
|||
|
||||
interface IState {
|
||||
messaging?: ClientWidgetApi;
|
||||
disabledButtonIds: ModalButtonID[];
|
||||
}
|
||||
|
||||
const MAX_BUTTONS = 3;
|
||||
|
||||
export default class ModalWidgetDialog extends React.PureComponent<IProps, IState> {
|
||||
private readonly widget: Widget;
|
||||
private readonly possibleButtons: ModalButtonID[];
|
||||
private appFrame: React.RefObject<HTMLIFrameElement> = React.createRef();
|
||||
|
||||
state: IState = {};
|
||||
state: IState = {
|
||||
disabledButtonIds: [],
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
@ -58,6 +68,7 @@ export default class ModalWidgetDialog extends React.PureComponent<IProps, IStat
|
|||
creatorUserId: MatrixClientPeg.get().getUserId(),
|
||||
id: `modal_${this.props.sourceWidgetId}`,
|
||||
});
|
||||
this.possibleButtons = (this.props.widgetDefinition.buttons || []).map(b => b.id);
|
||||
}
|
||||
|
||||
public componentDidMount() {
|
||||
|
@ -79,12 +90,35 @@ export default class ModalWidgetDialog extends React.PureComponent<IProps, IStat
|
|||
private onLoad = () => {
|
||||
this.state.messaging.once("ready", this.onReady);
|
||||
this.state.messaging.on(`action:${WidgetApiFromWidgetAction.CloseModalWidget}`, this.onWidgetClose);
|
||||
this.state.messaging.on(`action:${WidgetApiFromWidgetAction.SetModalButtonEnabled}`, this.onButtonEnableToggle);
|
||||
};
|
||||
|
||||
private onWidgetClose = (ev: CustomEvent<IModalWidgetCloseRequest>) => {
|
||||
this.props.onFinished(true, ev.detail.data);
|
||||
}
|
||||
|
||||
private onButtonEnableToggle = (ev: CustomEvent<ISetModalButtonEnabledActionRequest>) => {
|
||||
ev.preventDefault();
|
||||
const isClose = ev.detail.data.button === BuiltInModalButtonID.Close;
|
||||
if (isClose || !this.possibleButtons.includes(ev.detail.data.button)) {
|
||||
return this.state.messaging.transport.reply(ev.detail, {
|
||||
error: {message: "Invalid button"},
|
||||
} as IWidgetApiErrorResponseData);
|
||||
}
|
||||
|
||||
let buttonIds: ModalButtonID[];
|
||||
if (ev.detail.data.enabled) {
|
||||
buttonIds = arrayFastClone(this.state.disabledButtonIds).filter(i => i !== ev.detail.data.button);
|
||||
} else {
|
||||
// use a set to swap the operation to avoid memory leaky arrays.
|
||||
const tempSet = new Set(this.state.disabledButtonIds);
|
||||
tempSet.add(ev.detail.data.button);
|
||||
buttonIds = Array.from(tempSet);
|
||||
}
|
||||
this.setState({disabledButtonIds: buttonIds});
|
||||
this.state.messaging.transport.reply(ev.detail, {} as IWidgetApiAcknowledgeResponseData);
|
||||
};
|
||||
|
||||
public render() {
|
||||
const templated = this.widget.getCompleteUrl({
|
||||
currentRoomId: RoomViewStore.getRoomId(),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue