mirror of
https://github.com/spacebarchat/client.git
synced 2024-11-22 02:12:38 +01:00
move channel and role storage to app root
This commit is contained in:
parent
711e79cdfd
commit
81b57c4ebb
@ -21,11 +21,10 @@ export function EmptyChannelList() {
|
||||
}
|
||||
|
||||
interface Props {
|
||||
channelId?: string;
|
||||
guild: Guild;
|
||||
}
|
||||
|
||||
function ChannelList({ channelId, guild }: Props) {
|
||||
function ChannelList({ guild }: Props) {
|
||||
const app = useAppStore();
|
||||
|
||||
const renderChannelListItem = React.useCallback(
|
||||
@ -33,7 +32,7 @@ function ChannelList({ channelId, guild }: Props) {
|
||||
const permission = Permissions.getPermission(app.account!.id, guild, channel);
|
||||
if (!permission.has("VIEW_CHANNEL")) return null;
|
||||
|
||||
const active = channelId === channel.id;
|
||||
const active = app.activeChannelId === channel.id;
|
||||
const isCategory = channel.type === ChannelType.GuildCategory;
|
||||
return (
|
||||
<ChannelListItem
|
||||
@ -45,10 +44,10 @@ function ChannelList({ channelId, guild }: Props) {
|
||||
/>
|
||||
);
|
||||
},
|
||||
[app.account, channelId, guild],
|
||||
[app.account, app.activeChannelId, guild],
|
||||
);
|
||||
|
||||
return <List>{guild.channels.mapped.map((channel) => renderChannelListItem(channel))}</List>;
|
||||
return <List>{guild.channelsMapped.map((channel) => renderChannelListItem(channel))}</List>;
|
||||
}
|
||||
|
||||
export default observer(ChannelList);
|
||||
|
@ -1,7 +1,6 @@
|
||||
import { observer } from "mobx-react-lite";
|
||||
import styled from "styled-components";
|
||||
import Channel from "../stores/objects/Channel";
|
||||
import Guild from "../stores/objects/Guild";
|
||||
import { useAppStore } from "../stores/AppStore";
|
||||
import ChannelHeader from "./ChannelHeader";
|
||||
import ChannelList, { EmptyChannelList } from "./ChannelList";
|
||||
import Container from "./Container";
|
||||
@ -18,19 +17,14 @@ const Wrapper = styled(Container)`
|
||||
}
|
||||
`;
|
||||
|
||||
interface Props {
|
||||
channel?: Channel;
|
||||
guild?: Guild;
|
||||
guildId?: string;
|
||||
channelId?: string;
|
||||
}
|
||||
function ChannelSidebar() {
|
||||
const app = useAppStore();
|
||||
|
||||
function ChannelSidebar({ guild, channelId, guildId }: Props) {
|
||||
return (
|
||||
<Wrapper>
|
||||
{/* TODO: replace with dm search if no guild */}
|
||||
<ChannelHeader guild={guild} guildId={guildId} />
|
||||
{guild ? <ChannelList channelId={channelId} guild={guild} /> : <EmptyChannelList />}
|
||||
<ChannelHeader />
|
||||
{app.activeGuild ? <ChannelList guild={app.activeGuild} /> : <EmptyChannelList />}
|
||||
<UserPanel />
|
||||
</Wrapper>
|
||||
);
|
||||
|
@ -82,7 +82,7 @@ function GuildItem({ guild, active }: Props) {
|
||||
]);
|
||||
|
||||
const doNavigate = () => {
|
||||
const channel = guild.channels.mapped.find((x) => {
|
||||
const channel = guild.channelsMapped.find((x) => {
|
||||
const permission = Permissions.getPermission(app.account!.id, guild, x);
|
||||
return permission.has("VIEW_CHANNEL") && x.type !== ChannelType.GuildCategory;
|
||||
});
|
||||
|
@ -29,11 +29,7 @@ const Divider = styled.div`
|
||||
background-color: var(--text-disabled);
|
||||
`;
|
||||
|
||||
interface Props {
|
||||
guildId: string;
|
||||
}
|
||||
|
||||
function GuildSidebar({ guildId }: Props) {
|
||||
function GuildSidebar() {
|
||||
const app = useAppStore();
|
||||
const { openModal } = useModals();
|
||||
const navigate = useNavigate();
|
||||
@ -53,13 +49,13 @@ function GuildSidebar({ guildId }: Props) {
|
||||
}}
|
||||
action={() => navigate("/channels/@me")}
|
||||
margin={false}
|
||||
active={guildId === "@me"}
|
||||
active={app.activeGuildId === "@me"}
|
||||
/>
|
||||
<GuildSidebarListItem>
|
||||
<Divider key="divider" />
|
||||
</GuildSidebarListItem>
|
||||
<div aria-label="Servers">
|
||||
{app.guilds.getAll().map((guild) => renderGuildItem(guild, guild.id === guildId))}
|
||||
{app.guilds.getAll().map((guild) => renderGuildItem(guild, guild.id === app.activeGuildId))}
|
||||
</div>
|
||||
|
||||
<SidebarAction
|
||||
|
@ -75,19 +75,19 @@ function Content(props: Props2) {
|
||||
/**
|
||||
* Main component for rendering channel messages
|
||||
*/
|
||||
function Chat({ channel, guild, guildId }: Props) {
|
||||
function Chat() {
|
||||
const app = useAppStore();
|
||||
const logger = useLogger("Messages");
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!channel || !guild) return;
|
||||
if (!app.activeChannel || !app.activeGuild || app.activeChannelId === "@me") return;
|
||||
|
||||
runInAction(() => {
|
||||
app.gateway.onChannelOpen(guild.id, channel.id);
|
||||
app.gateway.onChannelOpen(app.activeGuildId!, app.activeChannelId!);
|
||||
});
|
||||
}, [channel, guild]);
|
||||
}, [app.activeChannel, app.activeGuild]);
|
||||
|
||||
if (guildId && guildId === "@me") {
|
||||
if (app.activeGuildId && app.activeGuildId === "@me") {
|
||||
return (
|
||||
<WrapperTwo>
|
||||
<span>Home Section Placeholder</span>
|
||||
@ -95,7 +95,7 @@ function Chat({ channel, guild, guildId }: Props) {
|
||||
);
|
||||
}
|
||||
|
||||
if (!guild || !channel) {
|
||||
if (!app.activeGuild || !app.activeChannel) {
|
||||
return (
|
||||
<WrapperTwo>
|
||||
<span
|
||||
@ -113,8 +113,8 @@ function Chat({ channel, guild, guildId }: Props) {
|
||||
|
||||
return (
|
||||
<WrapperTwo>
|
||||
<ChatHeader channel={channel} />
|
||||
<Content channel={channel} guild={guild} />
|
||||
<ChatHeader channel={app.activeChannel} />
|
||||
<Content channel={app.activeChannel} guild={app.activeGuild} />
|
||||
</WrapperTwo>
|
||||
);
|
||||
}
|
||||
|
@ -30,22 +30,25 @@ function ChannelPage() {
|
||||
const contextMenu = React.useContext(ContextMenuContext);
|
||||
const bannerContext = React.useContext(BannerContext);
|
||||
|
||||
const { guildId, channelId } = useParams<{
|
||||
guildId: string;
|
||||
channelId: string;
|
||||
}>();
|
||||
const guild = app.guilds.get(guildId!);
|
||||
const channel = guild?.channels.get(channelId!);
|
||||
const { guildId, channelId } = useParams<{ guildId: string; channelId: string }>();
|
||||
|
||||
React.useEffect(() => {
|
||||
console.log(guildId, channelId);
|
||||
if (guildId && channelId) {
|
||||
app.setActiveGuildId(guildId);
|
||||
app.setActiveChannelId(channelId);
|
||||
}
|
||||
}, [guildId, channelId]);
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<Banner />
|
||||
<Wrapper>
|
||||
{contextMenu.visible && <ContextMenu {...contextMenu} />}
|
||||
<GuildSidebar guildId={guildId!} />
|
||||
<ChannelSidebar channel={channel} guild={guild} channelId={channelId} guildId={guildId} />
|
||||
<GuildSidebar />
|
||||
<ChannelSidebar />
|
||||
<ErrorBoundary section="component">
|
||||
<Chat channel={channel} guild={guild} channelId={channelId} guildId={guildId} />
|
||||
<Chat />
|
||||
</ErrorBoundary>
|
||||
</Wrapper>
|
||||
</Container>
|
||||
|
@ -1,17 +1,21 @@
|
||||
import type { APIUser } from "@spacebarchat/spacebar-api-types/v9";
|
||||
import type { APIUser, Snowflake } from "@spacebarchat/spacebar-api-types/v9";
|
||||
import { action, computed, makeAutoObservable, observable } from "mobx";
|
||||
import secureLocalStorage from "react-secure-storage";
|
||||
import Logger from "../utils/Logger";
|
||||
import REST from "../utils/REST";
|
||||
import AccountStore from "./AccountStore";
|
||||
import ChannelStore from "./ChannelStore";
|
||||
import ExperimentsStore from "./ExperimentsStore";
|
||||
import GatewayConnectionStore from "./GatewayConnectionStore";
|
||||
import GuildStore from "./GuildStore";
|
||||
import MessageQueue from "./MessageQueue";
|
||||
import PresenceStore from "./PresenceStore";
|
||||
import PrivateChannelStore from "./PrivateChannelStore";
|
||||
import RoleStore from "./RoleStore";
|
||||
import ThemeStore from "./ThemeStore";
|
||||
import UserStore from "./UserStore";
|
||||
import Channel from "./objects/Channel";
|
||||
import Guild from "./objects/Guild";
|
||||
|
||||
// dev thing to force toggle branding on auth pages for testing.
|
||||
export const AUTH_NO_BRANDING = false;
|
||||
@ -33,13 +37,18 @@ export default class AppStore {
|
||||
@observable account: AccountStore | null = null;
|
||||
@observable gateway = new GatewayConnectionStore(this);
|
||||
@observable guilds = new GuildStore(this);
|
||||
// @observable channels = new ChannelStore(this);
|
||||
@observable roles = new RoleStore(this);
|
||||
@observable channels = new ChannelStore(this);
|
||||
@observable users = new UserStore();
|
||||
@observable privateChannels = new PrivateChannelStore(this);
|
||||
@observable rest = new REST(this);
|
||||
@observable experiments = new ExperimentsStore();
|
||||
@observable presences = new PresenceStore(this);
|
||||
@observable queue = new MessageQueue(this);
|
||||
@observable activeGuild: Guild | null = null;
|
||||
@observable activeGuildId: Snowflake | null | "@me" = "@me";
|
||||
@observable activeChannel: Channel | null = null;
|
||||
@observable activeChannelId: string | null = null;
|
||||
|
||||
constructor() {
|
||||
makeAutoObservable(this);
|
||||
@ -107,6 +116,22 @@ export default class AppStore {
|
||||
get isReady() {
|
||||
return !this.isAppLoading && this.isGatewayReady && this.isNetworkConnected;
|
||||
}
|
||||
|
||||
@action
|
||||
setActiveGuildId(id: Snowflake | null | "@me") {
|
||||
this.activeGuildId = id;
|
||||
|
||||
// try to resolve the guild
|
||||
this.activeGuild = (id ? this.guilds.get(id) : null) ?? null;
|
||||
}
|
||||
|
||||
@action
|
||||
setActiveChannelId(id: string | null) {
|
||||
this.activeChannelId = id;
|
||||
|
||||
// try to resolve the channel
|
||||
this.activeChannel = (id ? this.channels.get(id) : null) ?? null;
|
||||
}
|
||||
}
|
||||
|
||||
export const appStore = new AppStore();
|
||||
|
@ -1,5 +1,4 @@
|
||||
import type { APIChannel } from "@spacebarchat/spacebar-api-types/v9";
|
||||
import { ChannelType } from "@spacebarchat/spacebar-api-types/v9";
|
||||
import { action, computed, observable, ObservableMap } from "mobx";
|
||||
import AppStore from "./AppStore";
|
||||
import Channel from "./objects/Channel";
|
||||
@ -26,7 +25,6 @@ export default class ChannelStore {
|
||||
return this.channels.get(id);
|
||||
}
|
||||
|
||||
@computed
|
||||
getAll() {
|
||||
return Array.from(this.channels.values());
|
||||
}
|
||||
@ -41,46 +39,7 @@ export default class ChannelStore {
|
||||
return this.channels.size;
|
||||
}
|
||||
|
||||
private sortPosition(channels: Channel[]) {
|
||||
sortPosition(channels: Channel[]) {
|
||||
return channels.sort((a, b) => (a.position ?? 0) - (b.position ?? 0));
|
||||
}
|
||||
|
||||
@computed
|
||||
get mapped(): Channel[] {
|
||||
const channels = this.getAll();
|
||||
|
||||
const result: {
|
||||
id: string;
|
||||
children: Channel[];
|
||||
category: Channel | null;
|
||||
}[] = [];
|
||||
|
||||
const categories = this.sortPosition(channels.filter((x) => x.type === ChannelType.GuildCategory));
|
||||
const categorizedChannels = channels.filter((x) => x.type !== ChannelType.GuildCategory && x.parentId !== null);
|
||||
const uncategorizedChannels = this.sortPosition(
|
||||
channels.filter((x) => x.type !== ChannelType.GuildCategory && x.parentId === null),
|
||||
);
|
||||
|
||||
// for each category, add an object containing the category and its children
|
||||
categories.forEach((category) => {
|
||||
result.push({
|
||||
id: category.id,
|
||||
children: this.sortPosition(categorizedChannels.filter((x) => x.parentId === category.id)),
|
||||
category: category,
|
||||
});
|
||||
});
|
||||
|
||||
// add an object containing the remaining uncategorized channels
|
||||
result.push({
|
||||
id: "root",
|
||||
children: uncategorizedChannels,
|
||||
category: null,
|
||||
});
|
||||
|
||||
// flatten down to a single array where the category is the first element followed by its children
|
||||
return result
|
||||
.map((x) => [x.category, ...x.children])
|
||||
.flat()
|
||||
.filter((x) => x !== null) as Channel[];
|
||||
}
|
||||
}
|
||||
|
@ -559,7 +559,7 @@ export default class GatewayConnectionStore {
|
||||
this.logger.warn(`[ChannelCreate] Guild ${data.guild_id} not found for channel ${data.id}`);
|
||||
return;
|
||||
}
|
||||
guild.channels.add(data);
|
||||
guild.addChannel(data);
|
||||
};
|
||||
|
||||
private onChannelDelete = (data: GatewayChannelDeleteDispatchData) => {
|
||||
@ -573,7 +573,7 @@ export default class GatewayConnectionStore {
|
||||
this.logger.warn(`[ChannelDelete] Guild ${data.guild_id} not found for channel ${data.id}`);
|
||||
return;
|
||||
}
|
||||
guild.channels.remove(data.id);
|
||||
guild.removeChannel(data.id);
|
||||
};
|
||||
|
||||
private onMessageCreate = (data: GatewayMessageCreateDispatchData) => {
|
||||
@ -582,7 +582,7 @@ export default class GatewayConnectionStore {
|
||||
this.logger.warn(`[MessageCreate] Guild ${data.guild_id} not found for channel ${data.id}`);
|
||||
return;
|
||||
}
|
||||
const channel = guild.channels.get(data.channel_id);
|
||||
const channel = this.app.channels.get(data.channel_id);
|
||||
if (!channel) {
|
||||
this.logger.warn(`[MessageCreate] Channel ${data.channel_id} not found for message ${data.id}`);
|
||||
return;
|
||||
@ -598,7 +598,7 @@ export default class GatewayConnectionStore {
|
||||
this.logger.warn(`[MessageUpdate] Guild ${data.guild_id} not found for channel ${data.id}`);
|
||||
return;
|
||||
}
|
||||
const channel = guild.channels.get(data.channel_id);
|
||||
const channel = this.app.channels.get(data.channel_id);
|
||||
if (!channel) {
|
||||
this.logger.warn(`[MessageUpdate] Channel ${data.channel_id} not found for message ${data.id}`);
|
||||
return;
|
||||
@ -613,7 +613,7 @@ export default class GatewayConnectionStore {
|
||||
this.logger.warn(`[MessageDelete] Guild ${data.guild_id} not found for channel ${data.id}`);
|
||||
return;
|
||||
}
|
||||
const channel = guild.channels.get(data.channel_id);
|
||||
const channel = this.app.channels.get(data.channel_id);
|
||||
if (!channel) {
|
||||
this.logger.warn(`[MessageDelete] Channel ${data.channel_id} not found for message ${data.id}`);
|
||||
return;
|
||||
@ -632,7 +632,7 @@ export default class GatewayConnectionStore {
|
||||
this.logger.warn(`[TypingStart] Guild ${data.guild_id} not found for channel ${data.channel_id}`);
|
||||
return;
|
||||
}
|
||||
const channel = guild.channels.get(data.channel_id);
|
||||
const channel = this.app.channels.get(data.channel_id);
|
||||
if (!channel) {
|
||||
this.logger.warn(`[TypingStart] Channel ${data.channel_id} not found`);
|
||||
return;
|
||||
|
@ -56,7 +56,7 @@ export default class GuildMemberListStore {
|
||||
|
||||
for (const item of items) {
|
||||
if ("group" in item) {
|
||||
const role = this.guild.roles.get(item.group.id);
|
||||
const role = this.app.roles.get(item.group.id);
|
||||
|
||||
listData.push({
|
||||
title: `${(role?.name ?? item.group.id).toUpperCase()}`,
|
||||
|
@ -24,6 +24,10 @@ export default class RoleStore {
|
||||
roles.forEach((role) => this.add(role));
|
||||
}
|
||||
|
||||
getAll() {
|
||||
return Array.from(this.roles.values());
|
||||
}
|
||||
|
||||
@action
|
||||
remove(id: Snowflake) {
|
||||
this.roles.delete(id);
|
||||
|
@ -1,15 +1,16 @@
|
||||
import type { Snowflake } from "@spacebarchat/spacebar-api-types/globals";
|
||||
import type {
|
||||
APIGuild,
|
||||
GatewayGuild,
|
||||
GatewayGuildMemberListUpdateDispatchData,
|
||||
import {
|
||||
ChannelType,
|
||||
type APIChannel,
|
||||
type APIGuild,
|
||||
type GatewayGuild,
|
||||
type GatewayGuildMemberListUpdateDispatchData,
|
||||
} from "@spacebarchat/spacebar-api-types/v9";
|
||||
import { action, computed, makeObservable, observable } from "mobx";
|
||||
import { ObservableSet, action, computed, makeObservable, observable } from "mobx";
|
||||
import AppStore from "../AppStore";
|
||||
import ChannelStore from "../ChannelStore";
|
||||
import GuildMemberListStore from "../GuildMemberListStore";
|
||||
import GuildMemberStore from "../GuildMemberStore";
|
||||
import RoleStore from "../RoleStore";
|
||||
import Channel from "./Channel";
|
||||
|
||||
export default class Guild {
|
||||
private readonly app: AppStore;
|
||||
@ -19,13 +20,13 @@ export default class Guild {
|
||||
@observable threads: unknown[];
|
||||
@observable stickers: unknown[]; // TODO:
|
||||
@observable stageInstances: unknown[]; // TODO:
|
||||
@observable roles: RoleStore;
|
||||
@observable roles_: ObservableSet<Snowflake>;
|
||||
@observable memberCount: number;
|
||||
@observable lazy: boolean;
|
||||
@observable large: boolean;
|
||||
@observable guildScheduledEvents: unknown[]; // TODO:
|
||||
@observable emojis: unknown[]; // TODO:
|
||||
@observable channels: ChannelStore;
|
||||
@observable channels_: ObservableSet<Snowflake>;
|
||||
@observable name: string;
|
||||
@observable description: string | null = null;
|
||||
@observable icon: string | null = null;
|
||||
@ -58,8 +59,8 @@ export default class Guild {
|
||||
|
||||
constructor(app: AppStore, data: GatewayGuild) {
|
||||
this.app = app;
|
||||
this.roles = new RoleStore(app);
|
||||
this.channels = new ChannelStore(app);
|
||||
this.roles_ = new ObservableSet();
|
||||
this.channels_ = new ObservableSet();
|
||||
this.members = new GuildMemberStore(app, this);
|
||||
|
||||
this.id = data.id;
|
||||
@ -100,11 +101,11 @@ export default class Guild {
|
||||
this.nsfwLevel = data.properties.nsfw_level;
|
||||
this.hubType = data.properties.hub_type;
|
||||
|
||||
this.roles.addAll(data.roles);
|
||||
// FIXME: hack to prevent errors after guild creation where channels is undefined
|
||||
if (data.channels) {
|
||||
this.channels.addAll(data.channels);
|
||||
}
|
||||
app.roles.addAll(data.roles);
|
||||
app.channels.addAll(data.channels);
|
||||
|
||||
data.roles.forEach((role) => this.roles_.add(role.id));
|
||||
data.channels?.forEach((channel) => this.channels_.add(channel.id));
|
||||
|
||||
makeObservable(this);
|
||||
}
|
||||
@ -140,4 +141,65 @@ export default class Guild {
|
||||
.map((word) => word.substring(0, 1))
|
||||
.join("");
|
||||
}
|
||||
|
||||
@computed
|
||||
get channels() {
|
||||
return this.app.channels.getAll().filter((channel) => this.channels_.has(channel.id));
|
||||
}
|
||||
|
||||
@computed
|
||||
get roles() {
|
||||
return this.app.roles.getAll().filter((role) => this.roles_.has(role.id));
|
||||
}
|
||||
|
||||
@action
|
||||
addChannel(data: APIChannel) {
|
||||
this.channels_.add(data.id);
|
||||
this.app.channels.add(data);
|
||||
}
|
||||
|
||||
@action
|
||||
removeChannel(id: Snowflake) {
|
||||
this.channels_.delete(id);
|
||||
this.app.channels.remove(id);
|
||||
}
|
||||
|
||||
@computed
|
||||
get channelsMapped(): Channel[] {
|
||||
const channels = this.channels;
|
||||
|
||||
const result: {
|
||||
id: string;
|
||||
children: Channel[];
|
||||
category: Channel | null;
|
||||
}[] = [];
|
||||
|
||||
const categories = this.app.channels.sortPosition(channels.filter((x) => x.type === ChannelType.GuildCategory));
|
||||
const categorizedChannels = channels.filter((x) => x.type !== ChannelType.GuildCategory && x.parentId !== null);
|
||||
const uncategorizedChannels = this.app.channels.sortPosition(
|
||||
channels.filter((x) => x.type !== ChannelType.GuildCategory && x.parentId === null),
|
||||
);
|
||||
|
||||
// for each category, add an object containing the category and its children
|
||||
categories.forEach((category) => {
|
||||
result.push({
|
||||
id: category.id,
|
||||
children: this.app.channels.sortPosition(categorizedChannels.filter((x) => x.parentId === category.id)),
|
||||
category: category,
|
||||
});
|
||||
});
|
||||
|
||||
// add an object containing the remaining uncategorized channels
|
||||
result.push({
|
||||
id: "root",
|
||||
children: uncategorizedChannels,
|
||||
category: null,
|
||||
});
|
||||
|
||||
// flatten down to a single array where the category is the first element followed by its children
|
||||
return result
|
||||
.map((x) => [x.category, ...x.children])
|
||||
.flat()
|
||||
.filter((x) => x !== null) as Channel[];
|
||||
}
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ export default class GuildMember {
|
||||
this.user = data.user;
|
||||
this.nick = data.nick;
|
||||
this.avatar = data.avatar;
|
||||
this.roles = data.roles.map((role) => guild.roles.get(role)).filter(Boolean) as Role[];
|
||||
this.roles = data.roles.map((role) => app.roles.get(role)).filter(Boolean) as Role[];
|
||||
this.joined_at = data.joined_at;
|
||||
this.premium_since = data.premium_since;
|
||||
this.deaf = data.deaf;
|
||||
|
Loading…
Reference in New Issue
Block a user