Merge pull request #5142 from turt2live/travis/pinned_messages

Message/event pinning
pull/5293/head
Matthew Hodgson 2017-10-15 01:02:49 +01:00 committed by GitHub
commit f143315618
7 changed files with 174 additions and 5 deletions

View File

@ -43,26 +43,40 @@ module.exports = React.createClass({
getInitialState: function() {
return {
canRedact: false,
canPin: false,
};
},
componentWillMount: function() {
MatrixClientPeg.get().on('RoomMember.powerLevel', this._checkCanRedact);
this._checkCanRedact();
MatrixClientPeg.get().on('RoomMember.powerLevel', this._checkPermissions);
this._checkPermissions();
},
componentWillUnmount: function() {
const cli = MatrixClientPeg.get();
if (cli) {
cli.removeListener('RoomMember.powerLevel', this._checkCanRedact);
cli.removeListener('RoomMember.powerLevel', this._checkPermissions);
}
},
_checkCanRedact: function() {
_checkPermissions: function() {
const cli = MatrixClientPeg.get();
const room = cli.getRoom(this.props.mxEvent.getRoomId());
const canRedact = room.currentState.maySendRedactionForEvent(this.props.mxEvent, cli.credentials.userId);
this.setState({canRedact});
let canPin = room.currentState.mayClientSendStateEvent('m.room.pinned_events', cli);
// HACK: Intentionally say we can't pin if the user doesn't want to use the functionality
if (!UserSettingsStore.isFeatureEnabled("feature_pinning")) canPin = false;
this.setState({canRedact, canPin});
},
_isPinned: function() {
const room = MatrixClientPeg.get().getRoom(this.props.mxEvent.getRoomId());
const pinnedEvent = room.currentState.getStateEvents('m.room.pinned_events', '');
if (!pinnedEvent) return false;
return pinnedEvent.getContent().pinned.includes(this.props.mxEvent.getId());
},
onResendClick: function() {
@ -122,6 +136,28 @@ module.exports = React.createClass({
this.closeMenu();
},
onPinClick: function() {
MatrixClientPeg.get().getStateEvent(this.props.mxEvent.getRoomId(), 'm.room.pinned_events', '')
.catch(e => {
// Intercept the Event Not Found error and fall through the promise chain with no event.
if (e.errcode === "M_NOT_FOUND") return null;
throw e;
})
.then(event => {
const eventIds = (event ? event.pinned : []) || [];
if (!eventIds.includes(this.props.mxEvent.getId())) {
// Not pinned - add
eventIds.push(this.props.mxEvent.getId());
} else {
// Pinned - remove
eventIds.splice(eventIds.indexOf(this.props.mxEvent.getId()), 1);
}
MatrixClientPeg.get().sendStateEvent(this.props.mxEvent.getRoomId(), 'm.room.pinned_events', {pinned: eventIds}, '');
});
this.closeMenu();
},
closeMenu: function() {
if (this.props.onFinished) this.props.onFinished();
},
@ -147,6 +183,7 @@ module.exports = React.createClass({
let redactButton;
let cancelButton;
let forwardButton;
let pinButton;
let viewSourceButton;
let viewClearSourceButton;
let unhidePreviewButton;
@ -186,6 +223,14 @@ module.exports = React.createClass({
{ _t('Forward Message') }
</div>
);
if (this.state.canPin) {
pinButton = (
<div className="mx_MessageContextMenu_field" onClick={this.onPinClick}>
{this._isPinned() ? _t('Unpin Message') : _t('Pin Message')}
</div>
);
}
}
}
@ -246,6 +291,7 @@ module.exports = React.createClass({
{redactButton}
{cancelButton}
{forwardButton}
{pinButton}
{viewSourceButton}
{viewClearSourceButton}
{unhidePreviewButton}

View File

@ -200,6 +200,11 @@
"You have successfully set a password!": "You have successfully set a password!",
"You can now return to your account after signing out, and sign in on other devices.": "You can now return to your account after signing out, and sign in on other devices.",
"Continue": "Continue",
"Pin Message": "Pin Message",
"Unpin Message": "Unpin Message",
"Jump to message": "Jump to message",
"No pinned messages.": "No pinned messages.",
"Loading...": "Loading...",
"Please set a password!": "Please set a password!",
"This will allow you to return to your account after signing out, and sign in on other devices.": "This will allow you to return to your account after signing out, and sign in on other devices.",
"You have successfully set a password and an email address!": "You have successfully set a password and an email address!",

View File

@ -185,6 +185,11 @@
"You have successfully set a password and an email address!": "You have successfully set a password and an email address!",
"Remember, you can always set an email address in user settings if you change your mind.": "Remember, you can always set an email address in user settings if you change your mind.",
"Warning": "Warning",
"Pin Message": "Pin Message",
"Unpin Message": "Unpin Message",
"Jump to message": "Jump to message",
"No pinned messages.": "No pinned messages.",
"Loading...": "Loading...",
"Checking for an update...": "Checking for an update...",
"Error encountered (%(errorDetail)s).": "Error encountered (%(errorDetail)s).",
"No update available.": "No update available.",

View File

@ -68,6 +68,8 @@
@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";

View File

@ -0,0 +1,67 @@
/*
Copyright 2017 Travis Ralston
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_PinnedEventTile {
min-height: 40px;
margin-bottom: 5px;
width: 100%;
border-radius: 5px; // for the hover
}
.mx_PinnedEventTile:hover {
background-color: $event-selected-color;
}
.mx_PinnedEventTile .mx_PinnedEventTile_sender {
color: #868686;
font-size: 0.8em;
vertical-align: top;
display: block;
padding-bottom: 3px;
}
.mx_PinnedEventTile .mx_EventTile_content {
margin-left: 50px;
position: relative;
top: 0;
left: 0;
}
.mx_PinnedEventTile .mx_BaseAvatar {
float: left;
margin-right: 10px;
}
.mx_PinnedEventTile:hover .mx_PinnedEventTile_actions {
display: block;
}
.mx_PinnedEventTile_actions {
float: right;
margin-right: 10px;
display: none;
}
.mx_PinnedEventTile_unpinButton {
display: inline-block;
cursor: pointer;
margin-left: 10px;
}
.mx_PinnedEventTile_gotoButton {
display: inline-block;
font-size: 0.8em;
}

View File

@ -0,0 +1,37 @@
/*
Copyright 2017 Travis Ralston
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_PinnedEventsPanel {
border-top: 1px solid $primary-hairline-color;
}
.mx_PinnedEventsPanel_body {
max-height: 300px;
overflow-y: auto;
padding-bottom: 15px;
}
.mx_PinnedEventsPanel_header {
margin: 0;
padding-top: 8px;
padding-bottom: 15px;
}
.mx_PinnedEventsPanel_cancel {
margin: 12px;
float: right;
display: inline-block;
}

View File

@ -0,0 +1,7 @@
<svg width="16px" height="16px" viewbox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
<g transform="scale(0.03125)">
<path id="svg_2" fill="none" stroke="#76cfa6" stroke-width="40" stroke-linecap="round" stroke-linejoin="round" d="m315.802,402.338c12.73,-33.537 13.503,-69.629 3.623,-102.697l93.245,-103.107l7.831,7.831c10.411,10.409 27.283,10.409 37.691,0c10.41,-10.408 10.41,-27.281 0.001,-37.69l-112.869,-112.867c-10.407,-10.409 -27.279,-10.41 -37.689,-0.001c-10.408,10.41 -10.409,27.283 0.001,37.693l7.833,7.833l-103.107,93.243c-33.069,-9.878 -69.163,-9.107 -102.697,3.626c-4.7,1.785 -8.001,5.646 -9.059,10.604c-1.175,5.473 0.627,11.402 4.697,15.472l184.42,184.421c4.069,4.07 10,5.871 15.472,4.695c4.959,-1.055 8.82,-4.357 10.607,-9.056z"/>
<polyline id="svg_3" fill="none" stroke="#76cfa6" stroke-width="40" stroke-linecap="round" stroke-linejoin="round" points=" 180.951,297.927 46,466 215.319,332.295 "/>
<!--<line id="svg_4" fill="none" stroke="#76cfa6" stroke-width="40" stroke-linecap="round" stroke-linejoin="round" y2="219.549" y1="138.166" x2="255.531" x1="336.915"/>-->
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB