diff --git a/src/components/ChannelList/ChannelListItem.tsx b/src/components/ChannelList/ChannelListItem.tsx
index 6f5fc81..5a18378 100644
--- a/src/components/ChannelList/ChannelListItem.tsx
+++ b/src/components/ChannelList/ChannelListItem.tsx
@@ -4,13 +4,13 @@ import { useNavigate } from "react-router-dom";
import styled from "styled-components";
import { ContextMenuContext } from "../../contexts/ContextMenuContext";
import { modalController } from "../../controllers/modals";
+import { useAppStore } from "../../hooks/useAppStore";
import Channel from "../../stores/objects/Channel";
import { Permissions } from "../../utils/Permissions";
import Icon from "../Icon";
import SidebarPill from "../SidebarPill";
import Floating from "../floating/Floating";
import FloatingTrigger from "../floating/FloatingTrigger";
-import { useAppStore } from "../../hooks/useAppStore";
const ListItem = styled.div<{ isCategory?: boolean }>`
padding: ${(props) => (props.isCategory ? "16px 8px 0 0" : "1px 8px 0 0")};
@@ -90,7 +90,7 @@ function ChannelListItem({ channel, isCategory, active }: Props) {
alignItems: "center",
}}
>
-
+
{channel.channelIcon && !isCategory && (
{
+ channel.markAsRead();
+ }, [channel, guild]);
+
return (
diff --git a/src/stores/GatewayConnectionStore.ts b/src/stores/GatewayConnectionStore.ts
index 1d2d3e9..2fef56e 100644
--- a/src/stores/GatewayConnectionStore.ts
+++ b/src/stores/GatewayConnectionStore.ts
@@ -143,6 +143,8 @@ export default class GatewayConnectionStore {
this.dispatchHandlers.set(GatewayDispatchEvents.ChannelCreate, this.onChannelCreate);
this.dispatchHandlers.set(GatewayDispatchEvents.ChannelUpdate, this.onChannelUpdate);
this.dispatchHandlers.set(GatewayDispatchEvents.ChannelDelete, this.onChannelDelete);
+ // @ts-expect-error missing event in typings
+ this.dispatchHandlers.set("MESSAGE_ACK", this.onMessageAck);
this.dispatchHandlers.set(GatewayDispatchEvents.MessageCreate, this.onMessageCreate);
this.dispatchHandlers.set(GatewayDispatchEvents.MessageUpdate, this.onMessageUpdate);
@@ -633,6 +635,23 @@ export default class GatewayConnectionStore {
guild.removeChannel(data.id);
};
+ private onMessageAck = (data: { channel_id: string; message_id: string; version: number }) => {
+ // get readstate for channel
+ const readstate = this.app.readStateStore.get(data.channel_id);
+ if (!readstate) {
+ this.logger.warn(`[MessageAck] Readstate not found for channel ${data.channel_id}`);
+ return;
+ }
+
+ runInAction(() => {
+ readstate.lastMessageId = data.message_id;
+ });
+
+ this.logger.debug(
+ `[MessageAck] Updated last message id for channel readstate ${data.channel_id} to ${data.message_id}`,
+ );
+ };
+
private onMessageCreate = (data: GatewayMessageCreateDispatchData) => {
const guild = this.app.guilds.get(data.guild_id!);
if (!guild) {
diff --git a/src/stores/objects/Channel.ts b/src/stores/objects/Channel.ts
index 5a767a8..35f42c8 100644
--- a/src/stores/objects/Channel.ts
+++ b/src/stores/objects/Channel.ts
@@ -311,7 +311,8 @@ export default class Channel {
return listId;
}
- hasUnread() {
+ @computed
+ get unread() {
const readState = this.app.readStateStore.get(this.id);
if (!readState) {
// this.logger.warn(`Failed to find readstate for channel ${this.id}`); // this just causes unnecessary spam
@@ -320,4 +321,23 @@ export default class Channel {
return readState.lastMessageId !== this.lastMessageId;
}
+
+ markAsRead() {
+ const readState = this.app.readStateStore.get(this.id);
+ if (!readState) {
+ this.logger.warn(`Failed to find readstate for channel ${this.id}`); // this just causes unnecessary spam
+ return;
+ }
+
+ this.app.rest
+ .post(Routes.channelMessage(this.id, readState.lastMessageId) + "/ack", {
+ mention_count: readState.mentionCount,
+ })
+ .then((r) => {
+ this.logger.debug(`Acked ${this.lastMessageId} for channel ${this.id}`, r);
+ })
+ .catch((e) => {
+ this.logger.error(`Failed to ack ${this.lastMessageId} for channel ${this.id}`, e);
+ });
+ }
}
diff --git a/src/stores/objects/ReadState.ts b/src/stores/objects/ReadState.ts
index a4b255d..9b6c530 100644
--- a/src/stores/objects/ReadState.ts
+++ b/src/stores/objects/ReadState.ts
@@ -1,8 +1,10 @@
-import type { APIReadState } from "@spacebarchat/spacebar-api-types/v9";
+import { type APIReadState } from "@spacebarchat/spacebar-api-types/v9";
import { action, observable } from "mobx";
+import Logger from "../../utils/Logger";
import AppStore from "../AppStore";
export default class ReadState {
+ private readonly logger: Logger;
private readonly app: AppStore;
id: string;
@@ -11,9 +13,10 @@ export default class ReadState {
@observable mentionCount: number | null;
constructor(app: AppStore, data: APIReadState) {
+ this.logger = new Logger("ReadState");
this.app = app;
- this.id = data.id;
+ this.id = data.id; // channel id
this.lastMessageId = data.last_message_id;
this.lastPinTimestamp = data.last_pin_timestamp;
this.mentionCount = data.mention_count;