1
0
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:
Puyodead1 2024-06-03 11:54:02 -04:00
parent 1750b22af7
commit 14e6575dcb
No known key found for this signature in database
GPG Key ID: A4FA4FEC0DD353FC
4 changed files with 106 additions and 100 deletions

View File

@ -7,8 +7,6 @@ import { useAppStore } from "../stores/AppStore";
import Presence from "../stores/objects/Presence";
import User from "../stores/objects/User";
import Container from "./Container";
import Floating from "./floating/Floating";
import FloatingTrigger from "./floating/FloatingTrigger";
const Wrapper = styled(Container)<{ size: number; hasClick?: boolean }>`
background-color: transparent;
@ -16,43 +14,19 @@ const Wrapper = styled(Container)<{ size: number; hasClick?: boolean }>`
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 {
user?: User | AccountStore;
size?: number;
size: 32 | 80;
style?: React.CSSProperties;
onClick?: React.MouseEventHandler<HTMLDivElement> | null;
popoutPlacement?: "left" | "right" | "top" | "bottom";
presence?: Presence;
statusDotStyle?: {
size?: number;
left?: number;
bottom?: number;
borderThickness?: number;
};
showPresence?: boolean;
innerWrapperSize?: number;
isFloating?: boolean;
}
function Avatar(props: Props) {
@ -63,40 +37,79 @@ function Avatar(props: Props) {
const user = props.user ?? app.account;
if (!user) return null;
// if onClick is null, use a div. if we pass a function, use yes. otherwise use FloatingTrigger
const Base = props.onClick === null ? "div" : props.onClick ? Yes(props.onClick) : FloatingTrigger;
const presenceRingsTreatment = app.experiments.getTreatment("presence_rings");
const ringsEnabled = presenceRingsTreatment && presenceRingsTreatment.id === 2;
return (
<Floating
placement="right-start"
type="userPopout"
props={{
user: user as unknown as User,
}}
>
<Base>
<Wrapper size={props.size ?? 32} style={props.style} ref={ref} hasClick={props.onClick !== null}>
<InnerWrapper width={props.innerWrapperSize} height={props.innerWrapperSize}>
const children = (
<Wrapper size={props.size} style={props.style} ref={ref} hasClick={props.onClick !== null}>
{props.showPresence && props.presence ? (
!ringsEnabled ? (
<div
style={{
position: "relative",
display: "inline-block",
width: props.size,
height: props.size,
}}
>
<img
style={{
borderRadius: "50%",
width: props.size,
height: props.size,
objectFit: "cover",
}}
src={user.avatarUrl}
width={props.size ?? 32}
height={props.size ?? 32}
loading="eager"
/>
{props.showPresence && (
<StatusDot
color={app.theme.getStatusColor(props.presence?.status ?? PresenceUpdateStatus.Offline)}
{...props.statusDotStyle}
/>
)}
</InnerWrapper>
</Wrapper>
</Base>
</Floating>
<div
style={{
position: "absolute",
width: props.statusDotStyle?.size ?? 14,
height: props.statusDotStyle?.size ?? 14,
backgroundColor: app.theme.getStatusColor(
props.presence?.status ?? PresenceUpdateStatus.Offline,
),
borderRadius: "50%",
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);

View File

@ -1,7 +1,7 @@
import { observer } from "mobx-react-lite";
import styled from "styled-components";
import { modalController } from "../controllers/modals";
import { useAppStore } from "../stores/AppStore";
import User from "../stores/objects/User";
import Avatar from "./Avatar";
import Icon from "./Icon";
import IconButton from "./IconButton";
@ -22,7 +22,7 @@ const Container = styled.div`
background-color: var(--background-secondary-alt);
`;
const AvatarWrapper = styled(FloatingTrigger)`
const AvatarWrapper = styled.div`
display: flex;
align-items: center;
min-width: 120px;
@ -30,10 +30,6 @@ const AvatarWrapper = styled(FloatingTrigger)`
margin-right: 8px;
border-radius: 4px;
cursor: default;
&:hover {
background-color: var(--background-primary-alt);
}
`;
const Name = styled.div`
@ -68,6 +64,12 @@ const ActionsWrapper = styled.div`
display: flex;
`;
const SettingsButton = styled(IconButton)`
&:hover {
opacity: 0.8;
}
`;
function UserPanel() {
const app = useAppStore();
const presence = app.presences.get(app.account!.id);
@ -79,43 +81,35 @@ function UserPanel() {
};
return (
<Floating
placement="bottom"
type="userPopout"
props={{
user: app.account! as unknown as User,
}}
>
<Section>
<Container>
<AvatarWrapper>
<Avatar popoutPlacement="top" onClick={null} showPresence presence={presence} />
<Name>
<Username>{app.account?.username}</Username>
<Subtext>#{app.account?.discriminator}</Subtext>
</Name>
</AvatarWrapper>
<Section>
<Container>
<AvatarWrapper>
<Avatar popoutPlacement="top" onClick={null} showPresence presence={presence} size={32} />
<Name>
<Username>{app.account?.username}</Username>
<Subtext>#{app.account?.discriminator}</Subtext>
</Name>
</AvatarWrapper>
<ActionsWrapper>
<Floating
placement="top"
type="tooltip"
offset={10}
props={{
content: <span>Settings</span>,
}}
>
<FloatingTrigger>
<IconButton aria-label="settings" color="#fff" onClick={openSettingsModal}>
<Icon icon="mdiCog" size="20px" />
</IconButton>
</FloatingTrigger>
</Floating>
</ActionsWrapper>
</Container>
</Section>
</Floating>
<ActionsWrapper>
<Floating
placement="top"
type="tooltip"
offset={10}
props={{
content: <span>Settings</span>,
}}
>
<FloatingTrigger>
<SettingsButton aria-label="settings" color="#fff" onClick={openSettingsModal}>
<Icon icon="mdiCog" size="20px" />
</SettingsButton>
</FloatingTrigger>
</Floating>
</ActionsWrapper>
</Container>
</Section>
);
}
export default UserPanel;
export default observer(UserPanel);

View File

@ -184,11 +184,10 @@ function UserProfilePopout({ user, member }: Props) {
presence={presence}
statusDotStyle={{
size: 25,
left: 55,
bottom: 28,
borderThickness: 0.3,
}}
showPresence
innerWrapperSize={92}
isFloating
/>
</Top>
<Bottom>

View File

@ -42,7 +42,7 @@ function Message({ message, header }: Props) {
>
<MessageInfo>
{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" />
)}