mirror of
https://github.com/spacebarchat/client.git
synced 2024-11-22 02:12:38 +01:00
virtualized guild list
This commit is contained in:
parent
11f93c3ad2
commit
8af482cb89
@ -15,12 +15,10 @@ import SidebarPill, { PillType } from "./SidebarPill";
|
||||
import Tooltip from "./Tooltip";
|
||||
import CreateInviteModal from "./modals/CreateInviteModal";
|
||||
|
||||
export const GuildSidebarListItem = styled.li`
|
||||
export const GuildSidebarListItem = styled.div`
|
||||
position: relative;
|
||||
margin: 0 0 8px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
width: 72px;
|
||||
cursor: pointer;
|
||||
`;
|
||||
|
||||
@ -81,6 +79,13 @@ function GuildItem({ guild, active }: Props) {
|
||||
},
|
||||
]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (app.activeChannelId && app.activeGuildId === guild.id) return setPillType("active");
|
||||
else if (isHovered) return setPillType("hover");
|
||||
// TODO: unread
|
||||
else return setPillType("none");
|
||||
}, [app.activeGuildId, isHovered]);
|
||||
|
||||
const doNavigate = () => {
|
||||
const channel = guild.channels.find((x) => {
|
||||
const permission = Permissions.getPermission(app.account!.id, guild, x);
|
||||
@ -89,13 +94,6 @@ function GuildItem({ guild, active }: Props) {
|
||||
navigate(`/channels/${guild.id}${channel ? `/${channel.id}` : ""}`);
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
if (active) return setPillType("active");
|
||||
else if (isHovered) return setPillType("hover");
|
||||
// TODO: unread
|
||||
else return setPillType("none");
|
||||
}, [active, isHovered]);
|
||||
|
||||
return (
|
||||
<GuildSidebarListItem
|
||||
onContextMenu={(e) => {
|
||||
|
@ -2,24 +2,31 @@ import { useModals } from "@mattjennings/react-modal-stack";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import React from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { AutoSizer, List, ListRowProps } from "react-virtualized";
|
||||
import styled from "styled-components";
|
||||
import { useAppStore } from "../stores/AppStore";
|
||||
import Guild from "../stores/objects/Guild";
|
||||
import GuildItem, { GuildSidebarListItem } from "./GuildItem";
|
||||
import SidebarAction from "./SidebarAction";
|
||||
import AddServerModal from "./modals/AddServerModal";
|
||||
|
||||
const List = styled.ul`
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
const Container = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 0 0 48px;
|
||||
align-items: center;
|
||||
flex: 0 0 72px;
|
||||
margin: 4px 0 0 0;
|
||||
|
||||
@media (max-width: 560px) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.ReactVirtualized__List {
|
||||
scrollbar-width: none; /* Firefox */
|
||||
-ms-overflow-style: none; /* Internet Explorer 10+ */
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const Divider = styled.div`
|
||||
@ -31,49 +38,82 @@ const Divider = styled.div`
|
||||
|
||||
function GuildSidebar() {
|
||||
const app = useAppStore();
|
||||
|
||||
const { openModal } = useModals();
|
||||
const navigate = useNavigate();
|
||||
const { all } = app.guilds;
|
||||
const itemCount = all.length + 3; // add the home button, divider, and add server button
|
||||
|
||||
const renderGuildItem = React.useCallback((guild: Guild, active: boolean) => {
|
||||
return <GuildItem key={guild.id} guild={guild} active={active} />;
|
||||
}, []);
|
||||
const rowRenderer = ({ index, key, style }: ListRowProps) => {
|
||||
let element: React.ReactNode;
|
||||
if (index === 0) {
|
||||
// home button
|
||||
element = (
|
||||
<SidebarAction
|
||||
key="home"
|
||||
tooltip="Home"
|
||||
icon={{
|
||||
icon: "mdiHome",
|
||||
size: "24px",
|
||||
}}
|
||||
action={() => navigate("/channels/@me")}
|
||||
margin={false}
|
||||
active={app.activeGuildId === "@me"}
|
||||
/>
|
||||
);
|
||||
} else if (index === 1) {
|
||||
// divider
|
||||
element = (
|
||||
<GuildSidebarListItem>
|
||||
<Divider key="divider" />
|
||||
</GuildSidebarListItem>
|
||||
);
|
||||
} else if (index === itemCount - 1) {
|
||||
// add server button/any other end items
|
||||
element = (
|
||||
<SidebarAction
|
||||
key="add-server"
|
||||
tooltip="Add Server"
|
||||
icon={{
|
||||
icon: "mdiPlus",
|
||||
size: "24px",
|
||||
color: "var(--success)",
|
||||
}}
|
||||
action={() => {
|
||||
openModal(AddServerModal);
|
||||
}}
|
||||
margin={false}
|
||||
disablePill
|
||||
useGreenColorScheme
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
// regular guild item
|
||||
const guild = all[index - 2];
|
||||
element = <GuildItem key={key} guild={guild} />;
|
||||
}
|
||||
|
||||
return <div style={style}>{element}</div>;
|
||||
};
|
||||
|
||||
return (
|
||||
<List>
|
||||
<SidebarAction
|
||||
key="home"
|
||||
tooltip="Home"
|
||||
icon={{
|
||||
icon: "mdiHome",
|
||||
size: "24px",
|
||||
}}
|
||||
action={() => navigate("/channels/@me")}
|
||||
margin={false}
|
||||
active={app.activeGuildId === "@me"}
|
||||
/>
|
||||
<GuildSidebarListItem>
|
||||
<Divider key="divider" />
|
||||
</GuildSidebarListItem>
|
||||
<div aria-label="Servers">
|
||||
{app.guilds.getAll().map((guild) => renderGuildItem(guild, guild.id === app.activeGuildId))}
|
||||
</div>
|
||||
|
||||
<SidebarAction
|
||||
key="add-server"
|
||||
tooltip="Add Server"
|
||||
icon={{
|
||||
icon: "mdiPlus",
|
||||
size: "24px",
|
||||
color: "var(--success)",
|
||||
}}
|
||||
action={() => {
|
||||
openModal(AddServerModal);
|
||||
}}
|
||||
margin={false}
|
||||
disablePill
|
||||
useGreenColorScheme
|
||||
/>
|
||||
</List>
|
||||
<Container>
|
||||
<AutoSizer>
|
||||
{({ width, height }) => (
|
||||
<List
|
||||
height={height}
|
||||
overscanRowCount={2}
|
||||
rowCount={itemCount}
|
||||
rowHeight={({ index }) => {
|
||||
if (index === 1) return 8; // divider
|
||||
return 56; // item is 48 + 8 top margin
|
||||
}}
|
||||
rowRenderer={rowRenderer}
|
||||
width={width}
|
||||
/>
|
||||
)}
|
||||
</AutoSizer>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -33,10 +33,8 @@ function ChannelPage() {
|
||||
const { guildId, channelId } = useParams<{ guildId: string; channelId: string }>();
|
||||
|
||||
React.useEffect(() => {
|
||||
if (guildId && channelId) {
|
||||
app.setActiveGuildId(guildId);
|
||||
app.setActiveChannelId(channelId);
|
||||
}
|
||||
app.setActiveGuildId(guildId);
|
||||
app.setActiveChannelId(channelId);
|
||||
}, [guildId, channelId]);
|
||||
|
||||
return (
|
||||
|
@ -46,9 +46,9 @@ export default class AppStore {
|
||||
@observable presences = new PresenceStore(this);
|
||||
@observable queue = new MessageQueue(this);
|
||||
@observable activeGuild: Guild | null = null;
|
||||
@observable activeGuildId: Snowflake | null | "@me" = "@me";
|
||||
@observable activeGuildId: Snowflake | undefined | "@me" = "@me";
|
||||
@observable activeChannel: Channel | null = null;
|
||||
@observable activeChannelId: string | null = null;
|
||||
@observable activeChannelId: string | undefined = undefined;
|
||||
|
||||
constructor() {
|
||||
makeAutoObservable(this);
|
||||
@ -118,7 +118,7 @@ export default class AppStore {
|
||||
}
|
||||
|
||||
@action
|
||||
setActiveGuildId(id: Snowflake | null | "@me") {
|
||||
setActiveGuildId(id: Snowflake | undefined | "@me") {
|
||||
this.activeGuildId = id;
|
||||
|
||||
// try to resolve the guild
|
||||
@ -126,7 +126,7 @@ export default class AppStore {
|
||||
}
|
||||
|
||||
@action
|
||||
setActiveChannelId(id: string | null) {
|
||||
setActiveChannelId(id: string | undefined) {
|
||||
this.activeChannelId = id;
|
||||
|
||||
// try to resolve the channel
|
||||
|
@ -25,7 +25,8 @@ export default class ChannelStore {
|
||||
return this.channels.get(id);
|
||||
}
|
||||
|
||||
getAll() {
|
||||
@computed
|
||||
get all() {
|
||||
return Array.from(this.channels.values());
|
||||
}
|
||||
|
||||
|
@ -35,7 +35,7 @@ export default class GuildStore {
|
||||
}
|
||||
|
||||
@computed
|
||||
getAll() {
|
||||
get all() {
|
||||
return Array.from(this.guilds.values());
|
||||
}
|
||||
|
||||
|
@ -26,7 +26,7 @@ export default class PrivateChannelStore {
|
||||
}
|
||||
|
||||
@computed
|
||||
getAll() {
|
||||
get all() {
|
||||
return Array.from(this.channels.values());
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import type { Snowflake } from "@spacebarchat/spacebar-api-types/globals";
|
||||
import type { APIRole } from "@spacebarchat/spacebar-api-types/v9";
|
||||
import { action, makeObservable, observable, ObservableMap } from "mobx";
|
||||
import { action, computed, makeObservable, observable, ObservableMap } from "mobx";
|
||||
import AppStore from "./AppStore";
|
||||
import Role from "./objects/Role";
|
||||
|
||||
@ -24,7 +24,8 @@ export default class RoleStore {
|
||||
roles.forEach((role) => this.add(role));
|
||||
}
|
||||
|
||||
getAll() {
|
||||
@computed
|
||||
get all() {
|
||||
return Array.from(this.roles.values());
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,7 @@ export default class UserStore {
|
||||
}
|
||||
|
||||
@computed
|
||||
getAll() {
|
||||
get all() {
|
||||
return Array.from(this.users.values());
|
||||
}
|
||||
|
||||
|
@ -142,15 +142,14 @@ export default class Guild {
|
||||
|
||||
@computed
|
||||
get channels() {
|
||||
return this.app.channels
|
||||
.getAll()
|
||||
return this.app.channels.all
|
||||
.filter((channel) => this.channels_.has(channel.id))
|
||||
.sort((a, b) => (a.position ?? 0) - (b.position ?? 0));
|
||||
}
|
||||
|
||||
@computed
|
||||
get roles() {
|
||||
return this.app.roles.getAll().filter((role) => this.roles_.has(role.id));
|
||||
return this.app.roles.all.filter((role) => this.roles_.has(role.id));
|
||||
}
|
||||
|
||||
@action
|
||||
|
Loading…
Reference in New Issue
Block a user