mirror of
https://github.com/spacebarchat/client.git
synced 2024-11-22 02:12:38 +01:00
presence indicator rework
This commit is contained in:
parent
1750b22af7
commit
14e6575dcb
@ -7,8 +7,6 @@ import { useAppStore } from "../stores/AppStore";
|
|||||||
import Presence from "../stores/objects/Presence";
|
import Presence from "../stores/objects/Presence";
|
||||||
import User from "../stores/objects/User";
|
import User from "../stores/objects/User";
|
||||||
import Container from "./Container";
|
import Container from "./Container";
|
||||||
import Floating from "./floating/Floating";
|
|
||||||
import FloatingTrigger from "./floating/FloatingTrigger";
|
|
||||||
|
|
||||||
const Wrapper = styled(Container)<{ size: number; hasClick?: boolean }>`
|
const Wrapper = styled(Container)<{ size: number; hasClick?: boolean }>`
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
@ -16,43 +14,19 @@ const Wrapper = styled(Container)<{ size: number; hasClick?: boolean }>`
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const StatusDot = styled.i<{ color: string; size?: number; left?: number; bottom?: number }>`
|
|
||||||
border-radius: 50%;
|
|
||||||
border: 0.3rem solid var(--background-secondary);
|
|
||||||
background-color: ${(props) => props.color};
|
|
||||||
height: ${(props) => props.size ?? 16}px;
|
|
||||||
width: ${(props) => props.size ?? 16}px;
|
|
||||||
position: relative;
|
|
||||||
bottom: ${(props) => props.bottom ?? 16}px;
|
|
||||||
left: ${(props) => props.left ?? 20}px;
|
|
||||||
display: block;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const InnerWrapper = styled.div<{ width?: number; height?: number }>`
|
|
||||||
height: ${(props) => props.height ?? 40}px;
|
|
||||||
width: ${(props) => props.width ?? 40}px;
|
|
||||||
`;
|
|
||||||
|
|
||||||
function Yes(onClick: React.MouseEventHandler<HTMLDivElement>) {
|
|
||||||
return ({ children }: { children: React.ReactNode }) => {
|
|
||||||
return <div onClick={onClick}>{children}</div>;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
user?: User | AccountStore;
|
user?: User | AccountStore;
|
||||||
size?: number;
|
size: 32 | 80;
|
||||||
style?: React.CSSProperties;
|
style?: React.CSSProperties;
|
||||||
onClick?: React.MouseEventHandler<HTMLDivElement> | null;
|
onClick?: React.MouseEventHandler<HTMLDivElement> | null;
|
||||||
popoutPlacement?: "left" | "right" | "top" | "bottom";
|
popoutPlacement?: "left" | "right" | "top" | "bottom";
|
||||||
presence?: Presence;
|
presence?: Presence;
|
||||||
statusDotStyle?: {
|
statusDotStyle?: {
|
||||||
size?: number;
|
size?: number;
|
||||||
left?: number;
|
borderThickness?: number;
|
||||||
bottom?: number;
|
|
||||||
};
|
};
|
||||||
showPresence?: boolean;
|
showPresence?: boolean;
|
||||||
innerWrapperSize?: number;
|
isFloating?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
function Avatar(props: Props) {
|
function Avatar(props: Props) {
|
||||||
@ -63,40 +37,79 @@ function Avatar(props: Props) {
|
|||||||
const user = props.user ?? app.account;
|
const user = props.user ?? app.account;
|
||||||
if (!user) return null;
|
if (!user) return null;
|
||||||
|
|
||||||
// if onClick is null, use a div. if we pass a function, use yes. otherwise use FloatingTrigger
|
const presenceRingsTreatment = app.experiments.getTreatment("presence_rings");
|
||||||
const Base = props.onClick === null ? "div" : props.onClick ? Yes(props.onClick) : FloatingTrigger;
|
const ringsEnabled = presenceRingsTreatment && presenceRingsTreatment.id === 2;
|
||||||
|
|
||||||
return (
|
const children = (
|
||||||
<Floating
|
<Wrapper size={props.size} style={props.style} ref={ref} hasClick={props.onClick !== null}>
|
||||||
placement="right-start"
|
{props.showPresence && props.presence ? (
|
||||||
type="userPopout"
|
!ringsEnabled ? (
|
||||||
props={{
|
<div
|
||||||
user: user as unknown as User,
|
style={{
|
||||||
}}
|
position: "relative",
|
||||||
>
|
display: "inline-block",
|
||||||
<Base>
|
width: props.size,
|
||||||
<Wrapper size={props.size ?? 32} style={props.style} ref={ref} hasClick={props.onClick !== null}>
|
height: props.size,
|
||||||
<InnerWrapper width={props.innerWrapperSize} height={props.innerWrapperSize}>
|
}}
|
||||||
|
>
|
||||||
<img
|
<img
|
||||||
style={{
|
style={{
|
||||||
borderRadius: "50%",
|
borderRadius: "50%",
|
||||||
|
width: props.size,
|
||||||
|
height: props.size,
|
||||||
|
objectFit: "cover",
|
||||||
}}
|
}}
|
||||||
src={user.avatarUrl}
|
src={user.avatarUrl}
|
||||||
width={props.size ?? 32}
|
|
||||||
height={props.size ?? 32}
|
|
||||||
loading="eager"
|
loading="eager"
|
||||||
/>
|
/>
|
||||||
{props.showPresence && (
|
<div
|
||||||
<StatusDot
|
style={{
|
||||||
color={app.theme.getStatusColor(props.presence?.status ?? PresenceUpdateStatus.Offline)}
|
position: "absolute",
|
||||||
{...props.statusDotStyle}
|
width: props.statusDotStyle?.size ?? 14,
|
||||||
/>
|
height: props.statusDotStyle?.size ?? 14,
|
||||||
)}
|
backgroundColor: app.theme.getStatusColor(
|
||||||
</InnerWrapper>
|
props.presence?.status ?? PresenceUpdateStatus.Offline,
|
||||||
</Wrapper>
|
),
|
||||||
</Base>
|
borderRadius: "50%",
|
||||||
</Floating>
|
bottom: 0,
|
||||||
|
right: 0,
|
||||||
|
border: `${
|
||||||
|
props.statusDotStyle?.borderThickness ?? 0.2
|
||||||
|
}rem solid var(--background-secondary)`,
|
||||||
|
}}
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<img
|
||||||
|
width={props.size}
|
||||||
|
height={props.size}
|
||||||
|
style={{
|
||||||
|
borderRadius: "50%",
|
||||||
|
pointerEvents: "none",
|
||||||
|
border: `0.2rem solid ${app.theme.getStatusColor(
|
||||||
|
props.presence?.status ?? PresenceUpdateStatus.Offline,
|
||||||
|
)}`,
|
||||||
|
}}
|
||||||
|
src={user.avatarUrl}
|
||||||
|
loading="eager"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
) : (
|
||||||
|
<img
|
||||||
|
width={props.size}
|
||||||
|
height={props.size}
|
||||||
|
style={{
|
||||||
|
borderRadius: "50%",
|
||||||
|
pointerEvents: "none",
|
||||||
|
}}
|
||||||
|
src={user.avatarUrl}
|
||||||
|
loading="eager"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Wrapper>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return children;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default observer(Avatar);
|
export default observer(Avatar);
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
|
import { observer } from "mobx-react-lite";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import { modalController } from "../controllers/modals";
|
import { modalController } from "../controllers/modals";
|
||||||
import { useAppStore } from "../stores/AppStore";
|
import { useAppStore } from "../stores/AppStore";
|
||||||
import User from "../stores/objects/User";
|
|
||||||
import Avatar from "./Avatar";
|
import Avatar from "./Avatar";
|
||||||
import Icon from "./Icon";
|
import Icon from "./Icon";
|
||||||
import IconButton from "./IconButton";
|
import IconButton from "./IconButton";
|
||||||
@ -22,7 +22,7 @@ const Container = styled.div`
|
|||||||
background-color: var(--background-secondary-alt);
|
background-color: var(--background-secondary-alt);
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const AvatarWrapper = styled(FloatingTrigger)`
|
const AvatarWrapper = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
min-width: 120px;
|
min-width: 120px;
|
||||||
@ -30,10 +30,6 @@ const AvatarWrapper = styled(FloatingTrigger)`
|
|||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
cursor: default;
|
cursor: default;
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background-color: var(--background-primary-alt);
|
|
||||||
}
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const Name = styled.div`
|
const Name = styled.div`
|
||||||
@ -68,6 +64,12 @@ const ActionsWrapper = styled.div`
|
|||||||
display: flex;
|
display: flex;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const SettingsButton = styled(IconButton)`
|
||||||
|
&:hover {
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
function UserPanel() {
|
function UserPanel() {
|
||||||
const app = useAppStore();
|
const app = useAppStore();
|
||||||
const presence = app.presences.get(app.account!.id);
|
const presence = app.presences.get(app.account!.id);
|
||||||
@ -79,43 +81,35 @@ function UserPanel() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Floating
|
<Section>
|
||||||
placement="bottom"
|
<Container>
|
||||||
type="userPopout"
|
<AvatarWrapper>
|
||||||
props={{
|
<Avatar popoutPlacement="top" onClick={null} showPresence presence={presence} size={32} />
|
||||||
user: app.account! as unknown as User,
|
<Name>
|
||||||
}}
|
<Username>{app.account?.username}</Username>
|
||||||
>
|
<Subtext>#{app.account?.discriminator}</Subtext>
|
||||||
<Section>
|
</Name>
|
||||||
<Container>
|
</AvatarWrapper>
|
||||||
<AvatarWrapper>
|
|
||||||
<Avatar popoutPlacement="top" onClick={null} showPresence presence={presence} />
|
|
||||||
<Name>
|
|
||||||
<Username>{app.account?.username}</Username>
|
|
||||||
<Subtext>#{app.account?.discriminator}</Subtext>
|
|
||||||
</Name>
|
|
||||||
</AvatarWrapper>
|
|
||||||
|
|
||||||
<ActionsWrapper>
|
<ActionsWrapper>
|
||||||
<Floating
|
<Floating
|
||||||
placement="top"
|
placement="top"
|
||||||
type="tooltip"
|
type="tooltip"
|
||||||
offset={10}
|
offset={10}
|
||||||
props={{
|
props={{
|
||||||
content: <span>Settings</span>,
|
content: <span>Settings</span>,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<FloatingTrigger>
|
<FloatingTrigger>
|
||||||
<IconButton aria-label="settings" color="#fff" onClick={openSettingsModal}>
|
<SettingsButton aria-label="settings" color="#fff" onClick={openSettingsModal}>
|
||||||
<Icon icon="mdiCog" size="20px" />
|
<Icon icon="mdiCog" size="20px" />
|
||||||
</IconButton>
|
</SettingsButton>
|
||||||
</FloatingTrigger>
|
</FloatingTrigger>
|
||||||
</Floating>
|
</Floating>
|
||||||
</ActionsWrapper>
|
</ActionsWrapper>
|
||||||
</Container>
|
</Container>
|
||||||
</Section>
|
</Section>
|
||||||
</Floating>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default UserPanel;
|
export default observer(UserPanel);
|
||||||
|
@ -184,11 +184,10 @@ function UserProfilePopout({ user, member }: Props) {
|
|||||||
presence={presence}
|
presence={presence}
|
||||||
statusDotStyle={{
|
statusDotStyle={{
|
||||||
size: 25,
|
size: 25,
|
||||||
left: 55,
|
borderThickness: 0.3,
|
||||||
bottom: 28,
|
|
||||||
}}
|
}}
|
||||||
showPresence
|
showPresence
|
||||||
innerWrapperSize={92}
|
isFloating
|
||||||
/>
|
/>
|
||||||
</Top>
|
</Top>
|
||||||
<Bottom>
|
<Bottom>
|
||||||
|
@ -42,7 +42,7 @@ function Message({ message, header }: Props) {
|
|||||||
>
|
>
|
||||||
<MessageInfo>
|
<MessageInfo>
|
||||||
{header ? (
|
{header ? (
|
||||||
<Avatar key={message.author.id} user={message.author} size={40} />
|
<Avatar key={message.author.id} user={message.author} size={32} />
|
||||||
) : (
|
) : (
|
||||||
<MessageDetails message={message} position="left" />
|
<MessageDetails message={message} position="left" />
|
||||||
)}
|
)}
|
||||||
|
Loading…
Reference in New Issue
Block a user