Merge branch 'develop' into luke/fix-scrollbars-on-jump-to-first
commit
b3bb2d2aa5
|
@ -35,7 +35,7 @@ Head to the explanations under Steb 2b
|
||||||
## Step 2b: Adding a new language
|
## Step 2b: Adding a new language
|
||||||
|
|
||||||
1. Go to one of the projects listed https://translate.riot.im/projects/riot-web/
|
1. Go to one of the projects listed https://translate.riot.im/projects/riot-web/
|
||||||
2. Click the ``Start new language`` button at the bottom
|
2. Click the ``Start new translation`` button at the bottom
|
||||||
3. Select a language
|
3. Select a language
|
||||||
4. Start translating like in 2a.3
|
4. Start translating like in 2a.3
|
||||||
5. Repeat these steps for the other projects which are listed at the link of step 2b.1
|
5. Repeat these steps for the other projects which are listed at the link of step 2b.1
|
||||||
|
|
|
@ -17,22 +17,31 @@ limitations under the License.
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
import { DragDropContext } from 'react-beautiful-dnd';
|
||||||
|
import { MatrixClient } from 'matrix-js-sdk';
|
||||||
import { KeyCode } from 'matrix-react-sdk/lib/Keyboard';
|
import { KeyCode } from 'matrix-react-sdk/lib/Keyboard';
|
||||||
import sdk from 'matrix-react-sdk';
|
import sdk from 'matrix-react-sdk';
|
||||||
import dis from 'matrix-react-sdk/lib/dispatcher';
|
import dis from 'matrix-react-sdk/lib/dispatcher';
|
||||||
import MatrixClientPeg from 'matrix-react-sdk/lib/MatrixClientPeg';
|
|
||||||
import CallHandler from 'matrix-react-sdk/lib/CallHandler';
|
|
||||||
import AccessibleButton from 'matrix-react-sdk/lib/components/views/elements/AccessibleButton';
|
|
||||||
import VectorConferenceHandler from '../../VectorConferenceHandler';
|
import VectorConferenceHandler from '../../VectorConferenceHandler';
|
||||||
|
|
||||||
|
import SettingsStore from 'matrix-react-sdk/lib/settings/SettingsStore';
|
||||||
|
import TagOrderActions from 'matrix-react-sdk/lib/actions/TagOrderActions';
|
||||||
|
import RoomListActions from 'matrix-react-sdk/lib/actions/RoomListActions';
|
||||||
|
|
||||||
|
|
||||||
var LeftPanel = React.createClass({
|
var LeftPanel = React.createClass({
|
||||||
displayName: 'LeftPanel',
|
displayName: 'LeftPanel',
|
||||||
|
|
||||||
// NB. If you add props, don't forget to update
|
// NB. If you add props, don't forget to update
|
||||||
// shouldComponentUpdate!
|
// shouldComponentUpdate!
|
||||||
propTypes: {
|
propTypes: {
|
||||||
collapsed: React.PropTypes.bool.isRequired,
|
collapsed: PropTypes.bool.isRequired,
|
||||||
|
},
|
||||||
|
|
||||||
|
contextTypes: {
|
||||||
|
matrixClient: PropTypes.instanceOf(MatrixClient),
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
|
@ -161,13 +170,59 @@ var LeftPanel = React.createClass({
|
||||||
this.setState({ searchFilter: term });
|
this.setState({ searchFilter: term });
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onDragEnd: function(result) {
|
||||||
|
// Dragged to an invalid destination, not onto a droppable
|
||||||
|
if (!result.destination) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const dest = result.destination.droppableId;
|
||||||
|
|
||||||
|
if (dest === 'tag-panel-droppable') {
|
||||||
|
// Dispatch synchronously so that the TagPanel receives an
|
||||||
|
// optimistic update from TagOrderStore before the previous
|
||||||
|
// state is shown.
|
||||||
|
dis.dispatch(TagOrderActions.moveTag(
|
||||||
|
this.context.matrixClient,
|
||||||
|
result.draggableId,
|
||||||
|
result.destination.index,
|
||||||
|
), true);
|
||||||
|
} else {
|
||||||
|
this.onRoomTileEndDrag(result);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onRoomTileEndDrag: function(result) {
|
||||||
|
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 oldIndex = result.source.index;
|
||||||
|
const newIndex = result.destination.index;
|
||||||
|
|
||||||
|
dis.dispatch(RoomListActions.tagRoom(
|
||||||
|
this.context.matrixClient,
|
||||||
|
this.context.matrixClient.getRoom(roomId),
|
||||||
|
prevTag, newTag,
|
||||||
|
oldIndex, newIndex,
|
||||||
|
), true);
|
||||||
|
},
|
||||||
|
|
||||||
|
collectRoomList: function(ref) {
|
||||||
|
this._roomList = ref;
|
||||||
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
const RoomList = sdk.getComponent('rooms.RoomList');
|
const RoomList = sdk.getComponent('rooms.RoomList');
|
||||||
|
const TagPanel = sdk.getComponent('structures.TagPanel');
|
||||||
const BottomLeftMenu = sdk.getComponent('structures.BottomLeftMenu');
|
const BottomLeftMenu = sdk.getComponent('structures.BottomLeftMenu');
|
||||||
const CallPreview = sdk.getComponent('voip.CallPreview');
|
const CallPreview = sdk.getComponent('voip.CallPreview');
|
||||||
|
|
||||||
let topBox;
|
let topBox;
|
||||||
if (MatrixClientPeg.get().isGuest()) {
|
if (this.context.matrixClient.isGuest()) {
|
||||||
const LoginBox = sdk.getComponent('structures.LoginBox');
|
const LoginBox = sdk.getComponent('structures.LoginBox');
|
||||||
topBox = <LoginBox collapsed={ this.props.collapsed }/>;
|
topBox = <LoginBox collapsed={ this.props.collapsed }/>;
|
||||||
} else {
|
} else {
|
||||||
|
@ -183,16 +238,33 @@ var LeftPanel = React.createClass({
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const tagPanelEnabled = SettingsStore.isFeatureEnabled("feature_tag_panel");
|
||||||
|
const tagPanel = tagPanelEnabled ? <TagPanel /> : <div />;
|
||||||
|
|
||||||
|
const containerClasses = classNames(
|
||||||
|
"mx_LeftPanel_container",
|
||||||
|
{
|
||||||
|
"mx_LeftPanel_container_collapsed": this.props.collapsed,
|
||||||
|
"mx_LeftPanel_container_hasTagPanel": tagPanelEnabled,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<aside className={classes} onKeyDown={ this._onKeyDown } onFocus={ this._onFocus } onBlur={ this._onBlur }>
|
<DragDropContext onDragEnd={this.onDragEnd}>
|
||||||
{ topBox }
|
<div className={containerClasses}>
|
||||||
<CallPreview ConferenceHandler={VectorConferenceHandler} />
|
{ tagPanel }
|
||||||
<RoomList
|
<aside className={classes} onKeyDown={ this._onKeyDown } onFocus={ this._onFocus } onBlur={ this._onBlur }>
|
||||||
collapsed={this.props.collapsed}
|
{ topBox }
|
||||||
searchFilter={this.state.searchFilter}
|
<CallPreview ConferenceHandler={VectorConferenceHandler} />
|
||||||
ConferenceHandler={VectorConferenceHandler} />
|
<RoomList
|
||||||
<BottomLeftMenu collapsed={this.props.collapsed}/>
|
ref={this.collectRoomList}
|
||||||
</aside>
|
collapsed={this.props.collapsed}
|
||||||
|
searchFilter={this.state.searchFilter}
|
||||||
|
ConferenceHandler={VectorConferenceHandler} />
|
||||||
|
<BottomLeftMenu collapsed={this.props.collapsed}/>
|
||||||
|
</aside>
|
||||||
|
</div>
|
||||||
|
</DragDropContext>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -38,31 +38,6 @@ var debug = false;
|
||||||
|
|
||||||
const TRUNCATE_AT = 10;
|
const TRUNCATE_AT = 10;
|
||||||
|
|
||||||
var roomListTarget = {
|
|
||||||
canDrop: function() {
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
|
|
||||||
drop: function(props, monitor, component) {
|
|
||||||
if (debug) console.log("dropped on sublist")
|
|
||||||
},
|
|
||||||
|
|
||||||
hover: function(props, monitor, component) {
|
|
||||||
var item = monitor.getItem();
|
|
||||||
|
|
||||||
if (component.state.sortedList.length == 0 && props.editable) {
|
|
||||||
if (debug) console.log("hovering on sublist " + props.label + ", isOver=" + monitor.isOver());
|
|
||||||
|
|
||||||
if (item.targetList !== component) {
|
|
||||||
item.targetList.removeRoomTile(item.room);
|
|
||||||
item.targetList = component;
|
|
||||||
}
|
|
||||||
|
|
||||||
component.moveRoomTile(item.room, 0);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
var RoomSubList = React.createClass({
|
var RoomSubList = React.createClass({
|
||||||
displayName: 'RoomSubList',
|
displayName: 'RoomSubList',
|
||||||
|
|
||||||
|
@ -110,13 +85,17 @@ var RoomSubList = React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
componentWillMount: function() {
|
componentWillMount: function() {
|
||||||
this.sortList(this.applySearchFilter(this.props.list, this.props.searchFilter), this.props.order);
|
this.setState({
|
||||||
|
sortedList: this.applySearchFilter(this.props.list, this.props.searchFilter),
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
componentWillReceiveProps: function(newProps) {
|
componentWillReceiveProps: function(newProps) {
|
||||||
// order the room list appropriately before we re-render
|
// order the room list appropriately before we re-render
|
||||||
//if (debug) console.log("received new props, list = " + newProps.list);
|
//if (debug) console.log("received new props, list = " + newProps.list);
|
||||||
this.sortList(this.applySearchFilter(newProps.list, newProps.searchFilter), newProps.order);
|
this.setState({
|
||||||
|
sortedList: this.applySearchFilter(newProps.list, newProps.searchFilter),
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
applySearchFilter: function(list, filter) {
|
applySearchFilter: function(list, filter) {
|
||||||
|
@ -164,71 +143,6 @@ var RoomSubList = React.createClass({
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
tsOfNewestEvent: function(room) {
|
|
||||||
for (var i = room.timeline.length - 1; i >= 0; --i) {
|
|
||||||
var ev = room.timeline[i];
|
|
||||||
if (ev.getTs() &&
|
|
||||||
(Unread.eventTriggersUnreadCount(ev) ||
|
|
||||||
(ev.getSender() === MatrixClientPeg.get().credentials.userId))
|
|
||||||
) {
|
|
||||||
return ev.getTs();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// we might only have events that don't trigger the unread indicator,
|
|
||||||
// in which case use the oldest event even if normally it wouldn't count.
|
|
||||||
// This is better than just assuming the last event was forever ago.
|
|
||||||
if (room.timeline.length && room.timeline[0].getTs()) {
|
|
||||||
return room.timeline[0].getTs();
|
|
||||||
} else {
|
|
||||||
return Number.MAX_SAFE_INTEGER;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// TODO: factor the comparators back out into a generic comparator
|
|
||||||
// so that view_prev_room and view_next_room can do the right thing
|
|
||||||
|
|
||||||
recentsComparator: function(roomA, roomB) {
|
|
||||||
return this.tsOfNewestEvent(roomB) - this.tsOfNewestEvent(roomA);
|
|
||||||
},
|
|
||||||
|
|
||||||
lexicographicalComparator: function(roomA, roomB) {
|
|
||||||
return roomA.name > roomB.name ? 1 : -1;
|
|
||||||
},
|
|
||||||
|
|
||||||
// Generates the manual comparator using the given list
|
|
||||||
manualComparator: function(roomA, roomB) {
|
|
||||||
if (!roomA.tags[this.props.tagName] || !roomB.tags[this.props.tagName]) return 0;
|
|
||||||
|
|
||||||
// Make sure the room tag has an order element, if not set it to be the bottom
|
|
||||||
var a = roomA.tags[this.props.tagName].order;
|
|
||||||
var b = roomB.tags[this.props.tagName].order;
|
|
||||||
|
|
||||||
// Order undefined room tag orders to the bottom
|
|
||||||
if (a === undefined && b !== undefined) {
|
|
||||||
return 1;
|
|
||||||
} else if (a !== undefined && b === undefined) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return a == b ? this.lexicographicalComparator(roomA, roomB) : ( a > b ? 1 : -1);
|
|
||||||
},
|
|
||||||
|
|
||||||
sortList: function(list, order) {
|
|
||||||
if (list === undefined) list = this.state.sortedList;
|
|
||||||
if (order === undefined) order = this.props.order;
|
|
||||||
var comparator;
|
|
||||||
list = list || [];
|
|
||||||
if (order === "manual") comparator = this.manualComparator;
|
|
||||||
if (order === "recent") comparator = this.recentsComparator;
|
|
||||||
|
|
||||||
// Fix undefined orders here, and make sure the backend gets updated as well
|
|
||||||
this._fixUndefinedOrder(list);
|
|
||||||
|
|
||||||
//if (debug) console.log("sorting list for sublist " + this.props.label + " with length " + list.length + ", this.props.list = " + this.props.list);
|
|
||||||
this.setState({ sortedList: list.sort(comparator) });
|
|
||||||
},
|
|
||||||
|
|
||||||
_shouldShowNotifBadge: function(roomNotifState) {
|
_shouldShowNotifBadge: function(roomNotifState) {
|
||||||
const showBadgeInStates = [RoomNotifs.ALL_MESSAGES, RoomNotifs.ALL_MESSAGES_LOUD];
|
const showBadgeInStates = [RoomNotifs.ALL_MESSAGES, RoomNotifs.ALL_MESSAGES_LOUD];
|
||||||
return showBadgeInStates.indexOf(roomNotifState) > -1;
|
return showBadgeInStates.indexOf(roomNotifState) > -1;
|
||||||
|
@ -279,98 +193,6 @@ var RoomSubList = React.createClass({
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
},
|
},
|
||||||
|
|
||||||
moveRoomTile: function(room, atIndex) {
|
|
||||||
if (debug) console.log("moveRoomTile: id " + room.roomId + ", atIndex " + atIndex);
|
|
||||||
//console.log("moveRoomTile before: " + JSON.stringify(this.state.rooms));
|
|
||||||
var found = this.findRoomTile(room);
|
|
||||||
var rooms = this.state.sortedList;
|
|
||||||
if (found.room) {
|
|
||||||
if (debug) console.log("removing at index " + found.index + " and adding at index " + atIndex);
|
|
||||||
rooms.splice(found.index, 1);
|
|
||||||
rooms.splice(atIndex, 0, found.room);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (debug) console.log("Adding at index " + atIndex);
|
|
||||||
rooms.splice(atIndex, 0, room);
|
|
||||||
}
|
|
||||||
this.setState({ sortedList: rooms });
|
|
||||||
// console.log("moveRoomTile after: " + JSON.stringify(this.state.rooms));
|
|
||||||
},
|
|
||||||
|
|
||||||
// XXX: this isn't invoked via a property method but indirectly via
|
|
||||||
// the roomList property method. Unsure how evil this is.
|
|
||||||
removeRoomTile: function(room) {
|
|
||||||
if (debug) console.log("remove room " + room.roomId);
|
|
||||||
var found = this.findRoomTile(room);
|
|
||||||
var rooms = this.state.sortedList;
|
|
||||||
if (found.room) {
|
|
||||||
rooms.splice(found.index, 1);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
console.warn("Can't remove room " + room.roomId + " - can't find it");
|
|
||||||
}
|
|
||||||
this.setState({ sortedList: rooms });
|
|
||||||
},
|
|
||||||
|
|
||||||
findRoomTile: function(room) {
|
|
||||||
var index = this.state.sortedList.indexOf(room);
|
|
||||||
if (index >= 0) {
|
|
||||||
// console.log("found: room: " + room.roomId + " with index " + index);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (debug) console.log("didn't find room");
|
|
||||||
room = null;
|
|
||||||
}
|
|
||||||
return ({
|
|
||||||
room: room,
|
|
||||||
index: index,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
calcManualOrderTagData: function(index) {
|
|
||||||
// we sort rooms by the lexicographic ordering of the 'order' metadata on their tags.
|
|
||||||
// for convenience, we calculate this for now a floating point number between 0.0 and 1.0.
|
|
||||||
|
|
||||||
let orderA = 0.0; // by default we're next to the beginning of the list
|
|
||||||
if (index > 0) {
|
|
||||||
const prevTag = this.state.sortedList[index - 1].tags[this.props.tagName];
|
|
||||||
if (!prevTag) {
|
|
||||||
console.error("Previous room in sublist is not tagged to be in this list. This should never happen.");
|
|
||||||
} else if (prevTag.order === undefined) {
|
|
||||||
console.error("Previous room in sublist has no ordering metadata. This should never happen.");
|
|
||||||
} else {
|
|
||||||
orderA = prevTag.order;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let orderB = 1.0; // by default we're next to the end of the list too
|
|
||||||
if (index < this.state.sortedList.length - 1) {
|
|
||||||
const nextTag = this.state.sortedList[index + 1].tags[this.props.tagName];
|
|
||||||
if (!nextTag) {
|
|
||||||
console.error("Next room in sublist is not tagged to be in this list. This should never happen.");
|
|
||||||
} else if (nextTag.order === undefined) {
|
|
||||||
console.error("Next room in sublist has no ordering metadata. This should never happen.");
|
|
||||||
} else {
|
|
||||||
orderB = nextTag.order;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const order = (orderA + orderB) / 2.0;
|
|
||||||
|
|
||||||
if (order === orderA || order === orderB) {
|
|
||||||
console.error("Cannot describe new list position. This should be incredibly unlikely.");
|
|
||||||
this.state.sortedList.forEach((room, index) => {
|
|
||||||
MatrixClientPeg.get().setRoomTag(
|
|
||||||
room.roomId, this.props.tagName,
|
|
||||||
{order: index / this.state.sortedList.length},
|
|
||||||
);
|
|
||||||
});
|
|
||||||
return index / this.state.sortedList.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
return order;
|
|
||||||
},
|
|
||||||
|
|
||||||
makeRoomTiles: function() {
|
makeRoomTiles: function() {
|
||||||
var self = this;
|
var self = this;
|
||||||
var DNDRoomTile = sdk.getComponent("rooms.DNDRoomTile");
|
var DNDRoomTile = sdk.getComponent("rooms.DNDRoomTile");
|
||||||
|
@ -497,47 +319,6 @@ var RoomSubList = React.createClass({
|
||||||
this.props.onHeaderClick(false);
|
this.props.onHeaderClick(false);
|
||||||
},
|
},
|
||||||
|
|
||||||
// Fix any undefined order elements of a room in a manual ordered list
|
|
||||||
// room.tag[tagname].order
|
|
||||||
_fixUndefinedOrder: function(list) {
|
|
||||||
if (this.props.order === "manual") {
|
|
||||||
var order = 0.0;
|
|
||||||
var self = this;
|
|
||||||
|
|
||||||
// Find the highest (lowest position) order of a room in a manual ordered list
|
|
||||||
list.forEach(function(room) {
|
|
||||||
if (room.tags.hasOwnProperty(self.props.tagName)) {
|
|
||||||
if (order < room.tags[self.props.tagName].order) {
|
|
||||||
order = room.tags[self.props.tagName].order;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Fix any undefined order elements of a room in a manual ordered list
|
|
||||||
// Do this one at a time, as each time a rooms tag data is updated the RoomList
|
|
||||||
// gets triggered and another list is passed in. Doing it one at a time means that
|
|
||||||
// we always correctly calculate the highest order for the list - stops multiple
|
|
||||||
// rooms getting the same order. This is only really relevant for the first time this
|
|
||||||
// is run with historical room tag data, after that there should only be undefined
|
|
||||||
// in the list at a time anyway.
|
|
||||||
for (let i = 0; i < list.length; i++) {
|
|
||||||
if (list[i].tags[self.props.tagName] && list[i].tags[self.props.tagName].order === undefined) {
|
|
||||||
MatrixClientPeg.get().setRoomTag(list[i].roomId, self.props.tagName, {order: (order + 1.0) / 2.0}).finally(function() {
|
|
||||||
// Do any final stuff here
|
|
||||||
}).catch(function(err) {
|
|
||||||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
|
||||||
console.error("Failed to add tag " + self.props.tagName + " to room" + err);
|
|
||||||
Modal.createTrackedDialog('Failed to add tag to room', '', ErrorDialog, {
|
|
||||||
title: _t('Failed to add tag %(tagName)s to room', {tagName: self.props.tagName}),
|
|
||||||
description: ((err && err.message) ? err.message : _t('Operation failed')),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
var connectDropTarget = this.props.connectDropTarget;
|
var connectDropTarget = this.props.connectDropTarget;
|
||||||
var TruncatedList = sdk.getComponent('elements.TruncatedList');
|
var TruncatedList = sdk.getComponent('elements.TruncatedList');
|
||||||
|
@ -572,13 +353,17 @@ var RoomSubList = React.createClass({
|
||||||
{ subList }
|
{ subList }
|
||||||
</div>;
|
</div>;
|
||||||
|
|
||||||
return this.props.editable ? <Droppable droppableId={"room-sub-list-droppable_" + this.props.tagName}>
|
return this.props.editable ?
|
||||||
{ (provided, snapshot) => (
|
<Droppable
|
||||||
<div ref={provided.innerRef}>
|
droppableId={"room-sub-list-droppable_" + this.props.tagName}
|
||||||
{ subListContent }
|
type="draggable-RoomTile"
|
||||||
</div>
|
>
|
||||||
) }
|
{ (provided, snapshot) => (
|
||||||
</Droppable> : subListContent;
|
<div ref={provided.innerRef}>
|
||||||
|
{ subListContent }
|
||||||
|
</div>
|
||||||
|
) }
|
||||||
|
</Droppable> : subListContent;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
var Loader = sdk.getComponent("elements.Spinner");
|
var Loader = sdk.getComponent("elements.Spinner");
|
||||||
|
|
|
@ -20,6 +20,7 @@ limitations under the License.
|
||||||
import Promise from 'bluebird';
|
import Promise from 'bluebird';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import sdk from 'matrix-react-sdk';
|
import sdk from 'matrix-react-sdk';
|
||||||
import { _t, _td } from 'matrix-react-sdk/lib/languageHandler';
|
import { _t, _td } from 'matrix-react-sdk/lib/languageHandler';
|
||||||
import MatrixClientPeg from 'matrix-react-sdk/lib/MatrixClientPeg';
|
import MatrixClientPeg from 'matrix-react-sdk/lib/MatrixClientPeg';
|
||||||
|
@ -28,14 +29,15 @@ import DMRoomMap from 'matrix-react-sdk/lib/utils/DMRoomMap';
|
||||||
import * as Rooms from 'matrix-react-sdk/lib/Rooms';
|
import * as Rooms from 'matrix-react-sdk/lib/Rooms';
|
||||||
import * as RoomNotifs from 'matrix-react-sdk/lib/RoomNotifs';
|
import * as RoomNotifs from 'matrix-react-sdk/lib/RoomNotifs';
|
||||||
import Modal from 'matrix-react-sdk/lib/Modal';
|
import Modal from 'matrix-react-sdk/lib/Modal';
|
||||||
|
import RoomListActions from 'matrix-react-sdk/lib/actions/RoomListActions';
|
||||||
|
|
||||||
module.exports = React.createClass({
|
module.exports = React.createClass({
|
||||||
displayName: 'RoomTileContextMenu',
|
displayName: 'RoomTileContextMenu',
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
room: React.PropTypes.object.isRequired,
|
room: PropTypes.object.isRequired,
|
||||||
/* callback called when the menu is dismissed */
|
/* callback called when the menu is dismissed */
|
||||||
onFinished: React.PropTypes.func,
|
onFinished: PropTypes.func,
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialState() {
|
getInitialState() {
|
||||||
|
@ -45,7 +47,7 @@ module.exports = React.createClass({
|
||||||
isFavourite: this.props.room.tags.hasOwnProperty("m.favourite"),
|
isFavourite: this.props.room.tags.hasOwnProperty("m.favourite"),
|
||||||
isLowPriority: this.props.room.tags.hasOwnProperty("m.lowpriority"),
|
isLowPriority: this.props.room.tags.hasOwnProperty("m.lowpriority"),
|
||||||
isDirectMessage: Boolean(dmRoomMap.getUserIdForRoomId(this.props.room.roomId)),
|
isDirectMessage: Boolean(dmRoomMap.getUserIdForRoomId(this.props.room.roomId)),
|
||||||
}
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
componentWillMount: function() {
|
componentWillMount: function() {
|
||||||
|
@ -57,42 +59,16 @@ module.exports = React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
_toggleTag: function(tagNameOn, tagNameOff) {
|
_toggleTag: function(tagNameOn, tagNameOff) {
|
||||||
var self = this;
|
if (!MatrixClientPeg.get().isGuest()) {
|
||||||
const roomId = this.props.room.roomId;
|
Promise.delay(500).then(() => {
|
||||||
var cli = MatrixClientPeg.get();
|
dis.dispatch(RoomListActions.tagRoom(
|
||||||
if (!cli.isGuest()) {
|
MatrixClientPeg.get(),
|
||||||
Promise.delay(500).then(function() {
|
this.props.room,
|
||||||
if (tagNameOff !== null && tagNameOff !== undefined) {
|
tagNameOff, tagNameOn,
|
||||||
cli.deleteRoomTag(roomId, tagNameOff).finally(function() {
|
undefined, 0,
|
||||||
// Close the context menu
|
), true);
|
||||||
if (self.props.onFinished) {
|
|
||||||
self.props.onFinished();
|
|
||||||
};
|
|
||||||
}).catch(function(err) {
|
|
||||||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
|
||||||
Modal.createTrackedDialog('Failed to remove tag from room 1', '', ErrorDialog, {
|
|
||||||
title: _t('Failed to remove tag %(tagName)s from room', {tagName: tagNameOff}),
|
|
||||||
description: ((err && err.message) ? err.message : _t('Operation failed')),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tagNameOn !== null && tagNameOn !== undefined) {
|
this.props.onFinished();
|
||||||
// If the tag ordering meta data is required, it is added by
|
|
||||||
// the RoomSubList when it sorts its rooms
|
|
||||||
cli.setRoomTag(roomId, tagNameOn, {}).finally(function() {
|
|
||||||
// Close the context menu
|
|
||||||
if (self.props.onFinished) {
|
|
||||||
self.props.onFinished();
|
|
||||||
};
|
|
||||||
}).catch(function(err) {
|
|
||||||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
|
||||||
Modal.createTrackedDialog('Failed to remove tag from room 2', '', ErrorDialog, {
|
|
||||||
title: _t('Failed to remove tag %(tagName)s from room', {tagName: tagNameOn}),
|
|
||||||
description: ((err && err.message) ? err.message : _t('Operation failed')),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -132,22 +108,22 @@ module.exports = React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
_onClickDM: function() {
|
_onClickDM: function() {
|
||||||
|
if (MatrixClientPeg.get().isGuest()) return;
|
||||||
|
|
||||||
const newIsDirectMessage = !this.state.isDirectMessage;
|
const newIsDirectMessage = !this.state.isDirectMessage;
|
||||||
this.setState({
|
this.setState({
|
||||||
isDirectMessage: newIsDirectMessage,
|
isDirectMessage: newIsDirectMessage,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (MatrixClientPeg.get().isGuest()) return;
|
|
||||||
|
|
||||||
Rooms.guessAndSetDMRoom(
|
Rooms.guessAndSetDMRoom(
|
||||||
this.props.room, newIsDirectMessage
|
this.props.room, newIsDirectMessage,
|
||||||
).delay(500).finally(() => {
|
).delay(500).finally(() => {
|
||||||
// Close the context menu
|
// Close the context menu
|
||||||
if (this.props.onFinished) {
|
if (this.props.onFinished) {
|
||||||
this.props.onFinished();
|
this.props.onFinished();
|
||||||
};
|
}
|
||||||
}, (err) => {
|
}, (err) => {
|
||||||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||||
Modal.createTrackedDialog('Failed to set Direct Message status of room', '', ErrorDialog, {
|
Modal.createTrackedDialog('Failed to set Direct Message status of room', '', ErrorDialog, {
|
||||||
title: _t('Failed to set Direct Message status of room'),
|
title: _t('Failed to set Direct Message status of room'),
|
||||||
description: ((err && err.message) ? err.message : _t('Operation failed')),
|
description: ((err && err.message) ? err.message : _t('Operation failed')),
|
||||||
|
@ -165,7 +141,7 @@ module.exports = React.createClass({
|
||||||
// Close the context menu
|
// Close the context menu
|
||||||
if (this.props.onFinished) {
|
if (this.props.onFinished) {
|
||||||
this.props.onFinished();
|
this.props.onFinished();
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_onClickReject: function() {
|
_onClickReject: function() {
|
||||||
|
@ -177,7 +153,7 @@ module.exports = React.createClass({
|
||||||
// Close the context menu
|
// Close the context menu
|
||||||
if (this.props.onFinished) {
|
if (this.props.onFinished) {
|
||||||
this.props.onFinished();
|
this.props.onFinished();
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_onClickForget: function() {
|
_onClickForget: function() {
|
||||||
|
@ -185,8 +161,8 @@ module.exports = React.createClass({
|
||||||
MatrixClientPeg.get().forget(this.props.room.roomId).done(function() {
|
MatrixClientPeg.get().forget(this.props.room.roomId).done(function() {
|
||||||
dis.dispatch({ action: 'view_next_room' });
|
dis.dispatch({ action: 'view_next_room' });
|
||||||
}, function(err) {
|
}, function(err) {
|
||||||
var errCode = err.errcode || _td("unknown error code");
|
const errCode = err.errcode || _td("unknown error code");
|
||||||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||||
Modal.createTrackedDialog('Failed to forget room', '', ErrorDialog, {
|
Modal.createTrackedDialog('Failed to forget room', '', ErrorDialog, {
|
||||||
title: _t('Failed to forget room %(errCode)s', {errCode: errCode}),
|
title: _t('Failed to forget room %(errCode)s', {errCode: errCode}),
|
||||||
description: ((err && err.message) ? err.message : _t('Operation failed')),
|
description: ((err && err.message) ? err.message : _t('Operation failed')),
|
||||||
|
@ -196,20 +172,19 @@ module.exports = React.createClass({
|
||||||
// Close the context menu
|
// Close the context menu
|
||||||
if (this.props.onFinished) {
|
if (this.props.onFinished) {
|
||||||
this.props.onFinished();
|
this.props.onFinished();
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_saveNotifState: function(newState) {
|
_saveNotifState: function(newState) {
|
||||||
|
if (MatrixClientPeg.get().isGuest()) return;
|
||||||
|
|
||||||
const oldState = this.state.roomNotifState;
|
const oldState = this.state.roomNotifState;
|
||||||
const roomId = this.props.room.roomId;
|
const roomId = this.props.room.roomId;
|
||||||
var cli = MatrixClientPeg.get();
|
|
||||||
|
|
||||||
if (cli.isGuest()) return;
|
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
roomNotifState: newState,
|
roomNotifState: newState,
|
||||||
});
|
});
|
||||||
RoomNotifs.setRoomNotifsState(this.props.room.roomId, newState).done(() => {
|
RoomNotifs.setRoomNotifsState(roomId, newState).done(() => {
|
||||||
// delay slightly so that the user can see their state change
|
// delay slightly so that the user can see their state change
|
||||||
// before closing the menu
|
// before closing the menu
|
||||||
return Promise.delay(500).then(() => {
|
return Promise.delay(500).then(() => {
|
||||||
|
@ -217,7 +192,7 @@ module.exports = React.createClass({
|
||||||
// Close the context menu
|
// Close the context menu
|
||||||
if (this.props.onFinished) {
|
if (this.props.onFinished) {
|
||||||
this.props.onFinished();
|
this.props.onFinished();
|
||||||
};
|
}
|
||||||
});
|
});
|
||||||
}, (error) => {
|
}, (error) => {
|
||||||
// TODO: some form of error notification to the user
|
// TODO: some form of error notification to the user
|
||||||
|
@ -247,22 +222,22 @@ module.exports = React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
_renderNotifMenu: function() {
|
_renderNotifMenu: function() {
|
||||||
var alertMeClasses = classNames({
|
const alertMeClasses = classNames({
|
||||||
'mx_RoomTileContextMenu_notif_field': true,
|
'mx_RoomTileContextMenu_notif_field': true,
|
||||||
'mx_RoomTileContextMenu_notif_fieldSet': this.state.roomNotifState == RoomNotifs.ALL_MESSAGES_LOUD,
|
'mx_RoomTileContextMenu_notif_fieldSet': this.state.roomNotifState == RoomNotifs.ALL_MESSAGES_LOUD,
|
||||||
});
|
});
|
||||||
|
|
||||||
var allNotifsClasses = classNames({
|
const allNotifsClasses = classNames({
|
||||||
'mx_RoomTileContextMenu_notif_field': true,
|
'mx_RoomTileContextMenu_notif_field': true,
|
||||||
'mx_RoomTileContextMenu_notif_fieldSet': this.state.roomNotifState == RoomNotifs.ALL_MESSAGES,
|
'mx_RoomTileContextMenu_notif_fieldSet': this.state.roomNotifState == RoomNotifs.ALL_MESSAGES,
|
||||||
});
|
});
|
||||||
|
|
||||||
var mentionsClasses = classNames({
|
const mentionsClasses = classNames({
|
||||||
'mx_RoomTileContextMenu_notif_field': true,
|
'mx_RoomTileContextMenu_notif_field': true,
|
||||||
'mx_RoomTileContextMenu_notif_fieldSet': this.state.roomNotifState == RoomNotifs.MENTIONS_ONLY,
|
'mx_RoomTileContextMenu_notif_fieldSet': this.state.roomNotifState == RoomNotifs.MENTIONS_ONLY,
|
||||||
});
|
});
|
||||||
|
|
||||||
var muteNotifsClasses = classNames({
|
const muteNotifsClasses = classNames({
|
||||||
'mx_RoomTileContextMenu_notif_field': true,
|
'mx_RoomTileContextMenu_notif_field': true,
|
||||||
'mx_RoomTileContextMenu_notif_fieldSet': this.state.roomNotifState == RoomNotifs.MUTE,
|
'mx_RoomTileContextMenu_notif_fieldSet': this.state.roomNotifState == RoomNotifs.MUTE,
|
||||||
});
|
});
|
||||||
|
@ -272,22 +247,22 @@ module.exports = React.createClass({
|
||||||
<div className="mx_RoomTileContextMenu_notif_picker" >
|
<div className="mx_RoomTileContextMenu_notif_picker" >
|
||||||
<img src="img/notif-slider.svg" width="20" height="107" />
|
<img src="img/notif-slider.svg" width="20" height="107" />
|
||||||
</div>
|
</div>
|
||||||
<div className={ alertMeClasses } onClick={this._onClickAlertMe} >
|
<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_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_icon mx_filterFlipColor" src="img/icon-context-mute-off-copy.svg" width="16" height="12" />
|
||||||
{ _t('All messages (noisy)') }
|
{ _t('All messages (noisy)') }
|
||||||
</div>
|
</div>
|
||||||
<div className={ allNotifsClasses } onClick={this._onClickAllNotifs} >
|
<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_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_icon mx_filterFlipColor" src="img/icon-context-mute-off.svg" width="16" height="12" />
|
||||||
{ _t('All messages') }
|
{ _t('All messages') }
|
||||||
</div>
|
</div>
|
||||||
<div className={ mentionsClasses } onClick={this._onClickMentions} >
|
<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_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_icon mx_filterFlipColor" src="img/icon-context-mute-mentions.svg" width="16" height="12" />
|
||||||
{ _t('Mentions only') }
|
{ _t('Mentions only') }
|
||||||
</div>
|
</div>
|
||||||
<div className={ muteNotifsClasses } onClick={this._onClickMute} >
|
<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_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_icon mx_filterFlipColor" src="img/icon-context-mute.svg" width="16" height="12" />
|
||||||
{ _t('Mute') }
|
{ _t('Mute') }
|
||||||
|
@ -322,7 +297,7 @@ module.exports = React.createClass({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="mx_RoomTileContextMenu_leave" onClick={ leaveClickHandler } >
|
<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="img/icon_context_delete.svg" width="15" height="15" />
|
||||||
{ leaveText }
|
{ leaveText }
|
||||||
</div>
|
</div>
|
||||||
|
@ -351,17 +326,17 @@ module.exports = React.createClass({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className={ favouriteClasses } onClick={this._onClickFavourite} >
|
<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" 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_set" src="img/icon_context_fave_on.svg" width="15" height="15" />
|
||||||
{ _t('Favourite') }
|
{ _t('Favourite') }
|
||||||
</div>
|
</div>
|
||||||
<div className={ lowPriorityClasses } onClick={this._onClickLowPriority} >
|
<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" 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_set" src="img/icon_context_low_on.svg" width="15" height="15" />
|
||||||
{ _t('Low Priority') }
|
{ _t('Low Priority') }
|
||||||
</div>
|
</div>
|
||||||
<div className={ dmClasses } onClick={this._onClickDM} >
|
<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" 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_set" src="img/icon_context_person_on.svg" width="15" height="15" />
|
||||||
{ _t('Direct Chat') }
|
{ _t('Direct Chat') }
|
||||||
|
@ -372,7 +347,7 @@ module.exports = React.createClass({
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
const myMember = this.props.room.getMember(
|
const myMember = this.props.room.getMember(
|
||||||
MatrixClientPeg.get().credentials.userId
|
MatrixClientPeg.get().credentials.userId,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Can't set notif level or tags on non-join rooms
|
// Can't set notif level or tags on non-join rooms
|
||||||
|
@ -389,5 +364,5 @@ module.exports = React.createClass({
|
||||||
{ this._renderRoomTagMenu() }
|
{ this._renderRoomTagMenu() }
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
/*
|
||||||
|
Copyright 2018 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 { _t } from 'matrix-react-sdk/lib/languageHandler';
|
||||||
|
import dis from 'matrix-react-sdk/lib/dispatcher';
|
||||||
|
import TagOrderActions from 'matrix-react-sdk/lib/actions/TagOrderActions';
|
||||||
|
import MatrixClientPeg from 'matrix-react-sdk/lib/MatrixClientPeg';
|
||||||
|
|
||||||
|
export default class TagTileContextMenu extends React.Component {
|
||||||
|
static propTypes = {
|
||||||
|
tag: PropTypes.string.isRequired,
|
||||||
|
/* callback called when the menu is dismissed */
|
||||||
|
onFinished: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this._onViewCommunityClick = this._onViewCommunityClick.bind(this);
|
||||||
|
this._onRemoveClick = this._onRemoveClick.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
_onViewCommunityClick() {
|
||||||
|
dis.dispatch({
|
||||||
|
action: 'view_group',
|
||||||
|
group_id: this.props.tag,
|
||||||
|
});
|
||||||
|
this.props.onFinished();
|
||||||
|
}
|
||||||
|
|
||||||
|
_onRemoveClick() {
|
||||||
|
dis.dispatch(TagOrderActions.removeTag(
|
||||||
|
// XXX: Context menus don't have a MatrixClient context
|
||||||
|
MatrixClientPeg.get(),
|
||||||
|
this.props.tag,
|
||||||
|
));
|
||||||
|
this.props.onFinished();
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return <div>
|
||||||
|
<div className="mx_TagTileContextMenu_item" onClick={this._onViewCommunityClick} >
|
||||||
|
<img className="mx_TagTileContextMenu_item_icon" src="img/icons-groups.svg" width="15" height="15" />
|
||||||
|
{ _t('View Community') }
|
||||||
|
</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" />
|
||||||
|
{ _t('Remove') }
|
||||||
|
</div>
|
||||||
|
</div>;
|
||||||
|
}
|
||||||
|
}
|
|
@ -41,6 +41,7 @@ export default class DNDRoomTile extends React.Component {
|
||||||
key={props.room.roomId}
|
key={props.room.roomId}
|
||||||
draggableId={props.tagName + '_' + props.room.roomId}
|
draggableId={props.tagName + '_' + props.room.roomId}
|
||||||
index={props.index}
|
index={props.index}
|
||||||
|
type="draggable-RoomTile"
|
||||||
>
|
>
|
||||||
{ (provided, snapshot) => {
|
{ (provided, snapshot) => {
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -43,9 +43,6 @@
|
||||||
"Off": "Off",
|
"Off": "Off",
|
||||||
"On": "On",
|
"On": "On",
|
||||||
"Noisy": "Noisy",
|
"Noisy": "Noisy",
|
||||||
"Failed to set direct chat tag": "Failed to set direct chat tag",
|
|
||||||
"Failed to remove tag %(tagName)s from room": "Failed to remove tag %(tagName)s from room",
|
|
||||||
"Failed to add tag %(tagName)s to room": "Failed to add tag %(tagName)s to room",
|
|
||||||
"Search…": "Search…",
|
"Search…": "Search…",
|
||||||
"Search": "Search",
|
"Search": "Search",
|
||||||
"This Room": "This Room",
|
"This Room": "This Room",
|
||||||
|
@ -68,8 +65,6 @@
|
||||||
"What's New": "What's New",
|
"What's New": "What's New",
|
||||||
"Update": "Update",
|
"Update": "Update",
|
||||||
"What's new?": "What's new?",
|
"What's new?": "What's new?",
|
||||||
"Appear Offline": "Appear Offline",
|
|
||||||
"Away": "Away",
|
|
||||||
"A new version of Riot is available.": "A new version of Riot is available.",
|
"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>",
|
"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>",
|
||||||
"Set Password": "Set Password",
|
"Set Password": "Set Password",
|
||||||
|
@ -126,6 +121,7 @@
|
||||||
"Resend": "Resend",
|
"Resend": "Resend",
|
||||||
"Cancel Sending": "Cancel Sending",
|
"Cancel Sending": "Cancel Sending",
|
||||||
"Forward Message": "Forward Message",
|
"Forward Message": "Forward Message",
|
||||||
|
"Reply": "Reply",
|
||||||
"Unpin Message": "Unpin Message",
|
"Unpin Message": "Unpin Message",
|
||||||
"Pin Message": "Pin Message",
|
"Pin Message": "Pin Message",
|
||||||
"View Source": "View Source",
|
"View Source": "View Source",
|
||||||
|
@ -134,6 +130,10 @@
|
||||||
"Permalink": "Permalink",
|
"Permalink": "Permalink",
|
||||||
"Quote": "Quote",
|
"Quote": "Quote",
|
||||||
"Source URL": "Source URL",
|
"Source URL": "Source URL",
|
||||||
|
"Online": "Online",
|
||||||
|
"Away": "Away",
|
||||||
|
"Appear Offline": "Appear Offline",
|
||||||
|
"Failed to remove tag %(tagName)s from room": "Failed to remove tag %(tagName)s from room",
|
||||||
"Failed to set Direct Message status of room": "Failed to set Direct Message status of room",
|
"Failed to set Direct Message status of room": "Failed to set Direct Message status of room",
|
||||||
"unknown error code": "unknown error code",
|
"unknown error code": "unknown error code",
|
||||||
"Failed to forget room %(errCode)s": "Failed to forget room %(errCode)s",
|
"Failed to forget room %(errCode)s": "Failed to forget room %(errCode)s",
|
||||||
|
@ -147,6 +147,7 @@
|
||||||
"Favourite": "Favourite",
|
"Favourite": "Favourite",
|
||||||
"Low Priority": "Low Priority",
|
"Low Priority": "Low Priority",
|
||||||
"Direct Chat": "Direct Chat",
|
"Direct Chat": "Direct Chat",
|
||||||
|
"View Community": "View Community",
|
||||||
"Sorry, your browser is <b>not</b> able to run Riot.": "Sorry, your browser is <b>not</b> able to run Riot.",
|
"Sorry, your browser is <b>not</b> able to run Riot.": "Sorry, your browser is <b>not</b> able to run Riot.",
|
||||||
"Riot uses many advanced browser features, some of which are not available or experimental in your current browser.": "Riot uses many advanced browser features, some of which are not available or experimental in your current browser.",
|
"Riot uses many advanced browser features, some of which are not available or experimental in your current browser.": "Riot uses many advanced browser features, some of which are not available or experimental in your current browser.",
|
||||||
"Please install <chromeLink>Chrome</chromeLink> or <firefoxLink>Firefox</firefoxLink> for the best experience.": "Please install <chromeLink>Chrome</chromeLink> or <firefoxLink>Firefox</firefoxLink> for the best experience.",
|
"Please install <chromeLink>Chrome</chromeLink> or <firefoxLink>Firefox</firefoxLink> for the best experience.": "Please install <chromeLink>Chrome</chromeLink> or <firefoxLink>Firefox</firefoxLink> for the best experience.",
|
||||||
|
@ -156,10 +157,10 @@
|
||||||
"Couldn't load home page": "Couldn't load home page",
|
"Couldn't load home page": "Couldn't load home page",
|
||||||
"Login": "Login",
|
"Login": "Login",
|
||||||
"Register": "Register",
|
"Register": "Register",
|
||||||
"Invite to this room": "Invite to this room",
|
|
||||||
"Members": "Members",
|
"Members": "Members",
|
||||||
"%(count)s Members|other": "%(count)s Members",
|
"%(count)s Members|other": "%(count)s Members",
|
||||||
"%(count)s Members|one": "%(count)s Member",
|
"%(count)s Members|one": "%(count)s Member",
|
||||||
|
"Invite to this room": "Invite to this room",
|
||||||
"Files": "Files",
|
"Files": "Files",
|
||||||
"Notifications": "Notifications",
|
"Notifications": "Notifications",
|
||||||
"Rooms": "Rooms",
|
"Rooms": "Rooms",
|
||||||
|
@ -189,6 +190,7 @@
|
||||||
"Search for a room": "Search for a room",
|
"Search for a room": "Search for a room",
|
||||||
"#example": "#example",
|
"#example": "#example",
|
||||||
"more": "more",
|
"more": "more",
|
||||||
|
"Failed to add tag %(tagName)s to room": "Failed to add tag %(tagName)s to room",
|
||||||
"Expand panel": "Expand panel",
|
"Expand panel": "Expand panel",
|
||||||
"Collapse panel": "Collapse panel",
|
"Collapse panel": "Collapse panel",
|
||||||
"Filter room names": "Filter room names",
|
"Filter room names": "Filter room names",
|
||||||
|
@ -219,6 +221,5 @@
|
||||||
"Contributing code to Matrix and Riot": "Contributing code to Matrix and Riot",
|
"Contributing code to Matrix and Riot": "Contributing code to Matrix and Riot",
|
||||||
"Dev chat for the Riot/Web dev team": "Dev chat for the Riot/Web dev team",
|
"Dev chat for the Riot/Web dev team": "Dev chat for the Riot/Web dev team",
|
||||||
"Dev chat for the Dendrite dev team": "Dev chat for the Dendrite dev team",
|
"Dev chat for the Dendrite dev team": "Dev chat for the Dendrite dev team",
|
||||||
"Co-ordination for Riot/Web translators": "Co-ordination for Riot/Web translators",
|
"Co-ordination for Riot/Web translators": "Co-ordination for Riot/Web translators"
|
||||||
"Reply": "Reply"
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,10 +36,10 @@
|
||||||
@import "./matrix-react-sdk/views/elements/_EditableItemList.scss";
|
@import "./matrix-react-sdk/views/elements/_EditableItemList.scss";
|
||||||
@import "./matrix-react-sdk/views/elements/_MemberEventListSummary.scss";
|
@import "./matrix-react-sdk/views/elements/_MemberEventListSummary.scss";
|
||||||
@import "./matrix-react-sdk/views/elements/_ProgressBar.scss";
|
@import "./matrix-react-sdk/views/elements/_ProgressBar.scss";
|
||||||
|
@import "./matrix-react-sdk/views/elements/_Quote.scss";
|
||||||
@import "./matrix-react-sdk/views/elements/_RichText.scss";
|
@import "./matrix-react-sdk/views/elements/_RichText.scss";
|
||||||
@import "./matrix-react-sdk/views/elements/_RoleButton.scss";
|
@import "./matrix-react-sdk/views/elements/_RoleButton.scss";
|
||||||
@import "./matrix-react-sdk/views/elements/_ToolTipButton.scss";
|
@import "./matrix-react-sdk/views/elements/_ToolTipButton.scss";
|
||||||
@import "./matrix-react-sdk/views/elements/_Quote.scss";
|
|
||||||
@import "./matrix-react-sdk/views/groups/_GroupPublicityToggle.scss";
|
@import "./matrix-react-sdk/views/groups/_GroupPublicityToggle.scss";
|
||||||
@import "./matrix-react-sdk/views/groups/_GroupRoomList.scss";
|
@import "./matrix-react-sdk/views/groups/_GroupRoomList.scss";
|
||||||
@import "./matrix-react-sdk/views/groups/_GroupUserSettings.scss";
|
@import "./matrix-react-sdk/views/groups/_GroupUserSettings.scss";
|
||||||
|
@ -65,6 +65,7 @@
|
||||||
@import "./matrix-react-sdk/views/rooms/_PinnedEventTile.scss";
|
@import "./matrix-react-sdk/views/rooms/_PinnedEventTile.scss";
|
||||||
@import "./matrix-react-sdk/views/rooms/_PinnedEventsPanel.scss";
|
@import "./matrix-react-sdk/views/rooms/_PinnedEventsPanel.scss";
|
||||||
@import "./matrix-react-sdk/views/rooms/_PresenceLabel.scss";
|
@import "./matrix-react-sdk/views/rooms/_PresenceLabel.scss";
|
||||||
|
@import "./matrix-react-sdk/views/rooms/_QuotePreview.scss";
|
||||||
@import "./matrix-react-sdk/views/rooms/_RoomHeader.scss";
|
@import "./matrix-react-sdk/views/rooms/_RoomHeader.scss";
|
||||||
@import "./matrix-react-sdk/views/rooms/_RoomList.scss";
|
@import "./matrix-react-sdk/views/rooms/_RoomList.scss";
|
||||||
@import "./matrix-react-sdk/views/rooms/_RoomPreviewBar.scss";
|
@import "./matrix-react-sdk/views/rooms/_RoomPreviewBar.scss";
|
||||||
|
@ -72,7 +73,6 @@
|
||||||
@import "./matrix-react-sdk/views/rooms/_RoomTile.scss";
|
@import "./matrix-react-sdk/views/rooms/_RoomTile.scss";
|
||||||
@import "./matrix-react-sdk/views/rooms/_SearchableEntityList.scss";
|
@import "./matrix-react-sdk/views/rooms/_SearchableEntityList.scss";
|
||||||
@import "./matrix-react-sdk/views/rooms/_TopUnreadMessagesBar.scss";
|
@import "./matrix-react-sdk/views/rooms/_TopUnreadMessagesBar.scss";
|
||||||
@import "./matrix-react-sdk/views/rooms/_QuotePreview.scss";
|
|
||||||
@import "./matrix-react-sdk/views/settings/_DevicesPanel.scss";
|
@import "./matrix-react-sdk/views/settings/_DevicesPanel.scss";
|
||||||
@import "./matrix-react-sdk/views/settings/_IntegrationsManager.scss";
|
@import "./matrix-react-sdk/views/settings/_IntegrationsManager.scss";
|
||||||
@import "./matrix-react-sdk/views/voip/_CallView.scss";
|
@import "./matrix-react-sdk/views/voip/_CallView.scss";
|
||||||
|
@ -89,6 +89,7 @@
|
||||||
@import "./vector-web/views/context_menus/_MessageContextMenu.scss";
|
@import "./vector-web/views/context_menus/_MessageContextMenu.scss";
|
||||||
@import "./vector-web/views/context_menus/_PresenceContextMenuOption.scss";
|
@import "./vector-web/views/context_menus/_PresenceContextMenuOption.scss";
|
||||||
@import "./vector-web/views/context_menus/_RoomTileContextMenu.scss";
|
@import "./vector-web/views/context_menus/_RoomTileContextMenu.scss";
|
||||||
|
@import "./vector-web/views/context_menus/_TagTileContextMenu.scss";
|
||||||
@import "./vector-web/views/dialogs/_ChangelogDialog.scss";
|
@import "./vector-web/views/dialogs/_ChangelogDialog.scss";
|
||||||
@import "./vector-web/views/dialogs/_DevtoolsDialog.scss";
|
@import "./vector-web/views/dialogs/_DevtoolsDialog.scss";
|
||||||
@import "./vector-web/views/dialogs/_SetEmailDialog.scss";
|
@import "./vector-web/views/dialogs/_SetEmailDialog.scss";
|
||||||
|
|
|
@ -15,7 +15,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.mx_TagPanel {
|
.mx_TagPanel {
|
||||||
width: 60px;
|
flex: 0 0 60px;
|
||||||
background-color: $tertiary-accent-color;
|
background-color: $tertiary-accent-color;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
|
@ -37,6 +37,7 @@ limitations under the License.
|
||||||
.mx_TagPanel .mx_TagTile {
|
.mx_TagPanel .mx_TagTile {
|
||||||
padding: 6px 3px;
|
padding: 6px 3px;
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
.mx_TagPanel .mx_TagTile:focus,
|
.mx_TagPanel .mx_TagTile:focus,
|
||||||
.mx_TagPanel .mx_TagTile:hover,
|
.mx_TagPanel .mx_TagTile:hover,
|
||||||
|
@ -64,6 +65,23 @@ limitations under the License.
|
||||||
left: 5px;
|
left: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_TagTile_context_button {
|
||||||
|
min-width: 15px;
|
||||||
|
height: 15px;
|
||||||
|
position: absolute;
|
||||||
|
right: -5px;
|
||||||
|
top: 1px;
|
||||||
|
border-radius: 8px;
|
||||||
|
background-color: $neutral-badge-color;
|
||||||
|
color: #ffffff;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 10px;
|
||||||
|
text-align: center;
|
||||||
|
padding-top: 1px;
|
||||||
|
padding-left: 4px;
|
||||||
|
padding-right: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
.mx_TagPanel_createGroupButton {
|
.mx_TagPanel_createGroupButton {
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
margin-bottom: 17px;
|
margin-bottom: 17px;
|
||||||
|
|
|
@ -21,6 +21,27 @@ limitations under the License.
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_LeftPanel_container {
|
||||||
|
display: flex;
|
||||||
|
/* LeftPanel 235px */
|
||||||
|
flex: 0 0 235px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_LeftPanel_container.mx_LeftPanel_container_hasTagPanel {
|
||||||
|
/* TagPanel 60px + LeftPanel 235px */
|
||||||
|
flex: 0 0 295px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_LeftPanel_container_collapsed {
|
||||||
|
/* Collapsed LeftPanel 60px */
|
||||||
|
flex: 0 0 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_LeftPanel_container_collapsed.mx_LeftPanel_container_hasTagPanel {
|
||||||
|
/* TagPanel 60px + Collapsed LeftPanel 60px */
|
||||||
|
flex: 0 0 120px;
|
||||||
|
}
|
||||||
|
|
||||||
.mx_LeftPanel_hideButton {
|
.mx_LeftPanel_hideButton {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 10px;
|
top: 10px;
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
Copyright 2018 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_TagTileContextMenu_item {
|
||||||
|
padding-top: 8px;
|
||||||
|
padding-right: 20px;
|
||||||
|
padding-bottom: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
white-space: nowrap;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
line-height: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.mx_TagTileContextMenu_item_icon {
|
||||||
|
padding-right: 8px;
|
||||||
|
padding-left: 4px;
|
||||||
|
display: inline-block
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_TagTileContextMenu_separator {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
border-bottom-style: none;
|
||||||
|
border-left-style: none;
|
||||||
|
border-right-style: none;
|
||||||
|
border-top-style: solid;
|
||||||
|
border-top-width: 1px;
|
||||||
|
border-color: $menu-border-color;
|
||||||
|
}
|
Loading…
Reference in New Issue