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:
parent
1696c91b5a
commit
306dcb051f
31
src/components/Avatar.tsx
Normal file
31
src/components/Avatar.tsx
Normal 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);
|
@ -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)";
|
||||
}
|
||||
}};
|
||||
}
|
||||
`;
|
||||
|
@ -18,7 +18,7 @@ interface Props {
|
||||
function ChannelHeader({ text }: Props) {
|
||||
return (
|
||||
<Wrapper>
|
||||
<span>{text}</span>
|
||||
<header>{text}</header>
|
||||
</Wrapper>
|
||||
);
|
||||
}
|
||||
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
66
src/components/IconButton.tsx
Normal file
66
src/components/IconButton.tsx
Normal 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")};
|
||||
}
|
||||
`;
|
88
src/components/UserPanel.tsx
Normal file
88
src/components/UserPanel.tsx
Normal 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;
|
@ -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",
|
||||
|
@ -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,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user