Merge remote-tracking branch 'origin/develop' into travis/settings-cleanup-1

pull/21833/head
Travis Ralston 2019-02-25 09:40:50 -07:00
commit 4801b25f77
16 changed files with 111 additions and 93 deletions

View File

@ -249,12 +249,6 @@ textarea {
box-shadow: none; box-shadow: none;
} }
/* View Source Dialog overide */
.mx_Dialog_wrapper.mx_Dialog_viewsource .mx_Dialog {
padding-left: 10px;
padding-right: 10px;
}
.mx_Dialog { .mx_Dialog {
background-color: $primary-bg-color; background-color: $primary-bg-color;
color: $light-fg-color; color: $light-fg-color;

View File

@ -14,6 +14,19 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
.mx_ViewSource_label_left {
float: left;
}
.mx_ViewSource_label_right {
float: right;
}
.mx_ViewSource_label_bottom {
clear: both;
border-bottom: 1px solid #e5e5e5;
}
.mx_ViewSource pre { .mx_ViewSource pre {
text-align: left; text-align: left;
font-size: 12px; font-size: 12px;

View File

@ -40,6 +40,7 @@ limitations under the License.
} }
.mx_WhoIsTypingTile_remainingAvatarPlaceholder { .mx_WhoIsTypingTile_remainingAvatarPlaceholder {
position: relative;
display: inline-block; display: inline-block;
color: #acacac; color: #acacac;
background-color: #ddd; background-color: #ddd;

View File

@ -113,4 +113,29 @@ export default class BasePlatform {
reload() { reload() {
throw new Error("reload not implemented!"); throw new Error("reload not implemented!");
} }
supportsAutoLaunch(): boolean {
return false;
}
// XXX: Surely this should be a setting like any other?
async getAutoLaunchEnabled(): boolean {
return false;
}
async setAutoLaunchEnabled(enabled: boolean): void {
throw new Error("Unimplemented");
}
supportsMinimizeToTray(): boolean {
return false;
}
async getMinimizeToTrayEnabled(): boolean {
return false;
}
async setMinimizeToTrayEnabled(enabled: boolean): void {
throw new Error("Unimplemented");
}
} }

View File

@ -34,6 +34,7 @@ import GroupStore from '../../stores/GroupStore';
import FlairStore from '../../stores/FlairStore'; import FlairStore from '../../stores/FlairStore';
import { showGroupAddRoomDialog } from '../../GroupAddressPicker'; import { showGroupAddRoomDialog } from '../../GroupAddressPicker';
import {makeGroupPermalink, makeUserPermalink} from "../../matrix-to"; import {makeGroupPermalink, makeUserPermalink} from "../../matrix-to";
import {Group} from "matrix-js-sdk";
const LONG_DESC_PLACEHOLDER = _td( const LONG_DESC_PLACEHOLDER = _td(
`<h1>HTML for your community's page</h1> `<h1>HTML for your community's page</h1>
@ -569,7 +570,7 @@ export default React.createClass({
_onShareClick: function() { _onShareClick: function() {
const ShareDialog = sdk.getComponent("dialogs.ShareDialog"); const ShareDialog = sdk.getComponent("dialogs.ShareDialog");
Modal.createTrackedDialog('share community dialog', '', ShareDialog, { Modal.createTrackedDialog('share community dialog', '', ShareDialog, {
target: this._matrixClient.getGroup(this.props.groupId), target: this._matrixClient.getGroup(this.props.groupId) || new Group(this.props.groupId),
}); });
}, },

View File

@ -26,6 +26,7 @@ import dis from '../../dispatcher';
import VectorConferenceHandler from '../../VectorConferenceHandler'; import VectorConferenceHandler from '../../VectorConferenceHandler';
import TagPanelButtons from './TagPanelButtons'; import TagPanelButtons from './TagPanelButtons';
import SettingsStore from '../../settings/SettingsStore'; import SettingsStore from '../../settings/SettingsStore';
import {_t} from "../../languageHandler";
const LeftPanel = React.createClass({ const LeftPanel = React.createClass({
@ -212,6 +213,7 @@ const LeftPanel = React.createClass({
); );
const searchBox = (<SearchBox const searchBox = (<SearchBox
placeholder={ _t('Filter room names') }
onSearch={ this.onSearch } onSearch={ this.onSearch }
onCleared={ this.onSearchCleared } onCleared={ this.onSearchCleared }
collapsed={this.props.collapsed} />); collapsed={this.props.collapsed} />);

View File

@ -537,12 +537,12 @@ module.exports = React.createClass({
case 'picture_snapshot': case 'picture_snapshot':
this.uploadFile(payload.file); this.uploadFile(payload.file);
break; break;
case 'notifier_enabled':
case 'upload_failed': case 'upload_failed':
// 413: File was too big or upset the server in some way. // 413: File was too big or upset the server in some way.
if(payload.error.http_status === 413) { if (payload.error && payload.error.http_status === 413) {
this._fetchMediaConfig(true); this._fetchMediaConfig(true);
} }
case 'notifier_enabled':
case 'upload_started': case 'upload_started':
case 'upload_finished': case 'upload_finished':
this.forceUpdate(); this.forceUpdate();
@ -1305,7 +1305,10 @@ module.exports = React.createClass({
}, },
onSearchClick: function() { onSearchClick: function() {
this.setState({ searching: true, showingPinned: false }); this.setState({
searching: !this.state.searching,
showingPinned: false,
});
}, },
onCancelSearchClick: function() { onCancelSearchClick: function() {

View File

@ -1,5 +1,6 @@
/* /*
Copyright 2015, 2016 OpenMarket Ltd Copyright 2015, 2016 OpenMarket Ltd
Copyright 2019 Michael Telatynski <7t3chguy@gmail.com>
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -14,12 +15,9 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
'use strict';
import React from 'react'; import React from 'react';
import { _t } from '../../languageHandler'; import PropTypes from 'prop-types';
import { KeyCode } from '../../Keyboard'; import { KeyCode } from '../../Keyboard';
import sdk from '../../index';
import dis from '../../dispatcher'; import dis from '../../dispatcher';
import { throttle } from 'lodash'; import { throttle } from 'lodash';
import AccessibleButton from '../../components/views/elements/AccessibleButton'; import AccessibleButton from '../../components/views/elements/AccessibleButton';
@ -28,8 +26,10 @@ module.exports = React.createClass({
displayName: 'SearchBox', displayName: 'SearchBox',
propTypes: { propTypes: {
onSearch: React.PropTypes.func, onSearch: PropTypes.func,
onCleared: React.PropTypes.func, onCleared: PropTypes.func,
className: PropTypes.string,
placeholder: PropTypes.string.isRequired,
}, },
getInitialState: function() { getInitialState: function() {
@ -102,21 +102,22 @@ module.exports = React.createClass({
const clearButton = this.state.searchTerm.length > 0 ? const clearButton = this.state.searchTerm.length > 0 ?
(<AccessibleButton key="button" (<AccessibleButton key="button"
className="mx_SearchBox_closeButton" className="mx_SearchBox_closeButton"
onClick={ () => {this._clearSearch("button")} }> onClick={ () => {this._clearSearch("button"); } }>
</AccessibleButton>) : undefined; </AccessibleButton>) : undefined;
const className = this.props.className || "";
return ( return (
<div className="mx_SearchBox mx_textinput"> <div className="mx_SearchBox mx_textinput">
<input <input
key="searchfield" key="searchfield"
type="text" type="text"
ref="search" ref="search"
className="mx_textinput_icon mx_textinput_search" className={"mx_textinput_icon mx_textinput_search " + className}
value={ this.state.searchTerm } value={ this.state.searchTerm }
onFocus={ this._onFocus } onFocus={ this._onFocus }
onChange={ this.onChange } onChange={ this.onChange }
onKeyDown={ this._onKeyDown } onKeyDown={ this._onKeyDown }
placeholder={ _t('Filter room names') } placeholder={ this.props.placeholder }
/> />
{ clearButton } { clearButton }
</div> </div>

View File

@ -1,5 +1,6 @@
/* /*
Copyright 2015, 2016 OpenMarket Ltd Copyright 2015, 2016 OpenMarket Ltd
Copyright 2019 Michael Telatynski <7t3chguy@gmail.com>
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -14,11 +15,11 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
'use strict';
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import SyntaxHighlight from '../views/elements/SyntaxHighlight'; import SyntaxHighlight from '../views/elements/SyntaxHighlight';
import {_t} from "../../languageHandler";
import sdk from "../../index";
module.exports = React.createClass({ module.exports = React.createClass({
@ -27,31 +28,24 @@ module.exports = React.createClass({
propTypes: { propTypes: {
content: PropTypes.object.isRequired, content: PropTypes.object.isRequired,
onFinished: PropTypes.func.isRequired, onFinished: PropTypes.func.isRequired,
}, roomId: PropTypes.string.isRequired,
eventId: PropTypes.string.isRequired,
componentDidMount: function() {
document.addEventListener("keydown", this.onKeyDown);
},
componentWillUnmount: function() {
document.removeEventListener("keydown", this.onKeyDown);
},
onKeyDown: function(ev) {
if (ev.keyCode == 27) { // escape
ev.stopPropagation();
ev.preventDefault();
this.props.onFinished();
}
}, },
render: function() { render: function() {
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
return ( return (
<div className="mx_ViewSource"> <BaseDialog className="mx_ViewSource" onFinished={this.props.onFinished} title={_t('View Source')}>
<SyntaxHighlight className="json"> <div className="mx_ViewSource_label_left">Room ID: { this.props.roomId }</div>
{ JSON.stringify(this.props.content, null, 2) } <div className="mx_ViewSource_label_right">Event ID: { this.props.eventId }</div>
</SyntaxHighlight> <div className="mx_ViewSource_label_bottom" />
</div>
<div className="mx_Dialog_content">
<SyntaxHighlight className="json">
{ JSON.stringify(this.props.content, null, 2) }
</SyntaxHighlight>
</div>
</BaseDialog>
); );
}, },
}); });

View File

@ -66,7 +66,7 @@ module.exports = React.createClass({
} }
const scriptTag = document.createElement('script'); const scriptTag = document.createElement('script');
scriptTag.setAttribute( scriptTag.setAttribute(
'src', `${protocol}//www.google.com/recaptcha/api.js?onload=mx_on_recaptcha_loaded&render=explicit`, 'src', `${protocol}//www.recaptcha.net/recaptcha/api.js?onload=mx_on_recaptcha_loaded&render=explicit`,
); );
this.refs.recaptchaContainer.appendChild(scriptTag); this.refs.recaptchaContainer.appendChild(scriptTag);
} }

View File

@ -98,6 +98,8 @@ module.exports = React.createClass({
onViewSourceClick: function() { onViewSourceClick: function() {
const ViewSource = sdk.getComponent('structures.ViewSource'); const ViewSource = sdk.getComponent('structures.ViewSource');
Modal.createTrackedDialog('View Event Source', '', ViewSource, { Modal.createTrackedDialog('View Event Source', '', ViewSource, {
roomId: this.props.mxEvent.getRoomId(),
eventId: this.props.mxEvent.getId(),
content: this.props.mxEvent.event, content: this.props.mxEvent.event,
}, 'mx_Dialog_viewsource'); }, 'mx_Dialog_viewsource');
this.closeMenu(); this.closeMenu();
@ -106,6 +108,8 @@ module.exports = React.createClass({
onViewClearSourceClick: function() { onViewClearSourceClick: function() {
const ViewSource = sdk.getComponent('structures.ViewSource'); const ViewSource = sdk.getComponent('structures.ViewSource');
Modal.createTrackedDialog('View Clear Event Source', '', ViewSource, { Modal.createTrackedDialog('View Clear Event Source', '', ViewSource, {
roomId: this.props.mxEvent.getRoomId(),
eventId: this.props.mxEvent.getId(),
// FIXME: _clearEvent is private // FIXME: _clearEvent is private
content: this.props.mxEvent._clearEvent, content: this.props.mxEvent._clearEvent,
}, 'mx_Dialog_viewsource'); }, 'mx_Dialog_viewsource');
@ -211,7 +215,8 @@ module.exports = React.createClass({
}, },
render: function() { render: function() {
const eventStatus = this.props.mxEvent.status; const mxEvent = this.props.mxEvent;
const eventStatus = mxEvent.status;
let resendButton; let resendButton;
let redactButton; let redactButton;
let cancelButton; let cancelButton;
@ -251,8 +256,8 @@ module.exports = React.createClass({
); );
} }
if (isSent && this.props.mxEvent.getType() === 'm.room.message') { if (isSent && mxEvent.getType() === 'm.room.message') {
const content = this.props.mxEvent.getContent(); const content = mxEvent.getContent();
if (content.msgtype && content.msgtype !== 'm.bad.encrypted' && content.hasOwnProperty('body')) { if (content.msgtype && content.msgtype !== 'm.bad.encrypted' && content.hasOwnProperty('body')) {
forwardButton = ( forwardButton = (
<div className="mx_MessageContextMenu_field" onClick={this.onForwardClick}> <div className="mx_MessageContextMenu_field" onClick={this.onForwardClick}>
@ -282,7 +287,7 @@ module.exports = React.createClass({
</div> </div>
); );
if (this.props.mxEvent.getType() !== this.props.mxEvent.getWireType()) { if (mxEvent.getType() !== mxEvent.getWireType()) {
viewClearSourceButton = ( viewClearSourceButton = (
<div className="mx_MessageContextMenu_field" onClick={this.onViewClearSourceClick}> <div className="mx_MessageContextMenu_field" onClick={this.onViewClearSourceClick}>
{ _t('View Decrypted Source') } { _t('View Decrypted Source') }
@ -303,8 +308,11 @@ module.exports = React.createClass({
// XXX: if we use room ID, we should also include a server where the event can be found (other than in the domain of the event ID) // XXX: if we use room ID, we should also include a server where the event can be found (other than in the domain of the event ID)
const permalinkButton = ( const permalinkButton = (
<div className="mx_MessageContextMenu_field"> <div className="mx_MessageContextMenu_field">
<a href={makeEventPermalink(this.props.mxEvent.getRoomId(), this.props.mxEvent.getId())} <a href={makeEventPermalink(mxEvent.getRoomId(), mxEvent.getId())}
target="_blank" rel="noopener" onClick={this.onPermalinkClick}>{ _t('Share Message') }</a> target="_blank" rel="noopener" onClick={this.onPermalinkClick}>
{ mxEvent.isRedacted() || mxEvent.getType() !== 'm.room.message'
? _t('Share Permalink') : _t('Share Message') }
</a>
</div> </div>
); );
@ -318,12 +326,12 @@ module.exports = React.createClass({
// Bridges can provide a 'external_url' to link back to the source. // Bridges can provide a 'external_url' to link back to the source.
if ( if (
typeof(this.props.mxEvent.event.content.external_url) === "string" && typeof(mxEvent.event.content.external_url) === "string" &&
isUrlPermitted(this.props.mxEvent.event.content.external_url) isUrlPermitted(mxEvent.event.content.external_url)
) { ) {
externalURLButton = ( externalURLButton = (
<div className="mx_MessageContextMenu_field"> <div className="mx_MessageContextMenu_field">
<a href={this.props.mxEvent.event.content.external_url} <a href={mxEvent.event.content.external_url}
rel="noopener" target="_blank" onClick={this.closeMenu}>{ _t('Source URL') }</a> rel="noopener" target="_blank" onClick={this.closeMenu}>{ _t('Source URL') }</a>
</div> </div>
); );

View File

@ -321,6 +321,9 @@ module.exports = withMatrixClient(React.createClass({
const {tile, replyThread} = this.refs; const {tile, replyThread} = this.refs;
let e2eInfoCallback = null;
if (this.props.mxEvent.isEncrypted()) e2eInfoCallback = () => this.onCryptoClicked();
ContextualMenu.createMenu(MessageContextMenu, { ContextualMenu.createMenu(MessageContextMenu, {
chevronOffset: 10, chevronOffset: 10,
mxEvent: this.props.mxEvent, mxEvent: this.props.mxEvent,
@ -328,7 +331,7 @@ module.exports = withMatrixClient(React.createClass({
top: y, top: y,
eventTileOps: tile && tile.getEventTileOps ? tile.getEventTileOps() : undefined, eventTileOps: tile && tile.getEventTileOps ? tile.getEventTileOps() : undefined,
collapseReplyThread: replyThread && replyThread.canCollapse() ? replyThread.collapse : undefined, collapseReplyThread: replyThread && replyThread.canCollapse() ? replyThread.collapse : undefined,
e2eInfoCallback: () => this.onCryptoClicked(), e2eInfoCallback: e2eInfoCallback,
onFinished: function() { onFinished: function() {
self.setState({menu: false}); self.setState({menu: false});
}, },

View File

@ -339,12 +339,11 @@ module.exports = React.createClass({
return nameA.localeCompare(nameB); return nameA.localeCompare(nameB);
}, },
onSearchQueryChanged: function(ev) { onSearchQueryChanged: function(searchQuery) {
const q = ev.target.value;
this.setState({ this.setState({
searchQuery: q, searchQuery,
filteredJoinedMembers: this._filterMembers(this.state.members, 'join', q), filteredJoinedMembers: this._filterMembers(this.state.members, 'join', searchQuery),
filteredInvitedMembers: this._filterMembers(this.state.members, 'invite', q), filteredInvitedMembers: this._filterMembers(this.state.members, 'invite', searchQuery),
}); });
}, },
@ -438,6 +437,7 @@ module.exports = React.createClass({
return <div className="mx_MemberList"><Spinner /></div>; return <div className="mx_MemberList"><Spinner /></div>;
} }
const SearchBox = sdk.getComponent('structures.SearchBox');
const TruncatedList = sdk.getComponent("elements.TruncatedList"); const TruncatedList = sdk.getComponent("elements.TruncatedList");
const GeminiScrollbarWrapper = sdk.getComponent("elements.GeminiScrollbarWrapper"); const GeminiScrollbarWrapper = sdk.getComponent("elements.GeminiScrollbarWrapper");
@ -445,7 +445,6 @@ module.exports = React.createClass({
const room = cli.getRoom(this.props.roomId); const room = cli.getRoom(this.props.roomId);
let inviteButton; let inviteButton;
if (room && room.getMyMembership() === 'join') { if (room && room.getMyMembership() === 'join') {
const TintableSvg = sdk.getComponent("elements.TintableSvg");
const AccessibleButton = sdk.getComponent("elements.AccessibleButton"); const AccessibleButton = sdk.getComponent("elements.AccessibleButton");
inviteButton = inviteButton =
<AccessibleButton className="mx_MemberList_invite" onClick={this.onInviteButtonClick}> <AccessibleButton className="mx_MemberList_invite" onClick={this.onInviteButtonClick}>
@ -477,9 +476,10 @@ module.exports = React.createClass({
{ invitedSection } { invitedSection }
</div> </div>
</GeminiScrollbarWrapper> </GeminiScrollbarWrapper>
<input className="mx_MemberList_query mx_textinput_icon mx_textinput_search" id="mx_MemberList_query" type="text"
onChange={this.onSearchQueryChanged} value={this.state.searchQuery} <SearchBox className="mx_MemberList_query mx_textinput_icon mx_textinput_search"
placeholder={_t('Filter room members')} /> placeholder={ _t('Filter room members') }
onSearch={ this.onSearchQueryChanged } />
</div> </div>
); );
}, },

View File

@ -362,34 +362,6 @@ export default class MessageComposer extends React.Component {
const canSendMessages = !this.state.tombstone && const canSendMessages = !this.state.tombstone &&
this.props.room.maySendMessage(); this.props.room.maySendMessage();
// TODO: Remove temporary logging for riot-web#7838
// Note: we rip apart the power level event ourselves because we don't want to
// log too much data about it - just the bits we care about. Many of the variables
// logged here are to help figure out where in the stack the 'cannot post in room'
// warning is coming from. This means logging various numbers from the PL event to
// verify RoomState._maySendEventOfType is doing the right thing.
const room = this.props.room;
const plEvent = room.currentState.getStateEvents('m.room.power_levels', '');
let plEventString = "<no power level event>";
if (plEvent) {
const content = plEvent.getContent();
if (!content) {
plEventString = "<no event content>";
} else {
const stringifyFalsey = (v) => v === null ? '<null>' : (v === undefined ? '<undefined>' : v);
const actualUserPl = stringifyFalsey(content.users ? content.users[room.myUserId] : "<no users in content>");
const usersPl = stringifyFalsey(content.users_default);
const actualEventPl = stringifyFalsey(content.events ? content.events['m.room.message'] : "<no events in content>");
const eventPl = stringifyFalsey(content.events_default);
plEventString = `actualUserPl=${actualUserPl} defaultUserPl=${usersPl} actualEventPl=${actualEventPl} defaultEventPl=${eventPl}`;
}
}
console.log(
`[riot-web#7838] renderComposer() hasTombstone=${!!this.state.tombstone} maySendMessage=${room.maySendMessage()}` +
` myMembership=${room.getMyMembership()} maySendEvent=${room.currentState.maySendEvent('m.room.message', room.myUserId)}` +
` myUserId=${room.myUserId} roomId=${room.roomId} hasPlEvent=${!!plEvent} powerLevels='${plEventString}'`
);
if (canSendMessages) { if (canSendMessages) {
// This also currently includes the call buttons. Really we should // This also currently includes the call buttons. Really we should
// check separately for whether we can call, but this is slightly // check separately for whether we can call, but this is slightly
@ -469,8 +441,6 @@ export default class MessageComposer extends React.Component {
</div> </div>
</div>); </div>);
} else { } else {
// TODO: Remove temporary logging for riot-web#7838
console.log("[riot-web#7838] Falling back to showing cannot post in room error");
controls.push( controls.push(
<div key="controls_error" className="mx_MessageComposer_noperm_error"> <div key="controls_error" className="mx_MessageComposer_noperm_error">
{ _t('You do not have permission to post to this room') } { _t('You do not have permission to post to this room') }

View File

@ -170,6 +170,7 @@ module.exports = React.createClass({
width={24} width={24}
height={24} height={24}
resizeMethod="crop" resizeMethod="crop"
viewUserOnClick={true}
/> />
); );
}); });

View File

@ -540,6 +540,7 @@
"Labs": "Labs", "Labs": "Labs",
"Notifications": "Notifications", "Notifications": "Notifications",
"Start automatically after system login": "Start automatically after system login", "Start automatically after system login": "Start automatically after system login",
"Close button should minimize window to tray": "Close button should minimize window to tray",
"Preferences": "Preferences", "Preferences": "Preferences",
"Composer": "Composer", "Composer": "Composer",
"Timeline": "Timeline", "Timeline": "Timeline",
@ -1202,6 +1203,7 @@
"View Decrypted Source": "View Decrypted Source", "View Decrypted Source": "View Decrypted Source",
"Unhide Preview": "Unhide Preview", "Unhide Preview": "Unhide Preview",
"Share Message": "Share Message", "Share Message": "Share Message",
"Share Permalink": "Share Permalink",
"Quote": "Quote", "Quote": "Quote",
"Source URL": "Source URL", "Source URL": "Source URL",
"Collapse Reply Thread": "Collapse Reply Thread", "Collapse Reply Thread": "Collapse Reply Thread",