add provider for chats

This commit is contained in:
Nate Kelley 2025-01-27 14:20:26 -07:00
parent 604bfbdd6c
commit d5ee691de4
No known key found for this signature in database
GPG Key ID: FD90372AB8D98B4F
17 changed files with 197 additions and 42 deletions

Binary file not shown.

View File

@ -1,3 +1,6 @@
import type { FileType } from '../config';
export type BusterCollectionAsset = {
id: string;
type: FileType.COLLECTION;
};

View File

@ -1,8 +1,10 @@
import type { BusterDashboardMetric } from '../../../buster_rest/dashboards';
import type { DashboardConfig } from '../../dashboards';
import type { FileType } from '../config';
export type BusterDashboardAsset = {
id: string;
type: FileType.DASHBOARD;
metrics: BusterDashboardMetric[];
config: DashboardConfig;
created_at: string;

View File

@ -1,3 +1,6 @@
import type { FileType } from '../config';
export type BusterDatasetAsset = {
id: string;
type: FileType.DATASET;
};

View File

@ -1,7 +1,9 @@
import type { BusterChartConfigProps } from '@/components/charts';
import type { FileType } from '../config';
export type BusterMetricAsset = {
id: string;
type: FileType.METRIC;
title: string;
description: string | null;
time_frame: string;

View File

@ -1,3 +1,6 @@
import type { FileType } from '../config';
export type BusterTermAsset = {
id: string;
type: FileType.TERM;
};

View File

@ -1,3 +1,6 @@
import type { FileType } from '../config';
export type BusterValueAsset = {
id: string;
type: FileType.VALUE;
};

View File

@ -1,6 +1,6 @@
import type { BusterChatMessage } from './chatMessageInterfaces';
export interface BusterChat {
export interface IBusterChat {
id: string;
title: string;
is_favorited: boolean;

View File

@ -19,7 +19,7 @@ export type ChatUnsubscribeFromChat = BusterSocketRequestBase<'/chats/unsubscrib
export type ChatGetChatAsset = BusterSocketRequestBase<
'/chats/get/asset',
{ id: string; type: FileType; version_id?: string }
{ chat_id?: string; asset_id: string; type: FileType; version_id?: string }
>;
export type ChatListEmitPayload = BusterSocketRequestBase<

View File

@ -1,6 +1,6 @@
import type { RustApiError } from '../../buster_rest/errors';
import type { BusterChatAsset } from './chatAssetInterfaces';
import type { BusterChat, BusterChatListItem } from './chatInterfaces';
import type { IBusterChat, BusterChatListItem } from './chatInterfaces';
export enum ChatsResponses {
'/chats/list:getChatsList' = '/chats/list:getChatsList',
@ -25,7 +25,7 @@ export type Chat_unsubscribed = {
export type Chat_getChat = {
route: '/chats/get:getChat';
callback: (d: BusterChat) => void;
callback: (d: IBusterChat) => void;
onError?: (d: unknown | RustApiError) => void;
};
@ -39,13 +39,13 @@ export type Chat_getChatAsset = {
export type ChatPost_initializeChat = {
route: '/chats/post:initializeChat';
callback: (d: BusterChat) => void;
callback: (d: IBusterChat) => void;
onError?: (d: unknown | RustApiError) => void;
};
export type ChatPost_generatingTitle = {
route: '/chats/post:generatingTitle';
callback: (d: BusterChat) => void;
callback: (d: IBusterChat) => void;
onError?: (d: unknown | RustApiError) => void;
};

View File

@ -1,3 +1,5 @@
export * from './chatAssetInterfaces';
export * from './chatInterfaces';
export * from './chatRequests';
export * from './chatResponses';
export * from './config';

View File

@ -18,6 +18,7 @@ import {
OrganizationResponsesTypes,
OrganizationsEmits
} from './organizations';
import { ChatEmits, ChatResponseTypes, ChatsResponses } from './chats';
export type BusterSocketRequest =
| ThreadEmits
@ -31,7 +32,8 @@ export type BusterSocketRequest =
| TermsEmits
| PermissionsEmits
| BusterSearchEmits
| OrganizationsEmits;
| OrganizationsEmits
| ChatEmits;
export type BusterSocketResponse =
| ThreadResponseTypes
@ -45,7 +47,8 @@ export type BusterSocketResponse =
| TermsResponseTypes
| PermissionsResponseTypes
| SearchResponseTypes
| OrganizationResponsesTypes;
| OrganizationResponsesTypes
| ChatResponseTypes;
export type BusterSocketResponseRoute =
| keyof typeof ThreadResponses
@ -59,4 +62,5 @@ export type BusterSocketResponseRoute =
| keyof typeof TermsResponses
| keyof typeof PermissionsResponses
| keyof typeof SearchResponses
| keyof typeof OrganizationResponses;
| keyof typeof OrganizationResponses
| keyof typeof ChatsResponses;

View File

@ -24,6 +24,7 @@ import { BusterAssetsProvider } from './Assets/BusterAssetsProvider';
import { BusterUserResponse } from '@/api/buster_rest/users';
import { BusterPosthogProvider } from './Posthog/usePosthog';
import { BusterNotificationsProvider } from './BusterNotifications';
import { BusterChatProvider } from './Chats';
import { RoutePrefetcher } from './RoutePrefetcher';
import { BusterMessageDataProvider } from './MessageData';
@ -66,16 +67,19 @@ export const AppProviders: React.FC<
<BusterCollectionsProvider>
<BusterMessageDataProvider>
<BusterDashboardProvider>
{/* TODO: remove when we are ready to use chats */}
<BusterThreadsProvider>
<BusterSQLProvider>
<BusterTermsProvider>
<BusterPermissionsProvider>
<AppHotKeysProvider>
<BusterPosthogProvider>
{children}
<RoutePrefetcher />
</BusterPosthogProvider>
</AppHotKeysProvider>
<BusterChatProvider>
<AppHotKeysProvider>
<BusterPosthogProvider>
{children}
<RoutePrefetcher />
</BusterPosthogProvider>
</AppHotKeysProvider>
</BusterChatProvider>
</BusterPermissionsProvider>
</BusterTermsProvider>
</BusterSQLProvider>

View File

@ -0,0 +1,151 @@
import React, { useCallback, useRef, useState, useTransition } from 'react';
import {
createContext,
ContextSelector,
useContextSelector
} from '@fluentui/react-context-selector';
import { useBusterWebSocket } from '../BusterWebSocket';
import type { BusterChatAsset, IBusterChat } from '@/api/buster_socket/chats';
import { useMemoizedFn, useMount, useUnmount } from 'ahooks';
import type { FileType } from '@/api/buster_socket/chats';
export const useBusterChat = () => {
const busterSocket = useBusterWebSocket();
const [isPending, startTransition] = useTransition();
const chatsRef = useRef<Record<string, IBusterChat>>({});
const [seletedAssetId, setSeletedAssetId] = useState<Record<string, string | null>>({});
// GETTERS
const getSelectedAssetId = useCallback(
(chatId: string) => {
return seletedAssetId[chatId] || null;
},
[seletedAssetId]
);
// SETTERS
const onSetSelectedAssetId = useMemoizedFn((chatId: string, assetId: string | null) => {
setSeletedAssetId((prev) => ({ ...prev, [chatId]: assetId }));
});
// LISTENERS
const _onGetChat = useMemoizedFn((chat: IBusterChat) => {
chatsRef.current[chat.id] = chat;
startTransition(() => {
//just used to trigger UI update
});
return chat;
});
const _onGetChatAsset = useMemoizedFn((asset: BusterChatAsset) => {
const { id, type } = asset;
console.log('TODO: handle this. Put the asset in their respective chat');
return asset;
});
// EMITTERS
const unsubscribeFromChat = useMemoizedFn(({ chatId }: { chatId: string }) => {
return busterSocket.emit({
route: '/chats/unsubscribe',
payload: {
id: chatId
}
});
});
const subscribeToChat = useMemoizedFn(({ chatId }: { chatId: string }) => {
return busterSocket.emitAndOnce({
emitEvent: {
route: '/chats/get',
payload: {
id: chatId
}
},
responseEvent: {
route: '/chats/get:getChat',
callback: _onGetChat
}
});
});
const getChatAsset = useMemoizedFn(
({
chatId: chat_id,
assetId: asset_id,
type,
versionId: version_id
}: {
chatId?: string;
assetId: string;
type: FileType;
versionId?: string;
}) => {
return busterSocket.emitAndOnce({
emitEvent: {
route: '/chats/get/asset',
payload: {
type,
chat_id,
asset_id,
version_id
}
},
responseEvent: {
route: '/chats/get:getChatAsset',
callback: _onGetChatAsset
}
});
}
);
return {
getSelectedAssetId,
chats: chatsRef.current,
unsubscribeFromChat,
subscribeToChat,
getChatAsset,
onSetSelectedAssetId
};
};
const BusterChat = createContext<ReturnType<typeof useBusterChat>>(
{} as ReturnType<typeof useBusterChat>
);
export const BusterChatProvider: React.FC<{
children: React.ReactNode;
}> = ({ children }) => {
const value = useBusterChat();
return <BusterChat.Provider value={value}>{children}</BusterChat.Provider>;
};
export const useBusterChatContextSelector = <T,>(
selector: ContextSelector<ReturnType<typeof useBusterChat>, T>
) => useContextSelector(BusterChat, selector);
export const useBusterChatIndividual = ({ chatId }: { chatId: string }) => {
const chat = useBusterChatContextSelector((x) => x.chats[chatId]);
const subscribeToChat = useBusterChatContextSelector((x) => x.subscribeToChat);
const unsubscribeFromChat = useBusterChatContextSelector((x) => x.unsubscribeFromChat);
const selectedAssetId = useBusterChatContextSelector((x) => x.getSelectedAssetId(chatId));
const onSetSelectedAssetId = useBusterChatContextSelector((x) => x.onSetSelectedAssetId);
useMount(() => {
subscribeToChat({ chatId });
});
useUnmount(() => {
unsubscribeFromChat({ chatId });
});
return {
chat,
selectedAssetId,
onSetSelectedAssetId
};
};

View File

@ -0,0 +1 @@
export * from './ChatProvider';

View File

@ -1,35 +1,12 @@
import {
BusterDashboard,
BusterDashboardListItem,
BusterDashboardResponse,
BusterMetricDataResponse,
BusterVerificationStatus,
IBusterDashboardMetric
} from '@/api/buster_rest';
import { useParams, useRouter } from 'next/navigation';
import React, {
PropsWithChildren,
useContext,
useEffect,
useLayoutEffect,
useRef,
useState
} from 'react';
import { useBusterWebSocket } from '../BusterWebSocket';
import { useParams } from 'next/navigation';
import React, { PropsWithChildren, useEffect, useLayoutEffect, useState } from 'react';
import { useAppLayoutContextSelector } from '../BusterAppLayout';
import { BusterRoutes, createBusterRoute } from '@/routes';
import { DashboardUpdate, DashboardsListEmitPayload } from '@/api/buster_socket/dashboards';
import isEqual from 'lodash/isEqual';
import { useMemoizedFn, useMount, useUnmount } from 'ahooks';
import { useBusterAssetsContextSelector } from '@/context/Assets/BusterAssetsProvider';
import { upgradeDashboardMetric } from './dashboardContextHelper';
import { useBusterNotifications } from '../BusterNotifications';
import { useUnmount } from 'ahooks';
import {
useContextSelector,
createContext,
ContextSelector
} from '@fluentui/react-context-selector';
import { useBusterMessageDataContextSelector } from '../MessageData';
import { useDashboardLists } from './useDashboardLists';
import { useDashboardIndividual } from './useDashboardIndividual';
import { useDashboardMetrics } from './useDashboardMetrics';

View File

@ -7,7 +7,7 @@ import {
import { BusterNewThreadsProvider } from './BusterNewThreadsProvider';
import { BusterThreadsListProvider } from './BusterThreadsListProvider';
import { defaultIBusterThread } from './config';
import { useDebounceFn, useMemoizedFn, useMount, useWhyDidYouUpdate } from 'ahooks';
import { useDebounceFn, useMemoizedFn, useMount, useUnmount } from 'ahooks';
import type { IBusterThread, IBusterThreadMessage } from './interfaces';
import type { BusterThread, BusterThreadUser, BusterVerificationStatus } from '@/api/buster_rest';
import { useBusterWebSocket } from '../BusterWebSocket';