feat: Added ability to click another user to observe them (sync scroll position) (#2858)

* feat: Added ability to click another user to observe them, mainly for fun

* language, lower debounce, prevent tooltip from hiding when toggling observation

* fix: Don't allow observing self, added banner at top of screen

* Dont edit tooltip as it's confusing between our actions and theirs

* snapshots
This commit is contained in:
Tom Moor
2021-12-16 17:36:39 -08:00
committed by GitHub
parent 4266b2eb3c
commit 9a7b5ea1f4
12 changed files with 214 additions and 36 deletions

View File

@@ -11,7 +11,7 @@ type Props = {
icon?: React.ReactNode;
user?: User;
alt?: string;
onClick?: () => void;
onClick?: React.MouseEventHandler<HTMLImageElement>;
className?: string;
};

View File

@@ -2,7 +2,7 @@ import { observable } from "mobx";
import { observer } from "mobx-react";
import * as React from "react";
import { WithTranslation, withTranslation } from "react-i18next";
import styled from "styled-components";
import styled, { css } from "styled-components";
import User from "~/models/User";
import UserProfile from "~/scenes/UserProfile";
import Avatar from "~/components/Avatar";
@@ -12,8 +12,10 @@ type Props = WithTranslation & {
user: User;
isPresent: boolean;
isEditing: boolean;
isObserving: boolean;
isCurrentUser: boolean;
profileOnClick: boolean;
onClick?: React.MouseEventHandler<HTMLImageElement>;
};
@observer
@@ -30,45 +32,60 @@ class AvatarWithPresence extends React.Component<Props> {
};
render() {
const { user, isPresent, isEditing, isCurrentUser, t } = this.props;
const action = isPresent
const {
onClick,
user,
isPresent,
isEditing,
isObserving,
isCurrentUser,
t,
} = this.props;
const status = isPresent
? isEditing
? t("currently editing")
: t("currently viewing")
: t("previously edited");
return (
<>
<Tooltip
tooltip={
<Centered>
<strong>{user.name}</strong> {isCurrentUser && `(${t("You")})`}
{action && (
{status && (
<>
<br />
{action}
{status}
</>
)}
</Centered>
}
placement="bottom"
>
<AvatarWrapper isPresent={isPresent}>
<AvatarWrapper
$isPresent={isPresent}
$isObserving={isObserving}
$color={user.color}
>
<Avatar
src={user.avatarUrl}
onClick={
this.props.profileOnClick === false
? undefined
? onClick
: this.handleOpenProfile
}
size={32}
/>
</AvatarWrapper>
</Tooltip>
<UserProfile
user={user}
isOpen={this.isOpen}
onRequestClose={this.handleCloseProfile}
/>
{this.props.profileOnClick && (
<UserProfile
user={user}
isOpen={this.isOpen}
onRequestClose={this.handleCloseProfile}
/>
)}
</>
);
}
@@ -78,9 +95,47 @@ const Centered = styled.div`
text-align: center;
`;
const AvatarWrapper = styled.div<{ isPresent: boolean }>`
opacity: ${(props) => (props.isPresent ? 1 : 0.5)};
const AvatarWrapper = styled.div<{
$isPresent: boolean;
$isObserving: boolean;
$color: string;
}>`
opacity: ${(props) => (props.$isPresent ? 1 : 0.5)};
transition: opacity 250ms ease-in-out;
border-radius: 50%;
position: relative;
&:after {
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
border-radius: 50%;
transition: border-color 100ms ease-in-out;
border: 2px solid transparent;
pointer-events: none;
${(props) =>
props.$isObserving &&
css`
border: 2px solid ${props.$color};
box-shadow: inset 0 0 0 2px ${props.theme.background};
&:hover {
top: -1px;
left: -1px;
right: -1px;
bottom: -1px;
}
`}
}
&:hover:after {
border: 2px solid ${(props) => props.$color};
box-shadow: inset 0 0 0 2px ${(props) => props.theme.background};
}
`;
export default withTranslation()(AvatarWithPresence);