diff --git a/package.json b/package.json
index 6f14588c..279af451 100644
--- a/package.json
+++ b/package.json
@@ -143,7 +143,7 @@
"react-virtuoso": "^1.10.4",
"redux": "^4.1.0",
"revolt-api": "0.5.3-alpha.10",
- "revolt.js": "5.1.0-alpha.15",
+ "revolt.js": "5.2.0-patch.0",
"rimraf": "^3.0.2",
"sass": "^1.35.1",
"shade-blend-color": "^1.0.0",
diff --git a/src/components/navigation/left/HomeSidebar.tsx b/src/components/navigation/left/HomeSidebar.tsx
index 837638ad..d378fd75 100644
--- a/src/components/navigation/left/HomeSidebar.tsx
+++ b/src/components/navigation/left/HomeSidebar.tsx
@@ -5,7 +5,7 @@ import {
Notepad,
} from "@styled-icons/boxicons-solid";
import { observer } from "mobx-react-lite";
-import { Link, Redirect, useLocation, useParams } from "react-router-dom";
+import { Link, useLocation, useParams } from "react-router-dom";
import { RelationshipStatus } from "revolt-api/types/Users";
import { Text } from "preact-i18n";
@@ -16,48 +16,37 @@ import PaintCounter from "../../../lib/PaintCounter";
import { isTouchscreenDevice } from "../../../lib/isTouchscreenDevice";
import { useApplicationState } from "../../../mobx/State";
-import { dispatch } from "../../../redux";
-import { connectState } from "../../../redux/connector";
-import { Unreads } from "../../../redux/reducers/unreads";
import { useIntermediate } from "../../../context/intermediate/Intermediate";
import { AppContext } from "../../../context/revoltjs/RevoltClient";
import Category from "../../ui/Category";
import placeholderSVG from "../items/placeholder.svg";
-import { mapChannelWithUnread, useUnreads } from "./common";
import { GenericSidebarBase, GenericSidebarList } from "../SidebarBase";
import ButtonItem, { ChannelButton } from "../items/ButtonItem";
import ConnectionStatus from "../items/ConnectionStatus";
-type Props = {
- unreads: Unreads;
-};
-
-const HomeSidebar = observer((props: Props) => {
+export default observer(() => {
const { pathname } = useLocation();
const client = useContext(AppContext);
- const layout = useApplicationState().layout;
- const { channel } = useParams<{ channel: string }>();
+ const state = useApplicationState();
+ const { channel: currentChannel } = useParams<{ channel: string }>();
const { openScreen } = useIntermediate();
- const channels = [...client.channels.values()]
- .filter(
- (x) =>
- x.channel_type === "DirectMessage" ||
- x.channel_type === "Group",
- )
- .map((x) => mapChannelWithUnread(x, props.unreads));
+ const channels = [...client.channels.values()].filter(
+ (x) => x.channel_type === "DirectMessage" || x.channel_type === "Group",
+ );
- const obj = client.channels.get(channel);
- if (channel && !obj) return ;
- if (obj) useUnreads({ ...props, channel: obj });
+ const obj = client.channels.get(currentChannel);
+ // ! FIXME: move this globally
// Track what page the user was last on (in home page).
- useEffect(() => layout.setLastHomePath(pathname), [pathname]);
+ useEffect(() => state.layout.setLastHomePath(pathname), [pathname]);
- channels.sort((b, a) => a.timestamp.localeCompare(b.timestamp));
+ channels.sort((b, a) =>
+ a.last_message_id_or_past.localeCompare(b.last_message_id_or_past),
+ );
return (
@@ -127,31 +116,37 @@ const HomeSidebar = observer((props: Props) => {
{channels.length === 0 && (
)}
- {channels.map((x) => {
+ {channels.map((channel) => {
let user;
- if (x.channel.channel_type === "DirectMessage") {
- if (!x.channel.active) return null;
- user = x.channel.recipient;
+ if (channel.channel_type === "DirectMessage") {
+ if (!channel.active) return null;
+ user = channel.recipient;
- if (!user) {
- console.warn(
- `Skipped DM ${x.channel._id} because user was missing.`,
- );
- return null;
- }
+ if (!user) return null;
}
+ const isUnread = channel.isUnread(state.notifications);
+ const mentionCount = channel.getMentions(
+ state.notifications,
+ ).length;
+
return (
+ key={channel._id}
+ active={channel._id === currentChannel}
+ to={`/channel/${channel._id}`}>
0
+ ? "mention"
+ : isUnread
+ ? "unread"
+ : undefined
+ }
+ alertCount={mentionCount}
+ active={channel._id === currentChannel}
/>
);
@@ -161,13 +156,3 @@ const HomeSidebar = observer((props: Props) => {
);
});
-
-export default connectState(
- HomeSidebar,
- (state) => {
- return {
- unreads: state.unreads,
- };
- },
- true,
-);
diff --git a/src/components/navigation/left/ServerListSidebar.tsx b/src/components/navigation/left/ServerListSidebar.tsx
index 3a27f84b..ddb0948f 100644
--- a/src/components/navigation/left/ServerListSidebar.tsx
+++ b/src/components/navigation/left/ServerListSidebar.tsx
@@ -13,8 +13,6 @@ import PaintCounter from "../../../lib/PaintCounter";
import { isTouchscreenDevice } from "../../../lib/isTouchscreenDevice";
import { useApplicationState } from "../../../mobx/State";
-import { connectState } from "../../../redux/connector";
-import { Unreads } from "../../../redux/reducers/unreads";
import { useIntermediate } from "../../../context/intermediate/Intermediate";
import { useClient } from "../../../context/revoltjs/RevoltClient";
@@ -25,7 +23,6 @@ import UserHover from "../../common/user/UserHover";
import UserIcon from "../../common/user/UserIcon";
import IconButton from "../../ui/IconButton";
import LineDivider from "../../ui/LineDivider";
-import { mapChannelWithUnread } from "./common";
import { Children } from "../../../types/Preact";
@@ -193,47 +190,14 @@ function Swoosh() {
);
}
-interface Props {
- unreads: Unreads;
-}
-
-export const ServerListSidebar = observer(({ unreads }: Props) => {
+export default observer(() => {
const client = useClient();
const state = useApplicationState();
const { server: server_id } = useParams<{ server?: string }>();
const server = server_id ? client.servers.get(server_id) : undefined;
- const activeServers = [...client.servers.values()];
- const channels = [...client.channels.values()].map((x) =>
- mapChannelWithUnread(x, unreads),
- );
-
- const unreadChannels = channels
- .filter((x) => x.unread)
- .filter((x) => !state.notifications.isMuted(x.channel))
- .map((x) => x.channel?._id);
-
- const servers = activeServers.map((server) => {
- let alertCount = 0;
- for (const id of server.channel_ids) {
- const channel = channels.find((x) => x.channel?._id === id);
- if (channel?.alertCount) {
- alertCount += channel.alertCount;
- }
- }
-
- return {
- server,
- unread: (typeof server.channel_ids.find((x) =>
- unreadChannels.includes(x),
- ) !== "undefined"
- ? alertCount > 0
- ? "mention"
- : "unread"
- : undefined) as "mention" | "unread" | undefined,
- alertCount,
- };
- });
+ const servers = [...client.servers.values()];
+ const channels = [...client.channels.values()];
const history = useHistory();
const path = useLocation().pathname;
@@ -241,16 +205,16 @@ export const ServerListSidebar = observer(({ unreads }: Props) => {
let homeUnread: "mention" | "unread" | undefined;
let alertCount = 0;
- for (const x of channels) {
- if (x.channel?.channel_type === "Group" && x.unread) {
+ for (const channel of channels) {
+ if (channel?.channel_type === "Group" && channel.unread) {
homeUnread = "unread";
- alertCount += x.alertCount ?? 0;
+ alertCount += channel.mentions.length;
}
if (
- x.channel?.channel_type === "DirectMessage" &&
- x.channel.active &&
- x.unread
+ channel.channel_type === "DirectMessage" &&
+ channel.active &&
+ channel.unread
) {
alertCount++;
}
@@ -294,32 +258,40 @@ export const ServerListSidebar = observer(({ unreads }: Props) => {
- {servers.map((entry) => {
- const active = entry.server._id === server?._id;
+ {servers.map((server) => {
+ const active = server._id === server_id;
+
+ const isUnread = server.isUnread(state.notifications);
+ const mentionCount = server.getMentions(
+ state.notifications,
+ ).length;
return (
+ to={state.layout.getServerPath(server._id)}>
-
+ unread={
+ mentionCount > 0
+ ? "mention"
+ : isUnread
+ ? "unread"
+ : undefined
+ }
+ count={mentionCount}>
+
@@ -353,9 +325,3 @@ export const ServerListSidebar = observer(({ unreads }: Props) => {
);
});
-
-export default connectState(ServerListSidebar, (state) => {
- return {
- unreads: state.unreads,
- };
-});
diff --git a/src/components/navigation/left/ServerSidebar.tsx b/src/components/navigation/left/ServerSidebar.tsx
index 33e3f4b7..7ed142ef 100644
--- a/src/components/navigation/left/ServerSidebar.tsx
+++ b/src/components/navigation/left/ServerSidebar.tsx
@@ -11,25 +11,17 @@ import { internalEmit } from "../../../lib/eventEmitter";
import { isTouchscreenDevice } from "../../../lib/isTouchscreenDevice";
import { useApplicationState } from "../../../mobx/State";
-import { dispatch } from "../../../redux";
import { connectState } from "../../../redux/connector";
-import { Notifications } from "../../../redux/reducers/notifications";
-import { Unreads } from "../../../redux/reducers/unreads";
import { useClient } from "../../../context/revoltjs/RevoltClient";
import CollapsibleSection from "../../common/CollapsibleSection";
import ServerHeader from "../../common/ServerHeader";
import Category from "../../ui/Category";
-import { mapChannelWithUnread, useUnreads } from "./common";
import { ChannelButton } from "../items/ButtonItem";
import ConnectionStatus from "../items/ConnectionStatus";
-interface Props {
- unreads: Unreads;
-}
-
const ServerBase = styled.div`
height: 100%;
width: 240px;
@@ -56,7 +48,7 @@ const ServerList = styled.div`
}
`;
-const ServerSidebar = observer((props: Props) => {
+export default observer(() => {
const client = useClient();
const state = useApplicationState();
const { server: server_id, channel: channel_id } =
@@ -76,9 +68,7 @@ const ServerSidebar = observer((props: Props) => {
);
if (channel_id && !channel) return ;
- // Handle unreads; FIXME: should definitely not be here
- if (channel) useUnreads({ ...props, channel });
-
+ // ! FIXME: move this globally
// Track which channel the user was last on.
useEffect(() => {
if (!channel_id) return;
@@ -95,6 +85,8 @@ const ServerSidebar = observer((props: Props) => {
if (!entry) return;
const active = channel?._id === entry._id;
+ const isUnread = entry.isUnread(state.notifications);
+ const mentionCount = entry.getMentions(state.notifications);
return (
{
0
+ ? "mention"
+ : isUnread
+ ? "unread"
+ : undefined
+ }
compact
muted={state.notifications.isMuted(entry)}
/>
@@ -161,10 +158,3 @@ const ServerSidebar = observer((props: Props) => {
);
});
-
-export default connectState(ServerSidebar, (state) => {
- return {
- unreads: state.unreads,
- notifications: state.notifications,
- };
-});
diff --git a/src/components/navigation/left/common.ts b/src/components/navigation/left/common.ts
index 774934f4..ee5e5b66 100644
--- a/src/components/navigation/left/common.ts
+++ b/src/components/navigation/left/common.ts
@@ -1,7 +1,7 @@
import { reaction } from "mobx";
import { Channel } from "revolt.js/dist/maps/Channels";
-import { useLayoutEffect, useRef } from "preact/hooks";
+import { useLayoutEffect } from "preact/hooks";
import { dispatch } from "../../../redux";
import { Unreads } from "../../../redux/reducers/unreads";
diff --git a/src/mobx/stores/NotificationOptions.ts b/src/mobx/stores/NotificationOptions.ts
index 9bb3b23e..51c45511 100644
--- a/src/mobx/stores/NotificationOptions.ts
+++ b/src/mobx/stores/NotificationOptions.ts
@@ -195,12 +195,17 @@ export default class NotificationOptions implements Store, Persistent {
* @returns Whether this object is muted
*/
isMuted(target?: Channel | Server) {
+ var value: NotificationState | undefined;
if (target instanceof Channel) {
- return this.computeForChannel(target) === "muted";
+ value = this.computeForChannel(target);
} else if (target instanceof Server) {
- return this.computeForServer(target._id) === "muted";
- } else {
- return false;
+ value = this.computeForServer(target._id);
}
+
+ if (value === "muted") {
+ return true;
+ }
+
+ return false;
}
}
diff --git a/src/mobx/stores/ServerConfig.ts b/src/mobx/stores/ServerConfig.ts
index 6e4b5d9d..6b44bec1 100644
--- a/src/mobx/stores/ServerConfig.ts
+++ b/src/mobx/stores/ServerConfig.ts
@@ -41,6 +41,7 @@ export default class ServerConfig
*/
createClient() {
const client = new Client({
+ unreads: true,
autoReconnect: false,
apiURL: import.meta.env.VITE_API_URL,
debug: import.meta.env.DEV,
diff --git a/src/pages/channels/Channel.tsx b/src/pages/channels/Channel.tsx
index 9f4767f0..cf162c6d 100644
--- a/src/pages/channels/Channel.tsx
+++ b/src/pages/channels/Channel.tsx
@@ -1,5 +1,6 @@
import { Hash } from "@styled-icons/boxicons-regular";
import { Ghost } from "@styled-icons/boxicons-solid";
+import { reaction } from "mobx";
import { observer } from "mobx-react-lite";
import { useParams } from "react-router-dom";
import { Channel as ChannelI } from "revolt.js/dist/maps/Channels";
@@ -10,7 +11,6 @@ import { useEffect, useState } from "preact/hooks";
import { isTouchscreenDevice } from "../../lib/isTouchscreenDevice";
-import { useApplicationState } from "../../mobx/State";
import { dispatch, getState } from "../../redux";
import { useClient } from "../../context/revoltjs/RevoltClient";
@@ -93,6 +93,23 @@ const TextChannel = observer(({ channel }: { channel: ChannelI }) => {
getState().sectionToggle[CHANNELS_SIDEBAR_KEY] ?? true,
);
+ // Mark channel as read.
+ useEffect(() => {
+ const checkUnread = () =>
+ channel.unread &&
+ channel.client.unreads!.markRead(
+ channel._id,
+ channel.last_message_id!,
+ true,
+ );
+
+ checkUnread();
+ return reaction(
+ () => channel.last_message_id,
+ () => checkUnread(),
+ );
+ }, [channel]);
+
return (