mirror of
https://github.com/revoltchat/revite.git
synced 2024-11-14 11:15:02 -05:00
Use UserIcon in server list header.
Re-design status context menu.
This commit is contained in:
parent
9243b3e20d
commit
f5b3b15188
4 changed files with 111 additions and 78 deletions
|
@ -47,18 +47,8 @@ interface Props {
|
||||||
export default function UserHeader({ user }: Props) {
|
export default function UserHeader({ user }: Props) {
|
||||||
const { writeClipboard } = useIntermediate();
|
const { writeClipboard } = useIntermediate();
|
||||||
|
|
||||||
function openPresenceSelector() {
|
|
||||||
openContextMenu("Status");
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Header borders placement="secondary">
|
<Header borders placement="secondary">
|
||||||
<UserIcon
|
|
||||||
target={user}
|
|
||||||
size={32}
|
|
||||||
status
|
|
||||||
onClick={openPresenceSelector}
|
|
||||||
/>
|
|
||||||
<HeaderBase>
|
<HeaderBase>
|
||||||
<Localizer>
|
<Localizer>
|
||||||
<Tooltip content={<Text id="app.special.copy_username" />}>
|
<Tooltip content={<Text id="app.special.copy_username" />}>
|
||||||
|
@ -69,7 +59,7 @@ export default function UserHeader({ user }: Props) {
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Localizer>
|
</Localizer>
|
||||||
<span className="status"
|
<span className="status"
|
||||||
onClick={openPresenceSelector}>
|
onClick={() => openContextMenu("Status")}>
|
||||||
<UserStatus user={user} />
|
<UserStatus user={user} />
|
||||||
</span>
|
</span>
|
||||||
</HeaderBase>
|
</HeaderBase>
|
||||||
|
|
|
@ -6,7 +6,7 @@ import ServerIcon from "../../common/ServerIcon";
|
||||||
import { Children } from "../../../types/Preact";
|
import { Children } from "../../../types/Preact";
|
||||||
import { Plus } from "@styled-icons/boxicons-regular";
|
import { Plus } from "@styled-icons/boxicons-regular";
|
||||||
import PaintCounter from "../../../lib/PaintCounter";
|
import PaintCounter from "../../../lib/PaintCounter";
|
||||||
import { attachContextMenu } from 'preact-context-menu';
|
import { attachContextMenu, openContextMenu } from 'preact-context-menu';
|
||||||
import { connectState } from "../../../redux/connector";
|
import { connectState } from "../../../redux/connector";
|
||||||
import { useLocation, useParams } from "react-router-dom";
|
import { useLocation, useParams } from "react-router-dom";
|
||||||
import { Unreads } from "../../../redux/reducers/unreads";
|
import { Unreads } from "../../../redux/reducers/unreads";
|
||||||
|
@ -15,10 +15,11 @@ import { Channel, Servers } from "revolt.js/dist/api/objects";
|
||||||
import { LastOpened } from "../../../redux/reducers/last_opened";
|
import { LastOpened } from "../../../redux/reducers/last_opened";
|
||||||
import { isTouchscreenDevice } from "../../../lib/isTouchscreenDevice";
|
import { isTouchscreenDevice } from "../../../lib/isTouchscreenDevice";
|
||||||
import { useIntermediate } from "../../../context/intermediate/Intermediate";
|
import { useIntermediate } from "../../../context/intermediate/Intermediate";
|
||||||
import { useChannels, useForceUpdate, useServers } from "../../../context/revoltjs/hooks";
|
import { useChannels, useForceUpdate, useSelf, useServers } from "../../../context/revoltjs/hooks";
|
||||||
|
|
||||||
import logoSVG from '../../../assets/logo.svg';
|
import logoSVG from '../../../assets/logo.svg';
|
||||||
import Tooltip from "../../common/Tooltip";
|
import Tooltip from "../../common/Tooltip";
|
||||||
|
import UserIcon from "../../common/user/UserIcon";
|
||||||
|
|
||||||
function Icon({ children, unread, size }: { children: Children, unread?: 'mention' | 'unread', size: number }) {
|
function Icon({ children, unread, size }: { children: Children, unread?: 'mention' | 'unread', size: number }) {
|
||||||
return (
|
return (
|
||||||
|
@ -81,7 +82,7 @@ const ServerList = styled.div`
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const ServerEntry = styled.div<{ active: boolean, invert?: boolean }>`
|
const ServerEntry = styled.div<{ active: boolean, home?: boolean }>`
|
||||||
height: 58px;
|
height: 58px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
@ -91,7 +92,7 @@ const ServerEntry = styled.div<{ active: boolean, invert?: boolean }>`
|
||||||
// outline: 1px solid red;
|
// outline: 1px solid red;
|
||||||
}
|
}
|
||||||
|
|
||||||
> div, > svg {
|
> div {
|
||||||
width: 46px;
|
width: 46px;
|
||||||
height: 46px;
|
height: 46px;
|
||||||
display: grid;
|
display: grid;
|
||||||
|
@ -139,10 +140,8 @@ const ServerEntry = styled.div<{ active: boolean, invert?: boolean }>`
|
||||||
` }
|
` }
|
||||||
}
|
}
|
||||||
|
|
||||||
${ props => props.active && props.invert && css`
|
${ props => (!props.active || props.home) && css`
|
||||||
img {
|
cursor: pointer;
|
||||||
filter: saturate(0) brightness(10);
|
|
||||||
}
|
|
||||||
` }
|
` }
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
@ -153,6 +152,7 @@ interface Props {
|
||||||
|
|
||||||
export function ServerListSidebar({ unreads, lastOpened }: Props) {
|
export function ServerListSidebar({ unreads, lastOpened }: Props) {
|
||||||
const ctx = useForceUpdate();
|
const ctx = useForceUpdate();
|
||||||
|
const self = useSelf(ctx);
|
||||||
const activeServers = useServers(undefined, ctx) as Servers.Server[];
|
const activeServers = useServers(undefined, ctx) as Servers.Server[];
|
||||||
const channels = (useChannels(undefined, ctx) as Channel[])
|
const channels = (useChannels(undefined, ctx) as Channel[])
|
||||||
.map(x => mapChannelWithUnread(x, unreads));
|
.map(x => mapChannelWithUnread(x, unreads));
|
||||||
|
@ -199,10 +199,13 @@ export function ServerListSidebar({ unreads, lastOpened }: Props) {
|
||||||
<ServersBase>
|
<ServersBase>
|
||||||
<ServerList>
|
<ServerList>
|
||||||
<ConditionalLink active={homeActive} to={lastOpened.home ? `/channel/${lastOpened.home}` : '/'}>
|
<ConditionalLink active={homeActive} to={lastOpened.home ? `/channel/${lastOpened.home}` : '/'}>
|
||||||
<ServerEntry invert active={homeActive}>
|
<ServerEntry home active={homeActive}>
|
||||||
<Icon size={42} unread={homeUnread}>
|
<div onContextMenu={attachContextMenu('Status')}
|
||||||
<img style={{ width: 32, height: 32 }} src={logoSVG} />
|
onClick={() => homeActive && openContextMenu("Status")}>
|
||||||
</Icon>
|
<Icon size={42} unread={homeUnread}>
|
||||||
|
<UserIcon target={self} size={32} status />
|
||||||
|
</Icon>
|
||||||
|
</div>
|
||||||
<span />
|
<span />
|
||||||
</ServerEntry>
|
</ServerEntry>
|
||||||
</ConditionalLink>
|
</ConditionalLink>
|
||||||
|
|
|
@ -19,8 +19,12 @@ import { Children } from "../types/Preact";
|
||||||
import LineDivider from "../components/ui/LineDivider";
|
import LineDivider from "../components/ui/LineDivider";
|
||||||
import { connectState } from "../redux/connector";
|
import { connectState } from "../redux/connector";
|
||||||
import { internalEmit } from "./eventEmitter";
|
import { internalEmit } from "./eventEmitter";
|
||||||
import { At, Bell, BellOff, Check, CheckSquare, ChevronRight, Block, Square, LeftArrowAlt } from "@styled-icons/boxicons-regular";
|
import { At, Bell, BellOff, Check, CheckSquare, ChevronRight, Block, Square, LeftArrowAlt, Trash } from "@styled-icons/boxicons-regular";
|
||||||
|
import { Cog } from "@styled-icons/boxicons-solid";
|
||||||
import { getNotificationState, Notifications, NotificationState } from "../redux/reducers/notifications";
|
import { getNotificationState, Notifications, NotificationState } from "../redux/reducers/notifications";
|
||||||
|
import UserStatus from "../components/common/user/UserStatus";
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
|
import IconButton from "../components/ui/IconButton";
|
||||||
|
|
||||||
interface ContextMenuData {
|
interface ContextMenuData {
|
||||||
user?: string;
|
user?: string;
|
||||||
|
@ -72,6 +76,7 @@ type Action =
|
||||||
| { action: "leave_server"; target: Servers.Server }
|
| { action: "leave_server"; target: Servers.Server }
|
||||||
| { action: "delete_server"; target: Servers.Server }
|
| { action: "delete_server"; target: Servers.Server }
|
||||||
| { action: "open_notification_options", channel: Channels.Channel }
|
| { action: "open_notification_options", channel: Channels.Channel }
|
||||||
|
| { action: "open_settings" }
|
||||||
| { action: "open_channel_settings", id: string }
|
| { action: "open_channel_settings", id: string }
|
||||||
| { action: "open_server_settings", id: string }
|
| { action: "open_server_settings", id: string }
|
||||||
| { action: "open_server_channel_settings", server: string, id: string }
|
| { action: "open_server_channel_settings", server: string, id: string }
|
||||||
|
@ -315,6 +320,7 @@ function ContextMenus(props: Props) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case "open_settings": history.push('/settings'); break;
|
||||||
case "open_channel_settings": history.push(`/channel/${data.id}/settings`); break;
|
case "open_channel_settings": history.push(`/channel/${data.id}/settings`); break;
|
||||||
case "open_server_channel_settings": history.push(`/server/${data.server}/channel/${data.id}/settings`); break;
|
case "open_server_channel_settings": history.push(`/server/${data.server}/channel/${data.id}/settings`); break;
|
||||||
case "open_server_settings": history.push(`/server/${data.id}/settings`); break;
|
case "open_server_settings": history.push(`/server/${data.id}/settings`); break;
|
||||||
|
@ -641,62 +647,75 @@ function ContextMenus(props: Props) {
|
||||||
return elements;
|
return elements;
|
||||||
}}
|
}}
|
||||||
</ContextMenuWithData>
|
</ContextMenuWithData>
|
||||||
<ContextMenu id="Status" onClose={contextClick}>
|
<ContextMenuWithData id="Status" onClose={contextClick} className="Status">
|
||||||
<span data-disabled={true}>@{client.user?.username}</span>
|
{() => <>
|
||||||
<LineDivider />
|
<div className="header">
|
||||||
<MenuItem
|
<div className="main">
|
||||||
data={{
|
<div>@{client.user!.username}</div>
|
||||||
action: "set_presence",
|
<div className="status"><UserStatus user={client.user!} /></div>
|
||||||
presence: Users.Presence.Online
|
</div>
|
||||||
}}
|
<IconButton>
|
||||||
disabled={!isOnline}
|
<MenuItem data={{ action: "open_settings" }}>
|
||||||
>
|
<Cog size={18} />
|
||||||
<div className="indicator online" />
|
</MenuItem>
|
||||||
<Text id={`app.status.online`} />
|
</IconButton>
|
||||||
</MenuItem>
|
</div>
|
||||||
<MenuItem
|
<LineDivider />
|
||||||
data={{
|
|
||||||
action: "set_presence",
|
|
||||||
presence: Users.Presence.Idle
|
|
||||||
}}
|
|
||||||
disabled={!isOnline}
|
|
||||||
>
|
|
||||||
<div className="indicator idle" />
|
|
||||||
<Text id={`app.status.idle`} />
|
|
||||||
</MenuItem>
|
|
||||||
<MenuItem
|
|
||||||
data={{
|
|
||||||
action: "set_presence",
|
|
||||||
presence: Users.Presence.Busy
|
|
||||||
}}
|
|
||||||
disabled={!isOnline}
|
|
||||||
>
|
|
||||||
<div className="indicator busy" />
|
|
||||||
<Text id={`app.status.busy`} />
|
|
||||||
</MenuItem>
|
|
||||||
<MenuItem
|
|
||||||
data={{
|
|
||||||
action: "set_presence",
|
|
||||||
presence: Users.Presence.Invisible
|
|
||||||
}}
|
|
||||||
disabled={!isOnline}
|
|
||||||
>
|
|
||||||
<div className="indicator invisible" />
|
|
||||||
<Text id={`app.status.invisible`} />
|
|
||||||
</MenuItem>
|
|
||||||
<LineDivider />
|
|
||||||
<MenuItem data={{ action: "set_status" }} disabled={!isOnline}>
|
|
||||||
<Text id={`app.context_menu.custom_status`} />
|
|
||||||
</MenuItem>
|
|
||||||
{client.user?.status?.text && (
|
|
||||||
<MenuItem
|
<MenuItem
|
||||||
data={{ action: "clear_status" }}
|
data={{
|
||||||
|
action: "set_presence",
|
||||||
|
presence: Users.Presence.Online
|
||||||
|
}}
|
||||||
disabled={!isOnline}
|
disabled={!isOnline}
|
||||||
>
|
>
|
||||||
<Text id={`app.context_menu.clear_status`} />
|
<div className="indicator online" />
|
||||||
|
<Text id={`app.status.online`} />
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
)}
|
<MenuItem
|
||||||
</ContextMenu>
|
data={{
|
||||||
|
action: "set_presence",
|
||||||
|
presence: Users.Presence.Idle
|
||||||
|
}}
|
||||||
|
disabled={!isOnline}
|
||||||
|
>
|
||||||
|
<div className="indicator idle" />
|
||||||
|
<Text id={`app.status.idle`} />
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem
|
||||||
|
data={{
|
||||||
|
action: "set_presence",
|
||||||
|
presence: Users.Presence.Busy
|
||||||
|
}}
|
||||||
|
disabled={!isOnline}
|
||||||
|
>
|
||||||
|
<div className="indicator busy" />
|
||||||
|
<Text id={`app.status.busy`} />
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem
|
||||||
|
data={{
|
||||||
|
action: "set_presence",
|
||||||
|
presence: Users.Presence.Invisible
|
||||||
|
}}
|
||||||
|
disabled={!isOnline}
|
||||||
|
>
|
||||||
|
<div className="indicator invisible" />
|
||||||
|
<Text id={`app.status.invisible`} />
|
||||||
|
</MenuItem>
|
||||||
|
<LineDivider />
|
||||||
|
<div className="header">
|
||||||
|
<div className="main">
|
||||||
|
<MenuItem data={{ action: "set_status" }} disabled={!isOnline}>
|
||||||
|
<Text id={`app.context_menu.custom_status`} />
|
||||||
|
</MenuItem>
|
||||||
|
</div>
|
||||||
|
{ client.user!.status?.text && <IconButton>
|
||||||
|
<MenuItem data={{ action: "clear_status" }}>
|
||||||
|
<Trash size={18} />
|
||||||
|
</MenuItem>
|
||||||
|
</IconButton> }
|
||||||
|
</div>
|
||||||
|
</>}
|
||||||
|
</ContextMenuWithData>
|
||||||
<ContextMenuWithData id="NotificationOptions" onClose={contextClick}>
|
<ContextMenuWithData id="NotificationOptions" onClose={contextClick}>
|
||||||
{({ channel }: { channel: Channels.Channel }) => {
|
{({ channel }: { channel: Channels.Channel }) => {
|
||||||
const state = props.notifications[channel._id];
|
const state = props.notifications[channel._id];
|
||||||
|
|
|
@ -4,17 +4,17 @@
|
||||||
padding: 6px 8px;
|
padding: 6px 8px;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
|
font-size: .875rem;
|
||||||
color: var(--secondary-foreground);
|
color: var(--secondary-foreground);
|
||||||
background: var(--primary-background) !important;
|
background: var(--primary-background) !important;
|
||||||
box-shadow: 0px 0px 8px 8px rgba(0, 0, 0, 0.05);
|
box-shadow: 0px 0px 8px 8px rgba(0, 0, 0, 0.05);
|
||||||
|
|
||||||
> span {
|
> span, .main > span {
|
||||||
gap: 6px;
|
gap: 6px;
|
||||||
margin: 2px 0;
|
margin: 2px 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
padding: 6px 8px;
|
padding: 6px 8px;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
font-size: .875rem;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
|
||||||
|
@ -33,6 +33,27 @@
|
||||||
color: var(--tertiary-foreground);
|
color: var(--tertiary-foreground);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.context-menu.Status {
|
||||||
|
.header {
|
||||||
|
gap: 8px;
|
||||||
|
display: flex;
|
||||||
|
padding: 6px 8px;
|
||||||
|
font-weight: 600;
|
||||||
|
align-items: center;
|
||||||
|
color: var(--foreground);
|
||||||
|
|
||||||
|
.main {
|
||||||
|
flex-grow: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status {
|
||||||
|
font-size: .6rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.indicator {
|
.indicator {
|
||||||
width: 8px;
|
width: 8px;
|
||||||
|
|
Loading…
Reference in a new issue