.
This commit is contained in:
@@ -13,32 +13,37 @@ interface ChannelVoiceState {
|
||||
|
||||
interface ChannelsVoiceState {
|
||||
channels: Record<ChannelId, ChannelVoiceState>;
|
||||
addUser: (channelId: ChannelId, userId: UserId, userVoiceState: UserVoiceState) => void;
|
||||
addUser: (
|
||||
channelId: ChannelId,
|
||||
userId: UserId,
|
||||
userVoiceState: UserVoiceState,
|
||||
) => void;
|
||||
removeUser: (channelId: ChannelId, userId: UserId) => void;
|
||||
removeChannel: (channelId: ChannelId) => void;
|
||||
}
|
||||
|
||||
export const useChannelsVoiceStateStore = create<ChannelsVoiceState>()(
|
||||
immer(
|
||||
(set, get) => ({
|
||||
channels: {},
|
||||
addUser: (channelId, userId, userVoiceState) => set((state) => {
|
||||
immer((set, get) => ({
|
||||
channels: {},
|
||||
addUser: (channelId, userId, userVoiceState) =>
|
||||
set((state) => {
|
||||
if (!state.channels[channelId]) {
|
||||
state.channels[channelId] = {
|
||||
users: {}
|
||||
}
|
||||
users: {},
|
||||
};
|
||||
}
|
||||
|
||||
state.channels[channelId].users[userId] = userVoiceState;
|
||||
}),
|
||||
removeUser: (channelId, userId) => set((state) => {
|
||||
removeUser: (channelId, userId) =>
|
||||
set((state) => {
|
||||
if (state.channels[channelId]) {
|
||||
delete state.channels[channelId].users[userId];
|
||||
}
|
||||
}),
|
||||
removeChannel: (channelId) => set((state) => {
|
||||
removeChannel: (channelId) =>
|
||||
set((state) => {
|
||||
delete state.channels[channelId];
|
||||
})
|
||||
})
|
||||
)
|
||||
)
|
||||
}),
|
||||
})),
|
||||
);
|
||||
|
||||
@@ -1,102 +1,187 @@
|
||||
import type { QueryClient } from '@tanstack/react-query';
|
||||
import { create } from 'zustand';
|
||||
import { messageSchema, type ChannelId, type Message, type MessageId, type ServerId } from '~/lib/api/types';
|
||||
import { GatewayClient } from '~/lib/websocket/gateway/client';
|
||||
import type { QueryClient } from "@tanstack/react-query";
|
||||
import { create } from "zustand";
|
||||
import {
|
||||
messageSchema,
|
||||
type ChannelId,
|
||||
type Message,
|
||||
type MessageId,
|
||||
type ServerId,
|
||||
} from "~/lib/api/types";
|
||||
import { GatewayClient } from "~/lib/websocket/gateway/client";
|
||||
import {
|
||||
ConnectionState,
|
||||
EventType,
|
||||
type EventData,
|
||||
type VoiceServerUpdateEvent
|
||||
} from '~/lib/websocket/gateway/types';
|
||||
import { useChannelsVoiceStateStore } from './channels-voice-state';
|
||||
import { usePrivateChannelsStore } from './private-channels-store';
|
||||
import { useServerChannelsStore } from './server-channels-store';
|
||||
import { useServerListStore } from './server-list-store';
|
||||
import { useUsersStore } from './users-store';
|
||||
type VoiceServerUpdateEvent,
|
||||
} from "~/lib/websocket/gateway/types";
|
||||
import { useChannelsVoiceStateStore } from "./channels-voice-state";
|
||||
import { usePrivateChannelsStore } from "./private-channels-store";
|
||||
import { useServerChannelsStore } from "./server-channels-store";
|
||||
import { useServerListStore } from "./server-list-store";
|
||||
import { useUsersStore } from "./users-store";
|
||||
|
||||
const GATEWAY_URL = 'ws://localhost:12345/gateway/ws';
|
||||
const GATEWAY_URL = "ws://localhost:12345/gateway/ws";
|
||||
|
||||
const HANDLERS = {
|
||||
[EventType.ADD_SERVER]: (self: GatewayState, data: Extract<EventData, { type: EventType.ADD_SERVER }>['data']) => {
|
||||
[EventType.ADD_SERVER]: (
|
||||
self: GatewayState,
|
||||
data: Extract<EventData, { type: EventType.ADD_SERVER }>["data"],
|
||||
) => {
|
||||
useServerListStore.getState().addServer(data.server);
|
||||
},
|
||||
|
||||
[EventType.REMOVE_SERVER]: (self: GatewayState, data: Extract<EventData, { type: EventType.REMOVE_SERVER }>['data']) => {
|
||||
[EventType.REMOVE_SERVER]: (
|
||||
self: GatewayState,
|
||||
data: Extract<EventData, { type: EventType.REMOVE_SERVER }>["data"],
|
||||
) => {
|
||||
useServerListStore.getState().removeServer(data.serverId);
|
||||
useServerChannelsStore.getState().removeServer(data.serverId);
|
||||
useChannelsVoiceStateStore.getState().removeChannel(data.serverId);
|
||||
},
|
||||
|
||||
[EventType.ADD_DM_CHANNEL]: (self: GatewayState, data: Extract<EventData, { type: EventType.ADD_DM_CHANNEL }>['data']) => {
|
||||
usePrivateChannelsStore.getState().addChannel(data.channel);
|
||||
[EventType.ADD_DM_CHANNEL]: (
|
||||
self: GatewayState,
|
||||
data: Extract<EventData, { type: EventType.ADD_DM_CHANNEL }>["data"],
|
||||
) => {
|
||||
usePrivateChannelsStore.getState().addChannel({
|
||||
...data.channel,
|
||||
recipients: data.recipients,
|
||||
});
|
||||
},
|
||||
|
||||
[EventType.REMOVE_DM_CHANNEL]: (self: GatewayState, data: Extract<EventData, { type: EventType.REMOVE_DM_CHANNEL }>['data']) => {
|
||||
[EventType.REMOVE_DM_CHANNEL]: (
|
||||
self: GatewayState,
|
||||
data: Extract<EventData, { type: EventType.REMOVE_DM_CHANNEL }>["data"],
|
||||
) => {
|
||||
usePrivateChannelsStore.getState().removeChannel(data.channelId);
|
||||
useChannelsVoiceStateStore.getState().removeChannel(data.channelId);
|
||||
},
|
||||
|
||||
[EventType.ADD_SERVER_CHANNEL]: (self: GatewayState, data: Extract<EventData, { type: EventType.ADD_SERVER_CHANNEL }>['data']) => {
|
||||
[EventType.ADD_SERVER_CHANNEL]: (
|
||||
self: GatewayState,
|
||||
data: Extract<
|
||||
EventData,
|
||||
{ type: EventType.ADD_SERVER_CHANNEL }
|
||||
>["data"],
|
||||
) => {
|
||||
useServerChannelsStore.getState().addChannel(data.channel);
|
||||
},
|
||||
|
||||
[EventType.REMOVE_SERVER_CHANNEL]: (self: GatewayState, data: Extract<EventData, { type: EventType.REMOVE_SERVER_CHANNEL }>['data']) => {
|
||||
useServerChannelsStore.getState().removeChannel(data.serverId, data.channelId);
|
||||
[EventType.REMOVE_SERVER_CHANNEL]: (
|
||||
self: GatewayState,
|
||||
data: Extract<
|
||||
EventData,
|
||||
{ type: EventType.REMOVE_SERVER_CHANNEL }
|
||||
>["data"],
|
||||
) => {
|
||||
useServerChannelsStore
|
||||
.getState()
|
||||
.removeChannel(data.serverId, data.channelId);
|
||||
useChannelsVoiceStateStore.getState().removeChannel(data.serverId);
|
||||
},
|
||||
|
||||
[EventType.ADD_USER]: (self: GatewayState, data: Extract<EventData, { type: EventType.ADD_USER }>['data']) => {
|
||||
[EventType.ADD_USER]: (
|
||||
self: GatewayState,
|
||||
data: Extract<EventData, { type: EventType.ADD_USER }>["data"],
|
||||
) => {
|
||||
useUsersStore.getState().addUser(data.user);
|
||||
},
|
||||
|
||||
[EventType.REMOVE_USER]: (self: GatewayState, data: Extract<EventData, { type: EventType.REMOVE_USER }>['data']) => {
|
||||
[EventType.REMOVE_USER]: (
|
||||
self: GatewayState,
|
||||
data: Extract<EventData, { type: EventType.REMOVE_USER }>["data"],
|
||||
) => {
|
||||
useUsersStore.getState().removeUser(data.userId);
|
||||
},
|
||||
|
||||
[EventType.ADD_SERVER_MEMBER]: (self: GatewayState, data: Extract<EventData, { type: EventType.ADD_SERVER_MEMBER }>['data']) => {
|
||||
[EventType.ADD_SERVER_MEMBER]: (
|
||||
self: GatewayState,
|
||||
data: Extract<EventData, { type: EventType.ADD_SERVER_MEMBER }>["data"],
|
||||
) => {
|
||||
useUsersStore.getState().addUser(data.user);
|
||||
},
|
||||
|
||||
[EventType.REMOVE_SERVER_MEMBER]: (self: GatewayState, data: Extract<EventData, { type: EventType.REMOVE_SERVER_MEMBER }>['data']) => {
|
||||
[EventType.REMOVE_SERVER_MEMBER]: (
|
||||
self: GatewayState,
|
||||
data: Extract<
|
||||
EventData,
|
||||
{ type: EventType.REMOVE_SERVER_MEMBER }
|
||||
>["data"],
|
||||
) => {
|
||||
useUsersStore.getState().removeUser(data.userId);
|
||||
},
|
||||
|
||||
[EventType.ADD_MESSAGE]: (self: GatewayState, data: Extract<EventData, { type: EventType.ADD_MESSAGE }>['data']) => {
|
||||
const message = messageSchema.parse(data.message)
|
||||
[EventType.ADD_MESSAGE]: (
|
||||
self: GatewayState,
|
||||
data: Extract<EventData, { type: EventType.ADD_MESSAGE }>["data"],
|
||||
) => {
|
||||
const message = messageSchema.parse(data.message);
|
||||
|
||||
if (self.queryClient) {
|
||||
self.queryClient.setQueryData(['messages', message.channelId], (oldData: {
|
||||
pages: Message[][],
|
||||
pageParams: MessageId[]
|
||||
}) => {
|
||||
return {
|
||||
pages: oldData?.pages ? [[message, ...oldData.pages[0]], ...oldData.pages.slice(1)] : [[message]],
|
||||
pageParams: oldData?.pageParams ?? [undefined, message.id]
|
||||
}
|
||||
});
|
||||
self.queryClient.setQueryData(
|
||||
["messages", message.channelId],
|
||||
(oldData: { pages: Message[][]; pageParams: MessageId[] }) => {
|
||||
return {
|
||||
pages: oldData?.pages
|
||||
? [
|
||||
[message, ...oldData.pages[0]],
|
||||
...oldData.pages.slice(1),
|
||||
]
|
||||
: [[message]],
|
||||
pageParams: oldData?.pageParams ?? [
|
||||
undefined,
|
||||
message.id,
|
||||
],
|
||||
};
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
[EventType.REMOVE_MESSAGE]: (self: GatewayState, data: Extract<EventData, { type: EventType.REMOVE_MESSAGE }>['data']) => {
|
||||
[EventType.REMOVE_MESSAGE]: (
|
||||
self: GatewayState,
|
||||
data: Extract<EventData, { type: EventType.REMOVE_MESSAGE }>["data"],
|
||||
) => {
|
||||
if (self.queryClient) {
|
||||
self.queryClient.setQueryData(['messages', data.channelId], (oldData: any) => {
|
||||
if (!oldData) return [];
|
||||
return oldData.filter((message: any) => message.id !== data.messageId);
|
||||
});
|
||||
self.queryClient.setQueryData(
|
||||
["messages", data.channelId],
|
||||
(oldData: any) => {
|
||||
if (!oldData) return [];
|
||||
return oldData.filter(
|
||||
(message: any) => message.id !== data.messageId,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
[EventType.VOICE_CHANNEL_CONNECTED]: (self: GatewayState, data: Extract<EventData, { type: EventType.VOICE_CHANNEL_CONNECTED }>['data']) => {
|
||||
useChannelsVoiceStateStore.getState().addUser(data.channelId, data.userId, {
|
||||
deaf: false,
|
||||
muted: false
|
||||
});
|
||||
[EventType.VOICE_CHANNEL_CONNECTED]: (
|
||||
self: GatewayState,
|
||||
data: Extract<
|
||||
EventData,
|
||||
{ type: EventType.VOICE_CHANNEL_CONNECTED }
|
||||
>["data"],
|
||||
) => {
|
||||
useChannelsVoiceStateStore
|
||||
.getState()
|
||||
.addUser(data.channelId, data.userId, {
|
||||
deaf: false,
|
||||
muted: false,
|
||||
});
|
||||
},
|
||||
|
||||
[EventType.VOICE_CHANNEL_DISCONNECTED]: (self: GatewayState, data: Extract<EventData, { type: EventType.VOICE_CHANNEL_DISCONNECTED }>['data']) => {
|
||||
useChannelsVoiceStateStore.getState().removeUser(data.channelId, data.userId);
|
||||
[EventType.VOICE_CHANNEL_DISCONNECTED]: (
|
||||
self: GatewayState,
|
||||
data: Extract<
|
||||
EventData,
|
||||
{ type: EventType.VOICE_CHANNEL_DISCONNECTED }
|
||||
>["data"],
|
||||
) => {
|
||||
useChannelsVoiceStateStore
|
||||
.getState()
|
||||
.removeUser(data.channelId, data.userId);
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
interface GatewayState {
|
||||
client: GatewayClient | null;
|
||||
@@ -110,16 +195,22 @@ interface GatewayState {
|
||||
|
||||
updateVoiceState: (serverId: ServerId, channelId: ChannelId) => void;
|
||||
requestVoiceStates: (serverId: ServerId) => void;
|
||||
onVoiceServerUpdate: (handler: (event: VoiceServerUpdateEvent['data']) => void | Promise<void>) => (() => void);
|
||||
onVoiceServerUpdate: (
|
||||
handler: (
|
||||
event: VoiceServerUpdateEvent["data"],
|
||||
) => void | Promise<void>,
|
||||
) => () => void;
|
||||
}
|
||||
|
||||
export const useGatewayStore = create<GatewayState>()((set, get) => {
|
||||
const client = new GatewayClient(GATEWAY_URL);
|
||||
|
||||
const voiceHandlers = new Set<(event: VoiceServerUpdateEvent['data']) => void>();
|
||||
const voiceHandlers = new Set<
|
||||
(event: VoiceServerUpdateEvent["data"]) => void
|
||||
>();
|
||||
|
||||
client.onEvent(EventType.VOICE_SERVER_UPDATE, (event) => {
|
||||
voiceHandlers.forEach(handler => handler(event));
|
||||
voiceHandlers.forEach((handler) => handler(event));
|
||||
});
|
||||
|
||||
for (const [type, handler] of Object.entries(HANDLERS)) {
|
||||
@@ -137,7 +228,7 @@ export const useGatewayStore = create<GatewayState>()((set, get) => {
|
||||
client.connect(token);
|
||||
set({ status: client.connectionState });
|
||||
|
||||
client.onControl('stateChange', (state) => {
|
||||
client.onControl("stateChange", (state) => {
|
||||
set({ status: state });
|
||||
});
|
||||
},
|
||||
@@ -163,9 +254,8 @@ export const useGatewayStore = create<GatewayState>()((set, get) => {
|
||||
voiceHandlers.add(handler);
|
||||
|
||||
return () => {
|
||||
console.log("removing voice server update handler", handler);
|
||||
voiceHandlers.delete(handler);
|
||||
};
|
||||
}
|
||||
},
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
@@ -20,23 +20,26 @@ export type DeleteServerConfirmModalData = {
|
||||
type: ModalType.CREATE_SERVER_CHANNEL;
|
||||
data: {
|
||||
serverId: ServerId;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export type CreateServerChannelModalData = {
|
||||
type: ModalType.CREATE_SERVER_CHANNEL;
|
||||
data: {
|
||||
serverId: ServerId;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export type ModalData = CreateServerChannelModalData | CreateServerInviteModalData | DeleteServerConfirmModalData;
|
||||
export type ModalData =
|
||||
| CreateServerChannelModalData
|
||||
| CreateServerInviteModalData
|
||||
| DeleteServerConfirmModalData;
|
||||
|
||||
interface ModalState {
|
||||
type: ModalType | null;
|
||||
data?: ModalData['data'];
|
||||
data?: ModalData["data"];
|
||||
isOpen: boolean;
|
||||
onOpen: (type: ModalType, data?: ModalData['data']) => void;
|
||||
onOpen: (type: ModalType, data?: ModalData["data"]) => void;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
@@ -46,4 +49,4 @@ export const useModalStore = create<ModalState>()((set) => ({
|
||||
isOpen: false,
|
||||
onOpen: (type, data) => set({ type, data, isOpen: true }),
|
||||
onClose: () => set({ type: null, isOpen: false }),
|
||||
}));
|
||||
}));
|
||||
|
||||
@@ -1,25 +1,33 @@
|
||||
import { create } from 'zustand'
|
||||
import { immer } from 'zustand/middleware/immer'
|
||||
import type { ChannelId, RecipientChannel } from '~/lib/api/types'
|
||||
import { create } from "zustand";
|
||||
import { immer } from "zustand/middleware/immer";
|
||||
import type { ChannelId, RecipientChannel } from "~/lib/api/types";
|
||||
|
||||
type PrivateChannelsStore = {
|
||||
channels: Record<ChannelId, RecipientChannel>
|
||||
addChannels: (channels: RecipientChannel[]) => void
|
||||
addChannel: (channel: RecipientChannel) => void
|
||||
removeChannel: (channelId: ChannelId) => void
|
||||
}
|
||||
channels: Record<ChannelId, RecipientChannel>;
|
||||
addChannels: (channels: RecipientChannel[]) => void;
|
||||
addChannel: (channel: RecipientChannel) => void;
|
||||
removeChannel: (channelId: ChannelId) => void;
|
||||
};
|
||||
|
||||
export const usePrivateChannelsStore = create<PrivateChannelsStore>()(
|
||||
immer(
|
||||
(set) => ({
|
||||
channels: {},
|
||||
addChannels: (channels: RecipientChannel[]) => set((state) => {
|
||||
immer((set) => ({
|
||||
channels: {},
|
||||
addChannels: (channels: RecipientChannel[]) =>
|
||||
set((state) => {
|
||||
for (const channel of channels) {
|
||||
state.channels[channel.id] = channel
|
||||
state.channels[channel.id] = channel;
|
||||
console.log("add channel", channel);
|
||||
}
|
||||
}),
|
||||
addChannel: (channel: RecipientChannel) => set((state) => { state.channels[channel.id] = channel }),
|
||||
removeChannel: (channelId: ChannelId) => set((state) => { delete state.channels[channelId] }),
|
||||
})
|
||||
)
|
||||
)
|
||||
addChannel: (channel: RecipientChannel) =>
|
||||
set((state) => {
|
||||
state.channels[channel.id] = {
|
||||
...channel,
|
||||
};
|
||||
}),
|
||||
removeChannel: (channelId: ChannelId) =>
|
||||
set((state) => {
|
||||
delete state.channels[channelId];
|
||||
}),
|
||||
})),
|
||||
);
|
||||
|
||||
@@ -1,41 +1,48 @@
|
||||
import { create } from 'zustand'
|
||||
import { immer } from 'zustand/middleware/immer'
|
||||
import type { ChannelId, ServerChannel, ServerId } from "~/lib/api/types"
|
||||
import { create } from "zustand";
|
||||
import { immer } from "zustand/middleware/immer";
|
||||
import type { ChannelId, ServerChannel, ServerId } from "~/lib/api/types";
|
||||
|
||||
type ServerChannelsStore = {
|
||||
channels: Record<ServerId, Record<ChannelId, ServerChannel>>
|
||||
addServer: (serverId: ServerId) => void
|
||||
addChannel: (channel: ServerChannel) => void
|
||||
addChannels: (channels: ServerChannel[]) => void
|
||||
removeChannel: (serverId: ServerId, channelId: ChannelId) => void
|
||||
removeServer: (serverId: ServerId) => void
|
||||
}
|
||||
channels: Record<ServerId, Record<ChannelId, ServerChannel>>;
|
||||
addServer: (serverId: ServerId) => void;
|
||||
addChannel: (channel: ServerChannel) => void;
|
||||
addChannels: (channels: ServerChannel[]) => void;
|
||||
removeChannel: (serverId: ServerId, channelId: ChannelId) => void;
|
||||
removeServer: (serverId: ServerId) => void;
|
||||
};
|
||||
|
||||
export const useServerChannelsStore = create<ServerChannelsStore>()(
|
||||
immer(
|
||||
(set, get) => ({
|
||||
channels: {},
|
||||
addServer: (serverId) => set((state) => {
|
||||
state.channels[serverId] = {}
|
||||
immer((set, get) => ({
|
||||
channels: {},
|
||||
addServer: (serverId) =>
|
||||
set((state) => {
|
||||
state.channels[serverId] = {};
|
||||
}),
|
||||
addChannel: (channel) => set((state) => {
|
||||
addChannel: (channel) =>
|
||||
set((state) => {
|
||||
if (state.channels[channel.serverId] === undefined) {
|
||||
state.channels[channel.serverId] = {}
|
||||
state.channels[channel.serverId] = {};
|
||||
}
|
||||
|
||||
state.channels[channel.serverId][channel.id] = channel
|
||||
state.channels[channel.serverId][channel.id] = channel;
|
||||
}),
|
||||
addChannels: (channels) => set((state) => {
|
||||
addChannels: (channels) =>
|
||||
set((state) => {
|
||||
for (const channel of channels) {
|
||||
if (state.channels[channel.serverId] === undefined) {
|
||||
state.channels[channel.serverId] = {}
|
||||
state.channels[channel.serverId] = {};
|
||||
}
|
||||
|
||||
state.channels[channel.serverId][channel.id] = channel
|
||||
state.channels[channel.serverId][channel.id] = channel;
|
||||
}
|
||||
}),
|
||||
removeChannel: (serverId, channelId) => set((state) => { delete state.channels[serverId][channelId] }),
|
||||
removeServer: (serverId) => set((state) => { delete state.channels[serverId] }),
|
||||
})
|
||||
)
|
||||
)
|
||||
removeChannel: (serverId, channelId) =>
|
||||
set((state) => {
|
||||
delete state.channels[serverId][channelId];
|
||||
}),
|
||||
removeServer: (serverId) =>
|
||||
set((state) => {
|
||||
delete state.channels[serverId];
|
||||
}),
|
||||
})),
|
||||
);
|
||||
|
||||
@@ -1,25 +1,30 @@
|
||||
import { create } from 'zustand'
|
||||
import { immer } from 'zustand/middleware/immer'
|
||||
import type { Server, ServerId, Uuid } from '~/lib/api/types'
|
||||
import { create } from "zustand";
|
||||
import { immer } from "zustand/middleware/immer";
|
||||
import type { Server, ServerId, Uuid } from "~/lib/api/types";
|
||||
|
||||
type ServerListStore = {
|
||||
servers: Record<ServerId, Server>
|
||||
addServers: (newServers: Server[]) => void
|
||||
addServer: (server: Server) => void
|
||||
removeServer: (serverId: Uuid) => void
|
||||
}
|
||||
servers: Record<ServerId, Server>;
|
||||
addServers: (newServers: Server[]) => void;
|
||||
addServer: (server: Server) => void;
|
||||
removeServer: (serverId: Uuid) => void;
|
||||
};
|
||||
|
||||
export const useServerListStore = create<ServerListStore>()(
|
||||
immer(
|
||||
(set) => ({
|
||||
servers: {},
|
||||
addServers: (servers: Server[]) => set((state) => {
|
||||
immer((set) => ({
|
||||
servers: {},
|
||||
addServers: (servers: Server[]) =>
|
||||
set((state) => {
|
||||
for (const server of servers) {
|
||||
state.servers[server.id] = server
|
||||
state.servers[server.id] = server;
|
||||
}
|
||||
}),
|
||||
addServer: (server: Server) => set((state) => { state.servers[server.id] = server }),
|
||||
removeServer: (serverId: Uuid) => set((state) => { delete state.servers[serverId] }),
|
||||
})
|
||||
)
|
||||
)
|
||||
addServer: (server: Server) =>
|
||||
set((state) => {
|
||||
state.servers[server.id] = server;
|
||||
}),
|
||||
removeServer: (serverId: Uuid) =>
|
||||
set((state) => {
|
||||
delete state.servers[serverId];
|
||||
}),
|
||||
})),
|
||||
);
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { create } from 'zustand'
|
||||
import { persist } from 'zustand/middleware'
|
||||
import { create } from "zustand";
|
||||
import { persist } from "zustand/middleware";
|
||||
|
||||
type TokenStore = {
|
||||
token?: string
|
||||
setToken: (token?: string) => void
|
||||
removeToken: () => void
|
||||
}
|
||||
token?: string;
|
||||
setToken: (token?: string) => void;
|
||||
removeToken: () => void;
|
||||
};
|
||||
|
||||
export const useTokenStore = create<TokenStore>()(
|
||||
persist(
|
||||
@@ -15,7 +15,7 @@ export const useTokenStore = create<TokenStore>()(
|
||||
removeToken: () => set({ token: undefined }),
|
||||
}),
|
||||
{
|
||||
name: 'token',
|
||||
name: "token",
|
||||
},
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
@@ -1,92 +1,78 @@
|
||||
import { useQuery } from "@tanstack/react-query"
|
||||
import { create as batshitCreate, keyResolver } from "@yornaath/batshit"
|
||||
import { create } from "zustand"
|
||||
import { immer } from "zustand/middleware/immer"
|
||||
import { getUser } from "~/lib/api/client/user"
|
||||
import type { FullUser, PartialUser, UserId } from "~/lib/api/types"
|
||||
import { create as batshitCreate, keyResolver } from "@yornaath/batshit";
|
||||
import { create } from "zustand";
|
||||
import { immer } from "zustand/middleware/immer";
|
||||
import { getUser } from "~/lib/api/client/user";
|
||||
import type { FullUser, PartialUser, UserId } from "~/lib/api/types";
|
||||
|
||||
type UsersStore = {
|
||||
users: Record<UserId, PartialUser>
|
||||
currentUserId: UserId | undefined
|
||||
fetchUsersIfNotPresent: (userIds: UserId[]) => Promise<void>
|
||||
addUser: (user: PartialUser) => void
|
||||
removeUser: (userId: UserId) => void
|
||||
setCurrentUserId: (userId: UserId) => void
|
||||
getCurrentUser: () => FullUser | undefined
|
||||
}
|
||||
users: Record<UserId, PartialUser>;
|
||||
currentUserId: UserId | undefined;
|
||||
fetchUsersIfNotPresent: (userIds: UserId[]) => Promise<void>;
|
||||
addUser: (user: PartialUser) => void;
|
||||
removeUser: (userId: UserId) => void;
|
||||
setCurrentUserId: (userId: UserId) => void;
|
||||
getCurrentUser: () => FullUser | undefined;
|
||||
};
|
||||
|
||||
const usersFetcher = batshitCreate({
|
||||
fetcher: async (userIds: UserId[]) => {
|
||||
let users = []
|
||||
let users = [];
|
||||
|
||||
for (const userId of userIds) {
|
||||
users.push(getUser(userId))
|
||||
users.push(getUser(userId));
|
||||
}
|
||||
|
||||
return await Promise.all(users)
|
||||
return await Promise.all(users);
|
||||
},
|
||||
resolver: keyResolver("id")
|
||||
})
|
||||
|
||||
export const useUserQuery = (userId: UserId) => useQuery(
|
||||
{
|
||||
queryKey: ["users", userId],
|
||||
queryFn: async () => {
|
||||
const user = await getUser(userId)
|
||||
return user
|
||||
},
|
||||
select: (data) => {
|
||||
useUsersStore.getState().addUser(data)
|
||||
return data
|
||||
}
|
||||
}
|
||||
)
|
||||
resolver: keyResolver("id"),
|
||||
});
|
||||
|
||||
export const useUsersStore = create<UsersStore>()(
|
||||
immer(
|
||||
(set, get) => ({
|
||||
users: {},
|
||||
currentUserId: undefined,
|
||||
fetchUsersIfNotPresent: async (userIds) => {
|
||||
let userPromises: Promise<PartialUser>[] = []
|
||||
for (const userId of userIds) {
|
||||
const user = get().users[userId]
|
||||
if (!user) {
|
||||
userPromises.push(usersFetcher.fetch(userId))
|
||||
}
|
||||
immer((set, get) => ({
|
||||
users: {},
|
||||
currentUserId: undefined,
|
||||
fetchUsersIfNotPresent: async (userIds) => {
|
||||
let userPromises: Promise<PartialUser>[] = [];
|
||||
for (const userId of userIds) {
|
||||
const user = get().users[userId];
|
||||
if (!user) {
|
||||
userPromises.push(usersFetcher.fetch(userId));
|
||||
}
|
||||
}
|
||||
|
||||
const users = await Promise.all(userPromises)
|
||||
const activeUsers = users.filter(Boolean)
|
||||
const users = await Promise.all(userPromises);
|
||||
const activeUsers = users.filter(Boolean);
|
||||
|
||||
set((state) => {
|
||||
for (const user of activeUsers) {
|
||||
if (user?.id)
|
||||
state.users[user.id] = user
|
||||
}
|
||||
|
||||
})
|
||||
},
|
||||
addUser: (user) => set((state) => {
|
||||
set((state) => {
|
||||
for (const user of activeUsers) {
|
||||
if (user?.id) state.users[user.id] = user;
|
||||
}
|
||||
});
|
||||
},
|
||||
addUser: (user) =>
|
||||
set((state) => {
|
||||
if (user.id !== get().currentUserId)
|
||||
state.users[user.id] = user
|
||||
state.users[user.id] = user;
|
||||
else {
|
||||
const currentUser = get().users[user.id]
|
||||
const currentUser = get().users[user.id];
|
||||
if (currentUser)
|
||||
state.users[user.id] = { ...currentUser, ...user }
|
||||
else
|
||||
state.users[user.id] = user
|
||||
state.users[user.id] = { ...currentUser, ...user };
|
||||
else state.users[user.id] = user;
|
||||
}
|
||||
}),
|
||||
removeUser: (userId) => set((state) => {
|
||||
delete state.users[userId]
|
||||
removeUser: (userId) =>
|
||||
set((state) => {
|
||||
delete state.users[userId];
|
||||
}),
|
||||
|
||||
setCurrentUserId: (userId) => set((state) => {
|
||||
state.currentUserId = userId
|
||||
setCurrentUserId: (userId) =>
|
||||
set((state) => {
|
||||
state.currentUserId = userId;
|
||||
}),
|
||||
|
||||
getCurrentUser: () => !!get().currentUserId ? get().users[get().currentUserId!] as FullUser : undefined
|
||||
}),
|
||||
)
|
||||
)
|
||||
getCurrentUser: () =>
|
||||
!!get().currentUserId
|
||||
? (get().users[get().currentUserId!] as FullUser)
|
||||
: undefined,
|
||||
})),
|
||||
);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { create } from 'zustand';
|
||||
import { useWebRTCStore } from './webrtc-store';
|
||||
import { create } from "zustand";
|
||||
import { useWebRTCStore } from "./webrtc-store";
|
||||
|
||||
interface VoiceState {
|
||||
activeChannel: { serverId: string; channelId: string } | null;
|
||||
@@ -20,7 +20,7 @@ export const useVoiceStateStore = create<VoiceState>()((set, get) => {
|
||||
joinVoiceChannel: (serverId, channelId) => {
|
||||
set({
|
||||
activeChannel: { serverId, channelId },
|
||||
error: null
|
||||
error: null,
|
||||
});
|
||||
},
|
||||
|
||||
@@ -41,6 +41,6 @@ export const useVoiceStateStore = create<VoiceState>()((set, get) => {
|
||||
|
||||
resetError: () => {
|
||||
set({ error: null });
|
||||
}
|
||||
},
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { create } from 'zustand';
|
||||
import { WebRTCClient } from '~/lib/websocket/voice/client';
|
||||
import { ConnectionState } from '~/lib/websocket/voice/types';
|
||||
import { useVoiceStateStore } from './voice-state-store';
|
||||
import { create } from "zustand";
|
||||
import { WebRTCClient } from "~/lib/websocket/voice/client";
|
||||
import { ConnectionState } from "~/lib/websocket/voice/types";
|
||||
import { useVoiceStateStore } from "./voice-state-store";
|
||||
|
||||
const VOICE_GATEWAY_URL = 'ws://localhost:12345/voice/ws';
|
||||
const VOICE_GATEWAY_URL = "ws://localhost:12345/voice/ws";
|
||||
|
||||
interface WebRTCState {
|
||||
client: WebRTCClient | null;
|
||||
@@ -22,11 +22,11 @@ export const useWebRTCStore = create<WebRTCState>()((set, get) => {
|
||||
(error) => {
|
||||
set({
|
||||
status: ConnectionState.ERROR,
|
||||
error: error.message
|
||||
error: error.message,
|
||||
});
|
||||
useVoiceStateStore.getState().setError(error.message);
|
||||
},
|
||||
(stream) => set({ remoteStream: stream })
|
||||
(stream) => set({ remoteStream: stream }),
|
||||
);
|
||||
|
||||
return {
|
||||
@@ -43,12 +43,12 @@ export const useWebRTCStore = create<WebRTCState>()((set, get) => {
|
||||
client.disconnect();
|
||||
set({
|
||||
status: ConnectionState.DISCONNECTED,
|
||||
remoteStream: null
|
||||
remoteStream: null,
|
||||
});
|
||||
},
|
||||
|
||||
createOffer: async (localStream) => {
|
||||
await client.createOffer(localStream);
|
||||
}
|
||||
},
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user