mirror of
https://github.com/vector-im/element-web.git
synced 2024-11-16 13:14:58 +08:00
Merge pull request #2478 from matrix-org/bwindels/jumptobottommakeover
Redesign: pull jump to bottom button out of room status bar
This commit is contained in:
commit
d803005d1c
@ -98,6 +98,7 @@
|
||||
@import "./views/rooms/_AuxPanel.scss";
|
||||
@import "./views/rooms/_EntityTile.scss";
|
||||
@import "./views/rooms/_EventTile.scss";
|
||||
@import "./views/rooms/_JumpToBottomButton.scss";
|
||||
@import "./views/rooms/_LinkPreviewWidget.scss";
|
||||
@import "./views/rooms/_MemberDeviceInfo.scss";
|
||||
@import "./views/rooms/_MemberInfo.scss";
|
||||
|
69
res/css/views/rooms/_JumpToBottomButton.scss
Normal file
69
res/css/views/rooms/_JumpToBottomButton.scss
Normal file
@ -0,0 +1,69 @@
|
||||
/*
|
||||
Copyright 2019 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.
|
||||
*/
|
||||
|
||||
@charset "utf-8";
|
||||
|
||||
.mx_JumpToBottomButton {
|
||||
z-index: 1000;
|
||||
position: absolute;
|
||||
// 12 because height is 50 but button is only 38 = 12+(50-38) = 24
|
||||
bottom: 12px;
|
||||
right: 24px;
|
||||
width: 38px;
|
||||
// give it a fixed height so the badge doesn't make
|
||||
// it taller and pop upwards when visible
|
||||
height: 50px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.mx_JumpToBottomButton_badge {
|
||||
position: relative;
|
||||
top: -12px;
|
||||
border-radius: 16px;
|
||||
font-weight: bold;
|
||||
font-size: 12px;
|
||||
line-height: 14px;
|
||||
text-align: center;
|
||||
// to be able to get it centered
|
||||
// with text-align in parent
|
||||
display: inline-block;
|
||||
padding: 0 4px;
|
||||
color: $secondary-accent-color;
|
||||
background-color: $warning-color;
|
||||
}
|
||||
|
||||
.mx_JumpToBottomButton_scrollDown {
|
||||
position: relative;
|
||||
height: 38px;
|
||||
border-radius: 19px;
|
||||
box-sizing: border-box;
|
||||
background: $primary-bg-color;
|
||||
border: 1.3px solid $roomtile-name-color;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.mx_JumpToBottomButton_scrollDown:before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
mask: url('$(res)/img/icon-jump-to-bottom.svg');
|
||||
mask-repeat: no-repeat;
|
||||
mask-position: 9px 14px;
|
||||
background: $roomtile-name-color;
|
||||
}
|
32
res/img/icon-jump-to-bottom.svg
Normal file
32
res/img/icon-jump-to-bottom.svg
Normal file
@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
id="svg4"
|
||||
width="18.666187"
|
||||
height="8.7375822"
|
||||
viewBox="0 0 18.666187 8.7375818"
|
||||
version="1.1"
|
||||
style="fill:none">
|
||||
<metadata
|
||||
id="metadata8">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs8" />
|
||||
<path
|
||||
id="path2-8-3"
|
||||
style="fill:none;stroke:#000000;stroke-width:1.29999995;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 17.909095,0.75599092 9.3330939,7.987602 0.75709259,0.75599092" />
|
||||
</svg>
|
After Width: | Height: | Size: 1016 B |
@ -45,14 +45,6 @@ module.exports = React.createClass({
|
||||
propTypes: {
|
||||
// the room this statusbar is representing.
|
||||
room: PropTypes.object.isRequired,
|
||||
|
||||
// the number of messages which have arrived since we've been scrolled up
|
||||
numUnreadMessages: PropTypes.number,
|
||||
|
||||
// this is true if we are fully scrolled-down, and are looking at
|
||||
// the end of the live timeline.
|
||||
atEndOfLiveTimeline: PropTypes.bool,
|
||||
|
||||
// This is true when the user is alone in the room, but has also sent a message.
|
||||
// Used to suggest to the user to invite someone
|
||||
sentMessageAndIsAlone: PropTypes.bool,
|
||||
@ -82,9 +74,6 @@ module.exports = React.createClass({
|
||||
// 'you are alone' bar
|
||||
onStopWarningClick: PropTypes.func,
|
||||
|
||||
// callback for when the user clicks on the 'scroll to bottom' button
|
||||
onScrollToBottomClick: PropTypes.func,
|
||||
|
||||
// callback for when we do something that changes the size of the
|
||||
// status bar. This is used to trigger a re-layout in the parent
|
||||
// component.
|
||||
@ -180,8 +169,6 @@ module.exports = React.createClass({
|
||||
// indicate other sizes.
|
||||
_getSize: function() {
|
||||
if (this._shouldShowConnectionError() ||
|
||||
this.props.numUnreadMessages ||
|
||||
!this.props.atEndOfLiveTimeline ||
|
||||
this.props.hasActiveCall ||
|
||||
this.props.sentMessageAndIsAlone
|
||||
) {
|
||||
@ -194,28 +181,6 @@ module.exports = React.createClass({
|
||||
|
||||
// return suitable content for the image on the left of the status bar.
|
||||
_getIndicator: function() {
|
||||
if (this.props.numUnreadMessages) {
|
||||
return (
|
||||
<div className="mx_RoomStatusBar_scrollDownIndicator"
|
||||
onClick={this.props.onScrollToBottomClick}>
|
||||
<img src={require("../../../res/img/newmessages.svg")} width="24" height="24"
|
||||
alt="" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const AccessibleButton = sdk.getComponent("elements.AccessibleButton");
|
||||
if (!this.props.atEndOfLiveTimeline) {
|
||||
return (
|
||||
<AccessibleButton className="mx_RoomStatusBar_scrollDownIndicator"
|
||||
onClick={this.props.onScrollToBottomClick}>
|
||||
<img src={require("../../../res/img/scrolldown.svg")} width="24" height="24"
|
||||
alt={_t("Scroll to bottom of page")}
|
||||
title={_t("Scroll to bottom of page")} />
|
||||
</AccessibleButton>
|
||||
);
|
||||
}
|
||||
|
||||
if (this.props.hasActiveCall) {
|
||||
const TintableSvg = sdk.getComponent("elements.TintableSvg");
|
||||
return (
|
||||
@ -231,9 +196,7 @@ module.exports = React.createClass({
|
||||
},
|
||||
|
||||
_shouldShowConnectionError: function() {
|
||||
// no conn bar trumps unread count since you can't get unread messages
|
||||
// without a connection! (technically may already have some but meh)
|
||||
// It also trumps the "some not sent" msg since you can't resend without
|
||||
// no conn bar trumps the "some not sent" msg since you can't resend without
|
||||
// a connection!
|
||||
// There's one situation in which we don't show this 'no connection' bar, and that's
|
||||
// if it's a resource limit exceeded error: those are shown in the top bar.
|
||||
@ -363,20 +326,6 @@ module.exports = React.createClass({
|
||||
return this._getUnsentMessageContent();
|
||||
}
|
||||
|
||||
// unread count trumps who is typing since the unread count is only
|
||||
// set when you've scrolled up
|
||||
if (this.props.numUnreadMessages) {
|
||||
// MUST use var name "count" for pluralization to kick in
|
||||
const unreadMsgs = _t("%(count)s new messages", {count: this.props.numUnreadMessages});
|
||||
|
||||
return (
|
||||
<div className="mx_RoomStatusBar_unreadMessagesBar"
|
||||
onClick={this.props.onScrollToBottomClick}>
|
||||
{ unreadMsgs }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (this.props.hasActiveCall) {
|
||||
return (
|
||||
<div className="mx_RoomStatusBar_callBar">
|
||||
|
@ -1473,11 +1473,10 @@ module.exports = React.createClass({
|
||||
|
||||
onStatusBarHidden: function() {
|
||||
// This is currently not desired as it is annoying if it keeps expanding and collapsing
|
||||
// TODO: Find a less annoying way of hiding the status bar
|
||||
/*if (this.unmounted) return;
|
||||
if (this.unmounted) return;
|
||||
this.setState({
|
||||
statusBarVisible: false,
|
||||
});*/
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
@ -1651,14 +1650,11 @@ module.exports = React.createClass({
|
||||
isStatusAreaExpanded = this.state.statusBarVisible;
|
||||
statusBar = <RoomStatusBar
|
||||
room={this.state.room}
|
||||
numUnreadMessages={this.state.numUnreadMessages}
|
||||
atEndOfLiveTimeline={this.state.atEndOfLiveTimeline}
|
||||
sentMessageAndIsAlone={this.state.isAlone}
|
||||
hasActiveCall={inCall}
|
||||
isPeeking={myMembership !== "join"}
|
||||
onInviteClick={this.onInviteButtonClick}
|
||||
onStopWarningClick={this.onStopAloneWarningClick}
|
||||
onScrollToBottomClick={this.jumpToLiveTimeline}
|
||||
onResize={this.onChildResize}
|
||||
onVisible={this.onStatusBarVisible}
|
||||
onHidden={this.onStatusBarHidden}
|
||||
@ -1864,6 +1860,14 @@ module.exports = React.createClass({
|
||||
onCloseClick={this.forgetReadMarker}
|
||||
/>);
|
||||
}
|
||||
let jumpToBottom;
|
||||
if (!this.state.atEndOfLiveTimeline) {
|
||||
const JumpToBottomButton = sdk.getComponent('rooms.JumpToBottomButton');
|
||||
jumpToBottom = (<JumpToBottomButton
|
||||
numUnreadMessages={this.state.numUnreadMessages}
|
||||
onScrollToBottomClick={this.jumpToLiveTimeline}
|
||||
/>);
|
||||
}
|
||||
const statusBarAreaClass = classNames(
|
||||
"mx_RoomView_statusArea",
|
||||
{
|
||||
@ -1901,6 +1905,7 @@ module.exports = React.createClass({
|
||||
{ auxPanel }
|
||||
<div className="mx_RoomView_timeline">
|
||||
{ topUnreadMessagesBar }
|
||||
{ jumpToBottom }
|
||||
{ messagePanel }
|
||||
{ searchResultsPanel }
|
||||
</div>
|
||||
|
32
src/components/views/rooms/JumpToBottomButton.js
Normal file
32
src/components/views/rooms/JumpToBottomButton.js
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
Copyright 2019 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 { _t } from '../../../languageHandler';
|
||||
import AccessibleButton from '../elements/AccessibleButton';
|
||||
|
||||
export default (props) => {
|
||||
let badge;
|
||||
if (props.numUnreadMessages) {
|
||||
badge = (<div className="mx_JumpToBottomButton_badge">{props.numUnreadMessages}</div>);
|
||||
}
|
||||
return (<div className="mx_JumpToBottomButton">
|
||||
<AccessibleButton className="mx_JumpToBottomButton_scrollDown"
|
||||
title={_t("Scroll to bottom of page")}
|
||||
onClick={props.onScrollToBottomClick}>
|
||||
</AccessibleButton>
|
||||
{ badge }
|
||||
</div>);
|
||||
};
|
@ -1,6 +1,7 @@
|
||||
/*
|
||||
Copyright 2016 OpenMarket Ltd
|
||||
Copyright 2017 Vector Creations Ltd
|
||||
Copyright 2019 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.
|
||||
|
Loading…
Reference in New Issue
Block a user