Remove useChannel

This commit is contained in:
Paul 2021-07-29 18:41:01 +01:00
parent 0571c065bd
commit 411fac2527
28 changed files with 259 additions and 257 deletions

View file

@ -1,10 +1,11 @@
import { observer } from "mobx-react-lite";
import { useHistory } from "react-router-dom"; import { useHistory } from "react-router-dom";
import { Channel } from "revolt.js";
import styled from "styled-components"; import styled from "styled-components";
import { Text } from "preact-i18n"; import { Text } from "preact-i18n";
import { useState } from "preact/hooks"; import { useState } from "preact/hooks";
import { Channel } from "../../mobx";
import { dispatch, getState } from "../../redux"; import { dispatch, getState } from "../../redux";
import Button from "../ui/Button"; import Button from "../ui/Button";
@ -46,7 +47,7 @@ type Props = {
channel: Channel; channel: Channel;
}; };
export default function AgeGate(props: Props) { export default observer((props: Props) => {
const history = useHistory(); const history = useHistory();
const [consent, setConsent] = useState( const [consent, setConsent] = useState(
getState().sectionToggle["nsfw"] ?? false, getState().sectionToggle["nsfw"] ?? false,
@ -105,4 +106,4 @@ export default function AgeGate(props: Props) {
</div> </div>
</Base> </Base>
); );
} });

View file

@ -5,7 +5,7 @@ import styled, { css } from "styled-components";
import { StateUpdater, useState } from "preact/hooks"; import { StateUpdater, useState } from "preact/hooks";
import { User } from "../../mobx"; import { Channel, User } from "../../mobx";
import { useData } from "../../mobx/State"; import { useData } from "../../mobx/State";
import { useClient } from "../../context/revoltjs/RevoltClient"; import { useClient } from "../../context/revoltjs/RevoltClient";
@ -28,7 +28,7 @@ export type AutoCompleteState =
} }
| { | {
type: "channel"; type: "channel";
matches: Channels.TextChannel[]; matches: Channel[];
} }
)); ));
@ -197,15 +197,13 @@ export function useAutoComplete(
if (type === "channel" && searchClues?.channels) { if (type === "channel" && searchClues?.channels) {
const channels = client.servers const channels = client.servers
.get(searchClues.channels.server) .get(searchClues.channels.server)
?.channels.map((x) => client.channels.get(x)) ?.channels.map((x) => store.channels.get(x))
.filter( .filter((x) => typeof x !== "undefined") as Channel[];
(x) => typeof x !== "undefined",
) as Channels.TextChannel[];
const matches = ( const matches = (
search.length > 0 search.length > 0
? channels.filter((channel) => ? channels.filter((channel) =>
channel.name.toLowerCase().match(regex), channel.name!.toLowerCase().match(regex),
) )
: channels : channels
) )

View file

@ -1,65 +1,67 @@
import { Hash, VolumeFull } from "@styled-icons/boxicons-regular"; import { Hash, VolumeFull } from "@styled-icons/boxicons-regular";
import { observer } from "mobx-react-lite";
import { Channels } from "revolt.js/dist/api/objects"; import { Channels } from "revolt.js/dist/api/objects";
import { useContext } from "preact/hooks"; import { useContext } from "preact/hooks";
import { Channel } from "../../mobx";
import { AppContext } from "../../context/revoltjs/RevoltClient"; import { AppContext } from "../../context/revoltjs/RevoltClient";
import { ImageIconBase, IconBaseProps } from "./IconBase"; import { ImageIconBase, IconBaseProps } from "./IconBase";
import fallback from "./assets/group.png"; import fallback from "./assets/group.png";
interface Props interface Props extends IconBaseProps<Channel> {
extends IconBaseProps<
Channels.GroupChannel | Channels.TextChannel | Channels.VoiceChannel
> {
isServerChannel?: boolean; isServerChannel?: boolean;
} }
export default function ChannelIcon( export default observer(
props: Props & Omit<JSX.HTMLAttributes<HTMLImageElement>, keyof Props>, (
) { props: Props & Omit<JSX.HTMLAttributes<HTMLImageElement>, keyof Props>,
const client = useContext(AppContext); ) => {
const client = useContext(AppContext);
const { const {
size, size,
target, target,
attachment, attachment,
isServerChannel: server, isServerChannel: server,
animate, animate,
children, children,
as, as,
...imgProps ...imgProps
} = props; } = props;
const iconURL = client.generateFileURL( const iconURL = client.generateFileURL(
target?.icon ?? attachment, target?.icon ?? attachment,
{ max_side: 256 }, { max_side: 256 },
animate, animate,
); );
const isServerChannel = const isServerChannel =
server || server ||
(target && (target &&
(target.channel_type === "TextChannel" || (target.channel_type === "TextChannel" ||
target.channel_type === "VoiceChannel")); target.channel_type === "VoiceChannel"));
if (typeof iconURL === "undefined") { if (typeof iconURL === "undefined") {
if (isServerChannel) { if (isServerChannel) {
if (target?.channel_type === "VoiceChannel") { if (target?.channel_type === "VoiceChannel") {
return <VolumeFull size={size} />; return <VolumeFull size={size} />;
}
return <Hash size={size} />;
} }
return <Hash size={size} />;
} }
}
return ( return (
// ! fixme: replace fallback with <picture /> + <source /> // ! fixme: replace fallback with <picture /> + <source />
<ImageIconBase <ImageIconBase
{...imgProps} {...imgProps}
width={size} width={size}
height={size} height={size}
loading="lazy" loading="lazy"
aria-hidden="true" aria-hidden="true"
square={isServerChannel} square={isServerChannel}
src={iconURL ?? fallback} src={iconURL ?? fallback}
/> />
); );
} },
);

View file

@ -1,7 +1,6 @@
import { Send, HappyAlt, ShieldX } from "@styled-icons/boxicons-solid"; import { Send, HappyAlt, ShieldX } from "@styled-icons/boxicons-solid";
import { Styleshare } from "@styled-icons/simple-icons"; import { Styleshare } from "@styled-icons/simple-icons";
import Axios, { CancelTokenSource } from "axios"; import Axios, { CancelTokenSource } from "axios";
import { Channel } from "revolt.js";
import { ChannelPermission } from "revolt.js/dist/api/permissions"; import { ChannelPermission } from "revolt.js/dist/api/permissions";
import styled from "styled-components"; import styled from "styled-components";
import { ulid } from "ulid"; import { ulid } from "ulid";
@ -20,6 +19,7 @@ import {
SMOOTH_SCROLL_ON_RECEIVE, SMOOTH_SCROLL_ON_RECEIVE,
} from "../../../lib/renderer/Singleton"; } from "../../../lib/renderer/Singleton";
import { Channel } from "../../../mobx";
import { dispatch, getState } from "../../../redux"; import { dispatch, getState } from "../../../redux";
import { Reply } from "../../../redux/reducers/queue"; import { Reply } from "../../../redux/reducers/queue";
@ -360,7 +360,7 @@ export default function MessageBox({ channel }: Props) {
users: { type: "channel", id: channel._id }, users: { type: "channel", id: channel._id },
channels: channels:
channel.channel_type === "TextChannel" channel.channel_type === "TextChannel"
? { server: channel.server } ? { server: channel.server! }
: undefined, : undefined,
}); });

View file

@ -10,7 +10,7 @@ import { Localizer, Text } from "preact-i18n";
import { isTouchscreenDevice } from "../../../lib/isTouchscreenDevice"; import { isTouchscreenDevice } from "../../../lib/isTouchscreenDevice";
import { stopPropagation } from "../../../lib/stopPropagation"; import { stopPropagation } from "../../../lib/stopPropagation";
import { User } from "../../../mobx"; import { Channel, User } from "../../../mobx";
import { useIntermediate } from "../../../context/intermediate/Intermediate"; import { useIntermediate } from "../../../context/intermediate/Intermediate";
@ -34,8 +34,8 @@ type CommonProps = Omit<
type UserProps = CommonProps & { type UserProps = CommonProps & {
user: User; user: User;
context?: Channels.Channel; context?: Channel;
channel?: Channels.DirectMessageChannel; channel?: Channel;
}; };
export const UserButton = observer((props: UserProps) => { export const UserButton = observer((props: UserProps) => {
@ -73,7 +73,7 @@ export const UserButton = observer((props: UserProps) => {
{ {
<div className={styles.subText}> <div className={styles.subText}>
{channel?.last_message && alert ? ( {channel?.last_message && alert ? (
channel.last_message.short (channel.last_message as { short: string }).short
) : ( ) : (
<UserStatus user={user} /> <UserStatus user={user} />
)} )}
@ -115,12 +115,12 @@ export const UserButton = observer((props: UserProps) => {
}); });
type ChannelProps = CommonProps & { type ChannelProps = CommonProps & {
channel: Channels.Channel & { unread?: string }; channel: Channel & { unread?: string };
user?: User; user?: User;
compact?: boolean; compact?: boolean;
}; };
export function ChannelButton(props: ChannelProps) { export const ChannelButton = observer((props: ChannelProps) => {
const { active, alert, alertCount, channel, user, compact, ...divProps } = const { active, alert, alertCount, channel, user, compact, ...divProps } =
props; props;
@ -153,12 +153,12 @@ export function ChannelButton(props: ChannelProps) {
{channel.channel_type === "Group" && ( {channel.channel_type === "Group" && (
<div className={styles.subText}> <div className={styles.subText}>
{channel.last_message && alert ? ( {channel.last_message && alert ? (
channel.last_message.short (channel.last_message as { short: string }).short
) : ( ) : (
<Text <Text
id="quantities.members" id="quantities.members"
plural={channel.recipients.length} plural={channel.recipients!.length}
fields={{ count: channel.recipients.length }} fields={{ count: channel.recipients!.length }}
/> />
)} )}
</div> </div>
@ -186,7 +186,7 @@ export function ChannelButton(props: ChannelProps) {
</div> </div>
</div> </div>
); );
} });
type ButtonProps = CommonProps & { type ButtonProps = CommonProps & {
onClick?: () => void; onClick?: () => void;

View file

@ -43,10 +43,16 @@ const HomeSidebar = observer((props: Props) => {
const { channel } = useParams<{ channel: string }>(); const { channel } = useParams<{ channel: string }>();
const { openScreen } = useIntermediate(); const { openScreen } = useIntermediate();
const ctx = useForceUpdate(); const store = useData();
const channels = useDMs(ctx); const channels = [...store.channels.values()]
.filter(
(x) =>
x.channel_type === "DirectMessage" ||
x.channel_type === "Group",
)
.map((x) => mapChannelWithUnread(x, props.unreads));
const obj = channels.find((x) => x?._id === channel); const obj = store.channels.get(channel);
if (channel && !obj) return <Redirect to="/" />; if (channel && !obj) return <Redirect to="/" />;
if (obj) useUnreads({ ...props, channel: obj }); if (obj) useUnreads({ ...props, channel: obj });
@ -60,12 +66,7 @@ const HomeSidebar = observer((props: Props) => {
}); });
}, [channel]); }, [channel]);
const channelsArr = channels channels.sort((b, a) => a.timestamp.localeCompare(b.timestamp));
.filter((x) => x.channel_type !== "SavedMessages")
.map((x) => mapChannelWithUnread(x, props.unreads));
const store = useData();
channelsArr.sort((b, a) => a.timestamp.localeCompare(b.timestamp));
return ( return (
<GenericSidebarBase padding> <GenericSidebarBase padding>
@ -132,20 +133,22 @@ const HomeSidebar = observer((props: Props) => {
}) })
} }
/> />
{channelsArr.length === 0 && ( {channels.length === 0 && (
<img src={placeholderSVG} loading="eager" /> <img src={placeholderSVG} loading="eager" />
)} )}
{channelsArr.map((x) => { {channels.map((x) => {
let user; let user;
if (x.channel_type === "DirectMessage") { if (x.channel.channel_type === "DirectMessage") {
if (!x.active) return null; if (!x.channel.active) return null;
const recipient = client.channels.getRecipient(x._id); const recipient = client.channels.getRecipient(
x.channel._id,
);
user = store.users.get(recipient); user = store.users.get(recipient);
if (!user) { if (!user) {
console.warn( console.warn(
`Skipped DM ${x._id} because user was missing.`, `Skipped DM ${x.channel._id} because user was missing.`,
); );
return null; return null;
} }
@ -153,14 +156,14 @@ const HomeSidebar = observer((props: Props) => {
return ( return (
<ConditionalLink <ConditionalLink
active={x._id === channel} active={x.channel._id === channel}
to={`/channel/${x._id}`}> to={`/channel/${x.channel._id}`}>
<ChannelButton <ChannelButton
user={user} user={user}
channel={x} channel={x.channel}
alert={x.unread} alert={x.unread}
alertCount={x.alertCount} alertCount={x.alertCount}
active={x._id === channel} active={x.channel._id === channel}
/> />
</ConditionalLink> </ConditionalLink>
); );

View file

@ -186,16 +186,18 @@ export const ServerListSidebar = observer(({ unreads, lastOpened }: Props) => {
const ctx = useForceUpdate(); const ctx = useForceUpdate();
const activeServers = useServers(undefined, ctx) as Servers.Server[]; const activeServers = useServers(undefined, ctx) as Servers.Server[];
const channels = (useChannels(undefined, ctx) as Channel[]).map((x) => const channels = [...store.channels.values()].map((x) =>
mapChannelWithUnread(x, unreads), mapChannelWithUnread(x, unreads),
); );
const unreadChannels = channels.filter((x) => x.unread).map((x) => x._id); const unreadChannels = channels
.filter((x) => x.unread)
.map((x) => x.channel?._id);
const servers = activeServers.map((server) => { const servers = activeServers.map((server) => {
let alertCount = 0; let alertCount = 0;
for (const id of server.channels) { for (const id of server.channels) {
const channel = channels.find((x) => x._id === id); const channel = channels.find((x) => x.channel?._id === id);
if (channel?.alertCount) { if (channel?.alertCount) {
alertCount += channel.alertCount; alertCount += channel.alertCount;
} }
@ -224,8 +226,9 @@ export const ServerListSidebar = observer(({ unreads, lastOpened }: Props) => {
let alertCount = 0; let alertCount = 0;
for (const x of channels) { for (const x of channels) {
if ( if (
((x.channel_type === "DirectMessage" && x.active) || (x.channel?.channel_type === "DirectMessage"
x.channel_type === "Group") && ? x.channel?.active
: true) &&
x.unread x.unread
) { ) {
homeUnread = "unread"; homeUnread = "unread";

View file

@ -1,3 +1,4 @@
import { observer } from "mobx-react-lite";
import { Redirect, useParams } from "react-router"; import { Redirect, useParams } from "react-router";
import { Channels } from "revolt.js/dist/api/objects"; import { Channels } from "revolt.js/dist/api/objects";
import styled from "styled-components"; import styled from "styled-components";
@ -8,6 +9,7 @@ import { useEffect } from "preact/hooks";
import ConditionalLink from "../../../lib/ConditionalLink"; import ConditionalLink from "../../../lib/ConditionalLink";
import PaintCounter from "../../../lib/PaintCounter"; import PaintCounter from "../../../lib/PaintCounter";
import { useData } from "../../../mobx/State";
import { dispatch } from "../../../redux"; import { dispatch } from "../../../redux";
import { connectState } from "../../../redux/connector"; import { connectState } from "../../../redux/connector";
import { Unreads } from "../../../redux/reducers/unreads"; import { Unreads } from "../../../redux/reducers/unreads";
@ -53,7 +55,7 @@ const ServerList = styled.div`
} }
`; `;
function ServerSidebar(props: Props) { const ServerSidebar = observer((props: Props) => {
const { server: server_id, channel: channel_id } = const { server: server_id, channel: channel_id } =
useParams<{ server?: string; channel?: string }>(); useParams<{ server?: string; channel?: string }>();
const ctx = useForceUpdate(); const ctx = useForceUpdate();
@ -61,13 +63,9 @@ function ServerSidebar(props: Props) {
const server = useServer(server_id, ctx); const server = useServer(server_id, ctx);
if (!server) return <Redirect to="/" />; if (!server) return <Redirect to="/" />;
const channels = ( const store = useData();
useChannels(server.channels, ctx).filter(
(entry) => typeof entry !== "undefined",
) as Readonly<Channels.TextChannel | Channels.VoiceChannel>[]
).map((x) => mapChannelWithUnread(x, props.unreads));
const channel = channels.find((x) => x?._id === channel_id); const channel = channel_id ? store.channels.get(channel_id) : undefined;
if (channel_id && !channel) return <Redirect to={`/server/${server_id}`} />; if (channel_id && !channel) return <Redirect to={`/server/${server_id}`} />;
if (channel) useUnreads({ ...props, channel }, ctx); if (channel) useUnreads({ ...props, channel }, ctx);
@ -85,7 +83,7 @@ function ServerSidebar(props: Props) {
const elements = []; const elements = [];
function addChannel(id: string) { function addChannel(id: string) {
const entry = channels.find((x) => x._id === id); const entry = store.channels.get(id);
if (!entry) return; if (!entry) return;
const active = channel?._id === entry._id; const active = channel?._id === entry._id;
@ -98,7 +96,8 @@ function ServerSidebar(props: Props) {
<ChannelButton <ChannelButton
channel={entry} channel={entry}
active={active} active={active}
alert={entry.unread} // ! FIXME: pull it out directly
alert={mapChannelWithUnread(entry, props.unreads).unread}
compact compact
/> />
</ConditionalLink> </ConditionalLink>
@ -141,7 +140,7 @@ function ServerSidebar(props: Props) {
<PaintCounter small /> <PaintCounter small />
</ServerBase> </ServerBase>
); );
} });
export default connectState(ServerSidebar, (state) => { export default connectState(ServerSidebar, (state) => {
return { return {

View file

@ -1,7 +1,6 @@
import { Channel } from "revolt.js";
import { useLayoutEffect } from "preact/hooks"; import { useLayoutEffect } from "preact/hooks";
import { Channel } from "../../../mobx";
import { dispatch } from "../../../redux"; import { dispatch } from "../../../redux";
import { Unreads } from "../../../redux/reducers/unreads"; import { Unreads } from "../../../redux/reducers/unreads";
@ -63,12 +62,12 @@ export function mapChannelWithUnread(channel: Channel, unreads: Unreads) {
channel.channel_type === "DirectMessage" || channel.channel_type === "DirectMessage" ||
channel.channel_type === "Group" channel.channel_type === "Group"
) { ) {
last_message_id = channel.last_message?._id; last_message_id = (channel.last_message as { _id: string })?._id;
} else if (channel.channel_type === "TextChannel") { } else if (channel.channel_type === "TextChannel") {
last_message_id = channel.last_message; last_message_id = channel.last_message as string;
} else { } else {
return { return {
...channel, channel,
unread: undefined, unread: undefined,
alertCount: undefined, alertCount: undefined,
timestamp: channel._id, timestamp: channel._id,
@ -85,7 +84,7 @@ export function mapChannelWithUnread(channel: Channel, unreads: Unreads) {
unread = "mention"; unread = "mention";
} else if ( } else if (
u.last_id && u.last_id &&
last_message_id.localeCompare(u.last_id) > 0 (last_message_id as string).localeCompare(u.last_id) > 0
) { ) {
unread = "unread"; unread = "unread";
} }
@ -95,7 +94,7 @@ export function mapChannelWithUnread(channel: Channel, unreads: Unreads) {
} }
return { return {
...channel, channel,
timestamp: last_message_id ?? channel._id, timestamp: last_message_id ?? channel._id,
unread, unread,
alertCount, alertCount,

View file

@ -8,6 +8,7 @@ import { ClientboundNotification } from "revolt.js/dist/websocket/notifications"
import { Text } from "preact-i18n"; import { Text } from "preact-i18n";
import { useContext, useEffect, useState } from "preact/hooks"; import { useContext, useEffect, useState } from "preact/hooks";
import { Channel } from "../../../mobx";
import { useData } from "../../../mobx/State"; import { useData } from "../../../mobx/State";
import { getState } from "../../../redux"; import { getState } from "../../../redux";
@ -17,11 +18,7 @@ import {
ClientStatus, ClientStatus,
StatusContext, StatusContext,
} from "../../../context/revoltjs/RevoltClient"; } from "../../../context/revoltjs/RevoltClient";
import { import { HookContext } from "../../../context/revoltjs/hooks";
HookContext,
useChannel,
useForceUpdate,
} from "../../../context/revoltjs/hooks";
import CollapsibleSection from "../../common/CollapsibleSection"; import CollapsibleSection from "../../common/CollapsibleSection";
import Button from "../../ui/Button"; import Button from "../../ui/Button";
@ -34,27 +31,19 @@ import { GenericSidebarBase, GenericSidebarList } from "../SidebarBase";
import { UserButton } from "../items/ButtonItem"; import { UserButton } from "../items/ButtonItem";
import { ChannelDebugInfo } from "./ChannelDebugInfo"; import { ChannelDebugInfo } from "./ChannelDebugInfo";
interface Props { export default function MemberSidebar({ channel }: { channel?: Channel }) {
ctx: HookContext;
}
export default function MemberSidebar(props: { channel?: Channels.Channel }) {
const ctx = useForceUpdate();
const { channel: cid } = useParams<{ channel: string }>();
const channel = props.channel ?? useChannel(cid, ctx);
switch (channel?.channel_type) { switch (channel?.channel_type) {
case "Group": case "Group":
return <GroupMemberSidebar channel={channel} ctx={ctx} />; return <GroupMemberSidebar channel={channel} />;
case "TextChannel": case "TextChannel":
return <ServerMemberSidebar channel={channel} ctx={ctx} />; return <ServerMemberSidebar channel={channel} />;
default: default:
return null; return null;
} }
} }
export const GroupMemberSidebar = observer( export const GroupMemberSidebar = observer(
({ channel }: Props & { channel: Channels.GroupChannel }) => { ({ channel }: { channel: Channel }) => {
const { openScreen } = useIntermediate(); const { openScreen } = useIntermediate();
const store = useData(); const store = useData();
@ -77,7 +66,7 @@ export const GroupMemberSidebar = observer(
voiceParticipants.sort((a, b) => a.username.localeCompare(b.username)); voiceParticipants.sort((a, b) => a.username.localeCompare(b.username));
}*/ }*/
members.sort((a, b) => { members?.sort((a, b) => {
// ! FIXME: should probably rewrite all this code // ! FIXME: should probably rewrite all this code
const l = const l =
+( +(
@ -141,21 +130,21 @@ export const GroupMemberSidebar = observer(
text={ text={
<span> <span>
<Text id="app.main.categories.members" />{" "} <Text id="app.main.categories.members" />{" "}
{channel.recipients.length} {channel.recipients?.length ?? 0}
</span> </span>
} }
/> />
}> }>
{members.length === 0 && ( {members?.length === 0 && (
<img src={placeholderSVG} loading="eager" /> <img src={placeholderSVG} loading="eager" />
)} )}
{members.map( {members?.map(
(user) => (user) =>
user && ( user && (
<UserButton <UserButton
key={user._id} key={user._id}
user={user} user={user}
context={channel} context={channel!}
onClick={() => onClick={() =>
openScreen({ openScreen({
id: "profile", id: "profile",
@ -173,7 +162,7 @@ export const GroupMemberSidebar = observer(
); );
export const ServerMemberSidebar = observer( export const ServerMemberSidebar = observer(
({ channel }: Props & { channel: Channels.TextChannel }) => { ({ channel }: { channel: Channel }) => {
const [members, setMembers] = useState<Servers.Member[] | undefined>( const [members, setMembers] = useState<Servers.Member[] | undefined>(
undefined, undefined,
); );
@ -193,7 +182,7 @@ export const ServerMemberSidebar = observer(
typeof members === "undefined" typeof members === "undefined"
) { ) {
client.members client.members
.fetchMembers(channel.server) .fetchMembers(channel.server!)
.then((members) => setMembers(members)); .then((members) => setMembers(members));
} }
}, [status]); }, [status]);

View file

@ -13,7 +13,7 @@ import { useContext, useEffect, useMemo, useState } from "preact/hooks";
import { internalSubscribe } from "../../lib/eventEmitter"; import { internalSubscribe } from "../../lib/eventEmitter";
import { User } from "../../mobx"; import { Channel, User } from "../../mobx";
import { Action } from "../../components/ui/Modal"; import { Action } from "../../components/ui/Modal";
@ -34,15 +34,15 @@ export type Screen =
actions: Action[]; actions: Action[];
} }
| ({ id: "special_prompt" } & ( | ({ id: "special_prompt" } & (
| { type: "leave_group"; target: Channels.GroupChannel } | { type: "leave_group"; target: Channel }
| { type: "close_dm"; target: Channels.DirectMessageChannel } | { type: "close_dm"; target: Channel }
| { type: "leave_server"; target: Servers.Server } | { type: "leave_server"; target: Servers.Server }
| { type: "delete_server"; target: Servers.Server } | { type: "delete_server"; target: Servers.Server }
| { type: "delete_channel"; target: Channels.TextChannel } | { type: "delete_channel"; target: Channel }
| { type: "delete_message"; target: Channels.Message } | { type: "delete_message"; target: Channels.Message }
| { | {
type: "create_invite"; type: "create_invite";
target: Channels.TextChannel | Channels.GroupChannel; target: Channel;
} }
| { type: "kick_member"; target: Servers.Server; user: User } | { type: "kick_member"; target: Servers.Server; user: User }
| { type: "ban_member"; target: Servers.Server; user: User } | { type: "ban_member"; target: Servers.Server; user: User }
@ -83,7 +83,7 @@ export type Screen =
| { id: "image_viewer"; attachment?: Attachment; embed?: EmbedImage } | { id: "image_viewer"; attachment?: Attachment; embed?: EmbedImage }
| { id: "modify_account"; field: "username" | "email" | "password" } | { id: "modify_account"; field: "username" | "email" | "password" }
| { id: "profile"; user_id: string } | { id: "profile"; user_id: string }
| { id: "channel_info"; channel_id: string } | { id: "channel_info"; channel: Channel }
| { id: "pending_requests"; users: User[] } | { id: "pending_requests"; users: User[] }
| { | {
id: "user_picker"; id: "user_picker";

View file

@ -9,7 +9,7 @@ import { useContext, useEffect, useState } from "preact/hooks";
import { TextReact } from "../../../lib/i18n"; import { TextReact } from "../../../lib/i18n";
import { User } from "../../../mobx"; import { Channel, User } from "../../../mobx";
import { useData } from "../../../mobx/State"; import { useData } from "../../../mobx/State";
import Message from "../../../components/common/messaging/Message"; import Message from "../../../components/common/messaging/Message";
@ -55,15 +55,15 @@ export function PromptModal({
} }
type SpecialProps = { onClose: () => void } & ( type SpecialProps = { onClose: () => void } & (
| { type: "leave_group"; target: Channels.GroupChannel } | { type: "leave_group"; target: Channel }
| { type: "close_dm"; target: Channels.DirectMessageChannel } | { type: "close_dm"; target: Channel }
| { type: "leave_server"; target: Servers.Server } | { type: "leave_server"; target: Servers.Server }
| { type: "delete_server"; target: Servers.Server } | { type: "delete_server"; target: Servers.Server }
| { type: "delete_channel"; target: Channels.TextChannel } | { type: "delete_channel"; target: Channel }
| { type: "delete_message"; target: Channels.Message } | { type: "delete_message"; target: Channels.Message }
| { | {
type: "create_invite"; type: "create_invite";
target: Channels.TextChannel | Channels.GroupChannel; target: Channel;
} }
| { type: "kick_member"; target: Servers.Server; user: User } | { type: "kick_member"; target: Servers.Server; user: User }
| { type: "ban_member"; target: Servers.Server; user: User } | { type: "ban_member"; target: Servers.Server; user: User }

View file

@ -1,23 +1,23 @@
import { X } from "@styled-icons/boxicons-regular"; import { X } from "@styled-icons/boxicons-regular";
import { observer } from "mobx-react-lite";
import styles from "./ChannelInfo.module.scss"; import styles from "./ChannelInfo.module.scss";
import { Channel } from "../../../mobx";
import Modal from "../../../components/ui/Modal"; import Modal from "../../../components/ui/Modal";
import Markdown from "../../../components/markdown/Markdown"; import Markdown from "../../../components/markdown/Markdown";
import { useChannel, useForceUpdate } from "../../revoltjs/hooks"; import { useClient } from "../../revoltjs/RevoltClient";
import { useForceUpdate } from "../../revoltjs/hooks";
import { getChannelName } from "../../revoltjs/util"; import { getChannelName } from "../../revoltjs/util";
interface Props { interface Props {
channel_id: string; channel: Channel;
onClose: () => void; onClose: () => void;
} }
export function ChannelInfo({ channel_id, onClose }: Props) { export const ChannelInfo = observer(({ channel, onClose }: Props) => {
const ctx = useForceUpdate();
const channel = useChannel(channel_id, ctx);
if (!channel) return null;
if ( if (
channel.channel_type === "DirectMessage" || channel.channel_type === "DirectMessage" ||
channel.channel_type === "SavedMessages" channel.channel_type === "SavedMessages"
@ -26,19 +26,20 @@ export function ChannelInfo({ channel_id, onClose }: Props) {
return null; return null;
} }
const client = useClient();
return ( return (
<Modal visible={true} onClose={onClose}> <Modal visible={true} onClose={onClose}>
<div className={styles.info}> <div className={styles.info}>
<div className={styles.header}> <div className={styles.header}>
<h1>{getChannelName(ctx.client, channel, true)}</h1> <h1>{getChannelName(client, channel, true)}</h1>
<div onClick={onClose}> <div onClick={onClose}>
<X size={36} /> <X size={36} />
</div> </div>
</div> </div>
<p> <p>
<Markdown content={channel.description} /> <Markdown content={channel.description!} />
</p> </p>
</div> </div>
</Modal> </Modal>
); );
} });

View file

@ -27,11 +27,7 @@ import {
StatusContext, StatusContext,
useClient, useClient,
} from "../../revoltjs/RevoltClient"; } from "../../revoltjs/RevoltClient";
import { import { useForceUpdate, useUserPermission } from "../../revoltjs/hooks";
useChannels,
useForceUpdate,
useUserPermission,
} from "../../revoltjs/hooks";
import { useIntermediate } from "../Intermediate"; import { useIntermediate } from "../Intermediate";
interface Props { interface Props {
@ -65,7 +61,6 @@ export function UserProfile({ user_id, onClose, dummy, dummyProfile }: Props) {
const [tab, setTab] = useState("profile"); const [tab, setTab] = useState("profile");
const ctx = useForceUpdate(); const ctx = useForceUpdate();
const channels = useChannels(undefined, ctx);
const permissions = useUserPermission(client.user!._id, ctx); const permissions = useUserPermission(client.user!._id, ctx);
const store = useData(); const store = useData();
@ -77,6 +72,12 @@ export function UserProfile({ user_id, onClose, dummy, dummyProfile }: Props) {
const user = store.users.get(user_id)!; const user = store.users.get(user_id)!;
const users = mutual?.users.map((id) => store.users.get(id)); const users = mutual?.users.map((id) => store.users.get(id));
const mutualGroups = [...store.channels.values()].filter(
(channel) =>
channel?.channel_type === "Group" &&
channel.recipients!.includes(user_id),
);
useLayoutEffect(() => { useLayoutEffect(() => {
if (!user_id) return; if (!user_id) return;
if (typeof profile !== "undefined") setProfile(undefined); if (typeof profile !== "undefined") setProfile(undefined);
@ -111,12 +112,6 @@ export function UserProfile({ user_id, onClose, dummy, dummyProfile }: Props) {
} }
}, [profile, status]); }, [profile, status]);
const mutualGroups = channels.filter(
(channel) =>
channel?.channel_type === "Group" &&
channel.recipients.includes(user_id),
);
const backgroundURL = const backgroundURL =
profile && profile &&
client.users.getBackgroundURL(profile, { width: 1000 }, true); client.users.getBackgroundURL(profile, { width: 1000 }, true);

View file

@ -77,13 +77,6 @@ function useObject(
: map.toArray(); : map.toArray();
} }
export function useChannel(id?: string, context?: HookContext) {
if (typeof id === "undefined") return;
return useObject("channels", id, context) as
| Readonly<Channels.Channel>
| undefined;
}
export function useChannels(ids?: string[], context?: HookContext) { export function useChannels(ids?: string[], context?: HookContext) {
return useObject("channels", ids, context) as ( return useObject("channels", ids, context) as (
| Readonly<Channels.Channel> | Readonly<Channels.Channel>

View file

@ -1,8 +1,10 @@
import { Client } from "revolt.js"; import { Client } from "revolt.js";
import { Channel, Message, User } from "revolt.js/dist/api/objects"; import { Message } from "revolt.js/dist/api/objects";
import { Text } from "preact-i18n"; import { Text } from "preact-i18n";
import { Channel } from "../../mobx";
import { Children } from "../../types/Preact"; import { Children } from "../../types/Preact";
export function takeError(error: any): string { export function takeError(error: any): string {

View file

@ -34,7 +34,7 @@ import {
import { Text } from "preact-i18n"; import { Text } from "preact-i18n";
import { useContext } from "preact/hooks"; import { useContext } from "preact/hooks";
import { User } from "../mobx"; import { Channel, User } from "../mobx";
import { useData } from "../mobx/State"; import { useData } from "../mobx/State";
import { dispatch } from "../redux"; import { dispatch } from "../redux";
import { connectState } from "../redux/connector"; import { connectState } from "../redux/connector";
@ -53,7 +53,6 @@ import {
useClient, useClient,
} from "../context/revoltjs/RevoltClient"; } from "../context/revoltjs/RevoltClient";
import { import {
useChannel,
useChannelPermission, useChannelPermission,
useForceUpdate, useForceUpdate,
useServer, useServer,
@ -86,7 +85,7 @@ type Action =
| { action: "copy_id"; id: string } | { action: "copy_id"; id: string }
| { action: "copy_selection" } | { action: "copy_selection" }
| { action: "copy_text"; content: string } | { action: "copy_text"; content: string }
| { action: "mark_as_read"; channel: Channels.Channel } | { action: "mark_as_read"; channel: Channel }
| { action: "retry_message"; message: QueuedMessage } | { action: "retry_message"; message: QueuedMessage }
| { action: "cancel_message"; message: QueuedMessage } | { action: "cancel_message"; message: QueuedMessage }
| { action: "mention"; user: string } | { action: "mention"; user: string }
@ -115,20 +114,17 @@ type Action =
| { action: "create_channel"; target: Servers.Server } | { action: "create_channel"; target: Servers.Server }
| { | {
action: "create_invite"; action: "create_invite";
target: target: Channel;
| Channels.GroupChannel
| Channels.TextChannel
| Channels.VoiceChannel;
} }
| { action: "leave_group"; target: Channels.GroupChannel } | { action: "leave_group"; target: Channel }
| { | {
action: "delete_channel"; action: "delete_channel";
target: Channels.TextChannel | Channels.VoiceChannel; target: Channel;
} }
| { action: "close_dm"; target: Channels.DirectMessageChannel } | { action: "close_dm"; target: Channel }
| { 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: Channel }
| { action: "open_settings" } | { 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 }
@ -172,9 +168,10 @@ function ContextMenus(props: Props) {
return; return;
const message = const message =
data.channel.channel_type === "TextChannel" typeof data.channel.last_message === "string"
? data.channel.last_message ? data.channel.last_message
: data.channel.last_message._id; : data.channel.last_message!._id;
dispatch({ dispatch({
type: "UNREADS_MARK_READ", type: "UNREADS_MARK_READ",
channel: data.channel._id, channel: data.channel._id,
@ -529,8 +526,10 @@ function ContextMenus(props: Props) {
pushDivider(); pushDivider();
} }
const channel = useChannel(cid, forceUpdate); const channel = cid ? store.channels.get(cid) : undefined;
const contextualChannel = useChannel(cxid, forceUpdate); const contextualChannel = cxid
? store.channels.get(cxid)
: undefined;
const targetChannel = channel ?? contextualChannel; const targetChannel = channel ?? contextualChannel;
const user = uid ? store.users.get(uid) : undefined; const user = uid ? store.users.get(uid) : undefined;
@ -541,7 +540,7 @@ function ContextMenus(props: Props) {
? targetChannel ? targetChannel
: undefined; : undefined;
const server = useServer( const server = useServer(
serverChannel ? serverChannel.server : sid, serverChannel ? serverChannel.server! : sid,
forceUpdate, forceUpdate,
); );
@ -551,7 +550,10 @@ function ContextMenus(props: Props) {
const serverPermissions = server const serverPermissions = server
? useServerPermission(server._id, forceUpdate) ? useServerPermission(server._id, forceUpdate)
: serverChannel : serverChannel
? useServerPermission(serverChannel.server, forceUpdate) ? useServerPermission(
serverChannel.server!,
forceUpdate,
)
: 0; : 0;
const userPermissions = user const userPermissions = user
? useUserPermission(user._id, forceUpdate) ? useUserPermission(user._id, forceUpdate)
@ -819,7 +821,7 @@ function ContextMenus(props: Props) {
generateAction( generateAction(
{ {
action: "open_server_channel_settings", action: "open_server_channel_settings",
server: channel.server, server: channel.server!,
id: channel._id, id: channel._id,
}, },
"open_channel_settings", "open_channel_settings",

View file

@ -73,7 +73,7 @@ export class User {
export class Channel { export class Channel {
_id: string; _id: string;
type: Channels.Channel["channel_type"]; channel_type: Channels.Channel["channel_type"];
// Direct Message // Direct Message
active: Nullable<boolean> = null; active: Nullable<boolean> = null;
@ -98,7 +98,7 @@ export class Channel {
constructor(data: Channels.Channel) { constructor(data: Channels.Channel) {
this._id = data._id; this._id = data._id;
this.type = data.channel_type; this.channel_type = data.channel_type;
switch (data.channel_type) { switch (data.channel_type) {
case "DirectMessage": { case "DirectMessage": {

View file

@ -1,3 +1,4 @@
import { observer } from "mobx-react-lite";
import { useParams, useHistory } from "react-router-dom"; import { useParams, useHistory } from "react-router-dom";
import { Channels } from "revolt.js/dist/api/objects"; import { Channels } from "revolt.js/dist/api/objects";
import styled from "styled-components"; import styled from "styled-components";
@ -6,10 +7,10 @@ import { useState } from "preact/hooks";
import { isTouchscreenDevice } from "../../lib/isTouchscreenDevice"; import { isTouchscreenDevice } from "../../lib/isTouchscreenDevice";
import { Channel as MobXChannel } from "../../mobx";
import { useData } from "../../mobx/State";
import { dispatch, getState } from "../../redux"; import { dispatch, getState } from "../../redux";
import { useChannel, useForceUpdate } from "../../context/revoltjs/hooks";
import AgeGate from "../../components/common/AgeGate"; import AgeGate from "../../components/common/AgeGate";
import MessageBox from "../../components/common/messaging/MessageBox"; import MessageBox from "../../components/common/messaging/MessageBox";
import JumpToBottom from "../../components/common/messaging/bars/JumpToBottom"; import JumpToBottom from "../../components/common/messaging/bars/JumpToBottom";
@ -36,19 +37,19 @@ const ChannelContent = styled.div`
`; `;
export function Channel({ id }: { id: string }) { export function Channel({ id }: { id: string }) {
const ctx = useForceUpdate(); const store = useData();
const channel = useChannel(id, ctx); const channel = store.channels.get(id);
if (!channel) return null; if (!channel) return null;
if (channel.channel_type === "VoiceChannel") { if (channel.channel_type === "VoiceChannel") {
return <VoiceChannel channel={channel} />; return <VoiceChannel channel={channel} />;
} }
return <TextChannel channel={channel} />; return <TextChannel channel={channel} />;
} }
const MEMBERS_SIDEBAR_KEY = "sidebar_members"; const MEMBERS_SIDEBAR_KEY = "sidebar_members";
function TextChannel({ channel }: { channel: Channels.Channel }) { const TextChannel = observer(({ channel }: { channel: MobXChannel }) => {
const [showMembers, setMembers] = useState( const [showMembers, setMembers] = useState(
getState().sectionToggle[MEMBERS_SIDEBAR_KEY] ?? true, getState().sectionToggle[MEMBERS_SIDEBAR_KEY] ?? true,
); );
@ -61,7 +62,9 @@ function TextChannel({ channel }: { channel: Channels.Channel }) {
gated={ gated={
(channel.channel_type === "TextChannel" || (channel.channel_type === "TextChannel" ||
channel.channel_type === "Group") && channel.channel_type === "Group") &&
channel.name.includes("nsfw") channel.name?.includes("nsfw")
? true
: false
}> }>
<ChannelHeader <ChannelHeader
channel={channel} channel={channel}
@ -96,9 +99,9 @@ function TextChannel({ channel }: { channel: Channels.Channel }) {
</ChannelMain> </ChannelMain>
</AgeGate> </AgeGate>
); );
} });
function VoiceChannel({ channel }: { channel: Channels.Channel }) { function VoiceChannel({ channel }: { channel: MobXChannel }) {
return ( return (
<> <>
<ChannelHeader channel={channel} /> <ChannelHeader channel={channel} />

View file

@ -2,14 +2,13 @@ import { At, Hash, Menu } from "@styled-icons/boxicons-regular";
import { Notepad, Group } from "@styled-icons/boxicons-solid"; import { Notepad, Group } from "@styled-icons/boxicons-solid";
import { observable } from "mobx"; import { observable } from "mobx";
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
import { Channel } from "revolt.js";
import styled from "styled-components"; import styled from "styled-components";
import { useContext } from "preact/hooks"; import { useContext } from "preact/hooks";
import { isTouchscreenDevice } from "../../lib/isTouchscreenDevice"; import { isTouchscreenDevice } from "../../lib/isTouchscreenDevice";
import { User } from "../../mobx"; import { Channel, User } from "../../mobx";
import { useData } from "../../mobx/State"; import { useData } from "../../mobx/State";
import { useIntermediate } from "../../context/intermediate/Intermediate"; import { useIntermediate } from "../../context/intermediate/Intermediate";
@ -131,7 +130,7 @@ export default observer(({ channel, toggleSidebar }: ChannelHeaderProps) => {
onClick={() => onClick={() =>
openScreen({ openScreen({
id: "channel_info", id: "channel_info",
channel_id: channel._id, channel,
}) })
}> }>
<Markdown <Markdown

View file

@ -41,7 +41,7 @@ export default function HeaderActions({
onClick={() => onClick={() =>
openScreen({ openScreen({
id: "user_picker", id: "user_picker",
omit: channel.recipients, omit: channel.recipients!,
callback: async (users) => { callback: async (users) => {
for (const user of users) { for (const user of users) {
await client.channels.addMember( await client.channels.addMember(

View file

@ -1,8 +1,11 @@
import { observer } from "mobx-react-lite";
import styled from "styled-components"; import styled from "styled-components";
import { Text } from "preact-i18n"; import { Text } from "preact-i18n";
import { useChannel, useForceUpdate } from "../../../context/revoltjs/hooks"; import { useData } from "../../../mobx/State";
import { useClient } from "../../../context/revoltjs/RevoltClient";
import { getChannelName } from "../../../context/revoltjs/util"; import { getChannelName } from "../../../context/revoltjs/util";
const StartBase = styled.div` const StartBase = styled.div`
@ -24,17 +27,18 @@ interface Props {
id: string; id: string;
} }
export default function ConversationStart({ id }: Props) { export default observer(({ id }: Props) => {
const ctx = useForceUpdate(); const store = useData();
const channel = useChannel(id, ctx); const client = useClient();
const channel = store.channels.get(id);
if (!channel) return null; if (!channel) return null;
return ( return (
<StartBase> <StartBase>
<h1>{getChannelName(ctx.client, channel, true)}</h1> <h1>{getChannelName(client, channel, true)}</h1>
<h4> <h4>
<Text id="app.main.channel.start.group" /> <Text id="app.main.channel.start.group" />
</h4> </h4>
</StartBase> </StartBase>
); );
} });

View file

@ -3,7 +3,9 @@ import { Route, useHistory, useParams } from "react-router-dom";
import { Text } from "preact-i18n"; import { Text } from "preact-i18n";
import { useChannel, useForceUpdate } from "../../context/revoltjs/hooks"; import { useData } from "../../mobx/State";
import { useClient } from "../../context/revoltjs/RevoltClient";
import { getChannelName } from "../../context/revoltjs/util"; import { getChannelName } from "../../context/revoltjs/util";
import Category from "../../components/ui/Category"; import Category from "../../components/ui/Category";
@ -14,8 +16,10 @@ import Permissions from "./channel/Permissions";
export default function ChannelSettings() { export default function ChannelSettings() {
const { channel: cid } = useParams<{ channel: string }>(); const { channel: cid } = useParams<{ channel: string }>();
const ctx = useForceUpdate();
const channel = useChannel(cid, ctx); const store = useData();
const client = useClient();
const channel = store.channels.get(cid);
if (!channel) return null; if (!channel) return null;
if ( if (
channel.channel_type === "SavedMessages" || channel.channel_type === "SavedMessages" ||
@ -49,7 +53,7 @@ export default function ChannelSettings() {
category: ( category: (
<Category <Category
variant="uniform" variant="uniform"
text={getChannelName(ctx.client, channel, true)} text={getChannelName(client, channel, true)}
/> />
), ),
id: "overview", id: "overview",

View file

@ -1,3 +1,4 @@
import { observer } from "mobx-react-lite";
import { Channels } from "revolt.js/dist/api/objects"; import { Channels } from "revolt.js/dist/api/objects";
import styled, { css } from "styled-components"; import styled, { css } from "styled-components";
@ -6,6 +7,8 @@ import { useContext, useEffect, useState } from "preact/hooks";
import TextAreaAutoSize from "../../../lib/TextAreaAutoSize"; import TextAreaAutoSize from "../../../lib/TextAreaAutoSize";
import { Channel } from "../../../mobx";
import { FileUploader } from "../../../context/revoltjs/FileUploads"; import { FileUploader } from "../../../context/revoltjs/FileUploads";
import { AppContext } from "../../../context/revoltjs/RevoltClient"; import { AppContext } from "../../../context/revoltjs/RevoltClient";
@ -13,10 +16,7 @@ import Button from "../../../components/ui/Button";
import InputBox from "../../../components/ui/InputBox"; import InputBox from "../../../components/ui/InputBox";
interface Props { interface Props {
channel: channel: Channel;
| Channels.GroupChannel
| Channels.TextChannel
| Channels.VoiceChannel;
} }
const Row = styled.div` const Row = styled.div`
@ -32,13 +32,13 @@ const Row = styled.div`
} }
`; `;
export default function Overview({ channel }: Props) { export default observer(({ channel }: Props) => {
const client = useContext(AppContext); const client = useContext(AppContext);
const [name, setName] = useState(channel.name); const [name, setName] = useState(channel.name ?? undefined);
const [description, setDescription] = useState(channel.description ?? ""); const [description, setDescription] = useState(channel.description ?? "");
useEffect(() => setName(channel.name), [channel.name]); useEffect(() => setName(channel.name ?? undefined), [channel.name]);
useEffect( useEffect(
() => setDescription(channel.description ?? ""), () => setDescription(channel.description ?? ""),
[channel.description], [channel.description],
@ -127,4 +127,4 @@ export default function Overview({ channel }: Props) {
</p> </p>
</div> </div>
); );
} });

View file

@ -1,8 +1,11 @@
import { observer } from "mobx-react-lite";
import { Channels } from "revolt.js/dist/api/objects"; import { Channels } from "revolt.js/dist/api/objects";
import { ChannelPermission } from "revolt.js/dist/api/permissions"; import { ChannelPermission } from "revolt.js/dist/api/permissions";
import { useContext, useEffect, useState } from "preact/hooks"; import { useContext, useEffect, useState } from "preact/hooks";
import { Channel } from "../../../mobx";
import { AppContext } from "../../../context/revoltjs/RevoltClient"; import { AppContext } from "../../../context/revoltjs/RevoltClient";
import { useServer } from "../../../context/revoltjs/hooks"; import { useServer } from "../../../context/revoltjs/hooks";
@ -21,21 +24,18 @@ const DEFAULT_PERMISSION_DM =
ChannelPermission.UploadFiles; ChannelPermission.UploadFiles;
interface Props { interface Props {
channel: channel: Channel;
| Channels.GroupChannel
| Channels.TextChannel
| Channels.VoiceChannel;
} }
// ! FIXME: bad code :) // ! FIXME: bad code :)
export default function Permissions({ channel }: Props) { export default observer(({ channel }: Props) => {
const [selected, setSelected] = useState("default"); const [selected, setSelected] = useState("default");
const client = useContext(AppContext); const client = useContext(AppContext);
type R = { name: string; permissions: number }; type R = { name: string; permissions: number };
const roles: { [key: string]: R } = {}; const roles: { [key: string]: R } = {};
if (channel.channel_type !== "Group") { if (channel.channel_type !== "Group") {
const server = useServer(channel.server); const server = useServer(channel.server!);
const a = server?.roles ?? {}; const a = server?.roles ?? {};
for (const b of Object.keys(a)) { for (const b of Object.keys(a)) {
roles[b] = { roles[b] = {
@ -110,4 +110,4 @@ export default function Permissions({ channel }: Props) {
</Button> </Button>
</div> </div>
); );
} });

View file

@ -1,5 +1,6 @@
import { XCircle } from "@styled-icons/boxicons-regular"; import { XCircle } from "@styled-icons/boxicons-regular";
import isEqual from "lodash.isequal"; import isEqual from "lodash.isequal";
import { observer } from "mobx-react-lite";
import { Channels, Servers, Users } from "revolt.js/dist/api/objects"; import { Channels, Servers, Users } from "revolt.js/dist/api/objects";
import { Route } from "revolt.js/dist/api/routes"; import { Route } from "revolt.js/dist/api/routes";
import { ulid } from "ulid"; import { ulid } from "ulid";
@ -8,8 +9,9 @@ import styles from "./Panes.module.scss";
import { Text } from "preact-i18n"; import { Text } from "preact-i18n";
import { useContext, useEffect, useState } from "preact/hooks"; import { useContext, useEffect, useState } from "preact/hooks";
import { useData } from "../../../mobx/State";
import { AppContext } from "../../../context/revoltjs/RevoltClient"; import { AppContext } from "../../../context/revoltjs/RevoltClient";
import { useChannels } from "../../../context/revoltjs/hooks";
import ChannelIcon from "../../../components/common/ChannelIcon"; import ChannelIcon from "../../../components/common/ChannelIcon";
import UserIcon from "../../../components/common/user/UserIcon"; import UserIcon from "../../../components/common/user/UserIcon";
@ -25,12 +27,12 @@ interface Props {
} }
// ! FIXME: really bad code // ! FIXME: really bad code
export function Categories({ server }: Props) { export const Categories = observer(({ server }: Props) => {
const client = useContext(AppContext); const client = useContext(AppContext);
const channels = useChannels(server.channels) as ( const store = useData();
| Channels.TextChannel const channels = server.channels
| Channels.VoiceChannel .map((id) => store.channels.get(id)!)
)[]; .filter((x) => typeof x !== "undefined");
const [cats, setCats] = useState<Servers.Category[]>( const [cats, setCats] = useState<Servers.Category[]>(
server.categories ?? [], server.categories ?? [],
@ -150,4 +152,4 @@ export function Categories({ server }: Props) {
})} })}
</div> </div>
); );
} });

View file

@ -9,7 +9,6 @@ import { useEffect, useState } from "preact/hooks";
import { useData } from "../../../mobx/State"; import { useData } from "../../../mobx/State";
import { useClient } from "../../../context/revoltjs/RevoltClient"; import { useClient } from "../../../context/revoltjs/RevoltClient";
import { useChannels, useForceUpdate } from "../../../context/revoltjs/hooks";
import { getChannelName } from "../../../context/revoltjs/util"; import { getChannelName } from "../../../context/revoltjs/util";
import UserIcon from "../../../components/common/user/UserIcon"; import UserIcon from "../../../components/common/user/UserIcon";
@ -26,14 +25,15 @@ export const Invites = observer(({ server }: Props) => {
InvitesNS.ServerInvite[] | undefined InvitesNS.ServerInvite[] | undefined
>(undefined); >(undefined);
const ctx = useForceUpdate();
const channels = useChannels(invites?.map((x) => x.channel) ?? [], ctx);
const store = useData(); const store = useData();
const client = useClient();
const users = invites?.map((invite) => store.users.get(invite.creator)); const users = invites?.map((invite) => store.users.get(invite.creator));
const channels = invites?.map((invite) =>
store.channels.get(invite.channel),
);
useEffect(() => { useEffect(() => {
ctx.client.servers client.servers
.fetchInvites(server._id) .fetchInvites(server._id)
.then((invites) => setInvites(invites)); .then((invites) => setInvites(invites));
}, []); }, []);
@ -57,7 +57,7 @@ export const Invites = observer(({ server }: Props) => {
{typeof invites === "undefined" && <Preloader type="ring" />} {typeof invites === "undefined" && <Preloader type="ring" />}
{invites?.map((invite, index) => { {invites?.map((invite, index) => {
const creator = users![index]; const creator = users![index];
const channel = channels.find((x) => x?._id === invite.channel); const channel = channels![index];
return ( return (
<div <div
@ -72,14 +72,14 @@ export const Invites = observer(({ server }: Props) => {
</span> </span>
<span> <span>
{channel && creator {channel && creator
? getChannelName(ctx.client, channel, true) ? getChannelName(client, channel, true)
: "#??"} : "#??"}
</span> </span>
<IconButton <IconButton
onClick={async () => { onClick={async () => {
setDelete([...deleting, invite._id]); setDelete([...deleting, invite._id]);
await ctx.client.deleteInvite(invite._id); await client.deleteInvite(invite._id);
setInvites( setInvites(
invites?.filter( invites?.filter(

View file

@ -1,4 +1,5 @@
import isEqual from "lodash.isequal"; import isEqual from "lodash.isequal";
import { observer } from "mobx-react-lite";
import { Servers, Server } from "revolt.js/dist/api/objects"; import { Servers, Server } from "revolt.js/dist/api/objects";
import styles from "./Panes.module.scss"; import styles from "./Panes.module.scss";
@ -7,6 +8,8 @@ import { useContext, useEffect, useState } from "preact/hooks";
import TextAreaAutoSize from "../../../lib/TextAreaAutoSize"; import TextAreaAutoSize from "../../../lib/TextAreaAutoSize";
import { useData } from "../../../mobx/State";
import { FileUploader } from "../../../context/revoltjs/FileUploads"; import { FileUploader } from "../../../context/revoltjs/FileUploads";
import { AppContext } from "../../../context/revoltjs/RevoltClient"; import { AppContext } from "../../../context/revoltjs/RevoltClient";
import { getChannelName } from "../../../context/revoltjs/util"; import { getChannelName } from "../../../context/revoltjs/util";
@ -19,8 +22,9 @@ interface Props {
server: Servers.Server; server: Servers.Server;
} }
export function Overview({ server }: Props) { export const Overview = observer(({ server }: Props) => {
const client = useContext(AppContext); const client = useContext(AppContext);
const store = useData();
const [name, setName] = useState(server.name); const [name, setName] = useState(server.name);
const [description, setDescription] = useState(server.description ?? ""); const [description, setDescription] = useState(server.description ?? "");
@ -170,15 +174,14 @@ export function Overview({ server }: Props) {
<option value="disabled"> <option value="disabled">
<Text id="general.disabled" /> <Text id="general.disabled" />
</option> </option>
{server.channels.map((id) => { {server.channels
const channel = client.channels.get(id); .map((id) => store.channels.get(id)!)
if (!channel) return null; .filter((x) => typeof x !== "undefined")
return ( .map((channel) => (
<option value={id}> <option value={channel._id}>
{getChannelName(client, channel, true)} {getChannelName(client, channel, true)}
</option> </option>
); ))}
})}
</ComboBox> </ComboBox>
</p> </p>
))} ))}
@ -190,4 +193,4 @@ export function Overview({ server }: Props) {
</p> </p>
</div> </div>
); );
} });