1
0
mirror of https://github.com/spacebarchat/client.git synced 2024-11-22 10:22:30 +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 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",
width: props.size,
height: props.size,
}} }}
> >
<Base>
<Wrapper size={props.size ?? 32} style={props.style} ref={ref} hasClick={props.onClick !== null}>
<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(
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"
/> />
)} )}
</InnerWrapper>
</Wrapper> </Wrapper>
</Base>
</Floating>
); );
return children;
} }
export default observer(Avatar); export default observer(Avatar);

View File

@ -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,17 +81,10 @@ function UserPanel() {
}; };
return ( return (
<Floating
placement="bottom"
type="userPopout"
props={{
user: app.account! as unknown as User,
}}
>
<Section> <Section>
<Container> <Container>
<AvatarWrapper> <AvatarWrapper>
<Avatar popoutPlacement="top" onClick={null} showPresence presence={presence} /> <Avatar popoutPlacement="top" onClick={null} showPresence presence={presence} size={32} />
<Name> <Name>
<Username>{app.account?.username}</Username> <Username>{app.account?.username}</Username>
<Subtext>#{app.account?.discriminator}</Subtext> <Subtext>#{app.account?.discriminator}</Subtext>
@ -106,16 +101,15 @@ function UserPanel() {
}} }}
> >
<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);

View File

@ -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>

View File

@ -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" />
)} )}