1
0
mirror of https://github.com/spacebarchat/client.git synced 2024-11-22 10:22:30 +01:00

feat: user panel

This commit is contained in:
Puyodead1 2023-05-10 20:38:10 -04:00
parent 1696c91b5a
commit 306dcb051f
No known key found for this signature in database
GPG Key ID: A4FA4FEC0DD353FC
8 changed files with 278 additions and 64 deletions

31
src/components/Avatar.tsx Normal file
View File

@ -0,0 +1,31 @@
import { observer } from "mobx-react-lite";
import styled from "styled-components";
import { useAppStore } from "../stores/AppStore";
import Container from "./Container";
const Wrapper = styled(Container)<{ size: number }>`
width: ${(props) => props.size}px;
height: ${(props) => props.size}px;
border-radius: 50%;
position: relative;
`;
interface Props {
size?: number;
}
function Avatar(props: Props) {
const app = useAppStore();
return (
<Wrapper size={props.size ?? 32}>
<img
src={app.account?.getAvatarURL()}
width={props.size ?? 32}
height={props.size ?? 32}
/>
</Wrapper>
);
}
export default observer(Avatar);

View File

@ -6,86 +6,89 @@ interface Props {
}
export default styled.button<Props>`
background: ${(props) => {
if (props.outlined) return "transparent";
switch (props.variant) {
case "primary":
return "var(--primary)";
case "secondary":
return "var(--secondary)";
case "danger":
return "var(--danger)";
case "success":
return "var(--success)";
case "warning":
return "var(--warning)";
default:
return "var(--primary)";
}
}};
border: ${(props) => {
if (!props.outlined) return "none";
switch (props.variant) {
case "primary":
return "1px solid var(--primary)";
case "secondary":
return "1px solid var(--secondary)";
case "danger":
return "1px solid var(--danger)";
case "success":
return "1px solid var(--success)";
case "warning":
return "1px solid var(--warning)";
default:
return "1px solid var(--primary)";
}
}};
color: var(--text);
padding: 8px 16px;
border-radius: 8px;
font-size: 13px;
font-weight: 700;
cursor: pointer;
outline: none;
transition: background 0.2s ease-in-out;
pointer-events:${(props) => (props.disabled ? "none" : null)};
opacity: ${(props) => (props.disabled ? 0.5 : 1)};
&:hover {
background: ${(props) => {
background: ${(props) => {
if (props.outlined) return "transparent";
switch (props.variant) {
case "primary":
return "var(--primary-light)";
return "var(--primary)";
case "secondary":
return "var(--secondary-light)";
return "var(--secondary)";
case "danger":
return "var(--danger-light)";
return "var(--danger)";
case "success":
return "var(--success-light)";
return "var(--success)";
case "warning":
return "var(--warning-light)";
return "var(--warning)";
default:
return "var(--primary-light)";
return "var(--primary)";
}
}};
&:active {
background: ${(props) => {
border: ${(props) => {
if (!props.outlined) return "none";
switch (props.variant) {
case "primary":
return "var(--primary-dark)";
return "1px solid var(--primary)";
case "secondary":
return "var(--secondary-dark)";
return "1px solid var(--secondary)";
case "danger":
return "var(--danger-dark)";
return "1px solid var(--danger)";
case "success":
return "var(--success-dark)";
return "1px solid var(--success)";
case "warning":
return "var(--warning-dark)";
return "1px solid var(--warning)";
default:
return "var(--primary-dark)";
return "1px solid var(--primary)";
}
}};
color: var(--text);
padding: 8px 16px;
border-radius: 8px;
font-size: 13px;
font-weight: 700;
cursor: pointer;
outline: none;
transition: background 0.2s ease-in-out;
pointer-events: ${(props) => (props.disabled ? "none" : null)};
opacity: ${(props) => (props.disabled ? 0.5 : 1)};
&:hover {
background: ${(props) => {
switch (props.variant) {
case "primary":
return "var(--primary-light)";
case "secondary":
return "var(--secondary-light)";
case "danger":
return "var(--danger-light)";
case "success":
return "var(--success-light)";
case "warning":
return "var(--warning-light)";
default:
return "var(--primary-light)";
}
}};
cursor: ${(props) => (props.disabled ? "not-allowed" : "pointer")};
}
&:active {
background: ${(props) => {
switch (props.variant) {
case "primary":
return "var(--primary-dark)";
case "secondary":
return "var(--secondary-dark)";
case "danger":
return "var(--danger-dark)";
case "success":
return "var(--success-dark)";
case "warning":
return "var(--warning-dark)";
default:
return "var(--primary-dark)";
}
}};
}
`;

View File

@ -18,7 +18,7 @@ interface Props {
function ChannelHeader({ text }: Props) {
return (
<Wrapper>
<span>{text}</span>
<header>{text}</header>
</Wrapper>
);
}

View File

@ -4,6 +4,7 @@ import { useAppStore } from "../stores/AppStore";
import ChannelHeader from "./ChannelHeader";
import ChannelList from "./ChannelList";
import Container from "./Container";
import UserPanel from "./UserPanel";
const Wrapper = styled(Container)`
display: flex;
@ -29,6 +30,7 @@ function ChannelSidebar() {
{/* // TODO: replace with dm search if no guild */}
<ChannelHeader text={guild?.name ?? "Channel Header"} />
<ChannelList />
<UserPanel />
</Wrapper>
);
}

View File

@ -0,0 +1,66 @@
import styled from "styled-components";
interface Props {
// variant?: "primary" | "secondary" | "danger" | "success" | "warning";
outlined?: boolean;
color?: string;
}
export default styled.button<Props>`
display: flex;
align-items: center;
justify-content: center;
position: relative;
margin: 0;
padding: 0;
width: 32px;
height: 32px;
border-radius: 4px;
cursor: pointer;
outline: none;
opacity: ${(props) => (props.disabled ? 0.5 : 1)};
background-color: transparent;
color: ${(props) => {
if (props.outlined) return "transparent";
// switch (props.variant) {
// case "primary":
// return "var(--primary)";
// case "secondary":
// return "var(--secondary)";
// case "danger":
// return "var(--danger)";
// case "success":
// return "var(--success)";
// case "warning":
// return "var(--warning)";
// default:
// return "var(--primary)";
// }
return props.color;
}};
border: ${(props) => {
if (!props.outlined) return "none";
// switch (props.variant) {
// case "primary":
// return "1px solid var(--primary)";
// case "secondary":
// return "1px solid var(--secondary)";
// case "danger":
// return "1px solid var(--danger)";
// case "success":
// return "1px solid var(--success)";
// case "warning":
// return "1px solid var(--warning)";
// default:
// return "1px solid var(--primary)";
// }
return props.color;
}};
&:hover {
background-color: var(--background-primary-alt);
cursor: ${(props) => (props.disabled ? "not-allowed" : "pointer")};
}
`;

View File

@ -0,0 +1,88 @@
import { MdSettings } from "react-icons/md";
import styled from "styled-components";
import { useAppStore } from "../stores/AppStore";
import Avatar from "./Avatar";
import IconButton from "./IconButton";
import Tooltip from "./Tooltip";
const Section = styled.section`
flex: 0 0 auto;
background-color: var(--background-secondary-alt);
`;
const Container = styled.div`
display: flex;
height: 52px;
align-items: center;
padding: 0 8px;
margin-bottom: 1px;
background-color: var(--background-secondary-alt);
`;
const AvatarWrapper = styled.div`
display: flex;
align-items: center;
min-width: 120px;
padding-left: 2px;
margin-right: 8px;
border-radius: 4px;
// &:hover {
// background-color: var(--background-primary-alt);
// }
`;
const Name = styled.div`
padding: 4px 0px 4px 8px;
margin-right: 4px;
`;
const Username = styled.div`
font-size: 14px;
font-weight: 600;
`;
const Discriminator = styled.div`
font-size: 12px;
`;
const ActionsWrapper = styled.div`
flex: 1;
flex-direction: row;
flex-wrap: no-wrap;
justify-content: flex-end;
align-items: stretch;
display: flex;
`;
function UserPanel() {
const app = useAppStore();
return (
<Section>
<Container>
<AvatarWrapper>
<Avatar />
<Name>
<div>
<Username>{app.account?.username}</Username>
</div>
<Discriminator>
#{app.account?.discriminator}
</Discriminator>
</Name>
</AvatarWrapper>
<ActionsWrapper>
<Tooltip title="Settings">
<IconButton aria-label="settings" disabled color="#fff">
<MdSettings size={20} />
</IconButton>
</Tooltip>
</ActionsWrapper>
</Container>
</Section>
);
}
export default UserPanel;

View File

@ -6,6 +6,7 @@ export type ThemeVariables =
| "backgroundPrimary"
| "backgroundPrimaryAlt"
| "backgroundSecondary"
| "backgroundSecondaryAlt"
| "backgroundTertiary"
| "text"
| "textSecondary"
@ -49,6 +50,7 @@ export const ThemePresets: Record<string, Theme> = {
backgroundPrimary: "#ffffff",
backgroundPrimaryAlt: "",
backgroundSecondary: "#ebe5e4",
backgroundSecondaryAlt: "#ebe5e4",
backgroundTertiary: "#e9e2e1",
text: "#000000",
textSecondary: "#bdbdbd",
@ -83,6 +85,7 @@ export const ThemePresets: Record<string, Theme> = {
backgroundPrimary: "#242424",
backgroundPrimaryAlt: "#2b2b2b",
backgroundSecondary: "#1b1b1b",
backgroundSecondaryAlt: "#181818",
backgroundTertiary: "#141414",
text: "#e9e2e1",
textSecondary: "#bdbdbd",

View File

@ -1,9 +1,13 @@
import {
APIUser,
CDNRoutes,
DefaultUserAvatarAssets,
ImageFormat,
UserFlags,
UserPremiumType,
} from "@spacebarchat/spacebar-api-types/v9";
import { observable } from "mobx";
import REST from "../utils/REST";
export default class AccountStore {
id: string;
@ -58,4 +62,21 @@ export default class AccountStore {
// this.phone = user.phone;
// this.nsfwAllowed = user.nsfw_allowed;
}
/**
* Gets the users display avatar url
* @returns The URL to the user's avatar or the default avatar if they don't have one.
*/
getAvatarURL(): string {
if (this.avatar)
return REST.makeCDNUrl(
CDNRoutes.userAvatar(this.id, this.avatar, ImageFormat.PNG),
);
else
return REST.makeCDNUrl(
CDNRoutes.defaultUserAvatar(
(Number(this.discriminator) % 5) as DefaultUserAvatarAssets,
),
);
}
}