.
This commit is contained in:
@@ -14,11 +14,11 @@ interface AppLayoutProps {
|
||||
}
|
||||
|
||||
export default function AppLayout({ children }: AppLayoutProps) {
|
||||
let servers = Object.values(useServerListStore(useShallow((state) => state.servers)));
|
||||
const servers = Object.values(useServerListStore(useShallow((state) => state.servers)));
|
||||
|
||||
const matches = useMatches();
|
||||
|
||||
let list = React.useMemo(() => {
|
||||
const list = React.useMemo(() => {
|
||||
return matches
|
||||
.map(
|
||||
(match) =>
|
||||
@@ -46,7 +46,7 @@ export default function AppLayout({ children }: AppLayoutProps) {
|
||||
<aside className="flex flex-col gap-2 p-2 h-full">
|
||||
<HomeButton />
|
||||
<Separator />
|
||||
{servers.map((server, _) => (
|
||||
{servers.map((server) => (
|
||||
<React.Fragment key={server.id}>
|
||||
<ServerButton server={server} />
|
||||
</React.Fragment>
|
||||
|
||||
@@ -18,14 +18,13 @@ export default function ChannelArea({ channel }: ChannelAreaProps) {
|
||||
);
|
||||
};
|
||||
|
||||
const { data, error, fetchNextPage, hasNextPage, isFetching, isFetchingNextPage, isPending, status } =
|
||||
useInfiniteQuery({
|
||||
queryKey: ["messages", channelId],
|
||||
initialPageParam: undefined,
|
||||
queryFn: fetchMessages,
|
||||
getNextPageParam: (lastPage) => (lastPage.length < 50 ? undefined : lastPage[lastPage.length - 1]?.id),
|
||||
staleTime: Infinity,
|
||||
});
|
||||
const { data, fetchNextPage, hasNextPage, isFetchingNextPage, isPending, status } = useInfiniteQuery({
|
||||
queryKey: ["messages", channelId],
|
||||
initialPageParam: undefined,
|
||||
queryFn: fetchMessages,
|
||||
getNextPageParam: (lastPage) => (lastPage.length < 50 ? undefined : lastPage[lastPage.length - 1]?.id),
|
||||
staleTime: Infinity,
|
||||
});
|
||||
|
||||
const fetchNextPageVisible = () => {
|
||||
if (!isFetchingNextPage && hasNextPage) fetchNextPage();
|
||||
|
||||
@@ -86,7 +86,7 @@ export default function ChatMessage({ message }: ChatMessageProps) {
|
||||
<div className="row-start-2 col-start-2 row-span-1 col-span-1">
|
||||
<div className="wrap-break-word contain-inline-size">{message.content}</div>
|
||||
<div className="grid gap-2 grid-cols-1 md:grid-cols-2 lg:grid-cols-3 justify-start">
|
||||
{message.attachments.map((file, i) => (
|
||||
{message.attachments.map((file) => (
|
||||
<div key={file.id}>
|
||||
<ChatMessageAttachment file={file} />
|
||||
</div>
|
||||
|
||||
@@ -20,7 +20,7 @@ interface ChannelListItemProps {
|
||||
}
|
||||
|
||||
const onDeleteChannel = async (channel: ServerChannel) => {
|
||||
const response = await deleteChannel(channel.serverId, channel.id);
|
||||
await deleteChannel(channel.serverId, channel.id);
|
||||
};
|
||||
|
||||
interface ChannelItemWrapperProps {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { Settings } from "lucide-react";
|
||||
import { useNavigate } from "react-router";
|
||||
import { ModalType, useModalStore } from "~/stores/modal-store";
|
||||
import { useTokenStore } from "~/stores/token-store";
|
||||
import { Button } from "../ui/button";
|
||||
import {
|
||||
@@ -13,13 +12,8 @@ import {
|
||||
|
||||
export function SettingsButton() {
|
||||
const setToken = useTokenStore((state) => state.setToken);
|
||||
const onOpen = useModalStore((state) => state.onOpen);
|
||||
const navigate = useNavigate();
|
||||
|
||||
const onUpdateProfile = () => {
|
||||
onOpen(ModalType.UPDATE_PROFILE);
|
||||
};
|
||||
|
||||
const onOpenSettings = () => {
|
||||
navigate("/app/settings");
|
||||
};
|
||||
@@ -33,7 +27,7 @@ export function SettingsButton() {
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="ghost" size="icon">
|
||||
<Settings className="size-5 m-1.5" />
|
||||
<Settings className="size-5" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { PhoneMissed, Signal } from "lucide-react";
|
||||
import { useShallow } from "zustand/react/shallow";
|
||||
import { useServerChannelsStore } from "~/stores/server-channels-store";
|
||||
import { useServerListStore } from "~/stores/server-list-store";
|
||||
import { useUsersStore } from "~/stores/users-store";
|
||||
@@ -11,14 +12,14 @@ import { OnlineStatus } from "./online-status";
|
||||
import { SettingsButton } from "./settings-button";
|
||||
|
||||
function VoiceStatus({ voiceState }: { voiceState: { serverId: string; channelId: string } }) {
|
||||
// const webrtcState = useWebRTCStore(state => state.status)
|
||||
|
||||
const leaveVoiceChannel = () => {
|
||||
useVoiceStateStore.getState().leaveVoiceChannel();
|
||||
};
|
||||
|
||||
const channel = useServerChannelsStore((state) => state.channels[voiceState.serverId]?.[voiceState.channelId]);
|
||||
const server = useServerListStore((state) => state.servers[voiceState.serverId]);
|
||||
const channel = useServerChannelsStore(
|
||||
useShallow((state) => state.channels[voiceState.serverId]?.[voiceState.channelId]),
|
||||
);
|
||||
const server = useServerListStore(useShallow((state) => state.servers[voiceState.serverId]));
|
||||
|
||||
return (
|
||||
<div className="gap-1 flex justify-between items-center ">
|
||||
@@ -29,8 +30,8 @@ function VoiceStatus({ voiceState }: { voiceState: { serverId: string; channelId
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Button variant="secondary" size="none" onClick={leaveVoiceChannel}>
|
||||
<PhoneMissed className="size-5 m-1.5" />
|
||||
<Button variant="destructive" size="icon" onClick={leaveVoiceChannel}>
|
||||
<PhoneMissed className="size-5" />
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -7,7 +7,7 @@ import { useTokenStore } from "~/stores/token-store";
|
||||
export function GatewayWebSocketConnectionManager() {
|
||||
const token = useTokenStore((state) => state.token);
|
||||
|
||||
const { setQueryClient } = useGatewayStore();
|
||||
const setQueryClient = useGatewayStore((state) => state.setQueryClient);
|
||||
|
||||
const queryClient = useQueryClient();
|
||||
useEffect(() => {
|
||||
@@ -17,7 +17,7 @@ export function GatewayWebSocketConnectionManager() {
|
||||
useEffect(() => {
|
||||
const { status, connect, disconnect } = useGatewayStore.getState();
|
||||
|
||||
if (!!token) {
|
||||
if (token) {
|
||||
connect(token);
|
||||
} else {
|
||||
if (status === ConnectionState.CONNECTED) {
|
||||
|
||||
@@ -35,7 +35,7 @@ export function WebRTCConnectionManager() {
|
||||
voiceState.leaveVoiceChannel();
|
||||
unsubscribe();
|
||||
};
|
||||
}, []);
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (webrtc.status === ConnectionState.DISCONNECTED) {
|
||||
|
||||
@@ -27,7 +27,7 @@ export default function CreateServerChannelModal() {
|
||||
|
||||
const isModalOpen = type === ModalType.CREATE_SERVER_CHANNEL && isOpen;
|
||||
|
||||
let form = useForm<z.infer<typeof schema>>({
|
||||
const form = useForm<z.infer<typeof schema>>({
|
||||
resolver: zodResolver(schema),
|
||||
defaultValues: {
|
||||
type: ChannelType.SERVER_TEXT,
|
||||
@@ -40,7 +40,7 @@ export default function CreateServerChannelModal() {
|
||||
};
|
||||
|
||||
const onSubmit = async (values: z.infer<typeof schema>) => {
|
||||
const response = await import("~/lib/api/client/server").then((m) =>
|
||||
await import("~/lib/api/client/server").then((m) =>
|
||||
m.default.createChannel((data as CreateServerChannelModalData["data"]).serverId, values),
|
||||
);
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ export default function CreateServerInviteModal() {
|
||||
const isModalOpen = type === ModalType.CREATE_SERVER_INVITE && isOpen;
|
||||
const inviteLink = `${origin}/app/invite/${inviteCode}`;
|
||||
|
||||
const onOpenChange = (openState: boolean) => {
|
||||
const onOpenChange = () => {
|
||||
onClose();
|
||||
};
|
||||
|
||||
|
||||
@@ -28,11 +28,11 @@ export default function CreateServerModal() {
|
||||
|
||||
const isModalOpen = type === ModalType.CREATE_SERVER && isOpen;
|
||||
|
||||
let form = useForm<z.infer<typeof schema>>({
|
||||
const form = useForm<z.infer<typeof schema>>({
|
||||
resolver: zodResolver(schema),
|
||||
});
|
||||
|
||||
const onOpenChange = (openState: boolean) => {
|
||||
const onOpenChange = () => {
|
||||
form.reset();
|
||||
onClose();
|
||||
};
|
||||
@@ -43,7 +43,7 @@ export default function CreateServerModal() {
|
||||
iconId = (await file.uploadFile(values.icon))[0];
|
||||
}
|
||||
|
||||
const response = await server.create({
|
||||
await server.create({
|
||||
name: values.name,
|
||||
iconId,
|
||||
});
|
||||
|
||||
@@ -31,7 +31,7 @@ export default function UpdateProfileModal() {
|
||||
|
||||
const isModalOpen = type === ModalType.UPDATE_PROFILE && isOpen;
|
||||
|
||||
let form = useForm<z.infer<typeof schema>>({
|
||||
const form = useForm<z.infer<typeof schema>>({
|
||||
resolver: zodResolver(schema),
|
||||
});
|
||||
|
||||
@@ -50,7 +50,7 @@ export default function UpdateProfileModal() {
|
||||
avatarId = (await file.uploadFile(values.avatar))[0];
|
||||
}
|
||||
|
||||
const response = await patchUser({
|
||||
await patchUser({
|
||||
displayName: values.displayName,
|
||||
avatarId,
|
||||
});
|
||||
|
||||
@@ -17,8 +17,8 @@ export function ThemeToggle() {
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="outline" size="icon">
|
||||
<Sun className="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
|
||||
<Moon className="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
|
||||
<Sun className="size-5 rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
|
||||
<Moon className="absolute size-5 rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
|
||||
<span className="sr-only">Toggle theme</span>
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
|
||||
@@ -24,7 +24,7 @@ export const useFetchUser = (userId: UserId) => {
|
||||
|
||||
export const useFetchUsers = (userIds: UserId[]) => {
|
||||
const query = useQuery({
|
||||
queryKey: ["users", userIds],
|
||||
queryKey: ["users", ...userIds],
|
||||
queryFn: async () => {
|
||||
const userIdsToFetch = userIds.filter((userId) => !useUsersStore.getState().users[userId]);
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ export async function paginatedMessages(channelId: ChannelId, limit: number, bef
|
||||
},
|
||||
});
|
||||
|
||||
return (response.data as any[]).map((value, _) => messageSchema.parse(value));
|
||||
return (response.data as unknown[]).map((value) => messageSchema.parse(value));
|
||||
}
|
||||
|
||||
export async function sendMessage(channelId: ChannelId, content: string, attachments?: Uuid[]) {
|
||||
|
||||
@@ -44,7 +44,7 @@ export interface FullUser {
|
||||
email: string;
|
||||
bot: boolean;
|
||||
system: boolean;
|
||||
settings: any;
|
||||
settings: unknown;
|
||||
}
|
||||
|
||||
export interface Server {
|
||||
|
||||
@@ -23,14 +23,14 @@ export function formatFileSize(bytes: number, decimals = 2): string {
|
||||
}
|
||||
|
||||
export function createPrefixedLogger(prefix: string, styles?: string[]) {
|
||||
const result: Record<string, (...args: any[]) => void> = {};
|
||||
const result: Record<string, (...args: unknown[]) => void> = {};
|
||||
|
||||
const methods = ["log", "trace", "debug", "info", "warn", "error"] as const;
|
||||
|
||||
for (const methodName of methods) {
|
||||
const originalMethod = console[methodName].bind(console);
|
||||
|
||||
result[methodName] = (...args: any[]) => {
|
||||
result[methodName] = (...args: unknown[]) => {
|
||||
if (typeof args[0] === "string") {
|
||||
originalMethod(`${prefix} ${args[0]}`, ...(styles || []), ...args.slice(1));
|
||||
} else {
|
||||
|
||||
@@ -142,7 +142,7 @@ export class GatewayClient {
|
||||
this.socket.onerror = this.onSocketError.bind(this);
|
||||
this.socket.onclose = this.onSocketClose.bind(this);
|
||||
} catch (error) {
|
||||
this.emitError(new Error("Failed to create WebSocket connection"));
|
||||
this.emitError(new Error("Failed to create WebSocket connection", { cause: error }));
|
||||
this.setState(ConnectionState.ERROR);
|
||||
}
|
||||
}
|
||||
@@ -242,6 +242,7 @@ export class GatewayClient {
|
||||
}
|
||||
|
||||
private handleEventMessage(event: EventData): void {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
this.emitEvent(event.type, event.data as any);
|
||||
}
|
||||
|
||||
@@ -274,6 +275,7 @@ export class GatewayClient {
|
||||
private emitControl<K extends keyof ControlEvents>(event: K, ...args: Parameters<ControlEvents[K]>): void {
|
||||
const handler = this.eventHandlers[event];
|
||||
if (handler) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
||||
(handler as Function)(...args);
|
||||
}
|
||||
}
|
||||
@@ -281,6 +283,7 @@ export class GatewayClient {
|
||||
private emitEvent<K extends keyof GatewayEvents>(event: K, ...args: Parameters<GatewayEvents[K]>): void {
|
||||
const handler = this.serverEventHandlers[event];
|
||||
if (handler) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
||||
(handler as Function)(...args);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
import type { ChannelId, Message, MessageId, PartialUser, Server, ServerId, UserId } from "~/lib/api/types";
|
||||
|
||||
type Channel = any; // TODO: Define Channel type
|
||||
import type {
|
||||
Channel,
|
||||
ChannelId,
|
||||
Message,
|
||||
MessageId,
|
||||
PartialUser,
|
||||
Server,
|
||||
ServerChannel,
|
||||
ServerId,
|
||||
UserId,
|
||||
} from "~/lib/api/types";
|
||||
|
||||
export enum ServerMessageType {
|
||||
AUTHENTICATE_ACCEPTED = "AUTHENTICATE_ACCEPTED",
|
||||
@@ -111,7 +119,7 @@ export interface AddDmChannelEvent {
|
||||
type: EventType.ADD_DM_CHANNEL;
|
||||
data: {
|
||||
channel: Channel;
|
||||
recipients: PartialUser[];
|
||||
recipients: UserId[];
|
||||
};
|
||||
}
|
||||
|
||||
@@ -125,7 +133,7 @@ export interface RemoveDmChannelEvent {
|
||||
export interface AddServerChannelEvent {
|
||||
type: EventType.ADD_SERVER_CHANNEL;
|
||||
data: {
|
||||
channel: Channel;
|
||||
channel: ServerChannel;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ export class WebRTCClient {
|
||||
this.socket.onmessage = this.handleServerMessage;
|
||||
|
||||
this.socket.onerror = (event) => {
|
||||
this.handleError(new Error("WebSocket error occurred"));
|
||||
this.handleError(new Error("WebSocket error occurred", { cause: event }));
|
||||
};
|
||||
|
||||
this.socket.onclose = (e) => {
|
||||
@@ -264,4 +264,4 @@ export class WebRTCClient {
|
||||
};
|
||||
}
|
||||
|
||||
const { log, warn, ...other } = createPrefixedLogger("%cWebRTC WS%c:", ["color: blue; font-weight: bold;", ""]);
|
||||
const { log, warn } = createPrefixedLogger("%cWebRTC WS%c:", ["color: blue; font-weight: bold;", ""]);
|
||||
|
||||
@@ -8,7 +8,7 @@ export async function clientLoader({ params }: Route.ClientLoaderArgs) {
|
||||
const response = await import("~/lib/api/client/server").then((m) => m.default.getInvite(inviteCode));
|
||||
|
||||
return redirect(`/app/server/${response.id}`);
|
||||
} catch (error) {
|
||||
} catch {
|
||||
return redirect("/app/@me");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import { Check } from "lucide-react";
|
||||
import { useShallow } from "zustand/react/shallow";
|
||||
import ChannelArea from "~/components/channel-area";
|
||||
import { Badge } from "~/components/ui/badge";
|
||||
import { useFetchUsers } from "~/hooks/use-fetch-user";
|
||||
import { usePrivateChannelsStore } from "~/stores/private-channels-store";
|
||||
import { useUsersStore } from "~/stores/users-store";
|
||||
|
||||
@@ -12,18 +13,26 @@ export default function Channel({ params }: Route.ComponentProps) {
|
||||
|
||||
const nativeChannel = usePrivateChannelsStore(useShallow((state) => state.channels[channelId]));
|
||||
|
||||
const recipients = nativeChannel?.recipients?.filter((recipient) => recipient !== currentUserId) || [];
|
||||
|
||||
useFetchUsers(recipients);
|
||||
|
||||
const recipientsUsers =
|
||||
useUsersStore(useShallow((state) => recipients.map((recipient) => state.users[recipient]).filter(Boolean))) ||
|
||||
[];
|
||||
|
||||
if (!nativeChannel) return null;
|
||||
|
||||
const recipients = nativeChannel.recipients.filter((recipient) => recipient.id !== currentUserId);
|
||||
|
||||
const renderSystemBadge = recipients.some((recipient) => recipient.system) && recipients.length === 1;
|
||||
const renderSystemBadge = recipientsUsers.some((recipient) => recipient.system) && recipients.length === 1;
|
||||
|
||||
const channel = {
|
||||
...nativeChannel,
|
||||
name: (
|
||||
<>
|
||||
<div className="flex items-center gap-2">
|
||||
<div>{recipients.map((recipient) => recipient.displayName || recipient.username).join(", ")}</div>
|
||||
<div>
|
||||
{recipientsUsers.map((recipient) => recipient.displayName || recipient.username).join(", ")}
|
||||
</div>
|
||||
{renderSystemBadge && (
|
||||
<Badge variant="default">
|
||||
{" "}
|
||||
|
||||
@@ -104,7 +104,7 @@ function ListComponent() {
|
||||
<div className="p-2 flex flex-col gap-1 h-full">
|
||||
{channels
|
||||
.sort((a, b) => ((a.lastMessageId ?? a.id) < (b.lastMessageId ?? b.id) ? 1 : -1))
|
||||
.map((channel, _) => (
|
||||
.map((channel) => (
|
||||
<React.Fragment key={channel.id}>
|
||||
<ServerChannelListItem channel={channel} />
|
||||
</React.Fragment>
|
||||
|
||||
@@ -37,7 +37,7 @@ export default function Settings() {
|
||||
avatarId = (await file.uploadFile(values.avatar))[0];
|
||||
}
|
||||
|
||||
const response = await patchUser({
|
||||
await patchUser({
|
||||
displayName:
|
||||
values.displayName === user?.displayName
|
||||
? undefined
|
||||
|
||||
@@ -38,8 +38,8 @@ export async function clientLoader() {
|
||||
}
|
||||
|
||||
export default function Login() {
|
||||
let navigate = useNavigate();
|
||||
let setToken = useTokenStore((state) => state.setToken);
|
||||
const navigate = useNavigate();
|
||||
const setToken = useTokenStore((state) => state.setToken);
|
||||
const { setCurrentUserId, addUser } = useUsersStore(
|
||||
useShallow((state) => {
|
||||
return {
|
||||
@@ -126,7 +126,7 @@ export default function Login() {
|
||||
</CardContent>
|
||||
<CardFooter style={{ viewTransitionName: "auth-card-footer-view" }}>
|
||||
<div className="flex items-center">
|
||||
<span className="text-muted-foreground text-sm">Don't have an account?</span>
|
||||
<span className="text-muted-foreground text-sm">Don't have an account?</span>
|
||||
<Link
|
||||
className={buttonVariants({
|
||||
variant: "link",
|
||||
|
||||
@@ -21,7 +21,7 @@ const schema = z.object({
|
||||
});
|
||||
|
||||
export default function Register() {
|
||||
let navigate = useNavigate();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const form = useForm<z.infer<typeof schema>>({
|
||||
resolver: zodResolver(schema),
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { redirect } from "react-router";
|
||||
import type { Route } from "./+types/index";
|
||||
|
||||
export function meta({}: Route.MetaArgs) {
|
||||
export function meta() {
|
||||
return [{ title: "New React Router App" }];
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ interface ChannelsVoiceState {
|
||||
}
|
||||
|
||||
export const useChannelsVoiceStateStore = create<ChannelsVoiceState>()(
|
||||
immer((set, get) => ({
|
||||
immer((set) => ({
|
||||
channels: {},
|
||||
addUser: (channelId, userId, userVoiceState) =>
|
||||
set((state) => {
|
||||
|
||||
@@ -109,9 +109,9 @@ const HANDLERS = {
|
||||
data: Extract<EventData, { type: EventType.REMOVE_MESSAGE }>["data"],
|
||||
) => {
|
||||
if (self.queryClient) {
|
||||
self.queryClient.setQueryData(["messages", data.channelId], (oldData: any) => {
|
||||
self.queryClient.setQueryData(["messages", data.channelId], (oldData: Message[]) => {
|
||||
if (!oldData) return [];
|
||||
return oldData.filter((message: any) => message.id !== data.messageId);
|
||||
return oldData.filter((message: Message) => message.id !== data.messageId);
|
||||
});
|
||||
}
|
||||
},
|
||||
@@ -159,6 +159,7 @@ export const useGatewayStore = create<GatewayState>()((set, get) => {
|
||||
});
|
||||
|
||||
for (const [type, handler] of Object.entries(HANDLERS)) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
client.onEvent(type, (data: any) => {
|
||||
handler(get(), data);
|
||||
});
|
||||
|
||||
@@ -12,7 +12,7 @@ type ServerChannelsStore = {
|
||||
};
|
||||
|
||||
export const useServerChannelsStore = create<ServerChannelsStore>()(
|
||||
immer((set, get) => ({
|
||||
immer((set) => ({
|
||||
channels: {},
|
||||
addServer: (serverId) =>
|
||||
set((state) => {
|
||||
|
||||
@@ -9,7 +9,7 @@ type TokenStore = {
|
||||
|
||||
export const useTokenStore = create<TokenStore>()(
|
||||
persist(
|
||||
(set, get) => ({
|
||||
(set) => ({
|
||||
token: undefined,
|
||||
setToken: (token?: string) => set({ token }),
|
||||
removeToken: () => set({ token: undefined }),
|
||||
|
||||
@@ -16,7 +16,7 @@ type UsersStore = {
|
||||
|
||||
const usersFetcher = batshitCreate({
|
||||
fetcher: async (userIds: UserId[]) => {
|
||||
let users = [];
|
||||
const users = [];
|
||||
|
||||
for (const userId of userIds) {
|
||||
users.push(getUser(userId));
|
||||
@@ -32,7 +32,7 @@ export const useUsersStore = create<UsersStore>()(
|
||||
users: {},
|
||||
currentUserId: undefined,
|
||||
fetchUsersIfNotPresent: async (userIds) => {
|
||||
let userPromises: Promise<PartialUser>[] = [];
|
||||
const userPromises: Promise<PartialUser>[] = [];
|
||||
for (const userId of userIds) {
|
||||
const user = get().users[userId];
|
||||
if (!user) {
|
||||
@@ -68,6 +68,6 @@ export const useUsersStore = create<UsersStore>()(
|
||||
state.currentUserId = userId;
|
||||
}),
|
||||
|
||||
getCurrentUser: () => (!!get().currentUserId ? (get().users[get().currentUserId!] as FullUser) : undefined),
|
||||
getCurrentUser: () => (get().currentUserId ? (get().users[get().currentUserId!] as FullUser) : undefined),
|
||||
})),
|
||||
);
|
||||
|
||||
@@ -15,7 +15,7 @@ interface WebRTCState {
|
||||
createOffer: (localStream: MediaStream) => Promise<void>;
|
||||
}
|
||||
|
||||
export const useWebRTCStore = create<WebRTCState>()((set, get) => {
|
||||
export const useWebRTCStore = create<WebRTCState>()((set) => {
|
||||
const client = new WebRTCClient(
|
||||
VOICE_GATEWAY_URL,
|
||||
(state) => set({ status: state }),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import js from "@eslint/js";
|
||||
import eslintConfigPrettier from "eslint-config-prettier/flat";
|
||||
import pluginReact from "eslint-plugin-react";
|
||||
import reactCompiler from "eslint-plugin-react-compiler";
|
||||
import reactHooks from "eslint-plugin-react-hooks";
|
||||
import { defineConfig } from "eslint/config";
|
||||
import globals from "globals";
|
||||
@@ -16,8 +16,14 @@ export default defineConfig([
|
||||
files: ["**/*.{js,mjs,cjs,ts,mts,cts,jsx,tsx}"],
|
||||
languageOptions: { globals: globals.browser },
|
||||
},
|
||||
tseslint.configs.recommended,
|
||||
pluginReact.configs.recommended,
|
||||
reactHooks.configs.recommended,
|
||||
eslintConfigPrettier,
|
||||
...tseslint.configs.recommended,
|
||||
pluginReact.configs.flat.recommended,
|
||||
reactHooks.configs["recommended-latest"],
|
||||
reactCompiler.configs.recommended,
|
||||
{
|
||||
files: ["**/*.{js,mjs,cjs,ts,mts,cts,jsx,tsx}"],
|
||||
rules: {
|
||||
"react/react-in-jsx-scope": "off",
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
Reference in New Issue
Block a user