diff --git a/src/components/views/rooms/PinnedEventsPanel.js b/src/components/views/rooms/PinnedEventsPanel.js new file mode 100644 index 0000000000..4e5efdd35e --- /dev/null +++ b/src/components/views/rooms/PinnedEventsPanel.js @@ -0,0 +1,106 @@ +/* +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. +*/ + +'use strict'; + +var React = require('react'); +var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg'); +var sdk = require('matrix-react-sdk'); +var AccessibleButton = require('matrix-react-sdk/lib/components/views/elements/AccessibleButton'); +import { _t } from "matrix-react-sdk/lib/languageHandler"; +import { EventTimeline } from "matrix-js-sdk"; + +module.exports = React.createClass({ + displayName: 'PinnedEventsPanel', + propTypes: { + // The Room from the js-sdk we're going to show pinned events for + room: React.PropTypes.object.isRequired, + + onCancelClick: React.PropTypes.func, + }, + + getInitialState: function() { + return { + loading: true, + }; + }, + + componentDidMount: function() { + const pinnedEvents = this.props.room.currentState.getStateEvents("m.room.pinned_events", ""); + if (!pinnedEvents || !pinnedEvents.getContent().pinned) { + this.setState({ loading: false, pinned: [] }); + } else { + const promises = []; + const cli = MatrixClientPeg.get(); + + pinnedEvents.getContent().pinned.map(eventId => { + promises.push(cli.getEventTimeline(this.props.room.getUnfilteredTimelineSet(), eventId, 0).then(timeline => { + return {eventId, timeline}; + })); + }); + + Promise.all(promises).then(contexts => { + this.setState({ loading: false, pinned: contexts }); + }); + } + }, + + _getPinnedTiles: function() { + const MessageEvent = sdk.getComponent("views.messages.MessageEvent"); + const MemberAvatar = sdk.getComponent("views.avatars.MemberAvatar"); + + if (this.state.pinned.length == 0) { + return
No pinned messages.
; + } + + return this.state.pinned.map(pinnedEvent => { + const event = pinnedEvent.timeline.getEvents().find(e => e.getId() === pinnedEvent.eventId); + const sender = this.props.room.getMember(event.getSender()); + const avatarSize = 40; + + // Don't show non-messages. Technically users can pin state/custom events, but we won't + // support those events. + if (event.getType() !== "m.room.message") return ''; + + return ( +
+ + + {sender.name} + + +
+ ); + }); + }, + + render: function() { + let tiles =
Loading...
; + if (this.state && !this.state.loading) { + tiles = this._getPinnedTiles(); + } + + return ( +
+
+ +

{_t("Pinned Messages")}

+ { tiles } +
+
+ ); + } +}); diff --git a/src/skins/vector/css/_components.scss b/src/skins/vector/css/_components.scss index 61bfa104aa..1d351c82d7 100644 --- a/src/skins/vector/css/_components.scss +++ b/src/skins/vector/css/_components.scss @@ -88,4 +88,5 @@ @import "./vector-web/views/rooms/_RoomDropTarget.scss"; @import "./vector-web/views/rooms/_RoomTooltip.scss"; @import "./vector-web/views/rooms/_SearchBar.scss"; +@import "./vector-web/views/rooms/_PinnedEventsPanel.scss"; @import "./vector-web/views/settings/_Notifications.scss"; diff --git a/src/skins/vector/css/vector-web/views/rooms/_PinnedEventsPanel.scss b/src/skins/vector/css/vector-web/views/rooms/_PinnedEventsPanel.scss new file mode 100644 index 0000000000..883eaa26e2 --- /dev/null +++ b/src/skins/vector/css/vector-web/views/rooms/_PinnedEventsPanel.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_PinnedEventsPanel { + border-top: 1px solid $primary-hairline-color; +} + +.mx_PinnedEventsPanel_body { + max-height: 300px; + overflow-y: scroll; +} + +.mx_PinnedEventsPanel_header { + margin: 0; + padding-top: 8px; + padding-bottom: 15px; +} + +.mx_PinnedEventsPanel_pinnedEvent { + min-height: 40px; + margin-bottom: 5px; + cursor: pointer; + width: 100%; + border-radius: 5px; // for the hover +} + +.mx_PinnedEventsPanel_pinnedEvent:hover { + background-color: $event-selected-color; +} + +.mx_PinnedEventsPanel_pinnedEvent .mx_PinnedEventsPanel_sender { + color: #868686; + font-size: 0.8em; + vertical-align: top; + display: block; +} + +.mx_PinnedEventsPanel_pinnedEvent .mx_EventTile_content { + margin-left: 50px; + position: relative; + top: 0; + left: 0; +} + +.mx_PinnedEventsPanel_pinnedEvent .mx_BaseAvatar { + float: left; + margin-right: 10px; +} + +.mx_PinnedEventsPanel_cancel { + margin: 12px; + float: right; + display: inline-block; +} diff --git a/src/skins/vector/img/icons-pin.svg b/src/skins/vector/img/icons-pin.svg new file mode 100644 index 0000000000..a6fbf13baa --- /dev/null +++ b/src/skins/vector/img/icons-pin.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file