diff --git a/res/css/_components.scss b/res/css/_components.scss index 579856f880..7975a71e4f 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -24,6 +24,7 @@ @import "./structures/_ViewSource.scss"; @import "./structures/login/_Login.scss"; @import "./views/avatars/_BaseAvatar.scss"; +@import "./views/avatars/_MemberStatusMessageAvatar.scss"; @import "./views/context_menus/_MessageContextMenu.scss"; @import "./views/context_menus/_RoomTileContextMenu.scss"; @import "./views/context_menus/_TagTileContextMenu.scss"; diff --git a/res/css/views/avatars/_MemberStatusMessageAvatar.scss b/res/css/views/avatars/_MemberStatusMessageAvatar.scss new file mode 100644 index 0000000000..166dc1a2c7 --- /dev/null +++ b/res/css/views/avatars/_MemberStatusMessageAvatar.scss @@ -0,0 +1,54 @@ +/* +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_MemberStatusMessageAvatar { +} + +.mx_MemberStatusMessageAvatar_contextMenu_message { + display: inline-block; + border-radius: 3px 0 0 3px; + border: 1px solid $input-border-color; + font-size: 13px; + padding: 7px 7px 7px 9px; + width: 135px; + background-color: $primary-bg-color !important; +} + +.mx_MemberStatusMessageAvatar_contextMenu_submit { + display: inline-block; +} + +.mx_MemberStatusMessageAvatar_contextMenu_submit img { + vertical-align: middle; + margin-left: 8px; +} + +.mx_MemberStatusMessageAvatar_contextMenu hr { + border: 0.5px solid $menu-border-color; +} + +.mx_MemberStatusMessageAvatar_contextMenu_clearIcon { + margin: 5px 15px 5px 5px; + vertical-align: middle; +} + +.mx_MemberStatusMessageAvatar_contextMenu_clear { + padding: 2px; +} + +.mx_MemberStatusMessageAvatar_contextMenu_hasStatus .mx_MemberStatusMessageAvatar_contextMenu_clear { + color: $warning-color; +} diff --git a/res/img/icons-checkmark.svg b/res/img/icons-checkmark.svg new file mode 100644 index 0000000000..748dc61995 --- /dev/null +++ b/res/img/icons-checkmark.svg @@ -0,0 +1,94 @@ + + + + diff --git a/src/components/views/avatars/MemberStatusMessageAvatar.js b/src/components/views/avatars/MemberStatusMessageAvatar.js new file mode 100644 index 0000000000..66122f9eee --- /dev/null +++ b/src/components/views/avatars/MemberStatusMessageAvatar.js @@ -0,0 +1,165 @@ +/* +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 '../../../languageHandler'; +import MatrixClientPeg from '../../../MatrixClientPeg'; +import AccessibleButton from '../elements/AccessibleButton'; +import MemberAvatar from '../avatars/MemberAvatar'; +import classNames from 'classnames'; +import * as ContextualMenu from "../../structures/ContextualMenu"; +import GenericElementContextMenu from "../context_menus/GenericElementContextMenu"; + +export default class MemberStatusMessageAvatar extends React.Component { + constructor(props, context) { + super(props, context); + this._onRoomStateEvents = this._onRoomStateEvents.bind(this); + this._onClick = this._onClick.bind(this); + this._onClearClick = this._onClearClick.bind(this); + this._onSubmit = this._onSubmit.bind(this); + this._onStatusChange = this._onStatusChange.bind(this); + } + + componentWillMount() { + if (this.props.member.userId !== MatrixClientPeg.get().getUserId()) { + throw new Error("Cannot use MemberStatusMessageAvatar on anyone but the logged in user"); + } + } + + componentDidMount() { + MatrixClientPeg.get().on("RoomState.events", this._onRoomStateEvents); + + if (this.props.member.user) { + this.setState({message: this.props.member.user.statusMessage}); + } else { + this.setState({message: ""}); + } + } + + componentWillUnmount() { + if (MatrixClientPeg.get()) { + MatrixClientPeg.get().removeListener("RoomState.events", this._onRoomStateEvents); + } + } + + _onRoomStateEvents(ev, state) { + if (ev.getStateKey() !== MatrixClientPeg.get().getUserId()) return; + if (ev.getType() !== "im.vector.user_status") return; + // TODO: We should be relying on `this.props.member.user.statusMessage` + this.setState({message: ev.getContent()["status"]}); + this.forceUpdate(); + } + + _onClick(e) { + e.stopPropagation(); + + const elementRect = e.target.getBoundingClientRect(); + + // The window X and Y offsets are to adjust position when zoomed in to page + const x = (elementRect.left + window.pageXOffset) - (elementRect.width / 2) + 3; + const chevronOffset = 12; + let y = elementRect.top + (elementRect.height / 2) + window.pageYOffset; + y = y - (chevronOffset + 4); // where 4 is 1/4 the height of the chevron + + const contextMenu = this._renderContextMenu(); + + ContextualMenu.createMenu(GenericElementContextMenu, { + chevronOffset: chevronOffset, + chevronFace: 'bottom', + left: x, + top: y, + menuWidth: 190, + element: contextMenu, + }); + } + + async _onClearClick(e) { + await MatrixClientPeg.get().setStatusMessage(""); + this.setState({message: ""}); + } + + _onSubmit(e) { + e.preventDefault(); + MatrixClientPeg.get().setStatusMessage(this.state.message); + } + + _onStatusChange(e) { + this.setState({message: e.target.value}); + } + + _renderContextMenu() { + const form =
; + + const clearIcon = this.state.message ? "img/cancel-red.svg" : "img/cancel.svg"; + const clearButton =