diff --git a/src/stores/GatewayConnectionStore.ts b/src/stores/GatewayConnectionStore.ts index 9fb7466..280b8c1 100644 --- a/src/stores/GatewayConnectionStore.ts +++ b/src/stores/GatewayConnectionStore.ts @@ -2,6 +2,7 @@ import { APIGuildMember, APIMessage, ChannelType, + GatewayActivity, GatewayChannelCreateDispatchData, GatewayChannelDeleteDispatchData, GatewayChannelUpdateDispatchData, @@ -42,10 +43,23 @@ const GATEWAY_VERSION = "9"; const GATEWAY_ENCODING = "json"; const RECONNECT_TIMEOUT = 10000; // start at 10 seconds, doubles each time +interface GatewaySession { + active: boolean; + activities: GatewayActivity[]; + client_info: { + client?: string; + os?: string; + version?: number; + }; + session_id: string; + status: PresenceUpdateStatus; +} + export default class GatewayConnectionStore { private readonly logger: Logger = new Logger("GatewayConnectionStore"); @observable private socket: WebSocket | null = null; - @observable private sessionId: string | null = null; + @observable public sessionId: string | null = null; + @observable public session: GatewaySession | undefined; @observable public readyState: number = WebSocket.CLOSED; private app: AppStore; @@ -67,6 +81,7 @@ export default class GatewayConnectionStore { makeObservable(this); } + /** * Starts connection to gateway */ @@ -449,8 +464,9 @@ export default class GatewayConnectionStore { */ private onReady = (data: GatewayReadyDispatchData) => { this.logger.info(`[Ready] took ${Date.now() - this.connectionStartTime!}ms`); - const { session_id, guilds, users, user, private_channels } = data; + const { session_id, guilds, users, user, private_channels, sessions } = data; this.sessionId = session_id; + this.session = (sessions as GatewaySession[]).find((x) => x.session_id === session_id); this.app.setUser(user); @@ -665,7 +681,7 @@ export default class GatewayConnectionStore { }; private onPresenceUpdate = (data: GatewayPresenceUpdateDispatchData) => { - this.app.presences.add(data); + this.app.presences.update(data); }; private onTypingStart = (data: GatewayTypingStartDispatchData) => { diff --git a/src/stores/GuildMemberListStore.ts b/src/stores/GuildMemberListStore.ts index 5b1b1c1..6ec761d 100644 --- a/src/stores/GuildMemberListStore.ts +++ b/src/stores/GuildMemberListStore.ts @@ -63,6 +63,12 @@ export default class GuildMemberListStore { data: [], }); } else { + // handle presence + this.app.presences.add({ + ...item.member.presence, + guild_id: this.guild.id, + }); + const member = this.guild.members.get(item.member.id); if (member) { listData[listData.length - 1].data.push({ diff --git a/src/stores/PresenceStore.ts b/src/stores/PresenceStore.ts index 73330ad..9dd378f 100644 --- a/src/stores/PresenceStore.ts +++ b/src/stores/PresenceStore.ts @@ -1,29 +1,11 @@ -import { - GatewayActivity, - GatewayGuildMemberListUpdateMember, - GatewayPresenceClientStatus, - GatewayPresenceUpdate, - PresenceUpdateStatus, - Snowflake, -} from "@spacebarchat/spacebar-api-types/v9"; -import { action, makeObservable, observable } from "mobx"; -import { OneKeyFrom } from "../utils/interfaces/common"; +import type { GatewayPresenceUpdateDispatchData, Snowflake } from "@spacebarchat/spacebar-api-types/v9"; +import { ObservableMap, action, computed, makeObservable, observable } from "mobx"; import AppStore from "./AppStore"; +import Presence from "./objects/Presence"; export default class PresenceStore { private readonly app: AppStore; - @observable presences = observable.map(); - @observable presencesForGuilds = observable.map< - Snowflake, - Map< - Snowflake, - Pick & { - timestamp: number; - } - > - >(); - @observable activities = observable.map(); - @observable clientStatuses = observable.map>(); + @observable presences = observable.map>(); constructor(app: AppStore) { this.app = app; @@ -32,50 +14,45 @@ export default class PresenceStore { } @action - add(presence: GatewayPresenceUpdate | GatewayGuildMemberListUpdateMember["presence"]) { - if (presence.status) { - this.presences.set(presence.user.id, presence.status); + add(data: GatewayPresenceUpdateDispatchData) { + if (!this.presences.has(data.guild_id)) { + this.presences.set(data.guild_id, observable.map()); } - if (presence.activities) { - this.activities.set(presence.user.id, presence.activities); - } - - if ("client_status" in presence) { - this.clientStatuses.set( - presence.user.id, - presence.client_status as OneKeyFrom, - ); - } - - if ("guild_id" in presence) { - const guild = this.presencesForGuilds.get(presence.guild_id); - if (guild) { - guild.set(presence.user.id, { - activities: presence.activities, - client_status: presence.client_status, - status: presence.status, - timestamp: Date.now(), - }); - } - } + this.presences.get(data.guild_id)?.set(data.user.id, new Presence(this.app, data)); } - // static getStatusColor(status: PresenceUpdateStatus) { - // const theme = + @action + addAll(data: GatewayPresenceUpdateDispatchData[]) { + data.forEach((p) => this.add(p)); + } - // switch (status) { - // case 'online': - // return theme.colors.palette.green80; - // case 'idle': - // return theme.colors.palette.yellow80; - // case 'dnd': - // return theme.colors.palette.red80; - // case 'offline': - // case 'invisible': - // return theme.colors.palette.gray80; - // } - // } + @computed + get all() { + return Array.from(this.presences.values()); + } + + @action + remove(id: Snowflake) { + this.presences.delete(id); + } + + @action + update(data: GatewayPresenceUpdateDispatchData) { + this.presences.get(data.guild_id)?.get(data.user.id)?.update(data); + } + + get(id: Snowflake) { + return this.presences.get(id); + } + + has(id: Snowflake) { + return this.presences.has(id); + } + + asList() { + return Array.from(this.presences.values()); + } get size() { return this.presences.size; diff --git a/src/stores/objects/Presence.ts b/src/stores/objects/Presence.ts new file mode 100644 index 0000000..d54c93b --- /dev/null +++ b/src/stores/objects/Presence.ts @@ -0,0 +1,36 @@ +import type { + APIUser, + GatewayActivity, + GatewayPresenceClientStatus, + GatewayPresenceUpdateDispatchData, + PresenceUpdateStatus, + Snowflake, +} from "@spacebarchat/spacebar-api-types/v9"; +import { action, observable } from "mobx"; +import AppStore from "../AppStore"; +import User from "./User"; + +export default class Presence { + private readonly app: AppStore; + + @observable public readonly user: User; + @observable public readonly guildId?: Snowflake; + @observable public readonly status: PresenceUpdateStatus | undefined; + @observable public readonly activities: GatewayActivity[] | undefined; + @observable public readonly clientStatus: GatewayPresenceClientStatus | undefined; + + constructor(app: AppStore, data: GatewayPresenceUpdateDispatchData) { + this.app = app; + + this.user = this.app.users.get(data.user.id) ?? new User(data.user as APIUser); // TODO: is this right? + this.guildId = data.guild_id; + this.status = data.status; + this.activities = data.activities; + this.clientStatus = data.client_status; + } + + @action + update(data: GatewayPresenceUpdateDispatchData) { + Object.assign(this, data); + } +}