Merge remote-tracking branch 'origin/experimental' into dbkr/sas

This commit is contained in:
David Baker 2019-01-23 17:32:18 +00:00
commit 6a90625d36
172 changed files with 1885 additions and 661 deletions

View file

@ -0,0 +1,27 @@
/*
Copyright 2019 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.
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.
*/
'use strict';
import React from 'react';
export default class AuthBody extends React.PureComponent {
render() {
return <div className="mx_AuthBody">
{ this.props.children }
</div>;
}
}

View file

@ -0,0 +1,57 @@
/*
Copyright 2017 Vector Creations Ltd
Copyright 2018, 2019 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.
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.
*/
'use strict';
const React = require('react');
import { _t } from '../../../languageHandler';
const dis = require('../../../dispatcher');
const AccessibleButton = require('../elements/AccessibleButton');
module.exports = React.createClass({
displayName: 'AuthButtons',
propTypes: {
},
onLoginClick: function() {
dis.dispatch({ action: 'start_login' });
},
onRegisterClick: function() {
dis.dispatch({ action: 'start_registration' });
},
render: function() {
const loginButton = (
<div className="mx_AuthButtons_loginButton_wrapper">
<AccessibleButton className="mx_AuthButtons_loginButton" element="button" onClick={this.onLoginClick}>
{ _t("Login") }
</AccessibleButton>
<AccessibleButton className="mx_AuthButtons_registerButton" element="button" onClick={this.onRegisterClick}>
{ _t("Register") }
</AccessibleButton>
</div>
);
return (
<div className="mx_AuthButtons">
{ loginButton }
</div>
);
},
});

View file

@ -1,5 +1,6 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
Copyright 2019 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.
@ -20,11 +21,11 @@ import { _t } from '../../../languageHandler';
import React from 'react';
module.exports = React.createClass({
displayName: 'LoginFooter',
displayName: 'AuthFooter',
render: function() {
return (
<div className="mx_Login_links">
<div className="mx_AuthFooter">
<a href="https://matrix.org">{ _t("powered by Matrix") }</a>
</div>
);

View file

@ -1,5 +1,6 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
Copyright 2019 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.
@ -17,14 +18,17 @@ limitations under the License.
'use strict';
const React = require('react');
import sdk from '../../../index';
module.exports = React.createClass({
displayName: 'LoginHeader',
displayName: 'AuthHeader',
render: function() {
const AuthHeaderLogo = sdk.getComponent('auth.AuthHeaderLogo');
return (
<div className="mx_Login_logo">
Matrix
<div className="mx_AuthHeader">
<AuthHeaderLogo />
</div>
);
},

View file

@ -0,0 +1,27 @@
/*
Copyright 2019 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.
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.
*/
'use strict';
import React from 'react';
export default class AuthHeaderLogo extends React.PureComponent {
render() {
return <div className="mx_AuthHeaderLogo">
Matrix
</div>;
}
}

View file

@ -0,0 +1,38 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
Copyright 2019 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.
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.
*/
'use strict';
const React = require('react');
import sdk from '../../../index';
module.exports = React.createClass({
displayName: 'AuthPage',
render: function() {
const AuthFooter = sdk.getComponent('auth.AuthFooter');
return (
<div className="mx_AuthPage">
<div className="mx_AuthPage_modal">
{ this.props.children }
</div>
<AuthFooter />
</div>
);
},
});

View file

@ -70,7 +70,7 @@ export default class CountryDropdown extends React.Component {
}
_flagImgForIso2(iso2) {
return <img src={`img/flags/${iso2}.png`} />;
return <img src={require(`../../../../res/img/flags/${iso2}.png`)} />;
}
_getShortOption(iso2) {

View file

@ -187,7 +187,7 @@ export const RecaptchaAuthEntry = React.createClass({
return <Loader />;
}
const CaptchaForm = sdk.getComponent("views.login.CaptchaForm");
const CaptchaForm = sdk.getComponent("views.auth.CaptchaForm");
const sitePublicKey = this.props.stageParams.public_key;
let errorSection;

View file

@ -201,7 +201,7 @@ class PasswordLogin extends React.Component {
disabled={disabled}
/>;
case PasswordLogin.LOGIN_FIELD_PHONE: {
const CountryDropdown = sdk.getComponent('views.login.CountryDropdown');
const CountryDropdown = sdk.getComponent('views.auth.CountryDropdown');
classes.mx_Login_phoneNumberField = true;
classes.mx_Login_field_has_prefix = true;
classes.error = this.props.loginIncorrect && !this.state.phoneNumber;

View file

@ -323,7 +323,7 @@ module.exports = React.createClass({
}
}
const CountryDropdown = sdk.getComponent('views.login.CountryDropdown');
const CountryDropdown = sdk.getComponent('views.auth.CountryDropdown');
let phoneSection;
if (!SdkConfig.get().disable_3pid_login) {
const phonePlaceholder = this._authStepIsRequired('m.login.msisdn') ? _t("Mobile phone number") : _t("Mobile phone number (optional)");

View file

@ -138,7 +138,7 @@ module.exports = React.createClass({
},
showHelpPopup: function() {
const CustomServerDialog = sdk.getComponent('login.CustomServerDialog');
const CustomServerDialog = sdk.getComponent('auth.CustomServerDialog');
Modal.createTrackedDialog('Custom Server Dialog', '', CustomServerDialog);
},

View file

@ -79,7 +79,7 @@ export default class GroupInviteTileContextMenu extends React.Component {
render() {
return <div>
<div className="mx_RoomTileContextMenu_leave" onClick={this._onClickReject} >
<img className="mx_RoomTileContextMenu_tag_icon" src="img/icon_context_delete.svg" width="15" height="15" />
<img className="mx_RoomTileContextMenu_tag_icon" src={require("../../../../res/img/icon_context_delete.svg")} width="15" height="15" />
{ _t('Reject') }
</div>
</div>;

View file

@ -245,26 +245,26 @@ module.exports = React.createClass({
return (
<div className="mx_RoomTileContextMenu">
<div className="mx_RoomTileContextMenu_notif_picker" >
<img src="img/notif-slider.svg" width="20" height="107" />
<img src={require("../../../../res/img/notif-slider.svg")} width="20" height="107" />
</div>
<div className={alertMeClasses} onClick={this._onClickAlertMe} >
<img className="mx_RoomTileContextMenu_notif_activeIcon" src="img/notif-active.svg" width="12" height="12" />
<img className="mx_RoomTileContextMenu_notif_icon mx_filterFlipColor" src="img/icon-context-mute-off-copy.svg" width="16" height="12" />
<img className="mx_RoomTileContextMenu_notif_activeIcon" src={require("../../../../res/img/notif-active.svg")} width="12" height="12" />
<img className="mx_RoomTileContextMenu_notif_icon mx_filterFlipColor" src={require("../../../../res/img/icon-context-mute-off-copy.svg")} width="16" height="12" />
{ _t('All messages (noisy)') }
</div>
<div className={allNotifsClasses} onClick={this._onClickAllNotifs} >
<img className="mx_RoomTileContextMenu_notif_activeIcon" src="img/notif-active.svg" width="12" height="12" />
<img className="mx_RoomTileContextMenu_notif_icon mx_filterFlipColor" src="img/icon-context-mute-off.svg" width="16" height="12" />
<img className="mx_RoomTileContextMenu_notif_activeIcon" src={require("../../../../res/img/notif-active.svg")} width="12" height="12" />
<img className="mx_RoomTileContextMenu_notif_icon mx_filterFlipColor" src={require("../../../../res/img/icon-context-mute-off.svg")} width="16" height="12" />
{ _t('All messages') }
</div>
<div className={mentionsClasses} onClick={this._onClickMentions} >
<img className="mx_RoomTileContextMenu_notif_activeIcon" src="img/notif-active.svg" width="12" height="12" />
<img className="mx_RoomTileContextMenu_notif_icon mx_filterFlipColor" src="img/icon-context-mute-mentions.svg" width="16" height="12" />
<img className="mx_RoomTileContextMenu_notif_activeIcon" src={require("../../../../res/img/notif-active.svg")} width="12" height="12" />
<img className="mx_RoomTileContextMenu_notif_icon mx_filterFlipColor" src={require("../../../../res/img/icon-context-mute-mentions.svg")} width="16" height="12" />
{ _t('Mentions only') }
</div>
<div className={muteNotifsClasses} onClick={this._onClickMute} >
<img className="mx_RoomTileContextMenu_notif_activeIcon" src="img/notif-active.svg" width="12" height="12" />
<img className="mx_RoomTileContextMenu_notif_icon mx_filterFlipColor" src="img/icon-context-mute.svg" width="16" height="12" />
<img className="mx_RoomTileContextMenu_notif_activeIcon" src={require("../../../../res/img/notif-active.svg")} width="12" height="12" />
<img className="mx_RoomTileContextMenu_notif_icon mx_filterFlipColor" src={require("../../../../res/img/icon-context-mute.svg")} width="16" height="12" />
{ _t('Mute') }
</div>
</div>
@ -298,7 +298,7 @@ module.exports = React.createClass({
return (
<div>
<div className="mx_RoomTileContextMenu_leave" onClick={leaveClickHandler} >
<img className="mx_RoomTileContextMenu_tag_icon" src="img/icon_context_delete.svg" width="15" height="15" />
<img className="mx_RoomTileContextMenu_tag_icon" src={require("../../../../res/img/icon_context_delete.svg")} width="15" height="15" />
{ leaveText }
</div>
</div>
@ -327,18 +327,18 @@ module.exports = React.createClass({
return (
<div>
<div className={favouriteClasses} onClick={this._onClickFavourite} >
<img className="mx_RoomTileContextMenu_tag_icon" src="img/icon_context_fave.svg" width="15" height="15" />
<img className="mx_RoomTileContextMenu_tag_icon_set" src="img/icon_context_fave_on.svg" width="15" height="15" />
<img className="mx_RoomTileContextMenu_tag_icon" src={require("../../../../res/img/icon_context_fave.svg")} width="15" height="15" />
<img className="mx_RoomTileContextMenu_tag_icon_set" src={require("../../../../res/img/icon_context_fave_on.svg")} width="15" height="15" />
{ _t('Favourite') }
</div>
<div className={lowPriorityClasses} onClick={this._onClickLowPriority} >
<img className="mx_RoomTileContextMenu_tag_icon" src="img/icon_context_low.svg" width="15" height="15" />
<img className="mx_RoomTileContextMenu_tag_icon_set" src="img/icon_context_low_on.svg" width="15" height="15" />
<img className="mx_RoomTileContextMenu_tag_icon" src={require("../../../../res/img/icon_context_low.svg")} width="15" height="15" />
<img className="mx_RoomTileContextMenu_tag_icon_set" src={require("../../../../res/img/icon_context_low_on.svg")} width="15" height="15" />
{ _t('Low Priority') }
</div>
<div className={dmClasses} onClick={this._onClickDM} >
<img className="mx_RoomTileContextMenu_tag_icon" src="img/icon_context_person.svg" width="15" height="15" />
<img className="mx_RoomTileContextMenu_tag_icon_set" src="img/icon_context_person_on.svg" width="15" height="15" />
<img className="mx_RoomTileContextMenu_tag_icon" src={require("../../../../res/img/icon_context_person.svg")} width="15" height="15" />
<img className="mx_RoomTileContextMenu_tag_icon_set" src={require("../../../../res/img/icon_context_person_on.svg")} width="15" height="15" />
{ _t('Direct Chat') }
</div>
</div>

View file

@ -59,7 +59,7 @@ export default class TagTileContextMenu extends React.Component {
<div className="mx_TagTileContextMenu_item" onClick={this._onViewCommunityClick} >
<TintableSvg
className="mx_TagTileContextMenu_item_icon"
src="img/icons-groups.svg"
src={require("../../../../res/img/icons-groups.svg")}
width="15"
height="15"
/>
@ -67,7 +67,7 @@ export default class TagTileContextMenu extends React.Component {
</div>
<hr className="mx_TagTileContextMenu_separator" />
<div className="mx_TagTileContextMenu_item" onClick={this._onRemoveClick} >
<img className="mx_TagTileContextMenu_item_icon" src="img/icon_context_delete.svg" width="15" height="15" />
<img className="mx_TagTileContextMenu_item_icon" src={require("../../../../res/img/icon_context_delete.svg")} width="15" height="15" />
{ _t('Remove') }
</div>
</div>;

View file

@ -111,7 +111,7 @@ export default React.createClass({
let cancelButton;
if (this.props.hasCancel) {
cancelButton = <AccessibleButton onClick={this._onCancelClick} className="mx_Dialog_cancelButton">
<TintableSvg src="img/icons-close-button.svg" width="35" height="35" />
<TintableSvg src={require("../../../../res/img/icons-close-button.svg")} width="35" height="35" />
</AccessibleButton>;
}

View file

@ -127,7 +127,7 @@ export default class ChatCreateOrReuseDialog extends React.Component {
onClick={this.props.onNewDMClick}
>
<div className="mx_RoomTile_avatar">
<img src="img/create-big.svg" width="26" height="26" />
<img src={require("../../../../res/img/create-big.svg")} width="26" height="26" />
</div>
<div className={labelClasses}><i>{ _t("Start new chat") }</i></div>
</AccessibleButton>;

View file

@ -26,11 +26,11 @@ import * as ContextualMenu from "../../structures/ContextualMenu";
const socials = [
{
name: 'Facebook',
img: 'img/social/facebook.png',
img: require("../../../../res/img/social/facebook.png"),
url: (url) => `https://www.facebook.com/sharer/sharer.php?u=${url}`,
}, {
name: 'Twitter',
img: 'img/social/twitter-2.png',
img: require("../../../../res/img/social/twitter-2.png"),
url: (url) => `https://twitter.com/home?status=${url}`,
}, /* // icon missing
name: 'Google Plus',
@ -38,15 +38,15 @@ const socials = [
url: (url) => `https://plus.google.com/share?url=${url}`,
},*/ {
name: 'LinkedIn',
img: 'img/social/linkedin.png',
img: require("../../../../res/img/social/linkedin.png"),
url: (url) => `https://www.linkedin.com/shareArticle?mini=true&url=${url}`,
}, {
name: 'Reddit',
img: 'img/social/reddit.png',
img: require("../../../../res/img/social/reddit.png"),
url: (url) => `http://www.reddit.com/submit?url=${url}`,
}, {
name: 'email',
img: 'img/social/email-1.png',
img: require("../../../../res/img/social/email-1.png"),
url: (url) => `mailto:?body=${url}`,
},
];
@ -202,7 +202,7 @@ export default class ShareDialog extends React.Component {
<div className="mx_ShareDialog_split">
<div className="mx_ShareDialog_qrcode_container">
<QRCode value={matrixToUrl} size={256} logoWidth={48} logo="img/matrix-m.svg" />
<QRCode value={matrixToUrl} size={256} logoWidth={48} logo={require("../../../../res/img/matrix-m.svg")} />
</div>
<div className="mx_ShareDialog_social_container">
{

View file

@ -0,0 +1,99 @@
/*
Copyright 2019 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.
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 PropTypes from 'prop-types';
import {Tab, TabbedView} from "../../structures/TabbedView";
import {_t, _td} from "../../../languageHandler";
import AccessibleButton from "../elements/AccessibleButton";
import GeneralSettingsTab from "../settings/tabs/GeneralSettingsTab";
import dis from '../../../dispatcher';
// TODO: Ditch this whole component
export class TempTab extends React.Component {
static propTypes = {
onClose: PropTypes.func.isRequired,
};
componentDidMount(): void {
dis.dispatch({action: "view_old_user_settings"});
this.props.onClose();
}
render() {
return <div>Hello World</div>;
}
}
export default class UserSettingsDialog extends React.Component {
static propTypes = {
onFinished: PropTypes.func.isRequired,
};
_getTabs() {
return [
new Tab(
_td("General"),
"mx_UserSettingsDialog_settingsIcon",
<GeneralSettingsTab />,
),
new Tab(
_td("Notifications"),
"mx_UserSettingsDialog_bellIcon",
<div>Notifications Test</div>,
),
new Tab(
_td("Preferences"),
"mx_UserSettingsDialog_preferencesIcon",
<div>Preferences Test</div>,
),
new Tab(
_td("Voice & Video"),
"mx_UserSettingsDialog_voiceIcon",
<div>Voice Test</div>,
),
new Tab(
_td("Security & Privacy"),
"mx_UserSettingsDialog_securityIcon",
<div>Security Test</div>,
),
new Tab(
_td("Help & About"),
"mx_UserSettingsDialog_helpIcon",
<div>Help Test</div>,
),
new Tab(
_td("Visit old settings"),
"mx_UserSettingsDialog_helpIcon",
<TempTab onClose={this.props.onFinished} />,
),
];
}
render() {
return (
<div className="mx_UserSettingsDialog">
<div className="mx_UserSettingsDialog_header">
{_t("Settings")}
<span className="mx_UserSettingsDialog_close">
<AccessibleButton className="mx_UserSettingsDialog_closeIcon" onClick={this.props.onFinished} />
</span>
</div>
<TabbedView tabs={this._getTabs()} />
</div>
);
}
}

View file

@ -1,5 +1,5 @@
/*
Copyright 2018 New Vector Ltd
Copyright 2018, 2019 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.
@ -184,6 +184,14 @@ export default React.createClass({
} else if (this.state.backupInfo === null) {
title = _t("Error");
content = _t("No backup found!");
} else if (this.state.recoverInfo && this.state.recoverInfo.imported === 0) {
title = _t("Error Restoring Backup");
content = <div>
<p>{_t(
"Backup could not be decrypted with this key: " +
"please verify that you entered the correct recovery key.",
)}</p>
</div>;
} else if (this.state.recoverInfo) {
title = _t("Backup Restored");
let failedToDecrypt;

View file

@ -18,7 +18,7 @@ import React from 'react';
import MatrixClientPeg from '../../../MatrixClientPeg';
import {instanceForInstanceId} from '../../../utils/DirectoryUtils';
const DEFAULT_ICON_URL = "img/network-matrix.svg";
const DEFAULT_ICON_URL = require("../../../../res/img/network-matrix.svg");
export default class NetworkDropdown extends React.Component {
constructor(props) {
@ -191,7 +191,7 @@ export default class NetworkDropdown extends React.Component {
} else if (!instance) {
key = server + '_all';
name = 'Matrix';
icon = <img src="img/network-matrix.svg" />;
icon = <img src={require("../../../../res/img/network-matrix.svg")} />;
span_class = 'mx_NetworkDropdown_menu_network';
} else {
key = server + '_inst_' + instance.instance_id;

View file

@ -28,41 +28,56 @@ import { KeyCode } from '../../../Keyboard';
* @returns {Object} rendered react
*/
export default function AccessibleButton(props) {
const {element, onClick, children, ...restProps} = props;
restProps.onClick = onClick;
// We need to consume enter onKeyDown and space onKeyUp
// otherwise we are risking also activating other keyboard focusable elements
// that might receive focus as a result of the AccessibleButtonClick action
// It's because we are using html buttons at a few places e.g. inside dialogs
// And divs which we report as role button to assistive technologies.
// Browsers handle space and enter keypresses differently and we are only adjusting to the
// inconsistencies here
restProps.onKeyDown = function(e) {
if (e.keyCode === KeyCode.ENTER) {
e.stopPropagation();
e.preventDefault();
return onClick(e);
}
if (e.keyCode === KeyCode.SPACE) {
e.stopPropagation();
e.preventDefault();
}
};
restProps.onKeyUp = function(e) {
if (e.keyCode === KeyCode.SPACE) {
e.stopPropagation();
e.preventDefault();
return onClick(e);
}
if (e.keyCode === KeyCode.ENTER) {
e.stopPropagation();
e.preventDefault();
}
};
const {element, onClick, children, kind, disabled, ...restProps} = props;
if (!disabled) {
restProps.onClick = onClick;
// We need to consume enter onKeyDown and space onKeyUp
// otherwise we are risking also activating other keyboard focusable elements
// that might receive focus as a result of the AccessibleButtonClick action
// It's because we are using html buttons at a few places e.g. inside dialogs
// And divs which we report as role button to assistive technologies.
// Browsers handle space and enter keypresses differently and we are only adjusting to the
// inconsistencies here
restProps.onKeyDown = function(e) {
if (e.keyCode === KeyCode.ENTER) {
e.stopPropagation();
e.preventDefault();
return onClick(e);
}
if (e.keyCode === KeyCode.SPACE) {
e.stopPropagation();
e.preventDefault();
}
};
restProps.onKeyUp = function(e) {
if (e.keyCode === KeyCode.SPACE) {
e.stopPropagation();
e.preventDefault();
return onClick(e);
}
if (e.keyCode === KeyCode.ENTER) {
e.stopPropagation();
e.preventDefault();
}
};
}
restProps.tabIndex = restProps.tabIndex || "0";
restProps.role = "button";
restProps.className = (restProps.className ? restProps.className + " " : "") +
"mx_AccessibleButton";
if (kind) {
// We apply a hasKind class to maintain backwards compatibility with
// buttons which might not know about kind and break
restProps.className += " mx_AccessibleButton_hasKind mx_AccessibleButton_kind_" + kind;
}
if (disabled) {
restProps.className += " mx_AccessibleButton_disabled";
}
return React.createElement(element, restProps, children);
}
@ -76,6 +91,12 @@ AccessibleButton.propTypes = {
children: PropTypes.node,
element: PropTypes.string,
onClick: PropTypes.func.isRequired,
// The kind of button, similar to how Bootstrap works.
// See available classes for AccessibleButton for options.
kind: PropTypes.string,
disabled: PropTypes.bool,
};
AccessibleButton.defaultProps = {

View file

@ -150,7 +150,7 @@ export default React.createClass({
showAddress={this.props.showAddress}
justified={true}
networkName="vector"
networkUrl="img/search-icon-vector.svg"
networkUrl={require("../../../../res/img/search-icon-vector.svg")}
/>
</div>,
);

View file

@ -54,7 +54,7 @@ export default React.createClass({
address.avatarMxc, 25, 25, 'crop',
));
} else if (address.addressType === 'email') {
imgUrls.push('img/icon-email-user.svg');
imgUrls.push(require("../../../../res/img/icon-email-user.svg"));
}
// Removing networks for now as they're not really supported
@ -141,7 +141,7 @@ export default React.createClass({
if (this.props.canDismiss) {
dismiss = (
<div className="mx_AddressTile_dismiss" onClick={this.props.onDismissed} >
<TintableSvg src="img/icon-address-delete.svg" width="9" height="9" />
<TintableSvg src={require("../../../../res/img/icon-address-delete.svg")} width="9" height="9" />
</div>
);
}

View file

@ -47,7 +47,7 @@ export default class AppPermission extends React.Component {
return (
<div className='mx_AppPermissionWarning'>
<div className='mx_AppPermissionWarningImage'>
<img src='img/warning.svg' alt={_t('Warning!')} />
<img src={require("../../../../res/img/warning.svg")} alt={_t('Warning!')} />
</div>
<div className='mx_AppPermissionWarningText'>
<span className='mx_AppPermissionWarningTextLabel'>{ _t('Do you want to load widget from URL:') }</span> <span className='mx_AppPermissionWarningTextURL'>{ this.state.curlBase }</span>

View file

@ -582,19 +582,21 @@ export default class AppTile extends React.Component {
// editing is done in scalar
const showEditButton = Boolean(this._scalarClient && this._canUserModify());
const deleteWidgetLabel = this._deleteWidgetLabel();
let deleteIcon = 'img/cancel_green.svg';
let deleteIcon = require("../../../../res/img/cancel_green.svg");
let deleteClasses = 'mx_AppTileMenuBarWidget';
if (this._canUserModify()) {
deleteIcon = 'img/icon-delete-pink.svg';
deleteIcon = require("../../../../res/img/icon-delete-pink.svg");
deleteClasses += ' mx_AppTileMenuBarWidgetDelete';
}
// Picture snapshot - only show button when apps are maximised.
const showPictureSnapshotButton = this._hasCapability('m.capability.screenshot') && this.props.show;
const showPictureSnapshotIcon = 'img/camera_green.svg';
const popoutWidgetIcon = 'img/button-new-window.svg';
const reloadWidgetIcon = 'img/button-refresh.svg';
const windowStateIcon = (this.props.show ? 'img/minimize.svg' : 'img/maximize.svg');
const showPictureSnapshotIcon = require("../../../../res/img/camera_green.svg");
const popoutWidgetIcon = require("../../../../res/img/button-new-window.svg");
const reloadWidgetIcon = require("../../../../res/img/button-refresh.svg");
const minimizeIcon = require("../../../../res/img/minimize.svg");
const maximizeIcon = require("../../../../res/img/maximize.svg");
const windowStateIcon = (this.props.show ? minimizeIcon : maximizeIcon);
let appTileClass;
if (this.props.miniMode) {
@ -653,7 +655,7 @@ export default class AppTile extends React.Component {
{ /* Edit widget */ }
{ showEditButton && <TintableSvgButton
src="img/edit_green.svg"
src={require("../../../../res/img/edit_green.svg")}
className={"mx_AppTileMenuBarWidget " +
(this.props.showDelete ? "mx_AppTileMenuBarWidgetPadding" : "")}
title={_t('Edit')}

View file

@ -5,7 +5,7 @@ const AppWarning = (props) => {
return (
<div className='mx_AppPermissionWarning'>
<div className='mx_AppPermissionWarningImage'>
<img src='img/warning.svg' alt='' />
<img src={require("../../../../res/img/warning.svg")} alt='' />
</div>
<div className='mx_AppPermissionWarningText'>
<span className='mx_AppPermissionWarningTextLabel'>{ props.errorMsg }</span>

View file

@ -25,7 +25,7 @@ const CreateRoomButton = function(props) {
<ActionButton action="view_create_room"
mouseOverAction={props.callout ? "callout_create_room" : null}
label={_t("Create new room")}
iconPath="img/icons-create-room.svg"
iconPath={require("../../../../res/img/icons-create-room.svg")}
size={props.size}
tooltip={props.tooltip}
/>

View file

@ -62,13 +62,13 @@ const EditableItem = React.createClass({
{ this.props.onAdd ?
<div className="mx_EditableItem_addButton">
<img className="mx_filterFlipColor"
src="img/plus.svg" width="14" height="14"
src={require("../../../../res/img/plus.svg")} width="14" height="14"
alt={_t("Add")} onClick={this.onAdd} />
</div>
:
<div className="mx_EditableItem_removeButton">
<img className="mx_filterFlipColor"
src="img/cancel-small.svg" width="14" height="14"
src={require("../../../../res/img/cancel-small.svg")} width="14" height="14"
alt={_t("Delete")} onClick={this.onRemove} />
</div>
}

View file

@ -0,0 +1,54 @@
/*
Copyright 2019 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.
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 PropTypes from 'prop-types';
export default class Field extends React.PureComponent {
static propTypes = {
// The field's ID, which binds the input and label together.
id: PropTypes.string.isRequired,
// The field's <input> type. Defaults to "text".
type: PropTypes.string,
// The field's label string.
label: PropTypes.string,
// The field's placeholder string.
placeholder: PropTypes.string,
// The type of field to create. Defaults to "input". Should be "input" or "select".
// To define options for a select, use <Field><option ... /></Field>
element: PropTypes.string,
// All other props pass through to the <input>.
}
render() {
const extraProps = Object.assign({}, this.props);
// Remove explicit properties that shouldn't be copied
delete extraProps.element;
delete extraProps.children;
// Set some defaults for the element
extraProps.type = extraProps.type || "text";
const element = this.props.element || "input";
const fieldInput = React.createElement(element, extraProps, this.props.children);
return <div className="mx_Field">
{fieldInput}
<label htmlFor={this.props.id}>{this.props.label}</label>
</div>;
}
}

View file

@ -24,7 +24,7 @@ const HomeButton = function(props) {
return (
<ActionButton action="view_home_page"
label={_t("Home")}
iconPath="img/icons-home.svg"
iconPath={require("../../../../res/img/icons-home.svg")}
size={props.size}
tooltip={props.tooltip}
/>

View file

@ -176,7 +176,7 @@ module.exports = React.createClass({
<img src={this.props.src} style={style} />
<div className="mx_ImageView_labelWrapper">
<div className="mx_ImageView_label">
<AccessibleButton className="mx_ImageView_cancel" onClick={ this.props.onFinished }><img src="img/cancel-white.svg" width="18" height="18" alt={ _t('Close') } /></AccessibleButton>
<AccessibleButton className="mx_ImageView_cancel" onClick={ this.props.onFinished }><img src={require("../../../../res/img/cancel-white.svg")} width="18" height="18" alt={ _t('Close') } /></AccessibleButton>
<div className="mx_ImageView_shim">
</div>
<div className="mx_ImageView_name">

View file

@ -26,7 +26,7 @@ module.exports = React.createClass({
return (
<div className="mx_InlineSpinner">
<img src="img/spinner.gif" width={w} height={h} className={imgClass} />
<img src={require("../../../../res/img/spinner.gif")} width={w} height={h} className={imgClass} />
</div>
);
},

View file

@ -80,7 +80,11 @@ export default class ManageIntegsButton extends React.Component {
});
if (this.state.scalarError && !this.scalarClient.hasCredentials()) {
integrationsWarningTriangle = <img src="img/warning.svg" title={_t('Integrations Error')} width="17" />;
integrationsWarningTriangle = <img
src={require("../../../../res/img/warning.svg")}
title={_t('Integrations Error')}
width="17"
/>;
// Popup shown when hovering over integrationsButton_error (via CSS)
integrationsErrorPopup = (
<span className="mx_RoomSettings_integrationsButton_errorPopup">
@ -91,7 +95,7 @@ export default class ManageIntegsButton extends React.Component {
integrationsButton = (
<AccessibleButton className={integrationsButtonClasses} onClick={this.onManageIntegrations} title={_t('Manage Integrations')}>
<TintableSvg src="img/feather-icons/grid.svg" width="20" height="20" />
<TintableSvg src={require("../../../../res/img/feather-icons/grid.svg")} width="20" height="20" />
{ integrationsWarningTriangle }
{ integrationsErrorPopup }
</AccessibleButton>

View file

@ -27,7 +27,7 @@ module.exports = React.createClass({
return (
<div className="mx_Spinner">
<div className="mx_Spinner_Msg">{ msg }</div>&nbsp;
<img src="img/spinner.gif" width={w} height={h} className={imgClass} />
<img src={require("../../../../res/img/spinner.gif")} width={w} height={h} className={imgClass} />
</div>
);
},

View file

@ -25,7 +25,7 @@ const RoomDirectoryButton = function(props) {
<ActionButton action="view_room_directory"
mouseOverAction={props.callout ? "callout_room_directory" : null}
label={_t("Room directory")}
iconPath="img/icons-directory.svg"
iconPath={require("../../../../res/img/icons-directory.svg")}
size={props.size}
tooltip={props.tooltip}
/>

View file

@ -24,7 +24,7 @@ const SettingsButton = function(props) {
return (
<ActionButton action="view_user_settings"
label={_t("Settings")}
iconPath="img/icons-settings.svg"
iconPath={require("../../../../res/img/icons-settings.svg")}
size={props.size}
tooltip={props.tooltip}
/>

View file

@ -27,7 +27,7 @@ module.exports = React.createClass({
const imgClass = this.props.imgClassName || "";
return (
<div className="mx_Spinner">
<img src="img/spinner.gif" width={w} height={h} className={imgClass} />
<img src={require("../../../../res/img/spinner.gif")} width={w} height={h} className={imgClass} />
</div>
);
},

View file

@ -25,7 +25,7 @@ const StartChatButton = function(props) {
<ActionButton action="view_create_chat"
mouseOverAction={props.callout ? "callout_start_chat" : null}
label={_t("Start chat")}
iconPath="img/icons-people.svg"
iconPath={require("../../../../res/img/icons-people.svg")}
size={props.size}
tooltip={props.tooltip}
/>

View file

@ -51,7 +51,7 @@ export default class CookieBar extends React.Component {
const toolbarClasses = "mx_MatrixToolbar";
return (
<div className={toolbarClasses}>
<img className="mx_MatrixToolbar_warning" src="img/warning.svg" width="24" height="23" alt="" />
<img className="mx_MatrixToolbar_warning" src={require("../../../../res/img/warning.svg")} width="24" height="23" alt="" />
<div className="mx_MatrixToolbar_content">
{ this.props.policyUrl ? _t(
"Please help improve Riot.im by sending <UsageDataLink>anonymous usage data</UsageDataLink>. " +
@ -95,7 +95,7 @@ export default class CookieBar extends React.Component {
{ _t("Yes, I want to help!") }
</AccessibleButton>
<AccessibleButton className="mx_MatrixToolbar_close" onClick={this.onReject}>
<img src="img/cancel.svg" width="18" height="18" alt={_t('Close')} />
<img src={require("../../../../res/img/cancel.svg")} width="18" height="18" alt={_t('Close')} />
</AccessibleButton>
</div>
);

View file

@ -35,11 +35,11 @@ module.exports = React.createClass({
render: function() {
return (
<div className="mx_MatrixToolbar">
<img className="mx_MatrixToolbar_warning" src="img/warning.svg" width="24" height="23" />
<img className="mx_MatrixToolbar_warning" src={require("../../../../res/img/warning.svg")} width="24" height="23" />
<div className="mx_MatrixToolbar_content">
{ _t('You are not receiving desktop notifications') } <a className="mx_MatrixToolbar_link" onClick={ this.onClick }> { _t('Enable them now') }</a>
</div>
<AccessibleButton className="mx_MatrixToolbar_close" onClick={ this.hideToolbar } ><img src="img/cancel.svg" width="18" height="18" alt={_t('Close')} /></AccessibleButton>
<AccessibleButton className="mx_MatrixToolbar_close" onClick={ this.hideToolbar } ><img src={require("../../../../res/img/cancel.svg")} width="18" height="18" alt={_t('Close')} /></AccessibleButton>
</div>
);
},

View file

@ -96,7 +96,7 @@ export default React.createClass({
}
return (
<div className="mx_MatrixToolbar">
<img className="mx_MatrixToolbar_warning" src="img/warning.svg" width="24" height="23" />
<img className="mx_MatrixToolbar_warning" src={require("../../../../res/img/warning.svg")} width="24" height="23" />
<div className="mx_MatrixToolbar_content">
{_t("A new version of Riot is available.")}
</div>

View file

@ -31,7 +31,7 @@ export default React.createClass({
return (
<div className={toolbarClasses} onClick={this.onUpdateClicked}>
<img className="mx_MatrixToolbar_warning"
src="img/warning.svg"
src={require("../../../../res/img/warning.svg")}
width="24"
height="23"
alt=""

View file

@ -71,9 +71,9 @@ export default React.createClass({
let image;
if (doneStatuses.includes(this.props.status)) {
image = <img className="mx_MatrixToolbar_warning" src="img/warning.svg" width="24" height="23" alt="" />;
image = <img className="mx_MatrixToolbar_warning" src={require("../../../../res/img/warning.svg")} width="24" height="23" alt="" />;
} else {
image = <img className="mx_MatrixToolbar_warning" src="img/spinner.gif" width="24" height="23" alt="" />;
image = <img className="mx_MatrixToolbar_warning" src={require("../../../../res/img/spinner.gif")} width="24" height="23" alt="" />;
}
return (
@ -83,7 +83,7 @@ export default React.createClass({
{message}
</div>
<AccessibleButton className="mx_MatrixToolbar_close" onClick={this.hideToolbar}>
<img src="img/cancel.svg" width="18" height="18" alt={_t('Close')} />
<img src={require("../../../../res/img/cancel.svg")} width="18" height="18" alt={_t('Close')} />
</AccessibleButton>
</div>
);

View file

@ -188,7 +188,7 @@ module.exports = React.createClass({
<div className="mx_MemberInfo">
<GeminiScrollbarWrapper autoshow={true}>
<AccessibleButton className="mx_MemberInfo_cancel" onClick={this._onCancel}>
<img src="img/cancel.svg" width="18" height="18" className="mx_filterFlipColor" />
<img src={require("../../../../res/img/cancel.svg")} width="18" height="18" className="mx_filterFlipColor" />
</AccessibleButton>
<div className="mx_MemberInfo_avatar">
{ avatar }

View file

@ -87,7 +87,7 @@ export default React.createClass({
const text = _t("and %(count)s others...", { count: overflowCount });
return (
<EntityTile className="mx_EntityTile_ellipsis" avatarJsx={
<BaseAvatar url="img/ellipsis.svg" name="..." width={36} height={36} />
<BaseAvatar url={require("../../../../res/img/ellipsis.svg")} name="..." width={36} height={36} />
} name={text} presenceState="online" suppressOnHover={true}
onClick={this._showFullMemberList} />
);
@ -214,7 +214,7 @@ export default React.createClass({
onClick={this.onInviteToGroupButtonClick}
>
<div className="mx_RightPanel_icon" >
<TintableSvg src="img/icon-invite-people.svg" width="18" height="14" />
<TintableSvg src={require("../../../../res/img/icon-invite-people.svg")} width="18" height="14" />
</div>
<div className="mx_RightPanel_message">{ _t('Invite to this community') }</div>
</AccessibleButton>);

View file

@ -215,7 +215,7 @@ module.exports = React.createClass({
<div className="mx_MemberInfo">
<GeminiScrollbarWrapper autoshow={true}>
<AccessibleButton className="mx_MemberInfo_cancel" onClick={this._onCancel}>
<img src="img/cancel.svg" width="18" height="18" className="mx_filterFlipColor" />
<img src={require("../../../../res/img/cancel.svg")} width="18" height="18" className="mx_filterFlipColor" />
</AccessibleButton>
<div className="mx_MemberInfo_avatar">
{ avatar }

View file

@ -77,7 +77,7 @@ export default React.createClass({
const text = _t("and %(count)s others...", { count: overflowCount });
return (
<EntityTile className="mx_EntityTile_ellipsis" avatarJsx={
<BaseAvatar url="img/ellipsis.svg" name="..." width={36} height={36} />
<BaseAvatar url={require("../../../../res/img/ellipsis.svg")} name="..." width={36} height={36} />
} name={text} presenceState="online" suppressOnHover={true}
onClick={this._showFullRoomList} />
);
@ -137,7 +137,7 @@ export default React.createClass({
onClick={this.onAddRoomToGroupButtonClick}
>
<div className="mx_RightPanel_icon" >
<TintableSvg src="img/icons-room-add.svg" width="18" height="14" />
<TintableSvg src={require("../../../../res/img/icons-room-add.svg")} width="18" height="14" />
</div>
<div className="mx_RightPanel_message">{ _t('Add rooms to this community') }</div>
</AccessibleButton>

View file

@ -1,59 +0,0 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
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.
*/
'use strict';
import SettingsStore from "../../../settings/SettingsStore";
const React = require('react');
module.exports = React.createClass({
displayName: 'LoginPage',
render: function() {
// FIXME: this should be turned into a proper skin with a StatusLoginPage component
if (SettingsStore.getValue("theme") === 'status') {
return (
<div className="mx_StatusLogin">
<div className="mx_StatusLogin_brand">
<img src="themes/status/img/logo.svg" alt="Status" width="221" height="53" />
</div>
<div className="mx_StatusLogin_content">
<div className="mx_StatusLogin_header">
<h1>Status Community Chat</h1>
<div className="mx_StatusLogin_subtitle">
A safer, decentralised communication
platform <a href="https://riot.im">powered by Riot</a>
</div>
</div>
{ this.props.children }
<div className="mx_StatusLogin_footer">
<p>This channel is for our development community.</p>
<p>Interested in SNT and discussions on the cryptocurrency market?</p>
<p><a href="https://t.me/StatusNetworkChat" target="_blank" className="mx_StatusLogin_footer_cta">Join Telegram Chat</a></p>
</div>
</div>
</div>
);
} else {
return (
<div className="mx_Login">
{ this.props.children }
</div>
);
}
},
});

View file

@ -81,7 +81,7 @@ export default class MAudioBody extends React.Component {
if (this.state.error !== null) {
return (
<span className="mx_MAudioBody" ref="body">
<img src="img/warning.svg" width="16" height="16" />
<img src={require("../../../../res/img/warning.svg")} width="16" height="16" />
{ _t("Error decrypting audio") }
</span>
);
@ -94,7 +94,7 @@ export default class MAudioBody extends React.Component {
// Not sure how tall the audio player is so not sure how tall it should actually be.
return (
<span className="mx_MAudioBody">
<img src="img/spinner.gif" alt={content.body} width="16" height="16" />
<img src={require("../../../../res/img/spinner.gif")} alt={content.body} width="16" height="16" />
</span>
);
}

View file

@ -29,15 +29,15 @@ import request from 'browser-request';
import Modal from '../../../Modal';
// A cached tinted copy of "img/download.svg"
// A cached tinted copy of require("../../../../res/img/download.svg")
let tintedDownloadImageURL;
// Track a list of mounted MFileBody instances so that we can update
// the "img/download.svg" when the tint changes.
// the require("../../../../res/img/download.svg") when the tint changes.
let nextMountId = 0;
const mounts = {};
/**
* Updates the tinted copy of "img/download.svg" when the tint changes.
* Updates the tinted copy of require("../../../../res/img/download.svg") when the tint changes.
*/
function updateTintedDownloadImage() {
// Download the svg as an XML document.
@ -46,7 +46,7 @@ function updateTintedDownloadImage() {
// Also note that we can't use fetch here because fetch doesn't support
// file URLs, which the download image will be if we're running from
// the filesystem (like in an Electron wrapper).
request({uri: "img/download.svg"}, (err, response, body) => {
request({uri: require("../../../../res/img/download.svg")}, (err, response, body) => {
if (err) return;
const svg = new DOMParser().parseFromString(body, "image/svg+xml");
@ -254,7 +254,7 @@ module.exports = React.createClass({
},
tint: function() {
// Update our tinted copy of "img/download.svg"
// Update our tinted copy of require("../../../../res/img/download.svg")
if (this.refs.downloadImage) {
this.refs.downloadImage.src = tintedDownloadImageURL;
}

View file

@ -282,7 +282,12 @@ export default class MImageBody extends React.Component {
// e2e image hasn't been decrypted yet
if (content.file !== undefined && this.state.decryptedUrl === null) {
placeholder = <img src="img/spinner.gif" alt={content.body} width="32" height="32" />;
placeholder = <img
src={require("../../../../res/img/spinner.gif")}
alt={content.body}
width="32"
height="32"
/>;
} else if (!this.state.imgLoaded) {
// Deliberately, getSpinner is left unimplemented here, MStickerBody overides
placeholder = this.getPlaceholder();
@ -363,7 +368,7 @@ export default class MImageBody extends React.Component {
if (this.state.error !== null) {
return (
<span className="mx_MImageBody" ref="body">
<img src="img/warning.svg" width="16" height="16" />
<img src={require("../../../../res/img/warning.svg")} width="16" height="16" />
{ _t("Error decrypting image") }
</span>
);

View file

@ -35,7 +35,7 @@ export default class MStickerBody extends MImageBody {
// img onLoad hasn't fired yet.
getPlaceholder() {
const TintableSVG = sdk.getComponent('elements.TintableSvg');
return <TintableSVG src="img/icons-show-stickers.svg" width="75" height="75" />;
return <TintableSVG src={require("../../../../res/img/icons-show-stickers.svg")} width="75" height="75" />;
}
// Tooltip to show on mouse over

View file

@ -135,7 +135,7 @@ module.exports = React.createClass({
if (this.state.error !== null) {
return (
<span className="mx_MVideoBody" ref="body">
<img src="img/warning.svg" width="16" height="16" />
<img src={require("../../../../res/img/warning.svg")} width="16" height="16" />
{ _t("Error decrypting video") }
</span>
);
@ -148,7 +148,7 @@ module.exports = React.createClass({
return (
<span className="mx_MVideoBody" ref="body">
<div className="mx_MImageBody_thumbnail mx_MImageBody_thumbnail_spinner" ref="image">
<img src="img/spinner.gif" alt={content.body} width="16" height="16" />
<img src={require("../../../../res/img/spinner.gif")} alt={content.body} width="16" height="16" />
</div>
</span>
);

View file

@ -48,7 +48,7 @@ module.exports = React.createClass({
return <div />; // We should never have been instaniated in this case
}
return <div className="mx_CreateEvent">
<img className="mx_CreateEvent_image" src="img/room-continuation.svg" />
<img className="mx_CreateEvent_image" src={require("../../../../res/img/room-continuation.svg")} />
<div className="mx_CreateEvent_header">
{_t("This room is a continuation of another conversation.")}
</div>

View file

@ -65,12 +65,12 @@ export default class GroupHeaderButtons extends HeaderButtons {
];
return [
<HeaderButton key="_groupMembersButton" title={_t('Members')} iconSrc="img/icons-people.svg"
<HeaderButton key="_groupMembersButton" title={_t('Members')} iconSrc={require("../../../../res/img/icons-people.svg")}
isHighlighted={this.isPhase(groupPhases)}
clickPhase={RightPanel.Phase.GroupMemberList}
analytics={['Right Panel', 'Group Member List Button', 'click']}
/>,
<HeaderButton key="_roomsButton" title={_t('Rooms')} iconSrc="img/icons-room-nobg.svg"
<HeaderButton key="_roomsButton" title={_t('Rooms')} iconSrc={require("../../../../res/img/icons-room-nobg.svg")}
isHighlighted={this.isPhase(roomPhases)}
clickPhase={RightPanel.Phase.GroupRoomList}
analytics={['Right Panel', 'Group Room List Button', 'click']}

View file

@ -52,17 +52,17 @@ export default class RoomHeaderButtons extends HeaderButtons {
];
return [
<HeaderButton key="_membersButton" title={_t('Members')} iconSrc="img/feather-icons/user.svg"
<HeaderButton key="_membersButton" title={_t('Members')} iconSrc={require("../../../../res/img/feather-icons/user.svg")}
isHighlighted={this.isPhase(membersPhases)}
clickPhase={RightPanel.Phase.RoomMemberList}
analytics={['Right Panel', 'Member List Button', 'click']}
/>,
<HeaderButton key="_filesButton" title={_t('Files')} iconSrc="img/feather-icons/files.svg"
<HeaderButton key="_filesButton" title={_t('Files')} iconSrc={require("../../../../res/img/feather-icons/files.svg")}
isHighlighted={this.isPhase(RightPanel.Phase.FilePanel)}
clickPhase={RightPanel.Phase.FilePanel}
analytics={['Right Panel', 'File List Button', 'click']}
/>,
<HeaderButton key="_notifsButton" title={_t('Notifications')} iconSrc="img/feather-icons/notifications.svg"
<HeaderButton key="_notifsButton" title={_t('Notifications')} iconSrc={require("../../../../res/img/feather-icons/notifications.svg")}
isHighlighted={this.isPhase(RightPanel.Phase.NotificationPanel)}
clickPhase={RightPanel.Phase.NotificationPanel}
analytics={['Right Panel', 'Notification List Button', 'click']}

View file

@ -131,7 +131,7 @@ module.exports = React.createClass({
if (i === this.state.index) {
selected = (
<div className="mx_RoomSettings_roomColor_selected">
<img src="img/tick.svg" width="17" height="14" alt="./" />
<img src={require("../../../../res/img/tick.svg")} width="17" height="14" alt="./" />
</div>
);
}

View file

@ -147,7 +147,7 @@ module.exports = React.createClass({
<div className="mx_RoomView_fileDropTarget">
<div className="mx_RoomView_fileDropTargetLabel"
title={_t("Drop File Here")}>
<TintableSvg src="img/upload-big.svg" width="45" height="59" />
<TintableSvg src={require("../../../../res/img/upload-big.svg")} width="45" height="59" />
<br />
{ _t("Drop file here to upload") }
</div>

View file

@ -160,7 +160,7 @@ const EntityTile = React.createClass({
if (this.props.showInviteButton) {
inviteButton = (
<div className="mx_EntityTile_invite">
<img src="img/plus.svg" width="16" height="16" />
<img src={require("../../../../res/img/plus.svg")} width="16" height="16" />
</div>
);
}
@ -169,8 +169,8 @@ const EntityTile = React.createClass({
const powerStatus = this.props.powerStatus;
if (powerStatus) {
const src = {
[EntityTile.POWER_STATUS_MODERATOR]: "img/mod.svg",
[EntityTile.POWER_STATUS_ADMIN]: "img/admin.svg",
[EntityTile.POWER_STATUS_MODERATOR]: require("../../../../res/img/mod.svg"),
[EntityTile.POWER_STATUS_ADMIN]: require("../../../../res/img/admin.svg"),
}[powerStatus];
const alt = {
[EntityTile.POWER_STATUS_MODERATOR]: _t("Moderator"),

View file

@ -768,23 +768,31 @@ module.exports.haveTileForEvent = function(e) {
function E2ePadlockUndecryptable(props) {
return (
<E2ePadlock alt={_t("Undecryptable")}
src="img/e2e-blocked.svg" width="12" height="12"
src={require("../../../../res/img/e2e-blocked.svg")} width="12" height="12"
style={{ marginLeft: "-1px" }} {...props} />
);
}
function E2ePadlockEncrypting(props) {
return <E2ePadlock alt={_t("Encrypting")} src="img/e2e-encrypting.svg" width="10" height="12" {...props} />;
return (
<E2ePadlock alt={_t("Encrypting")}
src={require("../../../../res/img/e2e-encrypting.svg")} width="10" height="12"
{...props} />
);
}
function E2ePadlockNotSent(props) {
return <E2ePadlock alt={_t("Encrypted, not sent")} src="img/e2e-not_sent.svg" width="10" height="12" {...props} />;
return (
<E2ePadlock alt={_t("Encrypted, not sent")}
src={require("../../../../res/img/e2e-not_sent.svg")} width="10" height="12"
{...props} />
);
}
function E2ePadlockVerified(props) {
return (
<E2ePadlock alt={_t("Encrypted by a verified device")}
src="img/e2e-verified.svg" width="10" height="12"
src={require("../../../../res/img/e2e-verified.svg")} width="10" height="12"
{...props} />
);
}
@ -792,7 +800,7 @@ function E2ePadlockVerified(props) {
function E2ePadlockUnverified(props) {
return (
<E2ePadlock alt={_t("Encrypted by an unverified device")}
src="img/e2e-warning.svg" width="15" height="12"
src={require("../../../../res/img/e2e-warning.svg")} width="15" height="12"
style={{ marginLeft: "-2px" }} {...props} />
);
}
@ -800,7 +808,7 @@ function E2ePadlockUnverified(props) {
function E2ePadlockUnencrypted(props) {
return (
<E2ePadlock alt={_t("Unencrypted message")}
src="img/e2e-unencrypted.svg" width="12" height="12"
src={require("../../../../res/img/e2e-unencrypted.svg")} width="12" height="12"
{...props} />
);
}

View file

@ -0,0 +1,32 @@
/*
Copyright 2019 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.
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 { _t } from '../../../languageHandler';
import AccessibleButton from '../elements/AccessibleButton';
export default (props) => {
let badge;
if (props.numUnreadMessages) {
badge = (<div className="mx_JumpToBottomButton_badge">{props.numUnreadMessages}</div>);
}
return (<div className="mx_JumpToBottomButton">
<AccessibleButton className="mx_JumpToBottomButton_scrollDown"
title={_t("Scroll to bottom of page")}
onClick={props.onScrollToBottomClick}>
</AccessibleButton>
{ badge }
</div>);
};

View file

@ -135,7 +135,7 @@ module.exports = React.createClass({
</div>
</div>
<img className="mx_LinkPreviewWidget_cancel mx_filterFlipColor"
src="img/cancel.svg" width="18" height="18"
src={require("../../../../res/img/cancel.svg")} width="18" height="18"
onClick={this.props.onCancelClick} />
</div>
);

View file

@ -27,19 +27,19 @@ export default class MemberDeviceInfo extends React.Component {
if (this.props.device.isBlocked()) {
indicator = (
<div className="mx_MemberDeviceInfo_blacklisted">
<img src="img/e2e-blocked.svg" width="12" height="12" style={{ marginLeft: "-1px" }} alt={_t("Blacklisted")} />
<img src={require("../../../../res/img/e2e-blocked.svg")} width="12" height="12" style={{ marginLeft: "-1px" }} alt={_t("Blacklisted")} />
</div>
);
} else if (this.props.device.isVerified()) {
indicator = (
<div className="mx_MemberDeviceInfo_verified">
<img src="img/e2e-verified.svg" width="10" height="12" alt={_t("Verified")} />
<img src={require("../../../../res/img/e2e-verified.svg")} width="10" height="12" alt={_t("Verified")} />
</div>
);
} else {
indicator = (
<div className="mx_MemberDeviceInfo_unverified">
<img src="img/e2e-warning.svg" width="15" height="12" style={{ marginLeft: "-2px" }} alt={_t("Unverified")} />
<img src={require("../../../../res/img/e2e-warning.svg")} width="15" height="12" style={{ marginLeft: "-2px" }} alt={_t("Unverified")} />
</div>
);
}

View file

@ -815,7 +815,7 @@ module.exports = withMatrixClient(React.createClass({
onClick={this.onNewDMClick}
>
<div className="mx_RoomTile_avatar">
<img src="img/create-big.svg" width="26" height="26" />
<img src={require("../../../../res/img/create-big.svg")} width="26" height="26" />
</div>
<div className={labelClasses}><i>{ _t("Start a chat") }</i></div>
</AccessibleButton>;
@ -963,7 +963,7 @@ module.exports = withMatrixClient(React.createClass({
<div className="mx_MemberInfo">
<div className="mx_MemberInfo_name">
<AccessibleButton className="mx_MemberInfo_cancel" onClick={this.onCancel}>
<img src="img/minimise.svg" width="10" height="16" className="mx_filterFlipColor" alt={_t('Close')} />
<img src={require("../../../../res/img/minimise.svg")} width="10" height="16" className="mx_filterFlipColor" alt={_t('Close')} />
</AccessibleButton>
<EmojiText element="h2">{ memberName }</EmojiText>
</div>

View file

@ -253,7 +253,7 @@ module.exports = React.createClass({
const text = _t("and %(count)s others...", { count: overflowCount });
return (
<EntityTile className="mx_EntityTile_ellipsis" avatarJsx={
<BaseAvatar url="img/ellipsis.svg" name="..." width={36} height={36} />
<BaseAvatar url={require("../../../../res/img/ellipsis.svg")} name="..." width={36} height={36} />
} name={text} presenceState="online" suppressOnHover={true}
onClick={onClick} />
);

View file

@ -155,12 +155,12 @@ export default class MessageComposer extends React.Component {
const fileAcceptedOrError = this.props.uploadAllowed(files[i]);
if (fileAcceptedOrError === true) {
acceptedFiles.push(<li key={i}>
<TintableSvg key={i} src="img/files.svg" width="16" height="16" /> { files[i].name || _t('Attachment') }
<TintableSvg key={i} src={require("../../../../res/img/files.svg")} width="16" height="16" /> { files[i].name || _t('Attachment') }
</li>);
fileList.push(files[i]);
} else {
failedFiles.push(<li key={i}>
<TintableSvg key={i} src="img/files.svg" width="16" height="16" /> { files[i].name || _t('Attachment') } <p>{ _t('Reason') + ": " + fileAcceptedOrError}</p>
<TintableSvg key={i} src={require("../../../../res/img/files.svg")} width="16" height="16" /> { files[i].name || _t('Attachment') } <p>{ _t('Reason') + ": " + fileAcceptedOrError}</p>
</li>);
}
}
@ -320,11 +320,11 @@ export default class MessageComposer extends React.Component {
const roomIsEncrypted = MatrixClientPeg.get().isRoomEncrypted(this.props.room.roomId);
if (roomIsEncrypted) {
// FIXME: show a /!\ if there are untrusted devices in the room...
e2eImg = 'img/e2e-verified.svg';
e2eImg = require("../../../../res/img/e2e-verified.svg");
e2eTitle = _t('Encrypted room');
e2eClass = 'mx_MessageComposer_e2eIcon';
} else {
e2eImg = 'img/e2e-unencrypted.svg';
e2eImg = require("../../../../res/img/e2e-unencrypted.svg");
e2eTitle = _t('Unencrypted room');
e2eClass = 'mx_MessageComposer_e2eIcon mx_filterFlipColor';
}
@ -344,16 +344,16 @@ export default class MessageComposer extends React.Component {
if (this.props.callState && this.props.callState !== 'ended') {
hangupButton =
<AccessibleButton key="controls_hangup" className="mx_MessageComposer_hangup" onClick={this.onHangupClick}>
<img src="img/hangup.svg" alt={_t('Hangup')} title={_t('Hangup')} width="25" height="25" />
<img src={require("../../../../res/img/hangup.svg")} alt={_t('Hangup')} title={_t('Hangup')} width="25" height="25" />
</AccessibleButton>;
} else {
callButton =
<AccessibleButton key="controls_call" className="mx_MessageComposer_voicecall" onClick={this.onVoiceCallClick} title={_t('Voice call')}>
<TintableSvg src="img/feather-icons/phone.svg" width="20" height="20" />
<TintableSvg src={require("../../../../res/img/feather-icons/phone.svg")} width="20" height="20" />
</AccessibleButton>;
videoCallButton =
<AccessibleButton key="controls_videocall" className="mx_MessageComposer_videocall" onClick={this.onCallClick} title={_t('Video call')}>
<TintableSvg src="img/feather-icons/video.svg" width="20" height="20" />
<TintableSvg src={require("../../../../res/img/feather-icons/video.svg")} width="20" height="20" />
</AccessibleButton>;
}
@ -395,7 +395,7 @@ export default class MessageComposer extends React.Component {
const uploadButton = (
<AccessibleButton key="controls_upload" className="mx_MessageComposer_upload"
onClick={this.onUploadClick} title={_t('Upload file')}>
<TintableSvg src="img/feather-icons/paperclip.svg" width="20" height="20" />
<TintableSvg src={require("../../../../res/img/feather-icons/paperclip.svg")} width="20" height="20" />
<input ref="uploadInput" type="file"
style={uploadInputStyle}
multiple
@ -407,7 +407,7 @@ export default class MessageComposer extends React.Component {
<AccessibleButton element="img" className="mx_MessageComposer_formatting"
alt={_t("Show Text Formatting Toolbar")}
title={_t("Show Text Formatting Toolbar")}
src="img/button-text-formatting.svg"
src={require("../../../../res/img/button-text-formatting.svg")}
onClick={this.onToggleFormattingClicked}
style={{visibility: this.state.showFormatting ? 'hidden' : 'visible'}}
key="controls_formatting" />
@ -451,7 +451,7 @@ export default class MessageComposer extends React.Component {
controls.push(<div className="mx_MessageComposer_replaced_wrapper">
<div className="mx_MessageComposer_replaced_valign">
<img className="mx_MessageComposer_roomReplaced_icon" src="img/room_replaced.svg" />
<img className="mx_MessageComposer_roomReplaced_icon" src={require("../../../../res/img/room_replaced.svg")} />
<span className="mx_MessageComposer_roomReplaced_header">
{_t("This room has been replaced and is no longer active.")}
</span><br />
@ -487,7 +487,7 @@ export default class MessageComposer extends React.Component {
title={_t(name)}
onMouseDown={onFormatButtonClicked}
key={name}
src={`img/button-text-${name}${suffix}.svg`}
src={require(`../../../../res/img/button-text-${name}${suffix}.svg`)}
height="17" />;
},
);
@ -500,11 +500,11 @@ export default class MessageComposer extends React.Component {
<img title={this.state.inputState.isRichTextEnabled ? _t("Turn Markdown on") : _t("Turn Markdown off")}
onMouseDown={this.onToggleMarkdownClicked}
className="mx_MessageComposer_formatbar_markdown mx_filterFlipColor"
src={`img/button-md-${!this.state.inputState.isRichTextEnabled}.png`} />
src={require(`../../../../res/img/button-md-${!this.state.inputState.isRichTextEnabled}.png`)} />
<AccessibleButton element="img" title={_t("Hide Text Formatting Toolbar")}
onClick={this.onToggleFormattingClicked}
className="mx_MessageComposer_formatbar_cancel mx_filterFlipColor"
src="img/icon-text-cancel.svg" />
src={require("../../../../res/img/icon-text-cancel.svg")} />
</div>
</div>;
}

View file

@ -1599,7 +1599,7 @@ export default class MessageComposerInput extends React.Component {
<img className="mx_MessageComposer_input_markdownIndicator mx_filterFlipColor"
onMouseDown={this.onMarkdownToggleClicked}
title={this.state.isRichTextEnabled ? _t("Markdown is disabled") : _t("Markdown is enabled")}
src={`img/button-md-${!this.state.isRichTextEnabled}.png`} />
src={require(`../../../../res/img/button-md-${!this.state.isRichTextEnabled}.png`)} />
<Editor ref={this._collectEditor}
dir="auto"
className="mx_MessageComposer_editor"

View file

@ -67,7 +67,7 @@ module.exports = React.createClass({
if (this._canUnpin()) {
unpinButton = (
<AccessibleButton onClick={this.onUnpinClicked} className="mx_PinnedEventTile_unpinButton">
<img src="img/cancel-red.svg" width="8" height="8" alt={_t('Unpin Message')} title={_t('Unpin Message')} />
<img src={require("../../../../res/img/cancel-red.svg")} width="8" height="8" alt={_t('Unpin Message')} title={_t('Unpin Message')} />
</AccessibleButton>
);
}

View file

@ -130,7 +130,7 @@ module.exports = React.createClass({
<div className="mx_PinnedEventsPanel">
<div className="mx_PinnedEventsPanel_body">
<AccessibleButton className="mx_PinnedEventsPanel_cancel" onClick={this.props.onCancelClick}>
<img className="mx_filterFlipColor" src="img/cancel.svg" width="18" height="18" />
<img className="mx_filterFlipColor" src={require("../../../../res/img/cancel.svg")} width="18" height="18" />
</AccessibleButton>
<h3 className="mx_PinnedEventsPanel_header">{ _t("Pinned Messages") }</h3>
{ tiles }

View file

@ -68,7 +68,7 @@ export default class ReplyPreview extends React.Component {
{ '💬 ' + _t('Replying') }
</EmojiText>
<div className="mx_ReplyPreview_header mx_ReplyPreview_cancel">
<img className="mx_filterFlipColor" src="img/cancel.svg" width="18" height="18"
<img className="mx_filterFlipColor" src={require("../../../../res/img/cancel.svg")} width="18" height="18"
onClick={cancelQuoting} />
</div>
<div className="mx_ReplyPreview_clear" />

View file

@ -312,14 +312,14 @@ module.exports = React.createClass({
</div>
<div className="mx_RoomHeader_avatarPicker_edit">
<label htmlFor="avatarInput" ref="file_label">
<img src="img/camera.svg"
<img src={require("../../../../res/img/camera.svg")}
alt={_t("Upload avatar")} title={_t("Upload avatar")}
width="17" height="15" />
</label>
<input id="avatarInput" type="file" onChange={this.onAvatarSelected} />
</div>
<div className="mx_RoomHeader_avatarPicker_remove" onClick={this.onAvatarRemoveClick}>
<img src="img/cancel.svg"
<img src={require("../../../../res/img/cancel.svg")}
className="mx_filterFlipColor"
width="10"
alt={_t("Remove avatar")}
@ -337,7 +337,7 @@ module.exports = React.createClass({
if (this.props.onSettingsClick) {
settingsButton =
<AccessibleButton className="mx_RoomHeader_button" onClick={this.props.onSettingsClick} title={_t("Settings")}>
<TintableSvg src="img/feather-icons/settings.svg" width="20" height="20" />
<TintableSvg src={require("../../../../res/img/feather-icons/settings.svg")} width="20" height="20" />
</AccessibleButton>;
}
@ -353,7 +353,7 @@ module.exports = React.createClass({
<AccessibleButton className="mx_RoomHeader_button mx_RoomHeader_pinnedButton"
onClick={this.props.onPinnedClick} title={_t("Pinned Messages")}>
{ pinsIndicator }
<TintableSvg src="img/icons-pin.svg" width="16" height="16" />
<TintableSvg src={require("../../../../res/img/icons-pin.svg")} width="16" height="16" />
</AccessibleButton>;
}
@ -361,7 +361,7 @@ module.exports = React.createClass({
// if (this.props.onLeaveClick) {
// leave_button =
// <div className="mx_RoomHeader_button" onClick={this.props.onLeaveClick} title="Leave room">
// <TintableSvg src="img/leave.svg" width="26" height="20"/>
// <TintableSvg src={require("../../../../res/img/leave.svg")} width="26" height="20"/>
// </div>;
// }
@ -369,7 +369,7 @@ module.exports = React.createClass({
if (this.props.onForgetClick) {
forgetButton =
<AccessibleButton className="mx_RoomHeader_button" onClick={this.props.onForgetClick} title={_t("Forget room")}>
<TintableSvg src="img/leave.svg" width="26" height="20" />
<TintableSvg src={require("../../../../res/img/leave.svg")} width="26" height="20" />
</AccessibleButton>;
}
@ -377,7 +377,7 @@ module.exports = React.createClass({
if (this.props.onSearchClick && this.props.inRoom) {
searchButton =
<AccessibleButton className="mx_RoomHeader_button" onClick={this.props.onSearchClick} title={_t("Search")}>
<TintableSvg src="img/feather-icons/search.svg" width="20" height="20" />
<TintableSvg src={require("../../../../res/img/feather-icons/search.svg")} width="20" height="20" />
</AccessibleButton>;
}
@ -385,7 +385,7 @@ module.exports = React.createClass({
if (this.props.inRoom) {
shareRoomButton =
<AccessibleButton className="mx_RoomHeader_button" onClick={this.onShareRoomClick} title={_t('Share room')}>
<TintableSvg src="img/feather-icons/share.svg" width="20" height="20" />
<TintableSvg src={require("../../../../res/img/feather-icons/share.svg")} width="20" height="20" />
</AccessibleButton>;
}

View file

@ -593,7 +593,7 @@ module.exports = React.createClass({
subListsProps = subListsProps.filter((props => {
const len = props.list.length + (props.extraTiles ? props.extraTiles.length : 0);
return len !== 0 || (props.onAddRoom && !this.props.searchFilter);
return len !== 0 || props.onAddRoom;
}));
return subListsProps.reduce((components, props, i) => {

View file

@ -124,7 +124,7 @@ module.exports = React.createClass({
emailMatchBlock =
<div className="mx_RoomPreviewBar_warning">
<div className="mx_RoomPreviewBar_warningIcon">
<img src="img/warning.svg" width="24" height="23" title= "/!\\" alt="/!\\" />
<img src={require("../../../../res/img/warning.svg")} width="24" height="23" title= "/!\\" alt="/!\\" />
</div>
<div className="mx_RoomPreviewBar_warningText">
{ _t("This invitation was sent to an email address which is not associated with this account:") }

View file

@ -616,7 +616,7 @@ module.exports = React.createClass({
<div>
<label>
<input type="checkbox" ref="encrypt" onClick={this.onEnableEncryptionClick} />
<img className="mx_RoomSettings_e2eIcon mx_filterFlipColor" src="img/e2e-unencrypted.svg" width="12" height="12" />
<img className="mx_RoomSettings_e2eIcon mx_filterFlipColor" src={require("../../../../res/img/e2e-unencrypted.svg")} width="12" height="12" />
{ _t('Enable encryption') } { _t('(warning: cannot be disabled again!)') }
</label>
{ settings }
@ -627,8 +627,8 @@ module.exports = React.createClass({
<div>
<label>
{ isEncrypted
? <img className="mx_RoomSettings_e2eIcon" src="img/e2e-verified.svg" width="10" height="12" />
: <img className="mx_RoomSettings_e2eIcon mx_filterFlipColor" src="img/e2e-unencrypted.svg" width="12" height="12" />
? <img className="mx_RoomSettings_e2eIcon" src={require("../../../../res/img/e2e-verified.svg")} width="10" height="12" />
: <img className="mx_RoomSettings_e2eIcon mx_filterFlipColor" src={require("../../../../res/img/e2e-unencrypted.svg")} width="12" height="12" />
}
{ isEncrypted ? _t("Encryption is enabled in this room") : _t("Encryption is not enabled in this room") }.
</label>

View file

@ -392,7 +392,13 @@ module.exports = React.createClass({
let dmIndicator;
if (this._isDirectMessageRoom(this.props.room.roomId)) {
dmIndicator = <img src="img/icon_person.svg" className="mx_RoomTile_dm" width="11" height="13" alt="dm" />;
dmIndicator = <img
src={require("../../../../res/img/icon_person.svg")}
className="mx_RoomTile_dm"
width="11"
height="13"
alt="dm"
/>;
}
return <AccessibleButton tabIndex="0"

View file

@ -120,7 +120,7 @@ const SearchableEntityList = React.createClass({
const text = _t("and %(count)s others...", { count: overflowCount });
return (
<EntityTile className="mx_EntityTile_ellipsis" avatarJsx={
<BaseAvatar url="img/ellipsis.svg" name="..." width={36} height={36} />
<BaseAvatar url={require("../../../../res/img/ellipsis.svg")} name="..." width={36} height={36} />
} name={text} presenceState="online" suppressOnHover={true}
onClick={this._showAll} />
);

View file

@ -26,7 +26,7 @@ export function CancelButton(props) {
return (
<AccessibleButton className='mx_RoomHeader_cancelButton' onClick={onClick}>
<img src="img/cancel.svg" className='mx_filterFlipColor'
<img src={require("../../../../res/img/cancel.svg")} className='mx_filterFlipColor'
width="18" height="18" alt={_t("Cancel")} />
</AccessibleButton>
);

View file

@ -152,7 +152,7 @@ export default class Stickerpicker extends React.Component {
className='mx_Stickers_contentPlaceholder'>
<p>{ _t("You don't currently have any stickerpacks enabled") }</p>
<p className='mx_Stickers_addLink'>{ _t("Add some now") }</p>
<img src='img/stickerpack-placeholder.png' alt="" />
<img src={require("../../../../res/img/stickerpack-placeholder.png")} alt="" />
</AccessibleButton>
);
}
@ -351,7 +351,7 @@ export default class Stickerpicker extends React.Component {
onClick={this._onHideStickersClick}
ref='target'
title={_t("Hide Stickers")}>
<TintableSvg src="img/feather-icons/face.svg" width="20" height="20" />
<TintableSvg src={require("../../../../res/img/feather-icons/face.svg")} width="20" height="20" />
</AccessibleButton>;
} else {
// Show show-stickers button
@ -362,7 +362,7 @@ export default class Stickerpicker extends React.Component {
className="mx_MessageComposer_stickers"
onClick={this._onShowStickersClick}
title={_t("Show Stickers")}>
<TintableSvg src="img/feather-icons/face.svg" width="20" height="20" />
<TintableSvg src={require("../../../../res/img/feather-icons/face.svg")} width="20" height="20" />
</AccessibleButton>;
}
return <div>

View file

@ -1,6 +1,7 @@
/*
Copyright 2016 OpenMarket Ltd
Copyright 2017 Vector Creations Ltd
Copyright 2019 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.

View file

@ -19,6 +19,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import sdk from '../../../index';
import WhoIsTyping from '../../../WhoIsTyping';
import Timer from '../../../utils/Timer';
import MatrixClientPeg from '../../../MatrixClientPeg';
import MemberAvatar from '../avatars/MemberAvatar';
@ -43,11 +44,18 @@ module.exports = React.createClass({
getInitialState: function() {
return {
usersTyping: WhoIsTyping.usersTypingApartFromMe(this.props.room),
// a map with userid => Timer to delay
// hiding the "x is typing" message for a
// user so hiding it can coincide
// with the sent message by the other side
// resulting in less timeline jumpiness
delayedStopTypingTimers: {},
};
},
componentWillMount: function() {
MatrixClientPeg.get().on("RoomMember.typing", this.onRoomMemberTyping);
MatrixClientPeg.get().on("Room.timeline", this.onRoomTimeline);
},
componentDidUpdate: function(_, prevState) {
@ -64,18 +72,90 @@ module.exports = React.createClass({
const client = MatrixClientPeg.get();
if (client) {
client.removeListener("RoomMember.typing", this.onRoomMemberTyping);
client.removeListener("Room.timeline", this.onRoomTimeline);
}
Object.values(this.state.delayedStopTypingTimers).forEach((t) => t.abort());
},
isVisible: function() {
return this.state.usersTyping.length !== 0 || Object.keys(this.state.delayedStopTypingTimers).length !== 0;
},
onRoomTimeline: function(event, room) {
if (room && room.roomId === this.props.room.roomId) {
const userId = event.getSender();
// remove user from usersTyping
const usersTyping = this.state.usersTyping.filter((m) => m.userId !== userId);
this.setState({usersTyping});
// abort timer if any
this._abortUserTimer(userId);
}
},
onRoomMemberTyping: function(ev, member) {
const usersTyping = WhoIsTyping.usersTypingApartFromMeAndIgnored(this.props.room);
this.setState({
usersTyping: WhoIsTyping.usersTypingApartFromMeAndIgnored(this.props.room),
delayedStopTypingTimers: this._updateDelayedStopTypingTimers(usersTyping),
usersTyping,
});
},
_renderTypingIndicatorAvatars: function(limit) {
let users = this.state.usersTyping;
_updateDelayedStopTypingTimers(usersTyping) {
const usersThatStoppedTyping = this.state.usersTyping.filter((a) => {
return !usersTyping.some((b) => a.userId === b.userId);
});
const usersThatStartedTyping = usersTyping.filter((a) => {
return !this.state.usersTyping.some((b) => a.userId === b.userId);
});
// abort all the timers for the users that started typing again
usersThatStartedTyping.forEach((m) => {
const timer = this.state.delayedStopTypingTimers[m.userId];
if (timer) {
timer.abort();
}
});
// prepare new delayedStopTypingTimers object to update state with
let delayedStopTypingTimers = Object.assign({}, this.state.delayedStopTypingTimers);
// remove members that started typing again
delayedStopTypingTimers = usersThatStartedTyping.reduce((delayedStopTypingTimers, m) => {
delete delayedStopTypingTimers[m.userId];
return delayedStopTypingTimers;
}, delayedStopTypingTimers);
// start timer for members that stopped typing
delayedStopTypingTimers = usersThatStoppedTyping.reduce((delayedStopTypingTimers, m) => {
if (!delayedStopTypingTimers[m.userId]) {
const timer = new Timer(5000);
delayedStopTypingTimers[m.userId] = timer;
timer.start();
timer.finished().then(
() => this._removeUserTimer(m.userId), // on elapsed
() => {/* aborted */},
);
}
return delayedStopTypingTimers;
}, delayedStopTypingTimers);
return delayedStopTypingTimers;
},
_abortUserTimer: function(userId) {
const timer = this.state.delayedStopTypingTimers[userId];
if (timer) {
timer.abort();
this._removeUserTimer(userId);
}
},
_removeUserTimer: function(userId) {
const timer = this.state.delayedStopTypingTimers[userId];
if (timer) {
const delayedStopTypingTimers = Object.assign({}, this.state.delayedStopTypingTimers);
delete delayedStopTypingTimers[userId];
this.setState({delayedStopTypingTimers});
}
},
_renderTypingIndicatorAvatars: function(users, limit) {
let othersCount = 0;
if (users.length > limit) {
othersCount = users.length - limit + 1;
@ -106,8 +186,19 @@ module.exports = React.createClass({
},
render: function() {
let usersTyping = this.state.usersTyping;
const stoppedUsersOnTimer = Object.keys(this.state.delayedStopTypingTimers)
.map((userId) => this.props.room.getMember(userId));
// append the users that have been reported not typing anymore
// but have a timeout timer running so they can disappear
// when a message comes in
usersTyping = usersTyping.concat(stoppedUsersOnTimer);
// sort them so the typing members don't change order when
// moved to delayedStopTypingTimers
usersTyping.sort((a, b) => a.name.localeCompare(b.name));
const typingString = WhoIsTyping.whoIsTypingString(
this.state.usersTyping,
usersTyping,
this.props.whoIsTypingLimit,
);
if (!typingString) {
@ -119,7 +210,7 @@ module.exports = React.createClass({
return (
<li className="mx_WhoIsTypingTile">
<div className="mx_WhoIsTypingTile_avatars">
{ this._renderTypingIndicatorAvatars(this.props.whoIsTypingLimit) }
{ this._renderTypingIndicatorAvatars(usersTyping, this.props.whoIsTypingLimit) }
</div>
<div className="mx_WhoIsTypingTile_label">
<EmojiText>{ typingString }</EmojiText>

View file

@ -141,7 +141,7 @@ export default withMatrixClient(React.createClass({
return <div />;
}
const CountryDropdown = sdk.getComponent('views.login.CountryDropdown');
const CountryDropdown = sdk.getComponent('views.auth.CountryDropdown');
// XXX: This CSS relies on the CSS surrounding it in UserSettings as its in
// a tabular format to align the submit buttons
return (
@ -166,7 +166,7 @@ export default withMatrixClient(React.createClass({
</div>
</div>
<div className="mx_UserSettings_threepidButton mx_filterFlipColor">
<input type="image" value={_t("Add")} src="img/plus.svg" width="14" height="14" />
<input type="image" value={_t("Add")} src={require("../../../../res/img/plus.svg")} width="14" height="14" />
</div>
</form>
);

View file

@ -234,7 +234,7 @@ export default class KeyBackupPanel extends React.PureComponent {
}
let verifyButton;
if (!sig.device || !sig.device.isVerified()) {
if (sig.device && !sig.device.isVerified()) {
verifyButton = <div><br /><AccessibleButton className="mx_UserSettings_button"
onClick={this._verifyDevice} data-sigindex={i}>
{ _t("Verify...") }

View file

@ -0,0 +1,95 @@
/*
Copyright 2019 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.
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 MatrixClientPeg from "../../../../MatrixClientPeg";
import Field from "../../elements/Field";
import AccessibleButton from "../../elements/AccessibleButton";
export default class GeneralSettingsTab extends React.Component {
constructor() {
super();
const client = MatrixClientPeg.get();
this.state = {
userId: client.getUserId(),
displayName: client.getUser(client.getUserId()).displayName,
enableProfileSave: false,
};
}
_saveProfile = async (e) => {
e.stopPropagation();
e.preventDefault();
if (!this.state.enableProfileSave) return;
this.setState({enableProfileSave: false});
// TODO: What do we do about errors?
await MatrixClientPeg.get().setDisplayName(this.state.displayName);
// TODO: Support avatars
this.setState({enableProfileSave: true});
};
_onDisplayNameChanged = (e) => {
this.setState({
displayName: e.target.value,
enableProfileSave: true,
});
};
_renderProfileSection() {
// TODO: Ditch avatar placeholder and use the real thing
const form = (
<form onSubmit={this._saveProfile} autoComplete={false} noValidate={true}>
<div className="mx_GeneralSettingsTab_profile">
<div className="mx_GeneralSettingsTab_profileControls">
<p className="mx_GeneralSettingsTab_profileUsername">{this.state.userId}</p>
<Field id="profileDisplayName" label={_t("Display Name")}
type="text" value={this.state.displayName} autocomplete="off"
onChange={this._onDisplayNameChanged} />
</div>
<div className="mx_GeneralSettingsTab_profileAvatar">
<div />
</div>
</div>
<AccessibleButton onClick={this._saveProfile} kind="primary"
disabled={!this.state.enableProfileSave}>
{_t("Save")}
</AccessibleButton>
</form>
);
return (
<div className="mx_SettingsTab_section">
<span className="mx_SettingsTab_subheading">{_t("Profile")}</span>
{form}
</div>
);
}
render() {
return (
<div className="mx_SettingsTab">
<div className="mx_SettingsTab_heading">{_t("General")}</div>
{this._renderProfileSection()}
</div>
);
}
}

View file

@ -66,7 +66,7 @@ module.exports = React.createClass({
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
return (
<div className="mx_IncomingCallBox" id="incomingCallBox">
<img className="mx_IncomingCallBox_chevron" src="img/chevron-left.png" width="9" height="16" />
<img className="mx_IncomingCallBox_chevron" src={require("../../../../res/img/chevron-left.png")} width="9" height="16" />
<div className="mx_IncomingCallBox_title">
{ incomingCallText }
</div>