Merge branch 'develop' into t3chguy/rich_quoting_linear
This commit is contained in:
commit
4490715b60
7 changed files with 247 additions and 143 deletions
|
@ -78,8 +78,6 @@
|
||||||
"react": "^15.4.0",
|
"react": "^15.4.0",
|
||||||
"react-addons-css-transition-group": "15.3.2",
|
"react-addons-css-transition-group": "15.3.2",
|
||||||
"react-beautiful-dnd": "^4.0.0",
|
"react-beautiful-dnd": "^4.0.0",
|
||||||
"react-dnd": "^2.1.4",
|
|
||||||
"react-dnd-html5-backend": "^2.1.2",
|
|
||||||
"react-dom": "^15.4.0",
|
"react-dom": "^15.4.0",
|
||||||
"react-gemini-scrollbar": "matrix-org/react-gemini-scrollbar#5e97aef",
|
"react-gemini-scrollbar": "matrix-org/react-gemini-scrollbar#5e97aef",
|
||||||
"sanitize-html": "^1.14.1",
|
"sanitize-html": "^1.14.1",
|
||||||
|
|
|
@ -19,8 +19,6 @@ limitations under the License.
|
||||||
import * as Matrix from 'matrix-js-sdk';
|
import * as Matrix from 'matrix-js-sdk';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { DragDropContext } from 'react-dnd';
|
|
||||||
import HTML5Backend from 'react-dnd-html5-backend';
|
|
||||||
|
|
||||||
import { KeyCode, isOnlyCtrlOrCmdKeyEvent } from '../../Keyboard';
|
import { KeyCode, isOnlyCtrlOrCmdKeyEvent } from '../../Keyboard';
|
||||||
import Notifier from '../../Notifier';
|
import Notifier from '../../Notifier';
|
||||||
|
@ -347,4 +345,4 @@ const LoggedInView = React.createClass({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default DragDropContext(HTML5Backend)(LoggedInView);
|
export default LoggedInView;
|
||||||
|
|
|
@ -847,16 +847,36 @@ export default React.createClass({
|
||||||
}).close;
|
}).close;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_leaveRoomWarnings: function(roomId) {
|
||||||
|
const roomToLeave = MatrixClientPeg.get().getRoom(roomId);
|
||||||
|
// Show a warning if there are additional complications.
|
||||||
|
const joinRules = roomToLeave.currentState.getStateEvents('m.room.join_rules', '');
|
||||||
|
const warnings = [];
|
||||||
|
if (joinRules) {
|
||||||
|
const rule = joinRules.getContent().join_rule;
|
||||||
|
if (rule !== "public") {
|
||||||
|
warnings.push((
|
||||||
|
<span className="warning" key="non_public_warning">
|
||||||
|
{ _t("This room is not public. You will not be able to rejoin without an invite.") }
|
||||||
|
</span>
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return warnings;
|
||||||
|
},
|
||||||
|
|
||||||
_leaveRoom: function(roomId) {
|
_leaveRoom: function(roomId) {
|
||||||
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
||||||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||||
|
|
||||||
const roomToLeave = MatrixClientPeg.get().getRoom(roomId);
|
const roomToLeave = MatrixClientPeg.get().getRoom(roomId);
|
||||||
|
const warnings = this._leaveRoomWarnings(roomId);
|
||||||
|
|
||||||
Modal.createTrackedDialog('Leave room', '', QuestionDialog, {
|
Modal.createTrackedDialog('Leave room', '', QuestionDialog, {
|
||||||
title: _t("Leave room"),
|
title: _t("Leave room"),
|
||||||
description: (
|
description: (
|
||||||
<span>
|
<span>
|
||||||
{ _t("Are you sure you want to leave the room '%(roomName)s'?", {roomName: roomToLeave.name}) }
|
{ _t("Are you sure you want to leave the room '%(roomName)s'?", {roomName: roomToLeave.name}) }
|
||||||
|
{ warnings }
|
||||||
</span>
|
</span>
|
||||||
),
|
),
|
||||||
onFinished: (shouldLeave) => {
|
onFinished: (shouldLeave) => {
|
||||||
|
@ -1066,10 +1086,10 @@ export default React.createClass({
|
||||||
// this if we are not scrolled up in the view. To find out, delegate to
|
// this if we are not scrolled up in the view. To find out, delegate to
|
||||||
// the timeline panel. If the timeline panel doesn't exist, then we assume
|
// the timeline panel. If the timeline panel doesn't exist, then we assume
|
||||||
// it is safe to reset the timeline.
|
// it is safe to reset the timeline.
|
||||||
if (!self._loggedInView) {
|
if (!self._loggedInView || !self._loggedInView.child) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return self._loggedInView.getDecoratedComponentInstance().canResetTimelineInRoom(roomId);
|
return self._loggedInView.child.canResetTimelineInRoom(roomId);
|
||||||
});
|
});
|
||||||
|
|
||||||
cli.on('sync', function(state, prevState) {
|
cli.on('sync', function(state, prevState) {
|
||||||
|
|
|
@ -107,7 +107,11 @@ export default class Flair extends React.Component {
|
||||||
}
|
}
|
||||||
const profiles = await this._getGroupProfiles(groups);
|
const profiles = await this._getGroupProfiles(groups);
|
||||||
if (!this.unmounted) {
|
if (!this.unmounted) {
|
||||||
this.setState({profiles: profiles.filter((profile) => {return profile.avatarUrl;})});
|
this.setState({
|
||||||
|
profiles: profiles.filter((profile) => {
|
||||||
|
return profile ? profile.avatarUrl : false;
|
||||||
|
}),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@ limitations under the License.
|
||||||
'use strict';
|
'use strict';
|
||||||
const React = require("react");
|
const React = require("react");
|
||||||
const ReactDOM = require("react-dom");
|
const ReactDOM = require("react-dom");
|
||||||
|
import { DragDropContext } from 'react-beautiful-dnd';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
const GeminiScrollbar = require('react-gemini-scrollbar');
|
const GeminiScrollbar = require('react-gemini-scrollbar');
|
||||||
|
@ -32,6 +33,8 @@ const Receipt = require('../../../utils/Receipt');
|
||||||
import TagOrderStore from '../../../stores/TagOrderStore';
|
import TagOrderStore from '../../../stores/TagOrderStore';
|
||||||
import GroupStoreCache from '../../../stores/GroupStoreCache';
|
import GroupStoreCache from '../../../stores/GroupStoreCache';
|
||||||
|
|
||||||
|
import Modal from '../../../Modal';
|
||||||
|
|
||||||
const HIDE_CONFERENCE_CHANS = true;
|
const HIDE_CONFERENCE_CHANS = true;
|
||||||
|
|
||||||
function phraseForSection(section) {
|
function phraseForSection(section) {
|
||||||
|
@ -275,6 +278,103 @@ module.exports = React.createClass({
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onRoomTileEndDrag: function(result) {
|
||||||
|
if (!result.destination) return;
|
||||||
|
|
||||||
|
let newTag = result.destination.droppableId.split('_')[1];
|
||||||
|
let prevTag = result.source.droppableId.split('_')[1];
|
||||||
|
if (newTag === 'undefined') newTag = undefined;
|
||||||
|
if (prevTag === 'undefined') prevTag = undefined;
|
||||||
|
|
||||||
|
const roomId = result.draggableId.split('_')[1];
|
||||||
|
const room = MatrixClientPeg.get().getRoom(roomId);
|
||||||
|
|
||||||
|
const newIndex = result.destination.index;
|
||||||
|
|
||||||
|
// Evil hack to get DMs behaving
|
||||||
|
if ((prevTag === undefined && newTag === 'im.vector.fake.direct') ||
|
||||||
|
(prevTag === 'im.vector.fake.direct' && newTag === undefined)
|
||||||
|
) {
|
||||||
|
Rooms.guessAndSetDMRoom(
|
||||||
|
room, newTag === 'im.vector.fake.direct',
|
||||||
|
).catch((err) => {
|
||||||
|
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||||
|
console.error("Failed to set direct chat tag " + err);
|
||||||
|
Modal.createTrackedDialog('Failed to set direct chat tag', '', ErrorDialog, {
|
||||||
|
title: _t('Failed to set direct chat tag'),
|
||||||
|
description: ((err && err.message) ? err.message : _t('Operation failed')),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasChangedSubLists = result.source.droppableId !== result.destination.droppableId;
|
||||||
|
|
||||||
|
let newOrder = null;
|
||||||
|
|
||||||
|
// Is the tag ordered manually?
|
||||||
|
if (newTag && !newTag.match(/^(m\.lowpriority|im\.vector\.fake\.(invite|recent|direct|archived))$/)) {
|
||||||
|
const newList = Object.assign({}, this.state.lists[newTag]);
|
||||||
|
|
||||||
|
// If the room was moved "down" (increasing index) in the same list we
|
||||||
|
// need to use the orders of the tiles with indices shifted by +1
|
||||||
|
const offset = (
|
||||||
|
newTag === prevTag && result.source.index < result.destination.index
|
||||||
|
) ? 1 : 0;
|
||||||
|
|
||||||
|
const prevOrder = newIndex === 0 ?
|
||||||
|
0 : newList[offset + newIndex - 1].tags[newTag].order;
|
||||||
|
const nextOrder = newIndex === newList.length ?
|
||||||
|
1 : newList[offset + newIndex].tags[newTag].order;
|
||||||
|
|
||||||
|
newOrder = {
|
||||||
|
order: (prevOrder + nextOrder) / 2.0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// More evilness: We will still be dealing with moving to favourites/low prio,
|
||||||
|
// but we avoid ever doing a request with 'im.vector.fake.direct`.
|
||||||
|
//
|
||||||
|
// if we moved lists, remove the old tag
|
||||||
|
if (prevTag && prevTag !== 'im.vector.fake.direct' &&
|
||||||
|
hasChangedSubLists
|
||||||
|
) {
|
||||||
|
// Optimistic update of what will happen to the room tags
|
||||||
|
delete room.tags[prevTag];
|
||||||
|
|
||||||
|
MatrixClientPeg.get().deleteRoomTag(roomId, prevTag).catch(function(err) {
|
||||||
|
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||||
|
console.error("Failed to remove tag " + prevTag + " from room: " + err);
|
||||||
|
Modal.createTrackedDialog('Failed to remove tag from room', '', ErrorDialog, {
|
||||||
|
title: _t('Failed to remove tag %(tagName)s from room', {tagName: prevTag}),
|
||||||
|
description: ((err && err.message) ? err.message : _t('Operation failed')),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we moved lists or the ordering changed, add the new tag
|
||||||
|
if (newTag && newTag !== 'im.vector.fake.direct' &&
|
||||||
|
(hasChangedSubLists || newOrder)
|
||||||
|
) {
|
||||||
|
// Optimistic update of what will happen to the room tags
|
||||||
|
room.tags[newTag] = newOrder;
|
||||||
|
|
||||||
|
MatrixClientPeg.get().setRoomTag(roomId, newTag, newOrder).catch(function(err) {
|
||||||
|
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||||
|
console.error("Failed to add tag " + newTag + " to room: " + err);
|
||||||
|
Modal.createTrackedDialog('Failed to add tag to room', '', ErrorDialog, {
|
||||||
|
title: _t('Failed to add tag %(tagName)s to room', {tagName: newTag}),
|
||||||
|
description: ((err && err.message) ? err.message : _t('Operation failed')),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Refresh to display the optimistic updates - this needs to be done in the
|
||||||
|
// same tick as the drag finishing otherwise the room will pop back to its
|
||||||
|
// previous position - hence no delayed refresh
|
||||||
|
this.refreshRoomList();
|
||||||
|
},
|
||||||
|
|
||||||
_delayedRefreshRoomList: new rate_limited_func(function() {
|
_delayedRefreshRoomList: new rate_limited_func(function() {
|
||||||
this.refreshRoomList();
|
this.refreshRoomList();
|
||||||
}, 500),
|
}, 500),
|
||||||
|
@ -649,6 +749,7 @@ module.exports = React.createClass({
|
||||||
|
|
||||||
const self = this;
|
const self = this;
|
||||||
return (
|
return (
|
||||||
|
<DragDropContext onDragEnd={this.onRoomTileEndDrag}>
|
||||||
<GeminiScrollbar className="mx_RoomList_scrollbar"
|
<GeminiScrollbar className="mx_RoomList_scrollbar"
|
||||||
autoshow={true} onScroll={self._whenScrolling} ref="gemscroll">
|
autoshow={true} onScroll={self._whenScrolling} ref="gemscroll">
|
||||||
<div className="mx_RoomList">
|
<div className="mx_RoomList">
|
||||||
|
@ -757,6 +858,7 @@ module.exports = React.createClass({
|
||||||
onShowMoreRooms={self.onShowMoreRooms} />
|
onShowMoreRooms={self.onShowMoreRooms} />
|
||||||
</div>
|
</div>
|
||||||
</GeminiScrollbar>
|
</GeminiScrollbar>
|
||||||
|
</DragDropContext>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -35,10 +35,7 @@ module.exports = React.createClass({
|
||||||
displayName: 'RoomTile',
|
displayName: 'RoomTile',
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
connectDragSource: PropTypes.func,
|
|
||||||
connectDropTarget: PropTypes.func,
|
|
||||||
onClick: PropTypes.func,
|
onClick: PropTypes.func,
|
||||||
isDragging: PropTypes.bool,
|
|
||||||
|
|
||||||
room: PropTypes.object.isRequired,
|
room: PropTypes.object.isRequired,
|
||||||
collapsed: PropTypes.bool.isRequired,
|
collapsed: PropTypes.bool.isRequired,
|
||||||
|
@ -256,16 +253,7 @@ module.exports = React.createClass({
|
||||||
directMessageIndicator = <img src="img/icon_person.svg" className="mx_RoomTile_dm" width="11" height="13" alt="dm" />;
|
directMessageIndicator = <img src="img/icon_person.svg" className="mx_RoomTile_dm" width="11" height="13" alt="dm" />;
|
||||||
}
|
}
|
||||||
|
|
||||||
// These props are injected by React DnD,
|
return <AccessibleButton className={classes} tabIndex="0" onClick={this.onClick} onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave}>
|
||||||
// as defined by your `collect` function above:
|
|
||||||
const isDragging = this.props.isDragging;
|
|
||||||
const connectDragSource = this.props.connectDragSource;
|
|
||||||
const connectDropTarget = this.props.connectDropTarget;
|
|
||||||
|
|
||||||
|
|
||||||
let ret = (
|
|
||||||
<div> { /* Only native elements can be wrapped in a DnD object. */ }
|
|
||||||
<AccessibleButton className={classes} tabIndex="0" onClick={this.onClick} onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave}>
|
|
||||||
<div className={avatarClasses}>
|
<div className={avatarClasses}>
|
||||||
<div className="mx_RoomTile_avatar_container">
|
<div className="mx_RoomTile_avatar_container">
|
||||||
<RoomAvatar room={this.props.room} width={24} height={24} />
|
<RoomAvatar room={this.props.room} width={24} height={24} />
|
||||||
|
@ -278,13 +266,6 @@ module.exports = React.createClass({
|
||||||
</div>
|
</div>
|
||||||
{ /* { incomingCallBox } */ }
|
{ /* { incomingCallBox } */ }
|
||||||
{ tooltip }
|
{ tooltip }
|
||||||
</AccessibleButton>
|
</AccessibleButton>;
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
if (connectDropTarget) ret = connectDropTarget(ret);
|
|
||||||
if (connectDragSource) ret = connectDragSource(ret);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -781,7 +781,7 @@
|
||||||
"You have no visible notifications": "You have no visible notifications",
|
"You have no visible notifications": "You have no visible notifications",
|
||||||
"Scroll to bottom of page": "Scroll to bottom of page",
|
"Scroll to bottom of page": "Scroll to bottom of page",
|
||||||
"Message not sent due to unknown devices being present": "Message not sent due to unknown devices being present",
|
"Message not sent due to unknown devices being present": "Message not sent due to unknown devices being present",
|
||||||
"<showDevicesText>Show devices</showDevicesText>, <sendAnywayText>mark devices known and send</sendAnywayText> or <cancelText>cancel all</cancelText>.": "<showDevicesText>Show devices</showDevicesText>, <sendAnywayText>mark devices known and send</sendAnywayText> or <cancelText>cancel all</cancelText>.",
|
"<showDevicesText>Show devices</showDevicesText>, <sendAnywayText>send anyway</sendAnywayText> or <cancelText>cancel</cancelText>.": "<showDevicesText>Show devices</showDevicesText>, <sendAnywayText>send anyway</sendAnywayText> or <cancelText>cancel</cancelText>.",
|
||||||
"%(count)s of your messages have not been sent.|other": "Some of your messages have not been sent.",
|
"%(count)s of your messages have not been sent.|other": "Some of your messages have not been sent.",
|
||||||
"%(count)s of your messages have not been sent.|one": "Your message was not sent.",
|
"%(count)s of your messages have not been sent.|one": "Your message was not sent.",
|
||||||
"%(count)s <resendText>Resend all</resendText> or <cancelText>cancel all</cancelText> now. You can also select individual messages to resend or cancel.|other": "<resendText>Resend all</resendText> or <cancelText>cancel all</cancelText> now. You can also select individual messages to resend or cancel.",
|
"%(count)s <resendText>Resend all</resendText> or <cancelText>cancel all</cancelText> now. You can also select individual messages to resend or cancel.|other": "<resendText>Resend all</resendText> or <cancelText>cancel all</cancelText> now. You can also select individual messages to resend or cancel.",
|
||||||
|
@ -980,5 +980,6 @@
|
||||||
"Whether or not you're using the Richtext mode of the Rich Text Editor": "Whether or not you're using the Richtext mode of the Rich Text Editor",
|
"Whether or not you're using the Richtext mode of the Rich Text Editor": "Whether or not you're using the Richtext mode of the Rich Text Editor",
|
||||||
"Your homeserver's URL": "Your homeserver's URL",
|
"Your homeserver's URL": "Your homeserver's URL",
|
||||||
"Your identity server's URL": "Your identity server's URL",
|
"Your identity server's URL": "Your identity server's URL",
|
||||||
"<a>In reply to</a> <pill>": "<a>In reply to</a> <pill>"
|
"<a>In reply to</a> <pill>": "<a>In reply to</a> <pill>",
|
||||||
|
"This room is not public. You will not be able to rejoin without an invite.": "This room is not public. You will not be able to rejoin without an invite."
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue