Merge branch 'develop' into travis/reset-passphrase
This commit is contained in:
commit
01c5f84f60
74 changed files with 3231 additions and 866 deletions
|
@ -981,7 +981,7 @@ export default class GroupView extends React.Component {
|
|||
<Spinner />
|
||||
</div>;
|
||||
}
|
||||
const httpInviterAvatar = this.state.inviterProfile
|
||||
const httpInviterAvatar = this.state.inviterProfile && this.state.inviterProfile.avatarUrl
|
||||
? mediaFromMxc(this.state.inviterProfile.avatarUrl).getSquareThumbnailHttp(36)
|
||||
: null;
|
||||
|
||||
|
|
|
@ -34,7 +34,6 @@ import { UPDATE_EVENT } from "../../stores/AsyncStore";
|
|||
import ResizeNotifier from "../../utils/ResizeNotifier";
|
||||
import SettingsStore from "../../settings/SettingsStore";
|
||||
import RoomListStore, { LISTS_UPDATE_EVENT } from "../../stores/room-list/RoomListStore";
|
||||
import {Key} from "../../Keyboard";
|
||||
import IndicatorScrollbar from "../structures/IndicatorScrollbar";
|
||||
import AccessibleTooltipButton from "../views/elements/AccessibleTooltipButton";
|
||||
import { OwnProfileStore } from "../../stores/OwnProfileStore";
|
||||
|
@ -43,6 +42,7 @@ import LeftPanelWidget from "./LeftPanelWidget";
|
|||
import {replaceableComponent} from "../../utils/replaceableComponent";
|
||||
import {mediaFromMxc} from "../../customisations/Media";
|
||||
import SpaceStore, {UPDATE_SELECTED_SPACE} from "../../stores/SpaceStore";
|
||||
import { getKeyBindingsManager, RoomListAction } from "../../KeyBindingsManager";
|
||||
|
||||
interface IProps {
|
||||
isMinimized: boolean;
|
||||
|
@ -297,17 +297,18 @@ export default class LeftPanel extends React.Component<IProps, IState> {
|
|||
private onKeyDown = (ev: React.KeyboardEvent) => {
|
||||
if (!this.focusedElement) return;
|
||||
|
||||
switch (ev.key) {
|
||||
case Key.ARROW_UP:
|
||||
case Key.ARROW_DOWN:
|
||||
const action = getKeyBindingsManager().getRoomListAction(ev);
|
||||
switch (action) {
|
||||
case RoomListAction.NextRoom:
|
||||
case RoomListAction.PrevRoom:
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
this.onMoveFocus(ev.key === Key.ARROW_UP);
|
||||
this.onMoveFocus(action === RoomListAction.PrevRoom);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
private onEnter = () => {
|
||||
private selectRoom = () => {
|
||||
const firstRoom = this.listContainerRef.current.querySelector<HTMLDivElement>(".mx_RoomTile");
|
||||
if (firstRoom) {
|
||||
firstRoom.click();
|
||||
|
@ -388,8 +389,8 @@ export default class LeftPanel extends React.Component<IProps, IState> {
|
|||
>
|
||||
<RoomSearch
|
||||
isMinimized={this.props.isMinimized}
|
||||
onVerticalArrow={this.onKeyDown}
|
||||
onEnter={this.onEnter}
|
||||
onKeyDown={this.onKeyDown}
|
||||
onSelectRoom={this.selectRoom}
|
||||
/>
|
||||
<AccessibleTooltipButton
|
||||
className={classNames("mx_LeftPanel_exploreButton", {
|
||||
|
|
|
@ -444,6 +444,7 @@ class LoggedInView extends React.Component<IProps, IState> {
|
|||
case RoomAction.RoomScrollDown:
|
||||
case RoomAction.JumpToFirstMessage:
|
||||
case RoomAction.JumpToLatestMessage:
|
||||
// pass the event down to the scroll panel
|
||||
this._onScrollKeyPressed(ev);
|
||||
handled = true;
|
||||
break;
|
||||
|
|
|
@ -30,8 +30,11 @@ import SpaceStore, {UPDATE_SELECTED_SPACE} from "../../stores/SpaceStore";
|
|||
|
||||
interface IProps {
|
||||
isMinimized: boolean;
|
||||
onVerticalArrow(ev: React.KeyboardEvent): void;
|
||||
onEnter(ev: React.KeyboardEvent): boolean;
|
||||
onKeyDown(ev: React.KeyboardEvent): void;
|
||||
/**
|
||||
* @returns true if a room has been selected and the search field should be cleared
|
||||
*/
|
||||
onSelectRoom(): boolean;
|
||||
}
|
||||
|
||||
interface IState {
|
||||
|
@ -120,10 +123,11 @@ export default class RoomSearch extends React.PureComponent<IProps, IState> {
|
|||
break;
|
||||
case RoomListAction.NextRoom:
|
||||
case RoomListAction.PrevRoom:
|
||||
this.props.onVerticalArrow(ev);
|
||||
// we don't handle these actions here put pass the event on to the interested party (LeftPanel)
|
||||
this.props.onKeyDown(ev);
|
||||
break;
|
||||
case RoomListAction.SelectRoom: {
|
||||
const shouldClear = this.props.onEnter(ev);
|
||||
const shouldClear = this.props.onSelectRoom();
|
||||
if (shouldClear) {
|
||||
// wrap in set immediate to delay it so that we don't clear the filter & then change room
|
||||
setImmediate(() => {
|
||||
|
|
|
@ -16,10 +16,10 @@ limitations under the License.
|
|||
|
||||
import React, {createRef} from "react";
|
||||
import PropTypes from 'prop-types';
|
||||
import { Key } from '../../Keyboard';
|
||||
import Timer from '../../utils/Timer';
|
||||
import AutoHideScrollbar from "./AutoHideScrollbar";
|
||||
import {replaceableComponent} from "../../utils/replaceableComponent";
|
||||
import {getKeyBindingsManager, RoomAction} from "../../KeyBindingsManager";
|
||||
|
||||
const DEBUG_SCROLL = false;
|
||||
|
||||
|
@ -535,29 +535,19 @@ export default class ScrollPanel extends React.Component {
|
|||
* @param {object} ev the keyboard event
|
||||
*/
|
||||
handleScrollKey = ev => {
|
||||
switch (ev.key) {
|
||||
case Key.PAGE_UP:
|
||||
if (!ev.ctrlKey && !ev.shiftKey && !ev.altKey && !ev.metaKey) {
|
||||
this.scrollRelative(-1);
|
||||
}
|
||||
const roomAction = getKeyBindingsManager().getRoomAction(ev);
|
||||
switch (roomAction) {
|
||||
case RoomAction.ScrollUp:
|
||||
this.scrollRelative(-1);
|
||||
break;
|
||||
|
||||
case Key.PAGE_DOWN:
|
||||
if (!ev.ctrlKey && !ev.shiftKey && !ev.altKey && !ev.metaKey) {
|
||||
this.scrollRelative(1);
|
||||
}
|
||||
case RoomAction.RoomScrollDown:
|
||||
this.scrollRelative(1);
|
||||
break;
|
||||
|
||||
case Key.HOME:
|
||||
if (ev.ctrlKey && !ev.shiftKey && !ev.altKey && !ev.metaKey) {
|
||||
this.scrollToTop();
|
||||
}
|
||||
case RoomAction.JumpToFirstMessage:
|
||||
this.scrollToTop();
|
||||
break;
|
||||
|
||||
case Key.END:
|
||||
if (ev.ctrlKey && !ev.shiftKey && !ev.altKey && !ev.metaKey) {
|
||||
this.scrollToBottom();
|
||||
}
|
||||
case RoomAction.JumpToLatestMessage:
|
||||
this.scrollToBottom();
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -18,7 +18,7 @@ limitations under the License.
|
|||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import { _t, _td } from '../../../languageHandler';
|
||||
import * as sdk from '../../../index';
|
||||
import Modal from "../../../Modal";
|
||||
import PasswordReset from "../../../PasswordReset";
|
||||
|
@ -27,7 +27,9 @@ import classNames from 'classnames';
|
|||
import AuthPage from "../../views/auth/AuthPage";
|
||||
import CountlyAnalytics from "../../../CountlyAnalytics";
|
||||
import ServerPicker from "../../views/elements/ServerPicker";
|
||||
import PassphraseField from '../../views/auth/PassphraseField';
|
||||
import {replaceableComponent} from "../../../utils/replaceableComponent";
|
||||
import { PASSWORD_MIN_SCORE } from '../../views/auth/RegistrationForm';
|
||||
|
||||
// Phases
|
||||
// Show the forgot password inputs
|
||||
|
@ -137,10 +139,14 @@ export default class ForgotPassword extends React.Component {
|
|||
// refresh the server errors, just in case the server came back online
|
||||
await this._checkServerLiveliness(this.props.serverConfig);
|
||||
|
||||
await this['password_field'].validate({ allowEmpty: false });
|
||||
|
||||
if (!this.state.email) {
|
||||
this.showErrorDialog(_t('The email address linked to your account must be entered.'));
|
||||
} else if (!this.state.password || !this.state.password2) {
|
||||
this.showErrorDialog(_t('A new password must be entered.'));
|
||||
} else if (!this.state.passwordFieldValid) {
|
||||
this.showErrorDialog(_t('Please choose a strong password'));
|
||||
} else if (this.state.password !== this.state.password2) {
|
||||
this.showErrorDialog(_t('New passwords must match each other.'));
|
||||
} else {
|
||||
|
@ -186,6 +192,12 @@ export default class ForgotPassword extends React.Component {
|
|||
});
|
||||
}
|
||||
|
||||
onPasswordValidate(result) {
|
||||
this.setState({
|
||||
passwordFieldValid: result.valid,
|
||||
});
|
||||
}
|
||||
|
||||
renderForgot() {
|
||||
const Field = sdk.getComponent('elements.Field');
|
||||
|
||||
|
@ -230,12 +242,15 @@ export default class ForgotPassword extends React.Component {
|
|||
/>
|
||||
</div>
|
||||
<div className="mx_AuthBody_fieldRow">
|
||||
<Field
|
||||
<PassphraseField
|
||||
name="reset_password"
|
||||
type="password"
|
||||
label={_t('New Password')}
|
||||
label={_td('New Password')}
|
||||
value={this.state.password}
|
||||
minScore={PASSWORD_MIN_SCORE}
|
||||
onChange={this.onInputChanged.bind(this, "password")}
|
||||
fieldRef={field => this['password_field'] = field}
|
||||
onValidate={(result) => this.onPasswordValidate(result)}
|
||||
onFocus={() => CountlyAnalytics.instance.track("onboarding_forgot_password_newPassword_focus")}
|
||||
onBlur={() => CountlyAnalytics.instance.track("onboarding_forgot_password_newPassword_blur")}
|
||||
autoComplete="new-password"
|
||||
|
|
|
@ -40,7 +40,7 @@ enum RegistrationField {
|
|||
PasswordConfirm = "field_password_confirm",
|
||||
}
|
||||
|
||||
const PASSWORD_MIN_SCORE = 3; // safely unguessable: moderate protection from offline slow-hash scenario.
|
||||
export const PASSWORD_MIN_SCORE = 3; // safely unguessable: moderate protection from offline slow-hash scenario.
|
||||
|
||||
interface IProps {
|
||||
// Values pre-filled in the input boxes when the component loads
|
||||
|
|
|
@ -29,7 +29,9 @@ import dis from "../../../dispatcher/dispatcher";
|
|||
import IdentityAuthClient from "../../../IdentityAuthClient";
|
||||
import Modal from "../../../Modal";
|
||||
import {humanizeTime} from "../../../utils/humanize";
|
||||
import createRoom, {canEncryptToAllUsers, findDMForUser, privateShouldBeEncrypted} from "../../../createRoom";
|
||||
import createRoom, {
|
||||
canEncryptToAllUsers, ensureDMExists, findDMForUser, privateShouldBeEncrypted,
|
||||
} from "../../../createRoom";
|
||||
import {inviteMultipleToRoom, showCommunityInviteDialog} from "../../../RoomInvite";
|
||||
import {Key} from "../../../Keyboard";
|
||||
import {Action} from "../../../dispatcher/actions";
|
||||
|
@ -332,6 +334,7 @@ interface IInviteDialogState {
|
|||
threepidResultsMixin: { user: Member, userId: string}[];
|
||||
canUseIdentityServer: boolean;
|
||||
tryingIdentityServer: boolean;
|
||||
consultFirst: boolean;
|
||||
|
||||
// These two flags are used for the 'Go' button to communicate what is going on.
|
||||
busy: boolean,
|
||||
|
@ -380,6 +383,7 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
|
|||
threepidResultsMixin: [],
|
||||
canUseIdentityServer: !!MatrixClientPeg.get().getIdentityServerUrl(),
|
||||
tryingIdentityServer: false,
|
||||
consultFirst: false,
|
||||
|
||||
// These two flags are used for the 'Go' button to communicate what is going on.
|
||||
busy: false,
|
||||
|
@ -395,6 +399,10 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
|
|||
}
|
||||
}
|
||||
|
||||
private onConsultFirstChange = (ev) => {
|
||||
this.setState({consultFirst: ev.target.checked});
|
||||
}
|
||||
|
||||
static buildRecents(excludedTargetIds: Set<string>): {userId: string, user: RoomMember, lastActive: number}[] {
|
||||
const rooms = DMRoomMap.shared().getUniqueRoomsWithIndividuals(); // map of userId => js-sdk Room
|
||||
|
||||
|
@ -745,16 +753,34 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
|
|||
});
|
||||
}
|
||||
|
||||
this.setState({busy: true});
|
||||
try {
|
||||
await this.props.call.transfer(targetIds[0]);
|
||||
this.setState({busy: false});
|
||||
this.props.onFinished();
|
||||
} catch (e) {
|
||||
this.setState({
|
||||
busy: false,
|
||||
errorText: _t("Failed to transfer call"),
|
||||
if (this.state.consultFirst) {
|
||||
const dmRoomId = await ensureDMExists(MatrixClientPeg.get(), targetIds[0]);
|
||||
|
||||
dis.dispatch({
|
||||
action: 'place_call',
|
||||
type: this.props.call.type,
|
||||
room_id: dmRoomId,
|
||||
transferee: this.props.call,
|
||||
});
|
||||
dis.dispatch({
|
||||
action: 'view_room',
|
||||
room_id: dmRoomId,
|
||||
should_peek: false,
|
||||
joining: false,
|
||||
});
|
||||
this.props.onFinished();
|
||||
} else {
|
||||
this.setState({busy: true});
|
||||
try {
|
||||
await this.props.call.transfer(targetIds[0]);
|
||||
this.setState({busy: false});
|
||||
this.props.onFinished();
|
||||
} catch (e) {
|
||||
this.setState({
|
||||
busy: false,
|
||||
errorText: _t("Failed to transfer call"),
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1215,6 +1241,7 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
|
|||
let helpText;
|
||||
let buttonText;
|
||||
let goButtonFn;
|
||||
let consultSection;
|
||||
let keySharingWarning = <span />;
|
||||
|
||||
const identityServersEnabled = SettingsStore.getValue(UIFeature.IdentityServer);
|
||||
|
@ -1339,6 +1366,12 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
|
|||
title = _t("Transfer");
|
||||
buttonText = _t("Transfer");
|
||||
goButtonFn = this._transferCall;
|
||||
consultSection = <div>
|
||||
<label>
|
||||
<input type="checkbox" checked={this.state.consultFirst} onChange={this.onConsultFirstChange} />
|
||||
{_t("Consult first")}
|
||||
</label>
|
||||
</div>;
|
||||
} else {
|
||||
console.error("Unknown kind of InviteDialog: " + this.props.kind);
|
||||
}
|
||||
|
@ -1375,6 +1408,7 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
|
|||
{this._renderSection('recents')}
|
||||
{this._renderSection('suggestions')}
|
||||
</div>
|
||||
{consultSection}
|
||||
</div>
|
||||
</BaseDialog>
|
||||
);
|
||||
|
|
54
src/components/views/dialogs/SeshatResetDialog.tsx
Normal file
54
src/components/views/dialogs/SeshatResetDialog.tsx
Normal file
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
Copyright 2021 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 {_t} from "../../../languageHandler";
|
||||
import {replaceableComponent} from "../../../utils/replaceableComponent";
|
||||
|
||||
import BaseDialog from "./BaseDialog";
|
||||
import DialogButtons from "../elements/DialogButtons";
|
||||
|
||||
import {IDialogProps} from "./IDialogProps";
|
||||
|
||||
@replaceableComponent("views.dialogs.SeshatResetDialog")
|
||||
export default class SeshatResetDialog extends React.PureComponent<IDialogProps> {
|
||||
render() {
|
||||
return (
|
||||
<BaseDialog
|
||||
hasCancel={true}
|
||||
onFinished={this.props.onFinished.bind(null, false)}
|
||||
title={_t("Reset event store?")}>
|
||||
<div>
|
||||
<p>
|
||||
{_t("You most likely do not want to reset your event index store")}
|
||||
<br />
|
||||
{_t("If you do, please note that none of your messages will be deleted, " +
|
||||
"but the search experience might be degraded for a few moments" +
|
||||
"whilst the index is recreated",
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<DialogButtons
|
||||
primaryButton={_t("Reset event store")}
|
||||
onPrimaryButtonClick={this.props.onFinished.bind(null, true)}
|
||||
primaryButtonClass="danger"
|
||||
cancelButton={_t("Cancel")}
|
||||
onCancel={this.props.onFinished.bind(null, false)}
|
||||
/>
|
||||
</BaseDialog>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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";
|
||||
|
@ -41,15 +40,38 @@ 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;
|
||||
|
||||
|
@ -57,45 +79,42 @@ 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 = profileInfo.avatar_url;
|
||||
|
||||
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) {
|
||||
// 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,
|
||||
avatar_url: avatarUrl,
|
||||
body: message,
|
||||
displayname: this.props.displayName,
|
||||
avatar_url: this.props.avatarUrl,
|
||||
},
|
||||
msgtype: "m.text",
|
||||
body: this.props.message,
|
||||
displayname: displayname,
|
||||
avatar_url: avatarUrl,
|
||||
body: message,
|
||||
displayname: this.props.displayName,
|
||||
avatar_url: this.props.avatarUrl,
|
||||
},
|
||||
unsigned: {
|
||||
age: 97,
|
||||
|
@ -108,12 +127,15 @@ 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 Avatar.avatarUrlForUser({avatarUrl}, AVATAR_SIZE, AVATAR_SIZE, "crop");
|
||||
return Avatar.avatarUrlForUser(
|
||||
{ avatarUrl: this.props.avatarUrl },
|
||||
AVATAR_SIZE, AVATAR_SIZE, "crop",
|
||||
);
|
||||
},
|
||||
getMxcAvatarUrl: () => avatarUrl,
|
||||
getMxcAvatarUrl: () => this.props.avatarUrl,
|
||||
};
|
||||
|
||||
return event;
|
||||
|
@ -125,10 +147,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)}
|
||||
|
|
|
@ -132,7 +132,7 @@ export default class MVideoBody extends React.PureComponent<IProps, IState> {
|
|||
// enable the play button. Firefox does not seem to care either
|
||||
// way, so it's fine to do for all browsers.
|
||||
decryptedUrl: `data:${content?.info?.mimetype},`,
|
||||
decryptedThumbnailUrl: thumbnailUrl,
|
||||
decryptedThumbnailUrl: thumbnailUrl || `data:${content?.info?.mimetype},`,
|
||||
decryptedBlob: null,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -149,8 +149,8 @@ export default class AuxPanel extends React.Component<IProps, IState> {
|
|||
const callView = (
|
||||
<CallViewForRoom
|
||||
roomId={this.props.room.roomId}
|
||||
onResize={this.props.onResize}
|
||||
maxVideoHeight={this.props.maxHeight}
|
||||
resizeNotifier={this.props.resizeNotifier}
|
||||
/>
|
||||
);
|
||||
|
||||
|
|
|
@ -485,16 +485,14 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
|
|||
if (model.autoComplete && model.autoComplete.hasCompletions()) {
|
||||
const autoComplete = model.autoComplete;
|
||||
switch (autocompleteAction) {
|
||||
case AutocompleteAction.CompleteOrPrevSelection:
|
||||
case AutocompleteAction.PrevSelection:
|
||||
autoComplete.onUpArrow(event);
|
||||
autoComplete.selectPreviousSelection();
|
||||
handled = true;
|
||||
break;
|
||||
case AutocompleteAction.CompleteOrNextSelection:
|
||||
case AutocompleteAction.NextSelection:
|
||||
autoComplete.onDownArrow(event);
|
||||
handled = true;
|
||||
break;
|
||||
case AutocompleteAction.ApplySelection:
|
||||
autoComplete.onTab(event);
|
||||
autoComplete.selectNextSelection();
|
||||
handled = true;
|
||||
break;
|
||||
case AutocompleteAction.Cancel:
|
||||
|
@ -504,8 +502,10 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
|
|||
default:
|
||||
return; // don't preventDefault on anything else
|
||||
}
|
||||
} else if (autocompleteAction === AutocompleteAction.ApplySelection) {
|
||||
this.tabCompleteName(event);
|
||||
} else if (autocompleteAction === AutocompleteAction.CompleteOrPrevSelection
|
||||
|| autocompleteAction === AutocompleteAction.CompleteOrNextSelection) {
|
||||
// there is no current autocomplete window, try to open it
|
||||
this.tabCompleteName();
|
||||
handled = true;
|
||||
} else if (event.key === Key.BACKSPACE || event.key === Key.DELETE) {
|
||||
this.formatBarRef.current.hide();
|
||||
|
@ -517,7 +517,7 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
|
|||
}
|
||||
};
|
||||
|
||||
private async tabCompleteName(event: React.KeyboardEvent) {
|
||||
private async tabCompleteName() {
|
||||
try {
|
||||
await new Promise<void>(resolve => this.setState({showVisualBell: false}, resolve));
|
||||
const {model} = this.props;
|
||||
|
@ -540,7 +540,7 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
|
|||
|
||||
// Don't try to do things with the autocomplete if there is none shown
|
||||
if (model.autoComplete) {
|
||||
await model.autoComplete.onTab(event);
|
||||
await model.autoComplete.startSelection();
|
||||
if (!model.autoComplete.hasSelection()) {
|
||||
this.setState({showVisualBell: true});
|
||||
model.autoComplete.close();
|
||||
|
|
|
@ -936,7 +936,7 @@ export default class EventTile extends React.Component {
|
|||
);
|
||||
|
||||
const TooltipButton = sdk.getComponent('elements.TooltipButton');
|
||||
const keyRequestInfo = isEncryptionFailure ?
|
||||
const keyRequestInfo = isEncryptionFailure && !isRedacted ?
|
||||
<div className="mx_EventTile_keyRequestInfo">
|
||||
<span className="mx_EventTile_keyRequestInfo_text">
|
||||
{ keyRequestInfoContent }
|
||||
|
|
|
@ -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";
|
||||
import {replaceableComponent} from "../../../utils/replaceableComponent";
|
||||
|
@ -302,10 +303,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;
|
||||
|
@ -491,6 +494,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;
|
||||
|
@ -582,6 +608,7 @@ export default class RoomPreviewBar extends React.Component {
|
|||
{ titleElement }
|
||||
{ subTitleElements }
|
||||
</div>
|
||||
{ reasonElement }
|
||||
<div className="mx_RoomPreviewBar_actions">
|
||||
{ secondaryButton }
|
||||
{ extraComponents }
|
||||
|
|
|
@ -28,13 +28,12 @@ import Modal from "../../../Modal";
|
|||
import PassphraseField from "../auth/PassphraseField";
|
||||
import CountlyAnalytics from "../../../CountlyAnalytics";
|
||||
import {replaceableComponent} from "../../../utils/replaceableComponent";
|
||||
import { PASSWORD_MIN_SCORE } from '../auth/RegistrationForm';
|
||||
|
||||
const FIELD_OLD_PASSWORD = 'field_old_password';
|
||||
const FIELD_NEW_PASSWORD = 'field_new_password';
|
||||
const FIELD_NEW_PASSWORD_CONFIRM = 'field_new_password_confirm';
|
||||
|
||||
const PASSWORD_MIN_SCORE = 3; // safely unguessable: moderate protection from offline slow-hash scenario.
|
||||
|
||||
@replaceableComponent("views.settings.ChangePassword")
|
||||
export default class ChangePassword extends React.Component {
|
||||
static propTypes = {
|
||||
|
|
|
@ -26,6 +26,7 @@ import {formatBytes, formatCountLong} from "../../../utils/FormattingUtils";
|
|||
import EventIndexPeg from "../../../indexing/EventIndexPeg";
|
||||
import {SettingLevel} from "../../../settings/SettingLevel";
|
||||
import {replaceableComponent} from "../../../utils/replaceableComponent";
|
||||
import SeshatResetDialog from '../dialogs/SeshatResetDialog';
|
||||
|
||||
@replaceableComponent("views.settings.EventIndexPanel")
|
||||
export default class EventIndexPanel extends React.Component {
|
||||
|
@ -122,6 +123,20 @@ export default class EventIndexPanel extends React.Component {
|
|||
await this.updateState();
|
||||
}
|
||||
|
||||
_confirmEventStoreReset = () => {
|
||||
const self = this;
|
||||
const { close } = Modal.createDialog(SeshatResetDialog, {
|
||||
onFinished: async (success) => {
|
||||
if (success) {
|
||||
await SettingsStore.setValue('enableEventIndexing', null, SettingLevel.DEVICE, false);
|
||||
await EventIndexPeg.deleteEventIndex();
|
||||
await self._onEnable();
|
||||
close();
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
let eventIndexingSettings = null;
|
||||
const InlineSpinner = sdk.getComponent('elements.InlineSpinner');
|
||||
|
@ -167,7 +182,7 @@ export default class EventIndexPanel extends React.Component {
|
|||
);
|
||||
} else if (EventIndexPeg.platformHasSupport() && !EventIndexPeg.supportIsInstalled()) {
|
||||
const nativeLink = (
|
||||
"https://github.com/vector-im/element-web/blob/develop/" +
|
||||
"https://github.com/vector-im/element-desktop/blob/develop/" +
|
||||
"docs/native-node-modules.md#" +
|
||||
"adding-seshat-for-search-in-e2e-encrypted-rooms"
|
||||
);
|
||||
|
@ -212,7 +227,10 @@ export default class EventIndexPanel extends React.Component {
|
|||
eventIndexingSettings = (
|
||||
<div className='mx_SettingsTab_subsectionText'>
|
||||
<p>
|
||||
{_t("Message search initilisation failed")}
|
||||
{this.state.enabling
|
||||
? <InlineSpinner />
|
||||
: _t("Message search initilisation failed")
|
||||
}
|
||||
</p>
|
||||
{EventIndexPeg.error && (
|
||||
<details>
|
||||
|
@ -220,6 +238,11 @@ export default class EventIndexPanel extends React.Component {
|
|||
<code>
|
||||
{EventIndexPeg.error.message}
|
||||
</code>
|
||||
<p>
|
||||
<AccessibleButton key="delete" kind="danger" onClick={this._confirmEventStoreReset}>
|
||||
{_t("Reset")}
|
||||
</AccessibleButton>
|
||||
</p>
|
||||
</details>
|
||||
)}
|
||||
|
||||
|
|
|
@ -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";
|
||||
|
@ -63,6 +64,10 @@ interface IState extends IThemeState {
|
|||
systemFont: string;
|
||||
showAdvanced: boolean;
|
||||
layout: Layout;
|
||||
// User profile data for the message preview
|
||||
userId: string;
|
||||
displayName: string;
|
||||
avatarUrl: string;
|
||||
}
|
||||
|
||||
@replaceableComponent("views.settings.tabs.user.AppearanceUserSettingsTab")
|
||||
|
@ -84,9 +89,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.
|
||||
|
@ -307,6 +328,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>
|
||||
|
|
|
@ -40,9 +40,6 @@ interface IProps {
|
|||
// Another ongoing call to display information about
|
||||
secondaryCall?: MatrixCall,
|
||||
|
||||
// maxHeight style attribute for the video panel
|
||||
maxVideoHeight?: number;
|
||||
|
||||
// a callback which is called when the content in the callview changes
|
||||
// in a way that is likely to cause a resize.
|
||||
onResize?: any;
|
||||
|
@ -96,9 +93,6 @@ function exitFullscreen() {
|
|||
const CONTROLS_HIDE_DELAY = 1000;
|
||||
// Height of the header duplicated from CSS because we need to subtract it from our max
|
||||
// height to get the max height of the video
|
||||
const HEADER_HEIGHT = 44;
|
||||
const BOTTOM_PADDING = 10;
|
||||
const BOTTOM_MARGIN_TOP_BOTTOM = 10; // top margin plus bottom margin
|
||||
const CONTEXT_MENU_VPADDING = 8; // How far the context menu sits above the button (px)
|
||||
|
||||
@replaceableComponent("views.voip.CallView")
|
||||
|
@ -364,6 +358,11 @@ export default class CallView extends React.Component<IProps, IState> {
|
|||
CallHandler.sharedInstance().setActiveCallRoomId(userFacingRoomId);
|
||||
}
|
||||
|
||||
private onTransferClick = () => {
|
||||
const transfereeCall = CallHandler.sharedInstance().getTransfereeForCallId(this.props.call.callId);
|
||||
this.props.call.transferToCall(transfereeCall);
|
||||
}
|
||||
|
||||
public render() {
|
||||
const client = MatrixClientPeg.get();
|
||||
const callRoomId = CallHandler.roomIdForCall(this.props.call);
|
||||
|
@ -479,25 +478,52 @@ export default class CallView extends React.Component<IProps, IState> {
|
|||
// for voice calls (fills the bg)
|
||||
let contentView: React.ReactNode;
|
||||
|
||||
const transfereeCall = CallHandler.sharedInstance().getTransfereeForCallId(this.props.call.callId);
|
||||
const isOnHold = this.state.isLocalOnHold || this.state.isRemoteOnHold;
|
||||
let onHoldText = null;
|
||||
if (this.state.isRemoteOnHold) {
|
||||
const holdString = CallHandler.sharedInstance().hasAnyUnheldCall() ?
|
||||
_td("You held the call <a>Switch</a>") : _td("You held the call <a>Resume</a>");
|
||||
onHoldText = _t(holdString, {}, {
|
||||
a: sub => <AccessibleButton kind="link" onClick={this.onCallResumeClick}>
|
||||
{sub}
|
||||
</AccessibleButton>,
|
||||
});
|
||||
} else if (this.state.isLocalOnHold) {
|
||||
onHoldText = _t("%(peerName)s held the call", {
|
||||
peerName: this.props.call.getOpponentMember().name,
|
||||
});
|
||||
let holdTransferContent;
|
||||
if (transfereeCall) {
|
||||
const transferTargetRoom = MatrixClientPeg.get().getRoom(CallHandler.roomIdForCall(this.props.call));
|
||||
const transferTargetName = transferTargetRoom ? transferTargetRoom.name : _t("unknown person");
|
||||
|
||||
const transfereeRoom = MatrixClientPeg.get().getRoom(
|
||||
CallHandler.roomIdForCall(transfereeCall),
|
||||
);
|
||||
const transfereeName = transfereeRoom ? transfereeRoom.name : _t("unknown person");
|
||||
|
||||
holdTransferContent = <div className="mx_CallView_holdTransferContent">
|
||||
{_t(
|
||||
"Consulting with %(transferTarget)s. <a>Transfer to %(transferee)s</a>",
|
||||
{
|
||||
transferTarget: transferTargetName,
|
||||
transferee: transfereeName,
|
||||
},
|
||||
{
|
||||
a: sub => <AccessibleButton kind="link" onClick={this.onTransferClick}>{sub}</AccessibleButton>,
|
||||
},
|
||||
)}
|
||||
</div>;
|
||||
} else if (isOnHold) {
|
||||
let onHoldText = null;
|
||||
if (this.state.isRemoteOnHold) {
|
||||
const holdString = CallHandler.sharedInstance().hasAnyUnheldCall() ?
|
||||
_td("You held the call <a>Switch</a>") : _td("You held the call <a>Resume</a>");
|
||||
onHoldText = _t(holdString, {}, {
|
||||
a: sub => <AccessibleButton kind="link" onClick={this.onCallResumeClick}>
|
||||
{sub}
|
||||
</AccessibleButton>,
|
||||
});
|
||||
} else if (this.state.isLocalOnHold) {
|
||||
onHoldText = _t("%(peerName)s held the call", {
|
||||
peerName: this.props.call.getOpponentMember().name,
|
||||
});
|
||||
}
|
||||
holdTransferContent = <div className="mx_CallView_holdTransferContent">
|
||||
{onHoldText}
|
||||
</div>;
|
||||
}
|
||||
|
||||
if (this.props.call.type === CallType.Video) {
|
||||
let localVideoFeed = null;
|
||||
let onHoldContent = null;
|
||||
let onHoldBackground = null;
|
||||
const backgroundStyle: CSSProperties = {};
|
||||
const containerClasses = classNames({
|
||||
|
@ -505,9 +531,6 @@ export default class CallView extends React.Component<IProps, IState> {
|
|||
mx_CallView_video_hold: isOnHold,
|
||||
});
|
||||
if (isOnHold) {
|
||||
onHoldContent = <div className="mx_CallView_video_holdContent">
|
||||
{onHoldText}
|
||||
</div>;
|
||||
const backgroundAvatarUrl = avatarUrlForMember(
|
||||
// is it worth getting the size of the div to pass here?
|
||||
this.props.call.getOpponentMember(), 1024, 1024, 'crop',
|
||||
|
@ -519,22 +542,11 @@ export default class CallView extends React.Component<IProps, IState> {
|
|||
localVideoFeed = <VideoFeed type={VideoFeedType.Local} call={this.props.call} />;
|
||||
}
|
||||
|
||||
// if we're fullscreen, we don't want to set a maxHeight on the video element.
|
||||
const maxVideoHeight = getFullScreenElement() || !this.props.maxVideoHeight ? null : (
|
||||
this.props.maxVideoHeight - (HEADER_HEIGHT + BOTTOM_PADDING + BOTTOM_MARGIN_TOP_BOTTOM)
|
||||
);
|
||||
contentView = <div className={containerClasses}
|
||||
ref={this.contentRef} onMouseMove={this.onMouseMove}
|
||||
// Put the max height on here too because this div is ended up 4px larger than the content
|
||||
// and is causing it to scroll, and I am genuinely baffled as to why.
|
||||
style={{maxHeight: maxVideoHeight}}
|
||||
>
|
||||
contentView = <div className={containerClasses} ref={this.contentRef} onMouseMove={this.onMouseMove}>
|
||||
{onHoldBackground}
|
||||
<VideoFeed type={VideoFeedType.Remote} call={this.props.call} onResize={this.props.onResize}
|
||||
maxHeight={maxVideoHeight}
|
||||
/>
|
||||
<VideoFeed type={VideoFeedType.Remote} call={this.props.call} onResize={this.props.onResize} />
|
||||
{localVideoFeed}
|
||||
{onHoldContent}
|
||||
{holdTransferContent}
|
||||
{callControls}
|
||||
</div>;
|
||||
} else {
|
||||
|
@ -554,7 +566,7 @@ export default class CallView extends React.Component<IProps, IState> {
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mx_CallView_voice_holdText">{onHoldText}</div>
|
||||
{holdTransferContent}
|
||||
{callControls}
|
||||
</div>;
|
||||
}
|
||||
|
|
|
@ -19,6 +19,8 @@ import React from 'react';
|
|||
import CallHandler from '../../../CallHandler';
|
||||
import CallView from './CallView';
|
||||
import dis from '../../../dispatcher/dispatcher';
|
||||
import {Resizable} from "re-resizable";
|
||||
import ResizeNotifier from "../../../utils/ResizeNotifier";
|
||||
import {replaceableComponent} from "../../../utils/replaceableComponent";
|
||||
|
||||
interface IProps {
|
||||
|
@ -28,9 +30,7 @@ interface IProps {
|
|||
// maxHeight style attribute for the video panel
|
||||
maxVideoHeight?: number;
|
||||
|
||||
// a callback which is called when the content in the callview changes
|
||||
// in a way that is likely to cause a resize.
|
||||
onResize?: any;
|
||||
resizeNotifier: ResizeNotifier,
|
||||
}
|
||||
|
||||
interface IState {
|
||||
|
@ -79,11 +79,50 @@ export default class CallViewForRoom extends React.Component<IProps, IState> {
|
|||
return call;
|
||||
}
|
||||
|
||||
private onResizeStart = () => {
|
||||
this.props.resizeNotifier.startResizing();
|
||||
};
|
||||
|
||||
private onResize = () => {
|
||||
this.props.resizeNotifier.notifyTimelineHeightChanged();
|
||||
};
|
||||
|
||||
private onResizeStop = () => {
|
||||
this.props.resizeNotifier.stopResizing();
|
||||
};
|
||||
|
||||
public render() {
|
||||
if (!this.state.call) return null;
|
||||
// We subtract 8 as it the margin-bottom of the mx_CallViewForRoom_ResizeWrapper
|
||||
const maxHeight = this.props.maxVideoHeight - 8;
|
||||
|
||||
return <CallView call={this.state.call} pipMode={false}
|
||||
onResize={this.props.onResize} maxVideoHeight={this.props.maxVideoHeight}
|
||||
/>;
|
||||
return (
|
||||
<div className="mx_CallViewForRoom">
|
||||
<Resizable
|
||||
minHeight={380}
|
||||
maxHeight={maxHeight}
|
||||
enable={{
|
||||
top: false,
|
||||
right: false,
|
||||
bottom: true,
|
||||
left: false,
|
||||
topRight: false,
|
||||
bottomRight: false,
|
||||
bottomLeft: false,
|
||||
topLeft: false,
|
||||
}}
|
||||
onResizeStart={this.onResizeStart}
|
||||
onResize={this.onResize}
|
||||
onResizeStop={this.onResizeStop}
|
||||
className="mx_CallViewForRoom_ResizeWrapper"
|
||||
handleClasses={{bottom: "mx_CallViewForRoom_ResizeHandle"}}
|
||||
>
|
||||
<CallView
|
||||
call={this.state.call}
|
||||
pipMode={false}
|
||||
/>
|
||||
</Resizable>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,9 +30,6 @@ interface IProps {
|
|||
|
||||
type: VideoFeedType,
|
||||
|
||||
// maxHeight style attribute for the video element
|
||||
maxHeight?: number,
|
||||
|
||||
// a callback which is called when the video element is resized
|
||||
// due to a change in video metadata
|
||||
onResize?: (e: Event) => void,
|
||||
|
@ -82,9 +79,6 @@ export default class VideoFeed extends React.Component<IProps> {
|
|||
),
|
||||
};
|
||||
|
||||
let videoStyle = {};
|
||||
if (this.props.maxHeight) videoStyle = { maxHeight: this.props.maxHeight };
|
||||
|
||||
return <video className={classnames(videoClasses)} ref={this.vid} style={videoStyle} />;
|
||||
return <video className={classnames(videoClasses)} ref={this.vid} />;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue