Use UserIcon in server list header.

Re-design status context menu.
This commit is contained in:
Paul 2021-07-01 19:37:36 +01:00
parent 9243b3e20d
commit f5b3b15188
4 changed files with 111 additions and 78 deletions

View file

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

View file

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

View file

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

View file

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