mirror of
https://github.com/element-hq/element-web
synced 2024-11-27 11:47:23 +03:00
Merge branch 'develop' into travis/granular
This commit is contained in:
commit
69939e2fe3
21 changed files with 697 additions and 229 deletions
|
@ -22,7 +22,7 @@ import classNames from 'classnames';
|
|||
import { _t } from 'matrix-react-sdk/lib/languageHandler';
|
||||
import sdk from 'matrix-react-sdk';
|
||||
import dis from 'matrix-react-sdk/lib/dispatcher';
|
||||
import MatrixClient from 'matrix-js-sdk';
|
||||
import { MatrixClient } from 'matrix-js-sdk';
|
||||
import Analytics from 'matrix-react-sdk/lib/Analytics';
|
||||
import rate_limited_func from 'matrix-react-sdk/lib/ratelimitedfunc';
|
||||
import AccessibleButton from 'matrix-react-sdk/lib/components/views/elements/AccessibleButton';
|
||||
|
@ -90,6 +90,7 @@ module.exports = React.createClass({
|
|||
RoomMemberList: 'RoomMemberList',
|
||||
GroupMemberList: 'GroupMemberList',
|
||||
GroupRoomList: 'GroupRoomList',
|
||||
GroupRoomInfo: 'GroupRoomInfo',
|
||||
FilePanel: 'FilePanel',
|
||||
NotificationPanel: 'NotificationPanel',
|
||||
RoomMemberInfo: 'RoomMemberInfo',
|
||||
|
@ -205,7 +206,6 @@ module.exports = React.createClass({
|
|||
} else if (this.props.groupId) {
|
||||
this.setState({
|
||||
phase: this.Phase.GroupMemberList,
|
||||
groupId: payload.groupId,
|
||||
member: payload.member,
|
||||
});
|
||||
}
|
||||
|
@ -213,13 +213,20 @@ module.exports = React.createClass({
|
|||
} else if (payload.action === "view_group") {
|
||||
this.setState({
|
||||
phase: this.Phase.GroupMemberList,
|
||||
groupId: payload.groupId,
|
||||
member: null,
|
||||
});
|
||||
} else if (payload.action === "view_group_room") {
|
||||
this.setState({
|
||||
phase: this.Phase.GroupRoomInfo,
|
||||
groupRoomId: payload.groupRoomId,
|
||||
});
|
||||
} else if (payload.action === "view_group_room_list") {
|
||||
this.setState({
|
||||
phase: this.Phase.GroupRoomList,
|
||||
});
|
||||
} else if (payload.action === "view_group_user") {
|
||||
this.setState({
|
||||
phase: this.Phase.GroupMemberInfo,
|
||||
groupId: payload.groupId,
|
||||
member: payload.member,
|
||||
});
|
||||
} else if (payload.action === "view_room") {
|
||||
|
@ -242,6 +249,7 @@ module.exports = React.createClass({
|
|||
const GroupMemberList = sdk.getComponent('groups.GroupMemberList');
|
||||
const GroupMemberInfo = sdk.getComponent('groups.GroupMemberInfo');
|
||||
const GroupRoomList = sdk.getComponent('groups.GroupRoomList');
|
||||
const GroupRoomInfo = sdk.getComponent('groups.GroupRoomInfo');
|
||||
|
||||
const TintableSvg = sdk.getComponent("elements.TintableSvg");
|
||||
|
||||
|
@ -305,7 +313,7 @@ module.exports = React.createClass({
|
|||
analytics={['Right Panel', 'Group Member List Button', 'click']}
|
||||
/>,
|
||||
<HeaderButton key="_roomsButton" title={_t('Rooms')} iconSrc="img/icons-room.svg"
|
||||
isHighlighted={this.state.phase === this.Phase.GroupRoomList}
|
||||
isHighlighted={[this.Phase.GroupRoomList, this.Phase.GroupRoomInfo].includes(this.state.phase)}
|
||||
clickPhase={this.Phase.GroupRoomList}
|
||||
analytics={['Right Panel', 'Group Room List Button', 'click']}
|
||||
/>,
|
||||
|
@ -340,6 +348,11 @@ module.exports = React.createClass({
|
|||
groupMember={this.state.member}
|
||||
groupId={this.props.groupId}
|
||||
key={this.state.member.user_id} />;
|
||||
} else if (this.state.phase == this.Phase.GroupRoomInfo) {
|
||||
panel = <GroupRoomInfo
|
||||
groupRoomId={this.state.groupRoomId}
|
||||
groupId={this.props.groupId}
|
||||
key={this.state.groupRoomId} />;
|
||||
} else if (this.state.phase == this.Phase.NotificationPanel) {
|
||||
panel = <NotificationPanel />;
|
||||
} else if (this.state.phase == this.Phase.FilePanel) {
|
||||
|
|
|
@ -409,7 +409,7 @@ module.exports = React.createClass({
|
|||
|
||||
perms = null;
|
||||
if (guestRead || guestJoin) {
|
||||
perms = <div className="mx_RoomDirectory_perms">{guestRead} {guestJoin}</div>;
|
||||
perms = <div className="mx_RoomDirectory_perms">{guestRead}{guestJoin}</div>;
|
||||
}
|
||||
|
||||
var topic = rooms[i].topic || '';
|
||||
|
|
|
@ -246,10 +246,14 @@ var RoomSubList = React.createClass({
|
|||
roomNotificationCount: function(truncateAt) {
|
||||
var self = this;
|
||||
|
||||
if (this.props.isInvite) {
|
||||
return [0, true];
|
||||
}
|
||||
|
||||
return this.props.list.reduce(function(result, room, index) {
|
||||
if (truncateAt === undefined || index >= truncateAt) {
|
||||
var roomNotifState = RoomNotifs.getRoomNotifsState(room.roomId);
|
||||
var highlight = room.getUnreadNotificationCount('highlight') > 0 || self.props.isInvite;
|
||||
var highlight = room.getUnreadNotificationCount('highlight') > 0;
|
||||
var notificationCount = room.getUnreadNotificationCount();
|
||||
|
||||
const notifBadges = notificationCount > 0 && self._shouldShowNotifBadge(roomNotifState);
|
||||
|
@ -394,7 +398,8 @@ var RoomSubList = React.createClass({
|
|||
var subListNotifCount = subListNotifications[0];
|
||||
var subListNotifHighlight = subListNotifications[1];
|
||||
|
||||
var roomCount = this.props.list.length > 0 ? this.props.list.length : '';
|
||||
var totalTiles = this.props.list.length + (this.props.extraTiles || []).length;
|
||||
var roomCount = totalTiles > 0 ? totalTiles : '';
|
||||
|
||||
var chevronClasses = classNames({
|
||||
'mx_RoomSubList_chevron': true,
|
||||
|
|
|
@ -15,35 +15,24 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import sdk from 'matrix-react-sdk';
|
||||
import { _t } from 'matrix-react-sdk/lib/languageHandler';
|
||||
import MatrixClientPeg from 'matrix-react-sdk/lib/MatrixClientPeg';
|
||||
|
||||
class SendCustomEvent extends React.Component {
|
||||
static propTypes = {
|
||||
roomId: React.PropTypes.string.isRequired,
|
||||
onBack: React.PropTypes.func.isRequired,
|
||||
|
||||
eventType: React.PropTypes.string.isRequired,
|
||||
evContent: React.PropTypes.string.isRequired,
|
||||
class DevtoolsComponent extends React.Component {
|
||||
static contextTypes = {
|
||||
roomId: PropTypes.string.isRequired,
|
||||
};
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
eventType: '',
|
||||
evContent: '{\n\n}',
|
||||
};
|
||||
class GenericEditor extends DevtoolsComponent {
|
||||
// static propTypes = {onBack: PropTypes.func.isRequired};
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
this._send = this._send.bind(this);
|
||||
this.onBack = this.onBack.bind(this);
|
||||
this._onChange = this._onChange.bind(this);
|
||||
|
||||
this.state = {
|
||||
message: null,
|
||||
input_eventType: this.props.eventType,
|
||||
input_evContent: this.props.evContent,
|
||||
};
|
||||
this.onBack = this.onBack.bind(this);
|
||||
}
|
||||
|
||||
onBack() {
|
||||
|
@ -54,6 +43,10 @@ class SendCustomEvent extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
_onChange(e) {
|
||||
this.setState({[e.target.id]: e.target.type === 'checkbox' ? e.target.checked : e.target.value});
|
||||
}
|
||||
|
||||
_buttons() {
|
||||
return <div className="mx_Dialog_buttons">
|
||||
<button onClick={this.onBack}>{ _t('Back') }</button>
|
||||
|
@ -61,19 +54,64 @@ class SendCustomEvent extends React.Component {
|
|||
</div>;
|
||||
}
|
||||
|
||||
textInput(id, label) {
|
||||
return <div className="mx_DevTools_inputRow">
|
||||
<div className="mx_DevTools_inputLabelCell">
|
||||
<label htmlFor={id}>{ label }</label>
|
||||
</div>
|
||||
<div className="mx_DevTools_inputCell">
|
||||
<input id={id} onChange={this._onChange} value={this.state[id]} size="32" />
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
|
||||
class SendCustomEvent extends GenericEditor {
|
||||
static getLabel() { return _t('Send Custom Event'); }
|
||||
|
||||
static propTypes = {
|
||||
onBack: PropTypes.func.isRequired,
|
||||
forceStateEvent: PropTypes.bool,
|
||||
inputs: PropTypes.object,
|
||||
};
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
this._send = this._send.bind(this);
|
||||
|
||||
const {eventType, stateKey, evContent} = Object.assign({
|
||||
eventType: '',
|
||||
stateKey: '',
|
||||
evContent: '{\n\n}',
|
||||
}, this.props.inputs);
|
||||
|
||||
this.state = {
|
||||
isStateEvent: Boolean(this.props.forceStateEvent),
|
||||
|
||||
eventType,
|
||||
stateKey,
|
||||
evContent,
|
||||
};
|
||||
}
|
||||
|
||||
send(content) {
|
||||
return MatrixClientPeg.get().sendEvent(this.props.roomId, this.state.input_eventType, content);
|
||||
const cli = MatrixClientPeg.get();
|
||||
if (this.state.isStateEvent) {
|
||||
return cli.sendStateEvent(this.context.roomId, this.state.eventType, content, this.state.stateKey);
|
||||
} else {
|
||||
return cli.sendEvent(this.context.roomId, this.state.eventType, content);
|
||||
}
|
||||
}
|
||||
|
||||
async _send() {
|
||||
if (this.state.input_eventType === '') {
|
||||
if (this.state.eventType === '') {
|
||||
this.setState({ message: _t('You must specify an event type!') });
|
||||
return;
|
||||
}
|
||||
|
||||
let message;
|
||||
try {
|
||||
const content = JSON.parse(this.state.input_evContent);
|
||||
const content = JSON.parse(this.state.evContent);
|
||||
await this.send(content);
|
||||
message = _t('Event sent!');
|
||||
} catch (e) {
|
||||
|
@ -82,14 +120,6 @@ class SendCustomEvent extends React.Component {
|
|||
this.setState({ message });
|
||||
}
|
||||
|
||||
_additionalFields() {
|
||||
return <div />;
|
||||
}
|
||||
|
||||
_onChange(e) {
|
||||
this.setState({[`input_${e.target.id}`]: e.target.value});
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.message) {
|
||||
return <div>
|
||||
|
@ -102,87 +132,176 @@ class SendCustomEvent extends React.Component {
|
|||
|
||||
return <div>
|
||||
<div className="mx_Dialog_content">
|
||||
{ this._additionalFields() }
|
||||
<div className="mx_TextInputDialog_label">
|
||||
<label htmlFor="eventType"> { _t('Event Type') } </label>
|
||||
</div>
|
||||
<div>
|
||||
<input id="eventType" onChange={this._onChange} value={this.state.input_eventType} className="mx_TextInputDialog_input" size="64" />
|
||||
</div>
|
||||
{ this.textInput('eventType', _t('Event Type')) }
|
||||
{ this.state.isStateEvent && this.textInput('stateKey', _t('State Key')) }
|
||||
|
||||
<div className="mx_TextInputDialog_label">
|
||||
<br />
|
||||
|
||||
<div className="mx_UserSettings_profileLabelCell">
|
||||
<label htmlFor="evContent"> { _t('Event Content') } </label>
|
||||
</div>
|
||||
<div>
|
||||
<textarea id="evContent" onChange={this._onChange} value={this.state.input_evContent} className="mx_TextInputDialog_input" cols="63" rows="5" />
|
||||
<textarea id="evContent" onChange={this._onChange} value={this.state.evContent} className="mx_TextInputDialog_input" cols="63" rows="5" />
|
||||
</div>
|
||||
</div>
|
||||
{ this._buttons() }
|
||||
<div className="mx_Dialog_buttons">
|
||||
<button onClick={this.onBack}>{ _t('Back') }</button>
|
||||
{ !this.state.message && <button onClick={this._send}>{ _t('Send') }</button> }
|
||||
{ !this.state.message && !this.props.forceStateEvent && <div style={{float: "right"}}>
|
||||
<input id="isStateEvent" className="mx_DevTools_tgl mx_DevTools_tgl-flip" type="checkbox" onChange={this._onChange} checked={this.state.isStateEvent} />
|
||||
<label className="mx_DevTools_tgl-btn" data-tg-off="Event" data-tg-on="State Event" htmlFor="isStateEvent" />
|
||||
</div> }
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
|
||||
class SendCustomStateEvent extends SendCustomEvent {
|
||||
class SendAccountData extends GenericEditor {
|
||||
static getLabel() { return _t('Send Account Data'); }
|
||||
|
||||
static propTypes = {
|
||||
roomId: React.PropTypes.string.isRequired,
|
||||
onBack: React.PropTypes.func.isRequired,
|
||||
|
||||
eventType: React.PropTypes.string.isRequired,
|
||||
evContent: React.PropTypes.string.isRequired,
|
||||
stateKey: React.PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
eventType: '',
|
||||
evContent: '{\n\n}',
|
||||
stateKey: '',
|
||||
isRoomAccountData: PropTypes.bool,
|
||||
forceMode: PropTypes.bool,
|
||||
inputs: PropTypes.object,
|
||||
};
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
this.state['input_stateKey'] = this.props.stateKey;
|
||||
this._send = this._send.bind(this);
|
||||
|
||||
const {eventType, evContent} = Object.assign({
|
||||
eventType: '',
|
||||
evContent: '{\n\n}',
|
||||
}, this.props.inputs);
|
||||
|
||||
this.state = {
|
||||
isRoomAccountData: Boolean(this.props.isRoomAccountData),
|
||||
|
||||
eventType,
|
||||
evContent,
|
||||
};
|
||||
}
|
||||
|
||||
send(content) {
|
||||
const cli = MatrixClientPeg.get();
|
||||
return cli.sendStateEvent(this.props.roomId, this.state.input_eventType, content, this.state.input_stateKey);
|
||||
if (this.state.isRoomAccountData) {
|
||||
return cli.setRoomAccountData(this.context.roomId, this.state.eventType, content);
|
||||
}
|
||||
return cli.setAccountData(this.state.eventType, content);
|
||||
}
|
||||
|
||||
_additionalFields() {
|
||||
async _send() {
|
||||
if (this.state.eventType === '') {
|
||||
this.setState({ message: _t('You must specify an event type!') });
|
||||
return;
|
||||
}
|
||||
|
||||
let message;
|
||||
try {
|
||||
const content = JSON.parse(this.state.evContent);
|
||||
await this.send(content);
|
||||
message = _t('Event sent!');
|
||||
} catch (e) {
|
||||
message = _t('Failed to send custom event.') + ' (' + e.toString() + ')';
|
||||
}
|
||||
this.setState({ message });
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.message) {
|
||||
return <div>
|
||||
<div className="mx_Dialog_content">
|
||||
{ this.state.message }
|
||||
</div>
|
||||
{ this._buttons() }
|
||||
</div>;
|
||||
}
|
||||
|
||||
return <div>
|
||||
<div className="mx_TextInputDialog_label">
|
||||
<label htmlFor="stateKey"> { _t('State Key') } </label>
|
||||
<div className="mx_Dialog_content">
|
||||
{ this.textInput('eventType', _t('Event Type')) }
|
||||
<br />
|
||||
|
||||
<div className="mx_UserSettings_profileLabelCell">
|
||||
<label htmlFor="evContent"> { _t('Event Content') } </label>
|
||||
</div>
|
||||
<div>
|
||||
<textarea id="evContent" onChange={this._onChange} value={this.state.evContent} className="mx_TextInputDialog_input" cols="63" rows="5" />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<input id="stateKey" onChange={this._onChange} value={this.state.input_stateKey} className="mx_TextInputDialog_input" size="64" />
|
||||
<div className="mx_Dialog_buttons">
|
||||
<button onClick={this.onBack}>{ _t('Back') }</button>
|
||||
{ !this.state.message && <button onClick={this._send}>{ _t('Send') }</button> }
|
||||
{ !this.state.message && <div style={{float: "right"}}>
|
||||
<input id="isRoomAccountData" className="mx_DevTools_tgl mx_DevTools_tgl-flip" type="checkbox" onChange={this._onChange} checked={this.state.isRoomAccountData} disabled={this.props.forceMode} />
|
||||
<label className="mx_DevTools_tgl-btn" data-tg-off="Account Data" data-tg-on="Room Data" htmlFor="isRoomAccountData" />
|
||||
</div> }
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
|
||||
class RoomStateExplorer extends React.Component {
|
||||
class FilteredList extends React.Component {
|
||||
static propTypes = {
|
||||
setMode: React.PropTypes.func.isRequired,
|
||||
roomId: React.PropTypes.string.isRequired,
|
||||
onBack: React.PropTypes.func.isRequired,
|
||||
children: PropTypes.any,
|
||||
};
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
this.onQuery = this.onQuery.bind(this);
|
||||
|
||||
this.state = {
|
||||
query: '',
|
||||
};
|
||||
}
|
||||
|
||||
onQuery(ev) {
|
||||
this.setState({ query: ev.target.value });
|
||||
}
|
||||
|
||||
filterChildren() {
|
||||
if (this.state.query) {
|
||||
const lowerQuery = this.state.query.toLowerCase();
|
||||
return this.props.children.filter((child) => child.key.toLowerCase().includes(lowerQuery));
|
||||
}
|
||||
return this.props.children;
|
||||
}
|
||||
|
||||
render() {
|
||||
return <div>
|
||||
<input size="64"
|
||||
onChange={this.onQuery}
|
||||
value={this.state.query}
|
||||
placeholder={_t('Filter results')}
|
||||
className="mx_TextInputDialog_input mx_DevTools_RoomStateExplorer_query" />
|
||||
{ this.filterChildren() }
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
|
||||
class RoomStateExplorer extends DevtoolsComponent {
|
||||
static getLabel() { return _t('Explore Room State'); }
|
||||
|
||||
|
||||
static propTypes = {
|
||||
onBack: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
|
||||
const room = MatrixClientPeg.get().getRoom(this.props.roomId);
|
||||
const room = MatrixClientPeg.get().getRoom(this.context.roomId);
|
||||
this.roomStateEvents = room.currentState.events;
|
||||
|
||||
this.onBack = this.onBack.bind(this);
|
||||
this.editEv = this.editEv.bind(this);
|
||||
this.onQuery = this.onQuery.bind(this);
|
||||
}
|
||||
|
||||
state = {
|
||||
query: '',
|
||||
eventType: null,
|
||||
event: null,
|
||||
};
|
||||
this.state = {
|
||||
eventType: null,
|
||||
event: null,
|
||||
editing: false,
|
||||
};
|
||||
}
|
||||
|
||||
browseEventType(eventType) {
|
||||
return () => {
|
||||
|
@ -197,7 +316,9 @@ class RoomStateExplorer extends React.Component {
|
|||
}
|
||||
|
||||
onBack() {
|
||||
if (this.state.event) {
|
||||
if (this.state.editing) {
|
||||
this.setState({ editing: false });
|
||||
} else if (this.state.event) {
|
||||
this.setState({ event: null });
|
||||
} else if (this.state.eventType) {
|
||||
this.setState({ eventType: null });
|
||||
|
@ -207,20 +328,19 @@ class RoomStateExplorer extends React.Component {
|
|||
}
|
||||
|
||||
editEv() {
|
||||
const ev = this.state.event;
|
||||
this.props.setMode(SendCustomStateEvent, {
|
||||
eventType: ev.getType(),
|
||||
evContent: JSON.stringify(ev.getContent(), null, '\t'),
|
||||
stateKey: ev.getStateKey(),
|
||||
});
|
||||
}
|
||||
|
||||
onQuery(ev) {
|
||||
this.setState({ query: ev.target.value });
|
||||
this.setState({ editing: true });
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.event) {
|
||||
if (this.state.editing) {
|
||||
return <SendCustomEvent forceStateEvent={true} onBack={this.onBack} inputs={{
|
||||
eventType: this.state.event.getType(),
|
||||
evContent: JSON.stringify(this.state.event.getContent(), null, '\t'),
|
||||
stateKey: this.state.event.getStateKey(),
|
||||
}} />;
|
||||
}
|
||||
|
||||
return <div className="mx_ViewSource">
|
||||
<div className="mx_Dialog_content">
|
||||
<pre>{ JSON.stringify(this.state.event.event, null, 2) }</pre>
|
||||
|
@ -234,11 +354,9 @@ class RoomStateExplorer extends React.Component {
|
|||
|
||||
const rows = [];
|
||||
|
||||
const classes = 'mx_DevTools_RoomStateExplorer_button';
|
||||
if (this.state.eventType === null) {
|
||||
Object.keys(this.roomStateEvents).forEach((evType) => {
|
||||
// Skip this entry if does not contain search query
|
||||
if (this.state.query && !evType.toLowerCase().includes(this.state.query.toLowerCase())) return;
|
||||
|
||||
const stateGroup = this.roomStateEvents[evType];
|
||||
const stateKeys = Object.keys(stateGroup);
|
||||
|
||||
|
@ -249,7 +367,7 @@ class RoomStateExplorer extends React.Component {
|
|||
onClickFn = this.onViewSourceClick(stateGroup[stateKeys[0]]);
|
||||
}
|
||||
|
||||
rows.push(<button className="mx_DevTools_RoomStateExplorer_button" key={evType} onClick={onClickFn}>
|
||||
rows.push(<button className={classes} key={evType} onClick={onClickFn}>
|
||||
{ evType }
|
||||
</button>);
|
||||
});
|
||||
|
@ -257,12 +375,8 @@ class RoomStateExplorer extends React.Component {
|
|||
const evType = this.state.eventType;
|
||||
const stateGroup = this.roomStateEvents[evType];
|
||||
Object.keys(stateGroup).forEach((stateKey) => {
|
||||
// Skip this entry if does not contain search query
|
||||
if (this.state.query && !stateKey.toLowerCase().includes(this.state.query.toLowerCase())) return;
|
||||
|
||||
const ev = stateGroup[stateKey];
|
||||
rows.push(<button className="mx_DevTools_RoomStateExplorer_button" key={stateKey}
|
||||
onClick={this.onViewSourceClick(ev)}>
|
||||
rows.push(<button className={classes} key={stateKey} onClick={this.onViewSourceClick(ev)}>
|
||||
{ stateKey }
|
||||
</button>);
|
||||
});
|
||||
|
@ -270,8 +384,9 @@ class RoomStateExplorer extends React.Component {
|
|||
|
||||
return <div>
|
||||
<div className="mx_Dialog_content">
|
||||
<input onChange={this.onQuery} placeholder={_t('Filter results')} size="64" className="mx_TextInputDialog_input mx_DevTools_RoomStateExplorer_query" value={this.state.query} />
|
||||
{ rows }
|
||||
<FilteredList>
|
||||
{ rows }
|
||||
</FilteredList>
|
||||
</div>
|
||||
<div className="mx_Dialog_buttons">
|
||||
<button onClick={this.onBack}>{ _t('Back') }</button>
|
||||
|
@ -280,40 +395,157 @@ class RoomStateExplorer extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
export default class DevtoolsDialog extends React.Component {
|
||||
class AccountDataExplorer extends DevtoolsComponent {
|
||||
static getLabel() { return _t('Explore Account Data'); }
|
||||
|
||||
static propTypes = {
|
||||
roomId: React.PropTypes.string.isRequired,
|
||||
onFinished: React.PropTypes.func.isRequired,
|
||||
onBack: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
state = {
|
||||
mode: null,
|
||||
modeArgs: {},
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
|
||||
this.onBack = this.onBack.bind(this);
|
||||
this.editEv = this.editEv.bind(this);
|
||||
this._onChange = this._onChange.bind(this);
|
||||
|
||||
this.state = {
|
||||
isRoomAccountData: false,
|
||||
event: null,
|
||||
editing: false,
|
||||
};
|
||||
}
|
||||
|
||||
getData() {
|
||||
const cli = MatrixClientPeg.get();
|
||||
if (this.state.isRoomAccountData) {
|
||||
return cli.getRoom(this.context.roomId).accountData;
|
||||
}
|
||||
return cli.store.accountData;
|
||||
}
|
||||
|
||||
onViewSourceClick(event) {
|
||||
return () => {
|
||||
this.setState({ event });
|
||||
};
|
||||
}
|
||||
|
||||
onBack() {
|
||||
if (this.state.editing) {
|
||||
this.setState({ editing: false });
|
||||
} else if (this.state.event) {
|
||||
this.setState({ event: null });
|
||||
} else {
|
||||
this.props.onBack();
|
||||
}
|
||||
}
|
||||
|
||||
_onChange(e) {
|
||||
this.setState({[e.target.id]: e.target.type === 'checkbox' ? e.target.checked : e.target.value});
|
||||
}
|
||||
|
||||
editEv() {
|
||||
this.setState({ editing: true });
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.event) {
|
||||
if (this.state.editing) {
|
||||
return <SendAccountData isRoomAccountData={this.state.isRoomAccountData} onBack={this.onBack} inputs={{
|
||||
eventType: this.state.event.getType(),
|
||||
evContent: JSON.stringify(this.state.event.getContent(), null, '\t'),
|
||||
}} forceMode={true} />;
|
||||
}
|
||||
|
||||
return <div className="mx_ViewSource">
|
||||
<div className="mx_Dialog_content">
|
||||
<pre>{ JSON.stringify(this.state.event.event, null, 2) }</pre>
|
||||
</div>
|
||||
<div className="mx_Dialog_buttons">
|
||||
<button onClick={this.onBack}>{ _t('Back') }</button>
|
||||
<button onClick={this.editEv}>{ _t('Edit') }</button>
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
|
||||
const rows = [];
|
||||
|
||||
const classes = 'mx_DevTools_RoomStateExplorer_button';
|
||||
|
||||
const data = this.getData();
|
||||
Object.keys(data).forEach((evType) => {
|
||||
const ev = data[evType];
|
||||
rows.push(<button className={classes} key={evType} onClick={this.onViewSourceClick(ev)}>
|
||||
{ evType }
|
||||
</button>);
|
||||
});
|
||||
|
||||
return <div>
|
||||
<div className="mx_Dialog_content">
|
||||
<FilteredList>
|
||||
{ rows }
|
||||
</FilteredList>
|
||||
</div>
|
||||
<div className="mx_Dialog_buttons">
|
||||
<button onClick={this.onBack}>{ _t('Back') }</button>
|
||||
{ !this.state.message && <div style={{float: "right"}}>
|
||||
<input id="isRoomAccountData" className="mx_DevTools_tgl mx_DevTools_tgl-flip" type="checkbox" onChange={this._onChange} checked={this.state.isRoomAccountData} />
|
||||
<label className="mx_DevTools_tgl-btn" data-tg-off="Account Data" data-tg-on="Room Data" htmlFor="isRoomAccountData" />
|
||||
</div> }
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
|
||||
const Entries = [
|
||||
SendCustomEvent,
|
||||
RoomStateExplorer,
|
||||
SendAccountData,
|
||||
AccountDataExplorer,
|
||||
];
|
||||
|
||||
export default class DevtoolsDialog extends React.Component {
|
||||
static childContextTypes = {
|
||||
roomId: PropTypes.string.isRequired,
|
||||
// client: PropTypes.instanceOf(MatixClient),
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
roomId: PropTypes.string.isRequired,
|
||||
onFinished: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
this.onBack = this.onBack.bind(this);
|
||||
this.setMode = this.setMode.bind(this);
|
||||
this.onCancel = this.onCancel.bind(this);
|
||||
|
||||
this.state = {
|
||||
mode: null,
|
||||
};
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this._unmounted = true;
|
||||
}
|
||||
|
||||
getChildContext() {
|
||||
return { roomId: this.props.roomId };
|
||||
}
|
||||
|
||||
_setMode(mode) {
|
||||
return () => {
|
||||
this.setMode(mode);
|
||||
this.setState({ mode });
|
||||
};
|
||||
}
|
||||
|
||||
setMode(mode, modeArgs={}) {
|
||||
this.setState({ mode, modeArgs });
|
||||
}
|
||||
|
||||
onBack() {
|
||||
this.setState({ mode: null });
|
||||
if (this.prevMode) {
|
||||
this.setState({ mode: this.prevMode });
|
||||
this.prevMode = null;
|
||||
} else {
|
||||
this.setState({ mode: null });
|
||||
}
|
||||
}
|
||||
|
||||
onCancel() {
|
||||
|
@ -324,14 +556,27 @@ export default class DevtoolsDialog extends React.Component {
|
|||
let body;
|
||||
|
||||
if (this.state.mode) {
|
||||
body =
|
||||
<this.state.mode {...this.props} {...this.state.modeArgs} onBack={this.onBack} setMode={this.setMode} />;
|
||||
} else {
|
||||
body = <div>
|
||||
<div className="mx_Dialog_content">
|
||||
<button onClick={this._setMode(SendCustomEvent)}>{ _t('Send Custom Event') }</button>
|
||||
<button onClick={this._setMode(SendCustomStateEvent)}>{ _t('Send Custom State Event') }</button>
|
||||
<button onClick={this._setMode(RoomStateExplorer)}>{ _t('Explore Room State') }</button>
|
||||
<div className="mx_DevTools_label_left">{ this.state.mode.getLabel() }</div>
|
||||
<div className="mx_DevTools_label_right">Room ID: { this.props.roomId }</div>
|
||||
<div className="mx_DevTools_label_bottom" />
|
||||
<this.state.mode onBack={this.onBack} />
|
||||
</div>;
|
||||
} else {
|
||||
const classes = "mx_DevTools_RoomStateExplorer_button";
|
||||
body = <div>
|
||||
<div>
|
||||
<div className="mx_DevTools_label_left">{ _t('Toolbox') }</div>
|
||||
<div className="mx_DevTools_label_right">Room ID: { this.props.roomId }</div>
|
||||
<div className="mx_DevTools_label_bottom" />
|
||||
|
||||
<div className="mx_Dialog_content">
|
||||
{ Entries.map((Entry) => {
|
||||
const label = Entry.getLabel();
|
||||
const onClick = this._setMode(Entry);
|
||||
return <button className={classes} key={label} onClick={onClick}>{ label }</button>;
|
||||
}) }
|
||||
</div>
|
||||
</div>
|
||||
<div className="mx_Dialog_buttons">
|
||||
<button onClick={this.onCancel}>{ _t('Cancel') }</button>
|
||||
|
@ -342,7 +587,6 @@ export default class DevtoolsDialog extends React.Component {
|
|||
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
||||
return (
|
||||
<BaseDialog className="mx_QuestionDialog" onFinished={this.props.onFinished} title={_t('Developer Tools')}>
|
||||
<div>Room ID: { this.props.roomId }</div>
|
||||
{ body }
|
||||
</BaseDialog>
|
||||
);
|
||||
|
|
33
src/components/views/elements/InlineSpinner.js
Normal file
33
src/components/views/elements/InlineSpinner.js
Normal file
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
Copyright 2017 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.
|
||||
*/
|
||||
|
||||
const React = require('react');
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'InlineSpinner',
|
||||
|
||||
render: function() {
|
||||
var w = this.props.w || 16;
|
||||
var h = this.props.h || 16;
|
||||
var imgClass = this.props.imgClassName || "";
|
||||
|
||||
return (
|
||||
<div className="mx_InlineSpinner">
|
||||
<img src="img/spinner.gif" width={w} height={h} className={imgClass}/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
|
@ -70,6 +70,7 @@
|
|||
"What's new?": "What's new?",
|
||||
"A new version of Riot is available.": "A new version of Riot is available.",
|
||||
"To return to your account in future you need to <u>set a password</u>": "To return to your account in future you need to <u>set a password</u>",
|
||||
"Toolbox": "Toolbox",
|
||||
"Set Password": "Set Password",
|
||||
"Error encountered (%(errorDetail)s).": "Error encountered (%(errorDetail)s).",
|
||||
"Checking for an update...": "Checking for an update...",
|
||||
|
@ -106,7 +107,8 @@
|
|||
"Edit": "Edit",
|
||||
"Filter results": "Filter results",
|
||||
"Send Custom Event": "Send Custom Event",
|
||||
"Send Custom State Event": "Send Custom State Event",
|
||||
"Send Account Data": "Send Account Data",
|
||||
"Explore Account Data": "Explore Account Data",
|
||||
"Explore Room State": "Explore Room State",
|
||||
"Developer Tools": "Developer Tools",
|
||||
"You have successfully set a password!": "You have successfully set a password!",
|
||||
|
|
|
@ -35,7 +35,6 @@
|
|||
@import "./matrix-react-sdk/views/elements/_ProgressBar.scss";
|
||||
@import "./matrix-react-sdk/views/elements/_RichText.scss";
|
||||
@import "./matrix-react-sdk/views/elements/_RoleButton.scss";
|
||||
@import "./matrix-react-sdk/views/groups/_GroupInviteTile.scss";
|
||||
@import "./matrix-react-sdk/views/groups/_GroupRoomList.scss";
|
||||
@import "./matrix-react-sdk/views/login/_InteractiveAuthEntryComponents.scss";
|
||||
@import "./matrix-react-sdk/views/login/_ServerConfig.scss";
|
||||
|
@ -55,6 +54,8 @@
|
|||
@import "./matrix-react-sdk/views/rooms/_MemberInfo.scss";
|
||||
@import "./matrix-react-sdk/views/rooms/_MemberList.scss";
|
||||
@import "./matrix-react-sdk/views/rooms/_MessageComposer.scss";
|
||||
@import "./matrix-react-sdk/views/rooms/_PinnedEventTile.scss";
|
||||
@import "./matrix-react-sdk/views/rooms/_PinnedEventsPanel.scss";
|
||||
@import "./matrix-react-sdk/views/rooms/_PresenceLabel.scss";
|
||||
@import "./matrix-react-sdk/views/rooms/_RoomHeader.scss";
|
||||
@import "./matrix-react-sdk/views/rooms/_RoomList.scss";
|
||||
|
@ -68,8 +69,6 @@
|
|||
@import "./matrix-react-sdk/views/voip/_CallView.scss";
|
||||
@import "./matrix-react-sdk/views/voip/_IncomingCallbox.scss";
|
||||
@import "./matrix-react-sdk/views/voip/_VideoView.scss";
|
||||
@import "./matrix-react-sdk/views/rooms/_PinnedEventsPanel.scss";
|
||||
@import "./matrix-react-sdk/views/rooms/_PinnedEventTile.scss";
|
||||
@import "./vector-web/_fonts.scss";
|
||||
@import "./vector-web/structures/_CompatibilityPage.scss";
|
||||
@import "./vector-web/structures/_HomePage.scss";
|
||||
|
@ -86,6 +85,7 @@
|
|||
@import "./vector-web/views/dialogs/_SetPasswordDialog.scss";
|
||||
@import "./vector-web/views/directory/_NetworkDropdown.scss";
|
||||
@import "./vector-web/views/elements/_ImageView.scss";
|
||||
@import "./vector-web/views/elements/_InlineSpinner.scss";
|
||||
@import "./vector-web/views/elements/_Spinner.scss";
|
||||
@import "./vector-web/views/globals/_MatrixToolbar.scss";
|
||||
@import "./vector-web/views/messages/_DateSeparator.scss";
|
||||
|
|
|
@ -68,7 +68,7 @@ limitations under the License.
|
|||
box-shadow: none;
|
||||
}
|
||||
|
||||
.mx_GroupView_header_name:hover div:not(.mx_GroupView_editable) {
|
||||
.mx_GroupView_header_isUserMember .mx_GroupView_header_name:hover div:not(.mx_GroupView_editable) {
|
||||
color: $accent-color;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
@ -204,6 +204,7 @@ limitations under the License.
|
|||
flex-grow: 1;
|
||||
border-top: 1px solid $primary-hairline-color;
|
||||
padding-top: 10px;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.mx_GroupView .mx_RoomView_messageListWrapper {
|
||||
|
|
|
@ -18,6 +18,9 @@ limitations under the License.
|
|||
max-width: 960px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.mx_MyGroups .mx_RoomHeader_simpleHeader {
|
||||
|
@ -61,10 +64,26 @@ limitations under the License.
|
|||
/* Until the button is wired up */
|
||||
.mx_MyGroups_joinBox {
|
||||
visibility: hidden;
|
||||
|
||||
/* When joinBox wraps onto its own row, it should take up zero height so
|
||||
that there isn't an awkward gap between MyGroups_createBox and
|
||||
MyGroups_content.
|
||||
*/
|
||||
height: 0px;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
.mx_MyGroups_content {
|
||||
margin-left: 2px;
|
||||
|
||||
flex: 1 0 0;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.mx_MyGroups_content h3 {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.mx_MyGroups_placeholder {
|
||||
|
@ -75,19 +94,22 @@ limitations under the License.
|
|||
text-align: center;
|
||||
}
|
||||
|
||||
.mx_MyGroups_joinedGroups {
|
||||
.mx_MyGroups_joinedGroups .gm-scroll-view {
|
||||
border-top: 1px solid $primary-hairline-color;
|
||||
overflow-x: hidden;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-flow: wrap;
|
||||
justify-content: space-around;
|
||||
align-content: flex-start;
|
||||
}
|
||||
|
||||
.mx_MyGroups_joinedGroups .mx_GroupTile {
|
||||
.mx_MyGroups_joinedGroups .gm-scroll-view .mx_GroupTile {
|
||||
min-width: 300px;
|
||||
flex: 1 0 25%;
|
||||
max-width: 33%;
|
||||
flex: 1 0 300px;
|
||||
height: 75px;
|
||||
margin-bottom: 15px;
|
||||
margin-right: 10px;
|
||||
margin: 10px 0px;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
cursor: pointer;
|
||||
|
@ -100,6 +122,12 @@ limitations under the License.
|
|||
justify-content: center;
|
||||
}
|
||||
|
||||
.mx_GroupTile_profile h3.mx_GroupTile_name,
|
||||
.mx_GroupTile_profile .mx_GroupTile_groupId,
|
||||
.mx_GroupTile_profile .mx_GroupTile_desc {
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
.mx_GroupTile_profile h3.mx_GroupTile_name {
|
||||
margin: 0px;
|
||||
font-size: 15px;
|
||||
|
|
|
@ -33,3 +33,30 @@ limitations under the License.
|
|||
background-color: $primary-bg-color;
|
||||
}
|
||||
|
||||
.mx_CreateGroupDialog_input_hasPrefixAndSuffix {
|
||||
border-radius: 0px;
|
||||
}
|
||||
|
||||
.mx_CreateGroupDialog_input_group {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.mx_CreateGroupDialog_prefix,
|
||||
.mx_CreateGroupDialog_suffix {
|
||||
height: 35px;
|
||||
padding: 0px 5px;
|
||||
line-height: 37px;
|
||||
background-color: $input-border-color;
|
||||
border: 1px solid $input-border-color;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.mx_CreateGroupDialog_prefix {
|
||||
border-right: 0px;
|
||||
border-radius: 3px 0px 0px 3px;
|
||||
}
|
||||
|
||||
.mx_CreateGroupDialog_suffix {
|
||||
border-left: 0px;
|
||||
border-radius: 0px 3px 3px 0px;
|
||||
}
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
// --Matthew
|
||||
|
||||
.mx_UserPill,
|
||||
.mx_RoomPill {
|
||||
.mx_RoomPill,
|
||||
.mx_AtRoomPill {
|
||||
border-radius: 16px;
|
||||
display: inline-block;
|
||||
height: 20px;
|
||||
|
@ -24,7 +25,8 @@
|
|||
padding-right: 5px;
|
||||
}
|
||||
|
||||
.mx_EventTile_highlight .mx_EventTile_content .markdown-body a.mx_UserPill_me {
|
||||
.mx_EventTile_highlight .mx_EventTile_content .markdown-body a.mx_UserPill_me,
|
||||
.mx_EventTile_content .mx_AtRoomPill {
|
||||
color: $accent-fg-color;
|
||||
background-color: $mention-user-pill-bg-color;
|
||||
padding-right: 5px;
|
||||
|
@ -39,7 +41,8 @@
|
|||
}
|
||||
|
||||
.mx_UserPill .mx_BaseAvatar,
|
||||
.mx_RoomPill .mx_BaseAvatar {
|
||||
.mx_RoomPill .mx_BaseAvatar,
|
||||
.mx_AtRoomPill .mx_BaseAvatar {
|
||||
position: relative;
|
||||
left: -3px;
|
||||
top: 2px;
|
||||
|
|
|
@ -1,74 +0,0 @@
|
|||
/*
|
||||
Copyright 2017 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.
|
||||
*/
|
||||
|
||||
.mx_GroupInviteTile {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
font-size: 13px;
|
||||
display: block;
|
||||
height: 34px;
|
||||
}
|
||||
|
||||
.mx_GroupInviteTile_nameContainer {
|
||||
display: inline-block;
|
||||
width: 180px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.mx_GroupInviteTile_avatarContainer {
|
||||
display: inline-block;
|
||||
padding-top: 5px;
|
||||
padding-bottom: 5px;
|
||||
padding-left: 16px;
|
||||
padding-right: 6px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.mx_GroupInviteTile_name {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
width: 165px;
|
||||
vertical-align: middle;
|
||||
padding-left: 6px;
|
||||
padding-right: 6px;
|
||||
padding-top: 2px;
|
||||
padding-bottom: 3px;
|
||||
color: $roomtile-name-color;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.mx_GroupInviteTile_badge {
|
||||
display: inline-block;
|
||||
min-width: 15px;
|
||||
height: 15px;
|
||||
position: absolute;
|
||||
right: 8px; /*gutter */
|
||||
top: 9px;
|
||||
border-radius: 8px;
|
||||
color: $accent-fg-color;
|
||||
background-color: $group-alert-color;
|
||||
font-weight: 600;
|
||||
font-size: 10px;
|
||||
text-align: center;
|
||||
padding-top: 1px;
|
||||
padding-left: 4px;
|
||||
padding-right: 4px;
|
||||
}
|
||||
|
|
@ -19,18 +19,3 @@ limitations under the License.
|
|||
color: $primary-fg-color;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.mx_GroupRoomTile_delete {
|
||||
opacity: 0.4;
|
||||
position: absolute;
|
||||
top: 6px;
|
||||
right: 10px;
|
||||
cursor: pointer;
|
||||
|
||||
display: none;
|
||||
}
|
||||
|
||||
.mx_GroupRoomTile:hover > .mx_GroupRoomTile_delete {
|
||||
display: initial;
|
||||
}
|
||||
|
||||
|
|
|
@ -74,7 +74,7 @@ limitations under the License.
|
|||
margin: 0;
|
||||
padding: 2px 10px;
|
||||
// background-color: $e2e-verified-color;
|
||||
// border-bottom: 1px solid $primary-hairline-color;
|
||||
border-bottom: 1px solid $primary-hairline-color;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
|
|
|
@ -57,7 +57,7 @@ limitations under the License.
|
|||
font-size: 14px;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
max-width: 135px;
|
||||
max-width: 155px;
|
||||
}
|
||||
|
||||
.mx_EntityTile_details {
|
||||
|
|
|
@ -94,3 +94,19 @@ limitations under the License.
|
|||
cursor: pointer;
|
||||
}
|
||||
|
||||
.mx_MemberInfo label {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.mx_MemberInfo label .mx_MemberInfo_label_text {
|
||||
display: inline-block;
|
||||
max-width: 180px;
|
||||
vertical-align: text-top;
|
||||
}
|
||||
|
||||
.mx_MemberInfo input[type="radio"] {
|
||||
vertical-align: -2px;
|
||||
margin-right: 5px;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
|
|
|
@ -232,3 +232,17 @@ limitations under the License.
|
|||
.mx_RoomHeader_voipButtons {
|
||||
margin-top: 18px;
|
||||
}
|
||||
|
||||
.mx_RoomHeader_pinnedButton {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.mx_RoomHeader_unreadPinsIndicator {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 4px;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 8px;
|
||||
background-color: $warning-color;
|
||||
}
|
||||
|
|
|
@ -23,7 +23,6 @@ $mention-user-pill-bg-color: #ff0064;
|
|||
$other-user-pill-bg-color: rgba(0, 0, 0, 0.1);
|
||||
|
||||
// groups
|
||||
$group-alert-color: #774f7e;
|
||||
$group-my-groups-placeholder-bg: #f7f7f7;
|
||||
$group-my-groups-placeholder-fg: #888;
|
||||
|
||||
|
|
|
@ -100,6 +100,7 @@ limitations under the License.
|
|||
display: inline;
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
margin-right: 5px;
|
||||
height: 15px;
|
||||
border-radius: 11px;
|
||||
background-color: $plinth-bg-color;
|
||||
|
|
|
@ -17,3 +17,150 @@ limitations under the License.
|
|||
.mx_DevTools_RoomStateExplorer_button, .mx_DevTools_RoomStateExplorer_query {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.mx_DevTools_label_left {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.mx_DevTools_label_right {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.mx_DevTools_label_bottom {
|
||||
clear: both;
|
||||
border-bottom: 1px solid #e5e5e5;
|
||||
}
|
||||
|
||||
.mx_DevTools_inputRow
|
||||
{
|
||||
display: table-row;
|
||||
}
|
||||
|
||||
.mx_DevTools_inputLabelCell
|
||||
{
|
||||
padding-bottom: 21px;
|
||||
display: table-cell;
|
||||
font-weight: bold;
|
||||
padding-right: 24px;
|
||||
}
|
||||
|
||||
.mx_DevTools_inputCell {
|
||||
display: table-cell;
|
||||
padding-bottom: 21px;
|
||||
width: 240px;
|
||||
}
|
||||
|
||||
.mx_DevTools_inputCell input
|
||||
{
|
||||
display: inline-block;
|
||||
border: 0;
|
||||
border-bottom: 1px solid $input-underline-color;
|
||||
padding: 0;
|
||||
width: 240px;
|
||||
color: $input-fg-color;
|
||||
font-family: 'Open Sans', Helvetica, Arial, Sans-Serif;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.mx_DevTools_tgl {
|
||||
display: none;
|
||||
|
||||
// add default box-sizing for this scope
|
||||
&,
|
||||
&:after,
|
||||
&:before,
|
||||
& *,
|
||||
& *:after,
|
||||
& *:before,
|
||||
& + .mx_DevTools_tgl-btn {
|
||||
box-sizing: border-box;
|
||||
&::selection {
|
||||
background: none;
|
||||
}
|
||||
}
|
||||
|
||||
+ .mx_DevTools_tgl-btn {
|
||||
outline: 0;
|
||||
display: block;
|
||||
width: 7em;
|
||||
height: 2em;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
&:after,
|
||||
&:before {
|
||||
position: relative;
|
||||
display: block;
|
||||
content: "";
|
||||
width: 50%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
&:after {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
&:before {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&:checked + .mx_DevTools_tgl-btn:after {
|
||||
left: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
.mx_DevTools_tgl-flip {
|
||||
+ .mx_DevTools_tgl-btn {
|
||||
padding: 2px;
|
||||
transition: all .2s ease;
|
||||
font-family: sans-serif;
|
||||
perspective: 100px;
|
||||
&:after,
|
||||
&:before {
|
||||
display: inline-block;
|
||||
transition: all .4s ease;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
position: absolute;
|
||||
line-height: 2em;
|
||||
font-weight: bold;
|
||||
color: #fff;
|
||||
top: 0;
|
||||
left: 0;
|
||||
backface-visibility: hidden;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
&:after {
|
||||
content: attr(data-tg-on);
|
||||
background: #02C66F;
|
||||
transform: rotateY(-180deg);
|
||||
}
|
||||
|
||||
&:before {
|
||||
background: #FF3A19;
|
||||
content: attr(data-tg-off);
|
||||
}
|
||||
|
||||
&:active:before {
|
||||
transform: rotateY(-20deg);
|
||||
}
|
||||
}
|
||||
|
||||
&:checked + .mx_DevTools_tgl-btn {
|
||||
&:before {
|
||||
transform: rotateY(180deg);
|
||||
}
|
||||
|
||||
&:after {
|
||||
transform: rotateY(0);
|
||||
left: 0;
|
||||
background: #7FC6A6;
|
||||
}
|
||||
|
||||
&:active:after {
|
||||
transform: rotateY(20deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
Copyright 2017 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.
|
||||
*/
|
||||
|
||||
.mx_InlineSpinner {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.mx_InlineSpinner img {
|
||||
margin: 0px 6px;
|
||||
vertical-align: -3px;
|
||||
}
|
Loading…
Reference in a new issue