mirror of https://github.com/vector-im/riot-web
Iterate on forward dialog design feedback
Signed-off-by: Robin Townsend <robin@robin.town>pull/21833/head
parent
f34d61cf5d
commit
7a04502151
|
@ -23,7 +23,15 @@ limitations under the License.
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
height: 80vh;
|
height: 80vh;
|
||||||
|
|
||||||
.mx_ForwardDialog_preview {
|
> h3 {
|
||||||
|
margin: 0 0 6px;
|
||||||
|
color: $secondary-fg-color;
|
||||||
|
font-size: $font-12px;
|
||||||
|
font-weight: $font-semi-bold;
|
||||||
|
line-height: $font-15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .mx_ForwardDialog_preview {
|
||||||
max-height: 30%;
|
max-height: 30%;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
overflow: scroll;
|
overflow: scroll;
|
||||||
|
@ -43,7 +51,14 @@ limitations under the License.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_ForwardList {
|
> hr {
|
||||||
|
width: 100%;
|
||||||
|
border: none;
|
||||||
|
border-top: 1px solid $input-border-color;
|
||||||
|
margin: 12px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .mx_ForwardList {
|
||||||
display: contents;
|
display: contents;
|
||||||
|
|
||||||
.mx_SearchBox {
|
.mx_SearchBox {
|
||||||
|
@ -54,8 +69,6 @@ limitations under the License.
|
||||||
|
|
||||||
.mx_ForwardList_content {
|
.mx_ForwardList_content {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
// Push the scrollbar into the gutter
|
|
||||||
margin-right: -6px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_ForwardList_noResults {
|
.mx_ForwardList_noResults {
|
||||||
|
@ -64,30 +77,24 @@ limitations under the License.
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_ForwardList_results {
|
.mx_ForwardList_results {
|
||||||
margin-right: 6px;
|
|
||||||
|
|
||||||
&:not(:first-child) {
|
&:not(:first-child) {
|
||||||
margin-top: 24px;
|
margin-top: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
> h3 {
|
|
||||||
margin: 0;
|
|
||||||
color: $secondary-fg-color;
|
|
||||||
font-size: $font-12px;
|
|
||||||
font-weight: $font-semi-bold;
|
|
||||||
line-height: $font-15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_ForwardList_entry {
|
.mx_ForwardList_entry {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
margin-top: 12px;
|
|
||||||
height: 32px;
|
height: 32px;
|
||||||
|
padding: 6px;
|
||||||
|
border-radius: 8px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: $groupFilterPanel-bg-color;
|
||||||
|
}
|
||||||
|
|
||||||
.mx_ForwardList_roomButton {
|
.mx_ForwardList_roomButton {
|
||||||
display: flex;
|
display: flex;
|
||||||
margin-right: 12px;
|
margin-right: 12px;
|
||||||
flex-grow: 1;
|
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
|
||||||
.mx_DecoratedRoomAvatar {
|
.mx_DecoratedRoomAvatar {
|
||||||
|
@ -105,26 +112,39 @@ limitations under the License.
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_ForwardList_sendButton {
|
.mx_ForwardList_sendButton {
|
||||||
&.mx_ForwardList_sending, &.mx_ForwardList_sent {
|
position: relative;
|
||||||
&::before {
|
|
||||||
content: '';
|
&:not(.mx_ForwardList_canSend) .mx_ForwardList_sendLabel {
|
||||||
display: inline-block;
|
// Hide the "Send" label while preserving button size
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_ForwardList_sendIcon, .mx_NotificationBadge {
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_NotificationBadge {
|
||||||
|
opacity: 0.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.mx_ForwardList_sending .mx_ForwardList_sendIcon {
|
||||||
background-color: $button-primary-bg-color;
|
background-color: $button-primary-bg-color;
|
||||||
|
mask-image: url('$(res)/img/element-icons/circle-sending.svg');
|
||||||
mask-position: center;
|
mask-position: center;
|
||||||
mask-repeat: no-repeat;
|
mask-repeat: no-repeat;
|
||||||
mask-size: 14px;
|
mask-size: 14px;
|
||||||
width: 14px;
|
width: 14px;
|
||||||
height: 14px;
|
height: 14px;
|
||||||
margin-right: 5px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&.mx_ForwardList_sending::before {
|
&.mx_ForwardList_sent .mx_ForwardList_sendIcon {
|
||||||
mask-image: url('$(res)/img/element-icons/circle-sending.svg');
|
background-color: $button-primary-bg-color;
|
||||||
}
|
|
||||||
|
|
||||||
&.mx_ForwardList_sent::before {
|
|
||||||
mask-image: url('$(res)/img/element-icons/circle-sent.svg');
|
mask-image: url('$(res)/img/element-icons/circle-sent.svg');
|
||||||
|
mask-position: center;
|
||||||
|
mask-repeat: no-repeat;
|
||||||
|
mask-size: 14px;
|
||||||
|
width: 14px;
|
||||||
|
height: 14px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,9 +31,11 @@ import {avatarUrlForUser} from "../../../Avatar";
|
||||||
import EventTile from "../rooms/EventTile";
|
import EventTile from "../rooms/EventTile";
|
||||||
import SearchBox from "../../structures/SearchBox";
|
import SearchBox from "../../structures/SearchBox";
|
||||||
import DecoratedRoomAvatar from "../avatars/DecoratedRoomAvatar";
|
import DecoratedRoomAvatar from "../avatars/DecoratedRoomAvatar";
|
||||||
import AccessibleButton from "../elements/AccessibleButton";
|
import {Alignment} from '../elements/Tooltip';
|
||||||
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
|
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
|
||||||
import AutoHideScrollbar from "../../structures/AutoHideScrollbar";
|
import AutoHideScrollbar from "../../structures/AutoHideScrollbar";
|
||||||
|
import {StaticNotificationState} from "../../../stores/notifications/StaticNotificationState";
|
||||||
|
import NotificationBadge from "../rooms/NotificationBadge";
|
||||||
import {RoomPermalinkCreator} from "../../../utils/permalinks/Permalinks";
|
import {RoomPermalinkCreator} from "../../../utils/permalinks/Permalinks";
|
||||||
import {sortRooms} from "../../../stores/room-list/algorithms/tag-sorting/RecentAlgorithm";
|
import {sortRooms} from "../../../stores/room-list/algorithms/tag-sorting/RecentAlgorithm";
|
||||||
|
|
||||||
|
@ -82,52 +84,60 @@ const Entry: React.FC<IEntryProps> = ({ room, event, cli, onFinished }) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let button;
|
|
||||||
if (room.maySendMessage()) {
|
|
||||||
let label;
|
|
||||||
let className;
|
let className;
|
||||||
|
let disabled = false;
|
||||||
|
let title;
|
||||||
|
let icon;
|
||||||
if (sendState === SendState.CanSend) {
|
if (sendState === SendState.CanSend) {
|
||||||
label = _t("Send");
|
|
||||||
className = "mx_ForwardList_canSend";
|
className = "mx_ForwardList_canSend";
|
||||||
} else if (sendState === SendState.Sending) {
|
if (room.maySendMessage()) {
|
||||||
label = _t("Sending…");
|
title = _t("Send");
|
||||||
className = "mx_ForwardList_sending";
|
|
||||||
} else if (sendState === SendState.Sent) {
|
|
||||||
label = _t("Sent");
|
|
||||||
className = "mx_ForwardList_sent";
|
|
||||||
} else {
|
} else {
|
||||||
label = _t("Failed to send");
|
disabled = true;
|
||||||
className = "mx_ForwardList_sendFailed";
|
title = _t("You do not have permission to do this");
|
||||||
}
|
}
|
||||||
|
} else if (sendState === SendState.Sending) {
|
||||||
button =
|
className = "mx_ForwardList_sending";
|
||||||
<AccessibleButton
|
disabled = true;
|
||||||
kind={sendState === SendState.Failed ? "danger_outline" : "primary_outline"}
|
title = _t("Sending…");
|
||||||
className={`mx_ForwardList_sendButton ${className}`}
|
icon = <div className="mx_ForwardList_sendIcon"></div>;
|
||||||
onClick={send}
|
} else if (sendState === SendState.Sent) {
|
||||||
disabled={sendState !== SendState.CanSend}
|
className = "mx_ForwardList_sent";
|
||||||
>
|
disabled = true;
|
||||||
{ label }
|
title = _t("Sent");
|
||||||
</AccessibleButton>;
|
icon = <div className="mx_ForwardList_sendIcon"></div>;
|
||||||
} else {
|
} else {
|
||||||
button =
|
className = "mx_ForwardList_sendFailed";
|
||||||
<AccessibleTooltipButton
|
disabled = true;
|
||||||
kind="primary_outline"
|
title = _t("Failed to send");
|
||||||
className="mx_ForwardList_sendButton mx_ForwardList_canSend"
|
icon = <NotificationBadge
|
||||||
onClick={() => {}}
|
notification={StaticNotificationState.RED_EXCLAMATION}
|
||||||
disabled={true}
|
/>;
|
||||||
title={_t("You do not have permission to post to this room")}
|
|
||||||
>
|
|
||||||
{ _t("Send") }
|
|
||||||
</AccessibleTooltipButton>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return <div className="mx_ForwardList_entry">
|
return <div className="mx_ForwardList_entry">
|
||||||
<AccessibleButton className="mx_ForwardList_roomButton" onClick={jumpToRoom}>
|
<AccessibleTooltipButton
|
||||||
|
className="mx_ForwardList_roomButton"
|
||||||
|
onClick={jumpToRoom}
|
||||||
|
title={_t("Open link")}
|
||||||
|
yOffset={-20}
|
||||||
|
alignment={Alignment.Top}
|
||||||
|
>
|
||||||
<DecoratedRoomAvatar room={room} avatarSize={32} />
|
<DecoratedRoomAvatar room={room} avatarSize={32} />
|
||||||
<span className="mx_ForwardList_entry_name">{ room.name }</span>
|
<span className="mx_ForwardList_entry_name">{ room.name }</span>
|
||||||
</AccessibleButton>
|
</AccessibleTooltipButton>
|
||||||
{ button }
|
<AccessibleTooltipButton
|
||||||
|
kind={sendState === SendState.Failed ? "danger_outline" : "primary_outline"}
|
||||||
|
className={`mx_ForwardList_sendButton ${className}`}
|
||||||
|
onClick={send}
|
||||||
|
disabled={disabled}
|
||||||
|
title={title}
|
||||||
|
yOffset={-20}
|
||||||
|
alignment={Alignment.Top}
|
||||||
|
>
|
||||||
|
<div className="mx_ForwardList_sendLabel">{ _t("Send") }</div>
|
||||||
|
{ icon }
|
||||||
|
</AccessibleTooltipButton>
|
||||||
</div>;
|
</div>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -180,6 +190,7 @@ const ForwardDialog: React.FC<IProps> = ({ cli, event, permalinkCreator, onFinis
|
||||||
onFinished={onFinished}
|
onFinished={onFinished}
|
||||||
fixedWidth={false}
|
fixedWidth={false}
|
||||||
>
|
>
|
||||||
|
<h3>{ _t("Message preview") }</h3>
|
||||||
<div className={classnames("mx_ForwardDialog_preview", {
|
<div className={classnames("mx_ForwardDialog_preview", {
|
||||||
"mx_IRCLayout": previewLayout == Layout.IRC,
|
"mx_IRCLayout": previewLayout == Layout.IRC,
|
||||||
"mx_GroupLayout": previewLayout == Layout.Group,
|
"mx_GroupLayout": previewLayout == Layout.Group,
|
||||||
|
@ -191,11 +202,11 @@ const ForwardDialog: React.FC<IProps> = ({ cli, event, permalinkCreator, onFinis
|
||||||
permalinkCreator={permalinkCreator}
|
permalinkCreator={permalinkCreator}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<hr />
|
||||||
<div className="mx_ForwardList">
|
<div className="mx_ForwardList">
|
||||||
<h2>{ _t("Forward to") }</h2>
|
|
||||||
<SearchBox
|
<SearchBox
|
||||||
className="mx_textinput_icon mx_textinput_search"
|
className="mx_textinput_icon mx_textinput_search"
|
||||||
placeholder={ _t("Filter your rooms and DMs") }
|
placeholder={ _t("Search for rooms or people") }
|
||||||
onSearch={setQuery}
|
onSearch={setQuery}
|
||||||
autoComplete={true}
|
autoComplete={true}
|
||||||
autoFocus={true}
|
autoFocus={true}
|
||||||
|
|
|
@ -19,7 +19,7 @@ import React from 'react';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
import AccessibleButton from "./AccessibleButton";
|
import AccessibleButton from "./AccessibleButton";
|
||||||
import Tooltip from './Tooltip';
|
import Tooltip, {Alignment} from './Tooltip';
|
||||||
import {replaceableComponent} from "../../../utils/replaceableComponent";
|
import {replaceableComponent} from "../../../utils/replaceableComponent";
|
||||||
|
|
||||||
interface ITooltipProps extends React.ComponentProps<typeof AccessibleButton> {
|
interface ITooltipProps extends React.ComponentProps<typeof AccessibleButton> {
|
||||||
|
@ -28,6 +28,7 @@ interface ITooltipProps extends React.ComponentProps<typeof AccessibleButton> {
|
||||||
tooltipClassName?: string;
|
tooltipClassName?: string;
|
||||||
forceHide?: boolean;
|
forceHide?: boolean;
|
||||||
yOffset?: number;
|
yOffset?: number;
|
||||||
|
alignment?: Alignment;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
interface IState {
|
||||||
|
@ -66,13 +67,14 @@ export default class AccessibleTooltipButton extends React.PureComponent<IToolti
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
const {title, tooltip, children, tooltipClassName, forceHide, yOffset, ...props} = this.props;
|
const {title, tooltip, children, tooltipClassName, forceHide, yOffset, alignment, ...props} = this.props;
|
||||||
|
|
||||||
const tip = this.state.hover ? <Tooltip
|
const tip = this.state.hover ? <Tooltip
|
||||||
className="mx_AccessibleTooltipButton_container"
|
className="mx_AccessibleTooltipButton_container"
|
||||||
tooltipClassName={classNames("mx_AccessibleTooltipButton_tooltip", tooltipClassName)}
|
tooltipClassName={classNames("mx_AccessibleTooltipButton_tooltip", tooltipClassName)}
|
||||||
label={tooltip || title}
|
label={tooltip || title}
|
||||||
yOffset={yOffset}
|
yOffset={yOffset}
|
||||||
|
alignment={alignment}
|
||||||
/> : <div />;
|
/> : <div />;
|
||||||
return (
|
return (
|
||||||
<AccessibleButton
|
<AccessibleButton
|
||||||
|
|
|
@ -2202,11 +2202,13 @@
|
||||||
"PRO TIP: If you start a bug, please submit <debugLogsLink>debug logs</debugLogsLink> to help us track down the problem.": "PRO TIP: If you start a bug, please submit <debugLogsLink>debug logs</debugLogsLink> to help us track down the problem.",
|
"PRO TIP: If you start a bug, please submit <debugLogsLink>debug logs</debugLogsLink> to help us track down the problem.": "PRO TIP: If you start a bug, please submit <debugLogsLink>debug logs</debugLogsLink> to help us track down the problem.",
|
||||||
"Report a bug": "Report a bug",
|
"Report a bug": "Report a bug",
|
||||||
"Please view <existingIssuesLink>existing bugs on Github</existingIssuesLink> first. No match? <newIssueLink>Start a new one</newIssueLink>.": "Please view <existingIssuesLink>existing bugs on Github</existingIssuesLink> first. No match? <newIssueLink>Start a new one</newIssueLink>.",
|
"Please view <existingIssuesLink>existing bugs on Github</existingIssuesLink> first. No match? <newIssueLink>Start a new one</newIssueLink>.": "Please view <existingIssuesLink>existing bugs on Github</existingIssuesLink> first. No match? <newIssueLink>Start a new one</newIssueLink>.",
|
||||||
|
"You do not have permission to do this": "You do not have permission to do this",
|
||||||
"Sending…": "Sending…",
|
"Sending…": "Sending…",
|
||||||
"Sent": "Sent",
|
"Sent": "Sent",
|
||||||
|
"Open link": "Open link",
|
||||||
"Forward message": "Forward message",
|
"Forward message": "Forward message",
|
||||||
"Forward to": "Forward to",
|
"Message preview": "Message preview",
|
||||||
"Filter your rooms and DMs": "Filter your rooms and DMs",
|
"Search for rooms or people": "Search for rooms or people",
|
||||||
"Confirm abort of host creation": "Confirm abort of host creation",
|
"Confirm abort of host creation": "Confirm abort of host creation",
|
||||||
"Are you sure you wish to abort creation of the host? The process cannot be continued.": "Are you sure you wish to abort creation of the host? The process cannot be continued.",
|
"Are you sure you wish to abort creation of the host? The process cannot be continued.": "Are you sure you wish to abort creation of the host? The process cannot be continued.",
|
||||||
"Abort": "Abort",
|
"Abort": "Abort",
|
||||||
|
|
|
@ -101,30 +101,30 @@ describe("ForwardDialog", () => {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const firstButton = wrapper.find("AccessibleButton.mx_ForwardList_sendButton").first();
|
const firstButton = wrapper.find("AccessibleButton.mx_ForwardList_sendButton").first();
|
||||||
expect(firstButton.text()).toBe("Send");
|
expect(firstButton.render().is(".mx_ForwardList_canSend")).toBe(true);
|
||||||
|
|
||||||
act(() => { firstButton.simulate("click"); });
|
act(() => { firstButton.simulate("click"); });
|
||||||
expect(firstButton.text()).toBe("Sending…");
|
expect(firstButton.render().is(".mx_ForwardList_sending")).toBe(true);
|
||||||
|
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
cancelSend();
|
cancelSend();
|
||||||
// Wait one tick for the button to realize the send failed
|
// Wait one tick for the button to realize the send failed
|
||||||
await new Promise(resolve => setImmediate(resolve));
|
await new Promise(resolve => setImmediate(resolve));
|
||||||
});
|
});
|
||||||
expect(firstButton.text()).toBe("Failed to send");
|
expect(firstButton.render().is(".mx_ForwardList_sendFailed")).toBe(true);
|
||||||
|
|
||||||
const secondButton = wrapper.find("AccessibleButton.mx_ForwardList_sendButton").at(1);
|
const secondButton = wrapper.find("AccessibleButton.mx_ForwardList_sendButton").at(1);
|
||||||
expect(secondButton.render().text()).toBe("Send");
|
expect(secondButton.render().is(".mx_ForwardList_canSend")).toBe(true);
|
||||||
|
|
||||||
act(() => { secondButton.simulate("click"); });
|
act(() => { secondButton.simulate("click"); });
|
||||||
expect(secondButton.text()).toBe("Sending…");
|
expect(secondButton.render().is(".mx_ForwardList_sending")).toBe(true);
|
||||||
|
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
finishSend();
|
finishSend();
|
||||||
// Wait one tick for the button to realize the send succeeded
|
// Wait one tick for the button to realize the send succeeded
|
||||||
await new Promise(resolve => setImmediate(resolve));
|
await new Promise(resolve => setImmediate(resolve));
|
||||||
});
|
});
|
||||||
expect(secondButton.text()).toBe("Sent");
|
expect(secondButton.render().is(".mx_ForwardList_sent")).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("can render replies", async () => {
|
it("can render replies", async () => {
|
||||||
|
|
Loading…
Reference in New Issue