forked from matrix/element-web
Merge pull request #5142 from turt2live/travis/pinned_messages
Message/event pinning
This commit is contained in:
commit
f143315618
|
@ -43,26 +43,40 @@ module.exports = React.createClass({
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
return {
|
return {
|
||||||
canRedact: false,
|
canRedact: false,
|
||||||
|
canPin: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
componentWillMount: function() {
|
componentWillMount: function() {
|
||||||
MatrixClientPeg.get().on('RoomMember.powerLevel', this._checkCanRedact);
|
MatrixClientPeg.get().on('RoomMember.powerLevel', this._checkPermissions);
|
||||||
this._checkCanRedact();
|
this._checkPermissions();
|
||||||
},
|
},
|
||||||
|
|
||||||
componentWillUnmount: function() {
|
componentWillUnmount: function() {
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
if (cli) {
|
if (cli) {
|
||||||
cli.removeListener('RoomMember.powerLevel', this._checkCanRedact);
|
cli.removeListener('RoomMember.powerLevel', this._checkPermissions);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_checkCanRedact: function() {
|
_checkPermissions: function() {
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
const room = cli.getRoom(this.props.mxEvent.getRoomId());
|
const room = cli.getRoom(this.props.mxEvent.getRoomId());
|
||||||
|
|
||||||
const canRedact = room.currentState.maySendRedactionForEvent(this.props.mxEvent, cli.credentials.userId);
|
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() {
|
onResendClick: function() {
|
||||||
|
@ -122,6 +136,28 @@ module.exports = React.createClass({
|
||||||
this.closeMenu();
|
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() {
|
closeMenu: function() {
|
||||||
if (this.props.onFinished) this.props.onFinished();
|
if (this.props.onFinished) this.props.onFinished();
|
||||||
},
|
},
|
||||||
|
@ -147,6 +183,7 @@ module.exports = React.createClass({
|
||||||
let redactButton;
|
let redactButton;
|
||||||
let cancelButton;
|
let cancelButton;
|
||||||
let forwardButton;
|
let forwardButton;
|
||||||
|
let pinButton;
|
||||||
let viewSourceButton;
|
let viewSourceButton;
|
||||||
let viewClearSourceButton;
|
let viewClearSourceButton;
|
||||||
let unhidePreviewButton;
|
let unhidePreviewButton;
|
||||||
|
@ -186,6 +223,14 @@ module.exports = React.createClass({
|
||||||
{ _t('Forward Message') }
|
{ _t('Forward Message') }
|
||||||
</div>
|
</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}
|
{redactButton}
|
||||||
{cancelButton}
|
{cancelButton}
|
||||||
{forwardButton}
|
{forwardButton}
|
||||||
|
{pinButton}
|
||||||
{viewSourceButton}
|
{viewSourceButton}
|
||||||
{viewClearSourceButton}
|
{viewClearSourceButton}
|
||||||
{unhidePreviewButton}
|
{unhidePreviewButton}
|
||||||
|
|
|
@ -200,6 +200,11 @@
|
||||||
"You have successfully set a password!": "You have successfully set a password!",
|
"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.",
|
"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",
|
"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!",
|
"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.",
|
"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!",
|
"You have successfully set a password and an email address!": "You have successfully set a password and an email address!",
|
||||||
|
|
|
@ -185,6 +185,11 @@
|
||||||
"You have successfully set a password and an email address!": "You have successfully set a password and an email address!",
|
"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.",
|
"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",
|
"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...",
|
"Checking for an update...": "Checking for an update...",
|
||||||
"Error encountered (%(errorDetail)s).": "Error encountered (%(errorDetail)s).",
|
"Error encountered (%(errorDetail)s).": "Error encountered (%(errorDetail)s).",
|
||||||
"No update available.": "No update available.",
|
"No update available.": "No update available.",
|
||||||
|
|
|
@ -68,6 +68,8 @@
|
||||||
@import "./matrix-react-sdk/views/voip/_CallView.scss";
|
@import "./matrix-react-sdk/views/voip/_CallView.scss";
|
||||||
@import "./matrix-react-sdk/views/voip/_IncomingCallbox.scss";
|
@import "./matrix-react-sdk/views/voip/_IncomingCallbox.scss";
|
||||||
@import "./matrix-react-sdk/views/voip/_VideoView.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/_fonts.scss";
|
||||||
@import "./vector-web/structures/_CompatibilityPage.scss";
|
@import "./vector-web/structures/_CompatibilityPage.scss";
|
||||||
@import "./vector-web/structures/_HomePage.scss";
|
@import "./vector-web/structures/_HomePage.scss";
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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 |
Loading…
Reference in New Issue