From 2589754171be1defcdcee1aa9b949c5a5e7e1a50 Mon Sep 17 00:00:00 2001 From: Daniel D Orlando Date: Sun, 2 Apr 2023 12:48:26 -0700 Subject: [PATCH 01/36] feat: add data-provider --- client/src/data-provider/data-service.ts | 81 ++++++++ client/src/data-provider/endpoints.ts | 60 ++++++ client/src/data-provider/headers-helpers.ts | 9 + client/src/data-provider/index.ts | 7 + .../src/data-provider/react-query-service.ts | 172 ++++++++++++++++ client/src/data-provider/request.ts | 66 +++++++ client/src/data-provider/types.ts | 183 ++++++++++++++++++ 7 files changed, 578 insertions(+) create mode 100644 client/src/data-provider/data-service.ts create mode 100644 client/src/data-provider/endpoints.ts create mode 100644 client/src/data-provider/headers-helpers.ts create mode 100644 client/src/data-provider/index.ts create mode 100644 client/src/data-provider/react-query-service.ts create mode 100644 client/src/data-provider/request.ts create mode 100644 client/src/data-provider/types.ts diff --git a/client/src/data-provider/data-service.ts b/client/src/data-provider/data-service.ts new file mode 100644 index 0000000000..621831a0ee --- /dev/null +++ b/client/src/data-provider/data-service.ts @@ -0,0 +1,81 @@ +import * as t from './types'; +import request from './request'; +import * as endpoints from './endpoints'; + +export function getOpenAIModels(): Promise { + return request.get(endpoints.openAiModels()); +} + +export function postAICompletion(payload: t.TAICompletionRequest) { + return request.post(endpoints.getAICompletion(), payload); +} + +export function getConversations(pageNumber: string): Promise { + return request.get(endpoints.getConversations(pageNumber)); +} + +export function deleteConversation(payload: t.TDeleteConversationRequest) { + //todo: this should be a DELETE request + return request.post(endpoints.deleteConversation(), payload); +} + +export function clearAllConversations() { + return request.post(endpoints.deleteConversation()); +} + +export function getMessagesByConvoId(id: string): Promise { + return request.get(endpoints.getMessages(id)); +} + +export function getConversationById(id: string): Promise { + return request.get(endpoints.getConversationById(id)); +} + +export function updateConversation( + payload: t.TUpdateConversationRequest +): Promise { + return request.post(endpoints.updateConversation(), payload); +} + +export function updateCustomGpt(payload: t.TUpdateCustomGptRequest) { + return request.post(endpoints.customGpts(), payload); +} + +export function getCustomGpts(): Promise { + return request.get(endpoints.customGpts()); +} + +export function deleteCustomGpt(payload: t.TDeleteCustomGptRequest): Promise { + return request.post(endpoints.deleteCustomGpt(), payload); +} + +export function getModels(): Promise { + return request.get(endpoints.getModels()); +} + +export function getSearchEnabled(): Promise { + return request.get(endpoints.searchEnabled()); +} + +export function getSearchResults(q: string, pageNumber: string): Promise { + return request.get(endpoints.search(q, pageNumber)); +} + +export function getUser(): Promise { + return request.get(endpoints.user()); +} + +type TSearchFetcherProps = { + pre: () => void, + q: string, + pageNumber: string, + callback: (data: any) => void +}; + +export const searchFetcher = async ({ pre, q, pageNumber, callback }: TSearchFetcherProps) => { + pre(); + //@ts-ignore + const { data } = await request.get(endpoints.search(q, pageNumber)); + console.log('search data', data); + callback(data); +}; diff --git a/client/src/data-provider/endpoints.ts b/client/src/data-provider/endpoints.ts new file mode 100644 index 0000000000..72f2ac608b --- /dev/null +++ b/client/src/data-provider/endpoints.ts @@ -0,0 +1,60 @@ +export const openAiModels = () => { + return `/api/open-ai-models`; +}; + +export const getModels = () => { + return `/api/models`; +}; + +export const getAICompletion = () => { + return `/api/ask `; +}; + +export const user = () => { + return `/api/me`; +}; + +export const getMessages = (id: string) => { + return `/api/messages/${id}`; +}; + +export const getConversations = (pageNumber: string) => { + return `/api/convos?pageNumber=${pageNumber}`; +}; + +export const getConversationById = (id: string) => { + return `/api/convos/${id}`; +}; + +export const updateConversation = () => { + return `/api/convos/update`; +}; + +export const deleteConversation = () => { + return `/api/convos/clear`; +}; + +export const prompts = () => { + return `/api/prompts`; +}; + +export const customGpts = () => { + return `/api/customGpts`; +}; + +// TODO: turn this into a DELETE instead of POST on the backend +export const deleteCustomGpt = () => { + return `/api/customGpts/delete`; +}; + +export const generateTitle = () => { + return `/api/convos/gen_title`; +}; + +export const search = (q: string, pageNumber: string) => { + return `/api/search?q=${q}&pageNumber=${pageNumber}`; +} + +export const searchEnabled = () => { + return `/api/search/enable`; +} diff --git a/client/src/data-provider/headers-helpers.ts b/client/src/data-provider/headers-helpers.ts new file mode 100644 index 0000000000..195f7c7912 --- /dev/null +++ b/client/src/data-provider/headers-helpers.ts @@ -0,0 +1,9 @@ +import axios from 'axios'; + +export function setAcceptLanguageHeader(value: string): void { + axios.defaults.headers.common['Accept-Language'] = value; +} + +export function setTokenHeader(token: string) { + axios.defaults.headers.common['Authorization'] = 'Bearer ' + token; +} diff --git a/client/src/data-provider/index.ts b/client/src/data-provider/index.ts new file mode 100644 index 0000000000..ea66903770 --- /dev/null +++ b/client/src/data-provider/index.ts @@ -0,0 +1,7 @@ +export * from './data-service'; +// export * from './endpoints'; +export * from './request'; +export * from './types'; +export * from './react-query-service'; +export * from './headers-helpers'; +// export * from './events'; \ No newline at end of file diff --git a/client/src/data-provider/react-query-service.ts b/client/src/data-provider/react-query-service.ts new file mode 100644 index 0000000000..fa7b99c3d8 --- /dev/null +++ b/client/src/data-provider/react-query-service.ts @@ -0,0 +1,172 @@ +import { + UseQueryOptions, + useQuery, + useMutation, + useQueryClient, + UseMutationResult, + QueryObserverResult, +} from "@tanstack/react-query"; +import * as t from "./types"; +import * as dataService from "./data-service"; + +export enum QueryKeys { + messages = "messsages", + allConversations = "allConversations", + conversation = "conversation", + models = "models", + customGpts = "customGpts", + searchEnabled = "searchEnabled", + user = "user", +} + +export const useGetUserQuery = (): QueryObserverResult => { + return useQuery([QueryKeys.user], () => dataService.getUser(), { + refetchOnWindowFocus: false, + refetchOnReconnect: false, + refetchOnMount: false, + }); +}; + +export const useGetMessagesByConvoId = ( + id: string, + config?: UseQueryOptions +): QueryObserverResult => { + return useQuery([QueryKeys.messages, id], () => + dataService.getMessagesByConvoId(id), + { + refetchOnWindowFocus: false, + refetchOnReconnect: false, + refetchOnMount: false, + ...config, + } + ); +}; + +export const useGetConversationByIdQuery = ( + id: string, + config?: UseQueryOptions + ): QueryObserverResult => { + return useQuery([QueryKeys.conversation, id], () => + dataService.getConversationById(id), + { + refetchOnWindowFocus: false, + refetchOnReconnect: false, + refetchOnMount: false, + enabled: false, + ...config + } + ); +} + +export const useUpdateConversationMutation = ( + id: string +): UseMutationResult< + t.TUpdateConversationResponse, + unknown, + t.TUpdateConversationRequest, + unknown +> => { + const queryClient = useQueryClient(); + return useMutation( + (payload: t.TUpdateConversationRequest) => + dataService.updateConversation(payload), + { + onSuccess: () => { + queryClient.invalidateQueries([QueryKeys.conversation, id]); + }, + } + ); +}; + +// export const useDeleteConversationMutation = ( +// id: string +// ): UseMutationResult< + + +export const useUpdateCustomGptMutation = (): UseMutationResult< + t.TUpdateCustomGptResponse, + unknown, + t.TUpdateCustomGptRequest, + unknown +> => { + const queryClient = useQueryClient(); + return useMutation( + (payload: t.TUpdateCustomGptRequest) => + dataService.updateCustomGpt(payload), + { + onSuccess: () => { + queryClient.invalidateQueries([QueryKeys.customGpts]); + }, + } + ); +}; + +export const useGetCustomGptsQuery = (): QueryObserverResult< + t.TCustomGpt[], + unknown +> => { + return useQuery([QueryKeys.customGpts], () => dataService.getCustomGpts(), { + refetchOnWindowFocus: false, + refetchOnReconnect: false, + refetchOnMount: false, + }); +}; + +export const useDeleteCustomGptMutation = (): UseMutationResult< + t.TDeleteCustomGptResponse, + unknown, + t.TDeleteCustomGptRequest, + unknown +> => { + const queryClient = useQueryClient(); + return useMutation( + (payload: t.TDeleteCustomGptRequest) => + dataService.deleteCustomGpt(payload), + { + onSuccess: () => { + queryClient.invalidateQueries([QueryKeys.customGpts]); + }, + } + ); +}; + +export const useGetModelsQuery = (): QueryObserverResult< + t.TGetModelsResponse, + unknown +> => { + return useQuery([QueryKeys.models], () => dataService.getModels(), { + refetchOnWindowFocus: false, + refetchOnReconnect: false, + refetchOnMount: false, + }); +}; + +export const useClearConversationsMutation = (): UseMutationResult => { + const queryClient = useQueryClient(); + return useMutation(() => dataService.clearAllConversations(), { + onSuccess: () => { + queryClient.invalidateQueries([QueryKeys.allConversations]); + }, + }); +}; + +export const useGetConversationsQuery = (pageNumber: string): QueryObserverResult => { + return useQuery([QueryKeys.allConversations, pageNumber], () => + dataService.getConversations(pageNumber), { + refetchOnWindowFocus: false, + refetchOnReconnect: false, + refetchOnMount: false, + } + ); +} + +export const useGetSearchEnabledQuery = (config?: UseQueryOptions): QueryObserverResult => { + return useQuery([QueryKeys.searchEnabled], () => + dataService.getSearchEnabled(), { + refetchOnWindowFocus: false, + refetchOnReconnect: false, + refetchOnMount: false, + ...config, + } + ); +} diff --git a/client/src/data-provider/request.ts b/client/src/data-provider/request.ts new file mode 100644 index 0000000000..dcfb818d08 --- /dev/null +++ b/client/src/data-provider/request.ts @@ -0,0 +1,66 @@ +import axios, { AxiosRequestConfig } from "axios"; + +async function _get(url: string, options?: AxiosRequestConfig): Promise { + const response = await axios.get(url, { withCredentials: true, ...options}); + return response.data; +} + +async function _post(url: string, data?: any) { + const response = await axios.post(url, JSON.stringify(data), { + withCredentials: true, + headers: { "Content-Type": "application/json" }, + }); + return response.data; +} + +async function _postMultiPart( + url: string, + formData: FormData, + options?: AxiosRequestConfig +) { + const response = await axios.post(url, formData, { + withCredentials: true, + ...options, + headers: { "Content-Type": "multipart/form-data" }, + }); + return response.data; +} + +async function _put(url: string, data?: any) { + const response = await axios.put(url, JSON.stringify(data), { + withCredentials: true, + headers: { "Content-Type": "application/json" }, + }); + return response.data; +} + +async function _delete(url: string): Promise { + const response = await axios.delete(url, { withCredentials: true }); + return response.data; +} + +async function _deleteWithOptions( + url: string, + options?: AxiosRequestConfig +): Promise { + const response = await axios.delete(url, { withCredentials: true, ...options}); + return response.data; +} + +async function _patch(url: string, data?: any) { + const response = await axios.patch(url, JSON.stringify(data), { + withCredentials: true, + headers: { "Content-Type": "application/json" }, + }); + return response.data; +} + +export default { + get: _get, + post: _post, + postMultiPart: _postMultiPart, + put: _put, + delete: _delete, + deleteWithOptions: _deleteWithOptions, + patch: _patch, +}; diff --git a/client/src/data-provider/types.ts b/client/src/data-provider/types.ts new file mode 100644 index 0000000000..fb60f4e37f --- /dev/null +++ b/client/src/data-provider/types.ts @@ -0,0 +1,183 @@ +export type TMessage = { + messageId: string, + conversationId: string, + conversationSignature: string | null, + clientId: string, + invocationId: string, + parentMessageId: string, + sender: string, + text: string, + isCreatedByUser: boolean, + error: boolean, + createdAt: string, + updatedAt: string, + searchResult: string[], + submitting: boolean, + children?: any[] | undefined, + bgColor?: string, + model?: string, + cancelled?: boolean +}; + +export type TMessageTreeNode = {} + +export type TSearchMessage = {} + +export type TSearchMessageTreeNode = {} + +export type TMessageToAsk = {} + + +export enum EModelEndpoint { + azureOpenAI = 'azureOpenAI', + openAI = 'openAI', + bingAI = 'bingAI', + chatGPTBrowser = 'chatGPTBrowser' +} + +export type TConversation = { + conversationId: string; + title: string; + user: string | null; + endpoint: EModelEndpoint; + model: string; // for azureOpenAI, openAI, chatGPTBrowser only, eg. gpt-3.5-turbo + // for azureOpenAI, openAI only + chatGptLabel?: string; + promptPrefix?: string; + temperature?: number; + top_p?: number; + presence_penalty?: number; + // for bingAI only + jailbreak?: boolean; + jailbreakConversationId?: string; + conversationSignature?: string; + clientId?: string; + invocationId?: string; + toneStyle?: string; + suggestions?: string[]; + messages?: TMessage[]; + createdAt: string; + updatedAt: string; +} + +export type TPrompt = { + title: string, + prompt: string, + category: string, + createdAt: string, + updatedAt: string +}; + +export type TCustomGpt = { + chatGptLabel: string, + promptPrefix: string, + value: string, + createdAt: string, + updatedAt: string, + _id: string +}; + +export type TModel = { + _id: string, + name: string, + value: string, + model: string, + chatGptLabel?: string, + promptPrefix?: string +}; + +export type TUser = { + username: string, + display: string +}; + +export type TGetConversationsResponse = { + conversations: TConversation[], + pageNumber: string, + pageSize: string | number, + pages: string | number +}; + +export type TGetConversationResponse = { + data: TConversation +}; + +export type TGetMessagesResponse = { + data: TMessage[] +}; + +export type TDeleteConversationRequest = { + conversationId: string +}; + +export type TAICompletionRequest = { + chatGptLabel?: string, + conversationId: string, + current: boolean, + isCreatedByUser: boolean, + model: string, + messageId: string, + parentMessageId: string, + overrideParentMessageId?: boolean, + promptPrefix?: string, + sender: string, + text: string +}; + +export type TGetModelsResponse = { + hasOpenAI: boolean, + hasChatGpt: boolean, + hasBing: boolean +}; + +export type TOpenAIModel = { + object: string, + id: string, + ready: boolean, + owner: string, + created: string | null, + permissions: string[] | null +}; + +export type TOpenAIModels = { + models: { + object: string, + data: TOpenAIModel[] + } +}; + +export type TConversationUpdate = { + conversationId: string, + title?: string +}; +export type TUpdateConversationRequest = { + arg: {}, + withCredentials?: boolean +}; + +export type TUpdateConversationResponse = { + data: TConversation +}; + +export type TUpdateCustomGptRequest = { + value: string, + chatGptLabel: string, + promptPrefix?: string, + prevLabel?: string +}; + +export type TUpdateCustomGptResponse = {}; + +export type TDeleteCustomGptRequest = { + id: string +}; + +export type TDeleteCustomGptResponse = {}; + +export type TClearConversationsRequest = {}; + +export type TClearConversationsResponse = {}; + +export type TGetCustomGptsResponse = {}; + +export type TSearchResults = {}; \ No newline at end of file From 2048e34311552c87c0b9860b8fa126c52b6c9ca6 Mon Sep 17 00:00:00 2001 From: Daniel D Orlando Date: Sun, 2 Apr 2023 12:48:52 -0700 Subject: [PATCH 02/36] feat: add new data services to App.jsx --- client/src/App.jsx | 105 +++++++++++++++++++++++---------------------- 1 file changed, 54 insertions(+), 51 deletions(-) diff --git a/client/src/App.jsx b/client/src/App.jsx index cca1de99a8..beb796c1bf 100644 --- a/client/src/App.jsx +++ b/client/src/App.jsx @@ -1,15 +1,12 @@ -import React, { useEffect, useState } from 'react'; +import { useEffect } from 'react'; import { createBrowserRouter, RouterProvider, Navigate } from 'react-router-dom'; import Root from './routes/Root'; import Chat from './routes/Chat'; import Search from './routes/Search'; import store from './store'; -import userAuth from './utils/userAuth'; import { useRecoilState, useSetRecoilState } from 'recoil'; - import { ScreenshotProvider } from './utils/screenshotContext.jsx'; - -import axios from 'axios'; +import { useGetSearchEnabledQuery, useGetUserQuery, useGetModelsQuery, useGetEndpointsQuery, useGetPresetsQuery} from '~/data-provider'; const router = createBrowserRouter([ { @@ -43,58 +40,64 @@ const App = () => { const setEndpointsConfig = useSetRecoilState(store.endpointsConfig); const setPresets = useSetRecoilState(store.presets); + const searchEnabledQuery = useGetSearchEnabledQuery(); + const userQuery = useGetUserQuery(); + const modelsQuery = useGetModelsQuery(); + const endpointsQuery = useGetEndpointsQuery(); + const presetsQuery = useGetPresetsQuery(); + + if(endpointsQuery.data) { + setEndpointsConfig(endpointsQuery.data); + } else if(endpointsQuery.isError) { + console.error("Failed to get endpoints", endpointsQuery.error); + window.location.href = '/auth/login'; + } + + if(presetsQuery.data) { + setPresets(presetsQuery.data); + } else if(presetsQuery.isError) { + console.error("Failed to get presets", presetsQuery.error); + window.location.href = '/auth/login'; + } + useEffect(() => { - // fetch if seatch enabled - axios - .get('/api/search/enable', { - timeout: 1000, - withCredentials: true - }) - .then(res => { - setIsSearchEnabled(res.data); - }); + if (searchEnabledQuery.error) { + console.error("Failed to get search enabled", searchEnabledQuery.error); + } + if (searchEnabledQuery.data) { + setIsSearchEnabled(searchEnabledQuery.data); + } + }, [searchEnabledQuery.data, setIsSearchEnabled, searchEnabledQuery.error]); - // fetch user - userAuth() - .then(user => setUser(user)) - .catch(err => console.log(err)); + useEffect(() => { + if (userQuery.error) { + console.error("Failed to get user", userQuery.error); + } + if (userQuery.data) { + setUser(userQuery.data); + } + }, [userQuery.data, setUser, userQuery.error]); - // fetch models - axios - .get('/api/endpoints', { - timeout: 1000, - withCredentials: true - }) - .then(({ data }) => { - setEndpointsConfig(data); - }) - .catch(error => { - console.error(error); - console.log('Not login!'); - window.location.href = '/auth/login'; - }); - - // fetch presets - axios - .get('/api/presets', { - timeout: 1000, - withCredentials: true - }) - .then(({ data }) => { - setPresets(data); - }) - .catch(error => { - console.error(error); - console.log('Not login!'); - window.location.href = '/auth/login'; - }); - }, []); + useEffect(() => { + const { data, error } = modelsQuery; + if (error) { + console.error("Failed to get models", error); + } + if (data) { + const filter = { + chatgpt: data?.hasOpenAI, + chatgptCustom: data?.hasOpenAI, + bingai: data?.hasBing, + sydney: data?.hasBing, + chatgptBrowser: data?.hasChatGpt + }; + setModelsFilter(filter); + } + }, [modelsQuery.data, setModelsFilter, modelsQuery.error, modelsQuery]); if (user) return ( -
- -
+ ); else return
; }; From ccc2f392e27c23ba66e39605225aa8eaa4702550 Mon Sep 17 00:00:00 2001 From: Daniel D Orlando Date: Sun, 2 Apr 2023 12:49:12 -0700 Subject: [PATCH 03/36] feat: add conversation query to nav --- client/src/components/Nav/index.jsx | 75 ++++++++++------------------- 1 file changed, 26 insertions(+), 49 deletions(-) diff --git a/client/src/components/Nav/index.jsx b/client/src/components/Nav/index.jsx index aa6a7895e1..4bb12082a5 100644 --- a/client/src/components/Nav/index.jsx +++ b/client/src/components/Nav/index.jsx @@ -5,8 +5,9 @@ import Spinner from '../svg/Spinner'; import Pages from '../Conversations/Pages'; import Conversations from '../Conversations'; import NavLinks from './NavLinks'; -import { searchFetcher, swr } from '~/utils/fetchers'; -import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'; +import { searchFetcher } from '~/utils/fetchers'; +import { useRecoilValue, useSetRecoilState } from 'recoil'; +import { useGetConversationsQuery } from '~/data-provider'; import store from '~/store'; @@ -22,6 +23,9 @@ export default function Nav({ navVisible, setNavVisible }) { const [pageNumber, setPageNumber] = useState(1); // total pages const [pages, setPages] = useState(1); + + // data provider + const getConversationsQuery = useGetConversationsQuery(pageNumber); // search const searchQuery = useRecoilValue(store.searchQuery); @@ -34,29 +38,10 @@ export default function Nav({ navVisible, setNavVisible }) { const { conversationId } = conversation || {}; const setSearchResultMessages = useSetRecoilState(store.searchResultMessages); - // refreshConversationsHint is used for other components to ask refresh of Nav - const refreshConversationsHint = useRecoilValue(store.refreshConversationsHint); - const { refreshConversations } = store.useConversations(); const [isFetching, setIsFetching] = useState(false); - const onSuccess = (data, searchFetch = false) => { - if (isSearching) { - return; - } - - let { conversations, pages } = data; - if (pageNumber > pages) { - setPageNumber(pages); - } else { - if (!searchFetch) - conversations = conversations.sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt)); - setConversations(conversations); - setPages(pages); - } - }; - const onSearchSuccess = (data, expectedPage) => { const res = data; setConversations(res.conversations); @@ -85,19 +70,14 @@ export default function Nav({ navVisible, setNavVisible }) { if (conversationId == 'search') { newConversation(); } - // dispatch(setDisabled(false)); }; - const { data, isLoading, mutate } = swr(`/api/convos?pageNumber=${pageNumber}`, onSuccess, { - revalidateOnMount: false - }); - const nextPage = async () => { moveToTop(); if (!isSearching) { setPageNumber(prev => prev + 1); - await mutate(); + await getConversationsQuery.refetch() } else { await fetch(searchQuery, +pageNumber + 1); } @@ -108,17 +88,29 @@ export default function Nav({ navVisible, setNavVisible }) { if (!isSearching) { setPageNumber(prev => prev - 1); - await mutate(); + await getConversationsQuery.refetch() } else { await fetch(searchQuery, +pageNumber - 1); } }; useEffect(() => { - if (!isSearching) { - mutate(); + if (getConversationsQuery.data) { + if (isSearching) { + return; + } + let { conversations, pages } = getConversationsQuery.data; + if (pageNumber > pages) { + setPageNumber(pages); + } else { + if (!isSearching) { + conversations = conversations.sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt)); + } + setConversations(conversations); + setPages(pages); + } } - }, [pageNumber, conversationId, refreshConversationsHint]); + }, [getConversationsQuery.isSuccess, getConversationsQuery.data, isSearching, pageNumber]); const moveToTop = () => { const container = containerRef.current; @@ -127,31 +119,17 @@ export default function Nav({ navVisible, setNavVisible }) { } }; - const moveTo = () => { - const container = containerRef.current; - - if (container && scrollPositionRef.current !== null) { - const { scrollHeight, clientHeight } = container; - const maxScrollTop = scrollHeight - clientHeight; - - container.scrollTop = Math.min(maxScrollTop, scrollPositionRef.current); - } - }; const toggleNavVisible = () => { setNavVisible(prev => !prev); }; - // useEffect(() => { - // moveTo(); - // }, [data]); - useEffect(() => { setNavVisible(false); - }, [conversationId]); + }, [conversationId, setNavVisible]); const containerClasses = - isLoading && pageNumber === 1 + getConversationsQuery.isLoading && pageNumber === 1 ? 'flex flex-col gap-2 text-gray-100 text-sm h-full justify-center items-center' : 'flex flex-col gap-2 text-gray-100 text-sm'; @@ -176,8 +154,7 @@ export default function Nav({ navVisible, setNavVisible }) { ref={containerRef} >
- {/* {(isLoading && pageNumber === 1) ? ( */} - {(isLoading && pageNumber === 1) || isFetching ? ( + {(getConversationsQuery.isLoading && pageNumber === 1) || isFetching ? ( ) : ( Date: Sun, 2 Apr 2023 12:49:44 -0700 Subject: [PATCH 04/36] turn off no-debugger in eslintrc for debugginer purposes --- client/.eslintrc.js | 1 + 1 file changed, 1 insertion(+) diff --git a/client/.eslintrc.js b/client/.eslintrc.js index 2f2422bac8..7b8f7bd096 100644 --- a/client/.eslintrc.js +++ b/client/.eslintrc.js @@ -26,5 +26,6 @@ module.exports = { "rules": { 'react/prop-types': ['off'], 'react/display-name': ['off'], + "no-debugger":"off", } } From 8c392ac05e5cdc497f4810563bf692a2639a5e49 Mon Sep 17 00:00:00 2001 From: Daniel D Orlando Date: Sun, 2 Apr 2023 12:50:33 -0700 Subject: [PATCH 05/36] build: add react query --- client/package-lock.json | 672 +++++++++++++++++++++++++++++++++------ client/package.json | 8 +- 2 files changed, 583 insertions(+), 97 deletions(-) diff --git a/client/package-lock.json b/client/package-lock.json index 8db0845566..108ecefc15 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -66,7 +66,12 @@ "@babel/plugin-transform-runtime": "^7.19.6", "@babel/preset-env": "^7.20.2", "@babel/preset-react": "^7.18.6", + "@babel/preset-typescript": "^7.21.0", "@babel/runtime": "^7.20.13", + "@types/jest": "^29.5.0", + "@types/node": "^18.15.10", + "@types/react": "^18.0.30", + "@types/react-dom": "^18.0.11", "@vitejs/plugin-react": "^3.1.0", "autoprefixer": "^10.4.13", "babel-loader": "^9.1.2", @@ -139,7 +144,9 @@ }, "node_modules/@babel/code-frame": { "version": "7.18.6", - "license": "MIT", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", + "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "dev": true, "dependencies": { "@babel/highlight": "^7.18.6" }, @@ -498,7 +505,9 @@ }, "node_modules/@babel/helper-validator-identifier": { "version": "7.19.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "dev": true, "engines": { "node": ">=6.9.0" } @@ -540,7 +549,9 @@ }, "node_modules/@babel/highlight": { "version": "7.18.6", - "license": "MIT", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.18.6", "chalk": "^2.0.0", @@ -909,11 +920,13 @@ } }, "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.18.6", + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.21.4.tgz", + "integrity": "sha512-5hewiLct5OKyh6PLKEYaFclcqtIgCb6bmELouxjF6up5q3Sov7rOayW4RwhbaBL0dit8rA80GNfY+UuDp2mBbQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.20.2" }, "engines": { "node": ">=6.9.0" @@ -1016,6 +1029,21 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.21.4.tgz", + "integrity": "sha512-xz0D39NvhQn4t4RNsHmDnnsaQizIlUkdtYvLs8La1BlfjQ6JEwxkJGeqJMW2tAXx+q6H+WFuUTXNdYVpEya0YA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.20.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-transform-arrow-functions": { "version": "7.20.7", "dev": true, @@ -1570,6 +1598,24 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-transform-typescript": { + "version": "7.21.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.21.3.tgz", + "integrity": "sha512-RQxPz6Iqt8T0uw/WsJNReuBpWpBqs/n7mNo18sKLoTbMp+UrEekhH+pKSVC7gWz+DNjo9gryfV8YzCiT45RgMw==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-create-class-features-plugin": "^7.21.0", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-typescript": "^7.20.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-transform-unicode-escapes": { "version": "7.18.10", "dev": true, @@ -1721,6 +1767,25 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/preset-typescript": { + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.21.4.tgz", + "integrity": "sha512-sMLNWY37TCdRH/bJ6ZeeOH1nPuanED7Ai9Y/vH31IPqalioJ6ZNFUWONsakhv4r4n+I6gm5lmoE0olkgib/j/A==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-validator-option": "^7.21.0", + "@babel/plugin-syntax-jsx": "^7.21.4", + "@babel/plugin-transform-modules-commonjs": "^7.21.2", + "@babel/plugin-transform-typescript": "^7.21.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/regjsgen": { "version": "0.8.0", "dev": true, @@ -2480,7 +2545,9 @@ }, "node_modules/@jest/expect-utils": { "version": "29.5.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.5.0.tgz", + "integrity": "sha512-fmKzsidoXQT2KwnrwE0SQq3uj8Z763vzR8LnLBwC2qYWEFpjX8daRsk6rHUM1QvNlEW/UJXNXm59ztmJJWs2Mg==", + "dev": true, "dependencies": { "jest-get-type": "^29.4.3" }, @@ -2490,7 +2557,9 @@ }, "node_modules/@jest/schemas": { "version": "29.4.3", - "license": "MIT", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.4.3.tgz", + "integrity": "sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg==", + "dev": true, "dependencies": { "@sinclair/typebox": "^0.25.16" }, @@ -2500,7 +2569,9 @@ }, "node_modules/@jest/types": { "version": "29.5.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", + "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", + "dev": true, "dependencies": { "@jest/schemas": "^29.4.3", "@types/istanbul-lib-coverage": "^2.0.0", @@ -2515,7 +2586,9 @@ }, "node_modules/@jest/types/node_modules/ansi-styles": { "version": "4.3.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -2528,7 +2601,9 @@ }, "node_modules/@jest/types/node_modules/chalk": { "version": "4.1.2", - "license": "MIT", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -2542,7 +2617,9 @@ }, "node_modules/@jest/types/node_modules/color-convert": { "version": "2.0.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "dependencies": { "color-name": "~1.1.4" }, @@ -2552,18 +2629,24 @@ }, "node_modules/@jest/types/node_modules/color-name": { "version": "1.1.4", - "license": "MIT" + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "node_modules/@jest/types/node_modules/has-flag": { "version": "4.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, "engines": { "node": ">=8" } }, "node_modules/@jest/types/node_modules/supports-color": { "version": "7.2.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -3187,7 +3270,44 @@ }, "node_modules/@sinclair/typebox": { "version": "0.25.24", - "license": "MIT" + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.24.tgz", + "integrity": "sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ==", + "dev": true + }, + "node_modules/@tanstack/query-core": { + "version": "4.27.0", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-4.27.0.tgz", + "integrity": "sha512-sm+QncWaPmM73IPwFlmWSKPqjdTXZeFf/7aEmWh00z7yl2FjqophPt0dE1EHW9P1giMC5rMviv7OUbSDmWzXXA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/react-query": { + "version": "4.28.0", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-4.28.0.tgz", + "integrity": "sha512-8cGBV5300RHlvYdS4ea+G1JcZIt5CIuprXYFnsWggkmGoC0b5JaqG0fIX3qwDL9PTNkKvG76NGThIWbpXivMrQ==", + "dependencies": { + "@tanstack/query-core": "4.27.0", + "use-sync-external-store": "^1.2.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-native": "*" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } }, "node_modules/@types/body-parser": { "version": "1.19.2", @@ -3291,25 +3411,33 @@ }, "node_modules/@types/istanbul-lib-coverage": { "version": "2.0.4", - "license": "MIT" + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", + "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", + "dev": true }, "node_modules/@types/istanbul-lib-report": { "version": "3.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "dev": true, "dependencies": { "@types/istanbul-lib-coverage": "*" } }, "node_modules/@types/istanbul-reports": { "version": "3.0.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dev": true, "dependencies": { "@types/istanbul-lib-report": "*" } }, "node_modules/@types/jest": { "version": "29.5.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.0.tgz", + "integrity": "sha512-3Emr5VOl/aoBwnWcH/EFQvlSAmjV+XtV9GGu5mwdYew5vhQh0IUZx/60x0TzHDu09Bi7HMx10t/namdJw5QIcg==", + "dev": true, "dependencies": { "expect": "^29.0.0", "pretty-format": "^29.0.0" @@ -3349,7 +3477,9 @@ }, "node_modules/@types/node": { "version": "18.15.11", - "license": "MIT" + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.11.tgz", + "integrity": "sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==", + "dev": true }, "node_modules/@types/parse5": { "version": "6.0.3", @@ -3380,7 +3510,9 @@ }, "node_modules/@types/react-dom": { "version": "18.0.11", - "license": "MIT", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.0.11.tgz", + "integrity": "sha512-O38bPbI2CWtgw/OoQoY+BRelw7uysmXbWvw3nLWO21H1HSh+GOlqPuXshJfjmpNlKiiSDG9cc1JZAaMmVdcTlw==", + "dev": true, "dependencies": { "@types/react": "*" } @@ -3426,7 +3558,9 @@ }, "node_modules/@types/stack-utils": { "version": "2.0.1", - "license": "MIT" + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", + "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", + "dev": true }, "node_modules/@types/unist": { "version": "2.0.6", @@ -3446,14 +3580,18 @@ }, "node_modules/@types/yargs": { "version": "17.0.24", - "license": "MIT", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", + "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", + "dev": true, "dependencies": { "@types/yargs-parser": "*" } }, "node_modules/@types/yargs-parser": { "version": "21.0.0", - "license": "MIT" + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", + "dev": true }, "node_modules/@typescript-eslint/scope-manager": { "version": "5.57.0", @@ -3952,7 +4090,9 @@ }, "node_modules/ansi-styles": { "version": "3.2.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, "dependencies": { "color-convert": "^1.9.0" }, @@ -4647,7 +4787,9 @@ }, "node_modules/chalk": { "version": "2.4.2", - "license": "MIT", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -4700,6 +4842,9 @@ }, "node_modules/ci-info": { "version": "3.8.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", + "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", + "dev": true, "funding": [ { "type": "github", @@ -4774,14 +4919,18 @@ }, "node_modules/color-convert": { "version": "1.9.3", - "license": "MIT", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, "dependencies": { "color-name": "1.1.3" } }, "node_modules/color-name": { "version": "1.1.3", - "license": "MIT" + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true }, "node_modules/colorette": { "version": "2.0.19", @@ -5329,7 +5478,9 @@ }, "node_modules/diff-sequences": { "version": "29.4.3", - "license": "MIT", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.4.3.tgz", + "integrity": "sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==", + "dev": true, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } @@ -5713,7 +5864,9 @@ }, "node_modules/escape-string-regexp": { "version": "1.0.5", - "license": "MIT", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, "engines": { "node": ">=0.8.0" } @@ -6269,7 +6422,9 @@ }, "node_modules/expect": { "version": "29.5.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.5.0.tgz", + "integrity": "sha512-yM7xqUrCO2JdpFo4XpM82t+PJBFybdqoQuJLDGeDX2ij8NZzqRHyu3Hp188/JX7SWqud+7t4MUdvcgGBICMHZg==", + "dev": true, "dependencies": { "@jest/expect-utils": "^29.5.0", "jest-get-type": "^29.4.3", @@ -6859,7 +7014,9 @@ }, "node_modules/graceful-fs": { "version": "4.2.11", - "license": "ISC" + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true }, "node_modules/grapheme-splitter": { "version": "1.0.4", @@ -6895,7 +7052,9 @@ }, "node_modules/has-flag": { "version": "3.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, "engines": { "node": ">=4" } @@ -7807,7 +7966,9 @@ }, "node_modules/jest-diff": { "version": "29.5.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.5.0.tgz", + "integrity": "sha512-LtxijLLZBduXnHSniy0WMdaHjmQnt3g5sa16W4p0HqukYTTsyTW3GD1q41TyGl5YFXj/5B2U6dlh5FM1LIMgxw==", + "dev": true, "dependencies": { "chalk": "^4.0.0", "diff-sequences": "^29.4.3", @@ -7820,7 +7981,9 @@ }, "node_modules/jest-diff/node_modules/ansi-styles": { "version": "4.3.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -7833,7 +7996,9 @@ }, "node_modules/jest-diff/node_modules/chalk": { "version": "4.1.2", - "license": "MIT", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -7847,7 +8012,9 @@ }, "node_modules/jest-diff/node_modules/color-convert": { "version": "2.0.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "dependencies": { "color-name": "~1.1.4" }, @@ -7857,18 +8024,24 @@ }, "node_modules/jest-diff/node_modules/color-name": { "version": "1.1.4", - "license": "MIT" + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "node_modules/jest-diff/node_modules/has-flag": { "version": "4.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, "engines": { "node": ">=8" } }, "node_modules/jest-diff/node_modules/supports-color": { "version": "7.2.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -7878,14 +8051,18 @@ }, "node_modules/jest-get-type": { "version": "29.4.3", - "license": "MIT", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.4.3.tgz", + "integrity": "sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg==", + "dev": true, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-matcher-utils": { "version": "29.5.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.5.0.tgz", + "integrity": "sha512-lecRtgm/rjIK0CQ7LPQwzCs2VwW6WAahA55YBuI+xqmhm7LAaxokSB8C97yJeYyT+HvQkH741StzpU41wohhWw==", + "dev": true, "dependencies": { "chalk": "^4.0.0", "jest-diff": "^29.5.0", @@ -7898,7 +8075,9 @@ }, "node_modules/jest-matcher-utils/node_modules/ansi-styles": { "version": "4.3.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -7911,7 +8090,9 @@ }, "node_modules/jest-matcher-utils/node_modules/chalk": { "version": "4.1.2", - "license": "MIT", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -7925,7 +8106,9 @@ }, "node_modules/jest-matcher-utils/node_modules/color-convert": { "version": "2.0.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "dependencies": { "color-name": "~1.1.4" }, @@ -7935,18 +8118,24 @@ }, "node_modules/jest-matcher-utils/node_modules/color-name": { "version": "1.1.4", - "license": "MIT" + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "node_modules/jest-matcher-utils/node_modules/has-flag": { "version": "4.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, "engines": { "node": ">=8" } }, "node_modules/jest-matcher-utils/node_modules/supports-color": { "version": "7.2.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -7956,7 +8145,9 @@ }, "node_modules/jest-message-util": { "version": "29.5.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.5.0.tgz", + "integrity": "sha512-Kijeg9Dag6CKtIDA7O21zNTACqD5MD/8HfIV8pdD94vFyFuer52SigdC3IQMhab3vACxXMiFk+yMHNdbqtyTGA==", + "dev": true, "dependencies": { "@babel/code-frame": "^7.12.13", "@jest/types": "^29.5.0", @@ -7974,7 +8165,9 @@ }, "node_modules/jest-message-util/node_modules/ansi-styles": { "version": "4.3.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -7987,7 +8180,9 @@ }, "node_modules/jest-message-util/node_modules/chalk": { "version": "4.1.2", - "license": "MIT", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -8001,7 +8196,9 @@ }, "node_modules/jest-message-util/node_modules/color-convert": { "version": "2.0.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "dependencies": { "color-name": "~1.1.4" }, @@ -8011,25 +8208,33 @@ }, "node_modules/jest-message-util/node_modules/color-name": { "version": "1.1.4", - "license": "MIT" + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "node_modules/jest-message-util/node_modules/has-flag": { "version": "4.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, "engines": { "node": ">=8" } }, "node_modules/jest-message-util/node_modules/slash": { "version": "3.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, "engines": { "node": ">=8" } }, "node_modules/jest-message-util/node_modules/supports-color": { "version": "7.2.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -8039,7 +8244,9 @@ }, "node_modules/jest-util": { "version": "29.5.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.5.0.tgz", + "integrity": "sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ==", + "dev": true, "dependencies": { "@jest/types": "^29.5.0", "@types/node": "*", @@ -8054,7 +8261,9 @@ }, "node_modules/jest-util/node_modules/ansi-styles": { "version": "4.3.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -8067,7 +8276,9 @@ }, "node_modules/jest-util/node_modules/chalk": { "version": "4.1.2", - "license": "MIT", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -8081,7 +8292,9 @@ }, "node_modules/jest-util/node_modules/color-convert": { "version": "2.0.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "dependencies": { "color-name": "~1.1.4" }, @@ -8091,18 +8304,24 @@ }, "node_modules/jest-util/node_modules/color-name": { "version": "1.1.4", - "license": "MIT" + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "node_modules/jest-util/node_modules/has-flag": { "version": "4.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, "engines": { "node": ">=8" } }, "node_modules/jest-util/node_modules/supports-color": { "version": "7.2.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -10782,7 +11001,9 @@ }, "node_modules/pretty-format": { "version": "29.5.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.5.0.tgz", + "integrity": "sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw==", + "dev": true, "dependencies": { "@jest/schemas": "^29.4.3", "ansi-styles": "^5.0.0", @@ -10794,7 +11015,9 @@ }, "node_modules/pretty-format/node_modules/ansi-styles": { "version": "5.2.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, "engines": { "node": ">=10" }, @@ -12068,7 +12291,9 @@ }, "node_modules/stack-utils": { "version": "2.0.6", - "license": "MIT", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, "dependencies": { "escape-string-regexp": "^2.0.0" }, @@ -12078,7 +12303,9 @@ }, "node_modules/stack-utils/node_modules/escape-string-regexp": { "version": "2.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, "engines": { "node": ">=8" } @@ -12270,7 +12497,9 @@ }, "node_modules/supports-color": { "version": "5.5.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, "dependencies": { "has-flag": "^3.0.0" }, @@ -13692,6 +13921,9 @@ }, "@babel/code-frame": { "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", + "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "dev": true, "requires": { "@babel/highlight": "^7.18.6" } @@ -13924,7 +14156,10 @@ "dev": true }, "@babel/helper-validator-identifier": { - "version": "7.19.1" + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "dev": true }, "@babel/helper-validator-option": { "version": "7.21.0", @@ -13951,6 +14186,9 @@ }, "@babel/highlight": { "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.18.6", "chalk": "^2.0.0", @@ -14156,10 +14394,12 @@ } }, "@babel/plugin-syntax-jsx": { - "version": "7.18.6", + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.21.4.tgz", + "integrity": "sha512-5hewiLct5OKyh6PLKEYaFclcqtIgCb6bmELouxjF6up5q3Sov7rOayW4RwhbaBL0dit8rA80GNfY+UuDp2mBbQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.20.2" } }, "@babel/plugin-syntax-logical-assignment-operators": { @@ -14218,6 +14458,15 @@ "@babel/helper-plugin-utils": "^7.14.5" } }, + "@babel/plugin-syntax-typescript": { + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.21.4.tgz", + "integrity": "sha512-xz0D39NvhQn4t4RNsHmDnnsaQizIlUkdtYvLs8La1BlfjQ6JEwxkJGeqJMW2tAXx+q6H+WFuUTXNdYVpEya0YA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.20.2" + } + }, "@babel/plugin-transform-arrow-functions": { "version": "7.20.7", "dev": true, @@ -14513,6 +14762,18 @@ "@babel/helper-plugin-utils": "^7.18.9" } }, + "@babel/plugin-transform-typescript": { + "version": "7.21.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.21.3.tgz", + "integrity": "sha512-RQxPz6Iqt8T0uw/WsJNReuBpWpBqs/n7mNo18sKLoTbMp+UrEekhH+pKSVC7gWz+DNjo9gryfV8YzCiT45RgMw==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-create-class-features-plugin": "^7.21.0", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-typescript": "^7.20.0" + } + }, "@babel/plugin-transform-unicode-escapes": { "version": "7.18.10", "dev": true, @@ -14632,6 +14893,19 @@ "@babel/plugin-transform-react-pure-annotations": "^7.18.6" } }, + "@babel/preset-typescript": { + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.21.4.tgz", + "integrity": "sha512-sMLNWY37TCdRH/bJ6ZeeOH1nPuanED7Ai9Y/vH31IPqalioJ6ZNFUWONsakhv4r4n+I6gm5lmoE0olkgib/j/A==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-validator-option": "^7.21.0", + "@babel/plugin-syntax-jsx": "^7.21.4", + "@babel/plugin-transform-modules-commonjs": "^7.21.2", + "@babel/plugin-transform-typescript": "^7.21.3" + } + }, "@babel/regjsgen": { "version": "0.8.0", "dev": true @@ -14984,18 +15258,27 @@ }, "@jest/expect-utils": { "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.5.0.tgz", + "integrity": "sha512-fmKzsidoXQT2KwnrwE0SQq3uj8Z763vzR8LnLBwC2qYWEFpjX8daRsk6rHUM1QvNlEW/UJXNXm59ztmJJWs2Mg==", + "dev": true, "requires": { "jest-get-type": "^29.4.3" } }, "@jest/schemas": { "version": "29.4.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.4.3.tgz", + "integrity": "sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg==", + "dev": true, "requires": { "@sinclair/typebox": "^0.25.16" } }, "@jest/types": { "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", + "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", + "dev": true, "requires": { "@jest/schemas": "^29.4.3", "@types/istanbul-lib-coverage": "^2.0.0", @@ -15007,12 +15290,18 @@ "dependencies": { "ansi-styles": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "requires": { "color-convert": "^2.0.1" } }, "chalk": { "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -15020,18 +15309,30 @@ }, "color-convert": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "requires": { "color-name": "~1.1.4" } }, "color-name": { - "version": "1.1.4" + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "has-flag": { - "version": "4.0.0" + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true }, "supports-color": { "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, "requires": { "has-flag": "^4.0.0" } @@ -15464,7 +15765,24 @@ } }, "@sinclair/typebox": { - "version": "0.25.24" + "version": "0.25.24", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.24.tgz", + "integrity": "sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ==", + "dev": true + }, + "@tanstack/query-core": { + "version": "4.27.0", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-4.27.0.tgz", + "integrity": "sha512-sm+QncWaPmM73IPwFlmWSKPqjdTXZeFf/7aEmWh00z7yl2FjqophPt0dE1EHW9P1giMC5rMviv7OUbSDmWzXXA==" + }, + "@tanstack/react-query": { + "version": "4.28.0", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-4.28.0.tgz", + "integrity": "sha512-8cGBV5300RHlvYdS4ea+G1JcZIt5CIuprXYFnsWggkmGoC0b5JaqG0fIX3qwDL9PTNkKvG76NGThIWbpXivMrQ==", + "requires": { + "@tanstack/query-core": "4.27.0", + "use-sync-external-store": "^1.2.0" + } }, "@types/body-parser": { "version": "1.19.2", @@ -15555,22 +15873,34 @@ } }, "@types/istanbul-lib-coverage": { - "version": "2.0.4" + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", + "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", + "dev": true }, "@types/istanbul-lib-report": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "dev": true, "requires": { "@types/istanbul-lib-coverage": "*" } }, "@types/istanbul-reports": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dev": true, "requires": { "@types/istanbul-lib-report": "*" } }, "@types/jest": { "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.0.tgz", + "integrity": "sha512-3Emr5VOl/aoBwnWcH/EFQvlSAmjV+XtV9GGu5mwdYew5vhQh0IUZx/60x0TzHDu09Bi7HMx10t/namdJw5QIcg==", + "dev": true, "requires": { "expect": "^29.0.0", "pretty-format": "^29.0.0" @@ -15604,7 +15934,10 @@ "version": "0.7.31" }, "@types/node": { - "version": "18.15.11" + "version": "18.15.11", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.11.tgz", + "integrity": "sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==", + "dev": true }, "@types/parse5": { "version": "6.0.3" @@ -15630,6 +15963,9 @@ }, "@types/react-dom": { "version": "18.0.11", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.0.11.tgz", + "integrity": "sha512-O38bPbI2CWtgw/OoQoY+BRelw7uysmXbWvw3nLWO21H1HSh+GOlqPuXshJfjmpNlKiiSDG9cc1JZAaMmVdcTlw==", + "dev": true, "requires": { "@types/react": "*" } @@ -15668,7 +16004,10 @@ } }, "@types/stack-utils": { - "version": "2.0.1" + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", + "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", + "dev": true }, "@types/unist": { "version": "2.0.6" @@ -15685,12 +16024,18 @@ }, "@types/yargs": { "version": "17.0.24", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", + "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", + "dev": true, "requires": { "@types/yargs-parser": "*" } }, "@types/yargs-parser": { - "version": "21.0.0" + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", + "dev": true }, "@typescript-eslint/scope-manager": { "version": "5.57.0", @@ -16015,6 +16360,9 @@ }, "ansi-styles": { "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, "requires": { "color-convert": "^1.9.0" } @@ -16498,6 +16846,9 @@ }, "chalk": { "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, "requires": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -16525,7 +16876,10 @@ "dev": true }, "ci-info": { - "version": "3.8.0" + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", + "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", + "dev": true }, "cipher-base": { "version": "1.0.4", @@ -16565,12 +16919,18 @@ }, "color-convert": { "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, "requires": { "color-name": "1.1.3" } }, "color-name": { - "version": "1.1.3" + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true }, "colorette": { "version": "2.0.19", @@ -16914,7 +17274,10 @@ "version": "5.1.0" }, "diff-sequences": { - "version": "29.4.3" + "version": "29.4.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.4.3.tgz", + "integrity": "sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==", + "dev": true }, "diffie-hellman": { "version": "5.0.3", @@ -17182,7 +17545,10 @@ "dev": true }, "escape-string-regexp": { - "version": "1.0.5" + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true }, "eslint": { "version": "8.37.0", @@ -17552,6 +17918,9 @@ }, "expect": { "version": "29.5.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.5.0.tgz", + "integrity": "sha512-yM7xqUrCO2JdpFo4XpM82t+PJBFybdqoQuJLDGeDX2ij8NZzqRHyu3Hp188/JX7SWqud+7t4MUdvcgGBICMHZg==", + "dev": true, "requires": { "@jest/expect-utils": "^29.5.0", "jest-get-type": "^29.4.3", @@ -17937,7 +18306,10 @@ } }, "graceful-fs": { - "version": "4.2.11" + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true }, "grapheme-splitter": { "version": "1.0.4", @@ -17961,7 +18333,10 @@ "dev": true }, "has-flag": { - "version": "3.0.0" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true }, "has-property-descriptors": { "version": "1.0.0", @@ -18497,6 +18872,9 @@ }, "jest-diff": { "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.5.0.tgz", + "integrity": "sha512-LtxijLLZBduXnHSniy0WMdaHjmQnt3g5sa16W4p0HqukYTTsyTW3GD1q41TyGl5YFXj/5B2U6dlh5FM1LIMgxw==", + "dev": true, "requires": { "chalk": "^4.0.0", "diff-sequences": "^29.4.3", @@ -18506,12 +18884,18 @@ "dependencies": { "ansi-styles": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "requires": { "color-convert": "^2.0.1" } }, "chalk": { "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -18519,18 +18903,30 @@ }, "color-convert": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "requires": { "color-name": "~1.1.4" } }, "color-name": { - "version": "1.1.4" + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "has-flag": { - "version": "4.0.0" + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true }, "supports-color": { "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, "requires": { "has-flag": "^4.0.0" } @@ -18538,10 +18934,16 @@ } }, "jest-get-type": { - "version": "29.4.3" + "version": "29.4.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.4.3.tgz", + "integrity": "sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg==", + "dev": true }, "jest-matcher-utils": { "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.5.0.tgz", + "integrity": "sha512-lecRtgm/rjIK0CQ7LPQwzCs2VwW6WAahA55YBuI+xqmhm7LAaxokSB8C97yJeYyT+HvQkH741StzpU41wohhWw==", + "dev": true, "requires": { "chalk": "^4.0.0", "jest-diff": "^29.5.0", @@ -18551,12 +18953,18 @@ "dependencies": { "ansi-styles": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "requires": { "color-convert": "^2.0.1" } }, "chalk": { "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -18564,18 +18972,30 @@ }, "color-convert": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "requires": { "color-name": "~1.1.4" } }, "color-name": { - "version": "1.1.4" + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "has-flag": { - "version": "4.0.0" + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true }, "supports-color": { "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, "requires": { "has-flag": "^4.0.0" } @@ -18584,6 +19004,9 @@ }, "jest-message-util": { "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.5.0.tgz", + "integrity": "sha512-Kijeg9Dag6CKtIDA7O21zNTACqD5MD/8HfIV8pdD94vFyFuer52SigdC3IQMhab3vACxXMiFk+yMHNdbqtyTGA==", + "dev": true, "requires": { "@babel/code-frame": "^7.12.13", "@jest/types": "^29.5.0", @@ -18598,12 +19021,18 @@ "dependencies": { "ansi-styles": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "requires": { "color-convert": "^2.0.1" } }, "chalk": { "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -18611,21 +19040,36 @@ }, "color-convert": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "requires": { "color-name": "~1.1.4" } }, "color-name": { - "version": "1.1.4" + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "has-flag": { - "version": "4.0.0" + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true }, "slash": { - "version": "3.0.0" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true }, "supports-color": { "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, "requires": { "has-flag": "^4.0.0" } @@ -18634,6 +19078,9 @@ }, "jest-util": { "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.5.0.tgz", + "integrity": "sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ==", + "dev": true, "requires": { "@jest/types": "^29.5.0", "@types/node": "*", @@ -18645,12 +19092,18 @@ "dependencies": { "ansi-styles": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "requires": { "color-convert": "^2.0.1" } }, "chalk": { "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -18658,18 +19111,30 @@ }, "color-convert": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "requires": { "color-name": "~1.1.4" } }, "color-name": { - "version": "1.1.4" + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "has-flag": { - "version": "4.0.0" + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true }, "supports-color": { "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, "requires": { "has-flag": "^4.0.0" } @@ -20106,6 +20571,9 @@ }, "pretty-format": { "version": "29.5.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.5.0.tgz", + "integrity": "sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw==", + "dev": true, "requires": { "@jest/schemas": "^29.4.3", "ansi-styles": "^5.0.0", @@ -20113,7 +20581,10 @@ }, "dependencies": { "ansi-styles": { - "version": "5.2.0" + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true } } }, @@ -20909,12 +21380,18 @@ }, "stack-utils": { "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, "requires": { "escape-string-regexp": "^2.0.0" }, "dependencies": { "escape-string-regexp": { - "version": "2.0.0" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true } } }, @@ -21033,6 +21510,9 @@ }, "supports-color": { "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, "requires": { "has-flag": "^3.0.0" } diff --git a/client/package.json b/client/package.json index 75d8361f1a..147601d874 100644 --- a/client/package.json +++ b/client/package.json @@ -69,9 +69,14 @@ "tailwindcss-radix": "^2.8.0", "url": "^0.11.0", "use-react-screenshot": "github:danny-avila/use-react-screenshot#master", - "uuidv4": "^6.2.13" + "uuidv4": "^6.2.13", + "@tanstack/react-query": "^4.28.0" }, "devDependencies": { + "@types/jest": "^29.5.0", + "@types/node": "^18.15.10", + "@types/react": "^18.0.30", + "@types/react-dom": "^18.0.11", "@babel/cli": "^7.20.7", "@babel/core": "^7.20.12", "@babel/eslint-parser": "^7.19.1", @@ -80,6 +85,7 @@ "@babel/preset-react": "^7.18.6", "@babel/runtime": "^7.20.13", "@vitejs/plugin-react": "^3.1.0", + "@babel/preset-typescript": "^7.21.0", "autoprefixer": "^10.4.13", "babel-loader": "^9.1.2", "babel-plugin-root-import": "^6.6.0", From 10941bf6236188e0483e7b68286ff54e653f6f6b Mon Sep 17 00:00:00 2001 From: Daniel D Orlando Date: Sun, 2 Apr 2023 12:50:52 -0700 Subject: [PATCH 06/36] add DS_Store to gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index c7db0d1e8f..be695425ed 100644 --- a/.gitignore +++ b/.gitignore @@ -57,3 +57,4 @@ src/style - official.css /e2e/specs/.test-results/ /e2e/playwright-report/ /playwright/.cache/ +.DS_Store \ No newline at end of file From 39f53e6ddf01b3f483735e7199db3431410a84e1 Mon Sep 17 00:00:00 2001 From: Daniel D Orlando Date: Sun, 2 Apr 2023 12:52:18 -0700 Subject: [PATCH 07/36] add QueryClientProvider to main --- client/src/main.jsx | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/client/src/main.jsx b/client/src/main.jsx index 8738dcf27f..b27ce94477 100644 --- a/client/src/main.jsx +++ b/client/src/main.jsx @@ -1,9 +1,6 @@ -import React from 'react'; import { createRoot } from 'react-dom/client'; -// import { Provider } from 'react-redux'; -// import { store } from './src/store'; import { RecoilRoot } from 'recoil'; - +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { ThemeProvider } from './hooks/ThemeContext'; import App from './App'; import './style.css'; @@ -12,10 +9,14 @@ import './mobile.css'; const container = document.getElementById('root'); const root = createRoot(container); +const queryClient = new QueryClient(); + root.render( - - - - - + + + + + + + ); From c6d3bcd4572d664b8a25422b9942b51755f06754 Mon Sep 17 00:00:00 2001 From: Daniel D Orlando Date: Mon, 3 Apr 2023 08:11:29 -0700 Subject: [PATCH 08/36] feat: Add RQ to Conversation component, create temp Chat component with RQ for compare and debugging --- .../components/Conversations/Conversation.jsx | 39 ++++++--- .../src/data-provider/react-query-service.ts | 8 +- client/src/routes/ChatRQTemp.jsx | 79 +++++++++++++++++++ 3 files changed, 114 insertions(+), 12 deletions(-) create mode 100644 client/src/routes/ChatRQTemp.jsx diff --git a/client/src/components/Conversations/Conversation.jsx b/client/src/components/Conversations/Conversation.jsx index c95c9d1f4b..1ce42c85fd 100644 --- a/client/src/components/Conversations/Conversation.jsx +++ b/client/src/components/Conversations/Conversation.jsx @@ -1,6 +1,6 @@ -import React, { useState, useRef } from 'react'; +import { useState, useRef, useEffect} from 'react'; import { useRecoilState, useSetRecoilState } from 'recoil'; - +import { useUpdateConversationMutation } from '~/data-provider'; import RenameButton from './RenameButton'; import DeleteButton from './DeleteButton'; import ConvoIcon from '../svg/ConvoIcon'; @@ -15,6 +15,8 @@ export default function Conversation({ conversation, retainView }) { const { refreshConversations } = store.useConversations(); const { switchToConversation } = store.useConversation(); + const updateConvoMutation = useUpdateConversationMutation(currentConversation?.conversationId); + const [renaming, setRenaming] = useState(false); const inputRef = useRef(null); @@ -22,7 +24,7 @@ export default function Conversation({ conversation, retainView }) { const [titleInput, setTitleInput] = useState(title); - const rename = manualSWR(`/api/convos/update`, 'post'); + const rename = manualSWR(`/api/convos/update`, 'post'); const clickHandler = async () => { if (currentConversation?.conversationId === conversationId) { @@ -59,16 +61,31 @@ export default function Conversation({ conversation, retainView }) { if (titleInput === title) { return; } - rename.trigger({ conversationId, title: titleInput }).then(() => { - refreshConversations(); - if (conversationId == currentConversation?.conversationId) - setCurrentConversation(prevState => ({ - ...prevState, - title: titleInput - })); - }); + updateConvoMutation.mutate({ arg: { conversationId, title: titleInput }}); + + // rename.trigger({ conversationId, title: titleInput }).then(() => { + // refreshConversations(); + // if (conversationId == currentConversation?.conversationId) + // setCurrentConversation(prevState => ({ + // ...prevState, + // title: titleInput + // })); + // }); }; + useEffect(() => { + if (updateConvoMutation.isSuccess) { + // debugger; + refreshConversations(); + if (conversationId == currentConversation?.conversationId) + + setCurrentConversation(prevState => ({ + ...prevState, + title: titleInput + })); + } +}, [updateConvoMutation.isSuccess]); + const handleKeyDown = e => { if (e.key === 'Enter') { onRename(e); diff --git a/client/src/data-provider/react-query-service.ts b/client/src/data-provider/react-query-service.ts index fa7b99c3d8..cd1186c6c2 100644 --- a/client/src/data-provider/react-query-service.ts +++ b/client/src/data-provider/react-query-service.ts @@ -8,6 +8,8 @@ import { } from "@tanstack/react-query"; import * as t from "./types"; import * as dataService from "./data-service"; +import { useRecoilState, useResetRecoilState, useSetRecoilState } from 'recoil'; +import store from '~/store'; export enum QueryKeys { messages = "messsages", @@ -66,13 +68,17 @@ export const useUpdateConversationMutation = ( t.TUpdateConversationRequest, unknown > => { + const [conversation, setConversation] = useRecoilState(store.conversation); const queryClient = useQueryClient(); return useMutation( (payload: t.TUpdateConversationRequest) => dataService.updateConversation(payload), { - onSuccess: () => { + onSuccess: (res) => { + console.log('res', res); + setConversation(res); queryClient.invalidateQueries([QueryKeys.conversation, id]); + queryClient.invalidateQueries([QueryKeys.allConversations, id]); }, } ); diff --git a/client/src/routes/ChatRQTemp.jsx b/client/src/routes/ChatRQTemp.jsx new file mode 100644 index 0000000000..f2a88c2483 --- /dev/null +++ b/client/src/routes/ChatRQTemp.jsx @@ -0,0 +1,79 @@ +import React, { useEffect } from 'react'; +import { useNavigate, useParams } from 'react-router-dom'; +import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'; + +import Landing from '../components/ui/Landing'; +import Messages from '../components/Messages'; +import TextChat from '../components/Input'; + +import store from '~/store'; +import { useGetMessagesByConvoId, useGetConversationByIdQuery } from '~/data-provider'; + +export default function Chat() { + const searchQuery = useRecoilValue(store.searchQuery); + const [conversation, setConversation] = useRecoilState(store.conversation); + const setMessages = useSetRecoilState(store.messages); + const messagesTree = useRecoilValue(store.messagesTree); + const { newConversation } = store.useConversation(); + const { conversationId } = useParams(); + const navigate = useNavigate(); + + //disabled by default, we only enable it when messagesTree is null + const messagesQuery = useGetMessagesByConvoId(conversation?.conversationId, { enabled: false }); + + const conversationQuery = useGetConversationByIdQuery( + conversation?.conversationId, { + enabled: !!conversation?.conversationId && + conversation?.conversationId !== 'search' && + conversation?.conversationId !== 'new' + }); + + // when conversation changed or conversationId (in url) changed + useEffect(() => { + if (conversation === null) { + // no current conversation, we need to do something + if (conversationId === 'new') { + // create new + newConversation(); + } else if (conversationQuery.data) { + // fetch it from server + setConversation(conversationQuery.data); + setMessages(null); + } else if (conversationQuery.isError) { + console.error('failed to fetch the conversation'); + console.error(conversationQuery.error); + newConversation(); + } else { + navigate(`/chat/new`); + } + } else if (conversation?.conversationId === 'search') { + // jump to search page + navigate(`/search/${searchQuery}`); + } else if (conversation?.conversationId !== conversationId) { + // conversationId (in url) should always follow conversation?.conversationId, unless conversation is null + navigate(`/chat/${conversation?.conversationId}`); + } + }, [conversation, conversationId, newConversation, navigate, searchQuery, setMessages, conversationQuery]); + + // when messagesTree is null (<=> messages is null) + // we need to fetch message list from server + useEffect(() => { + if (messagesTree === null) { + messagesQuery.refetch(conversation?.conversationId); + } + }, [conversation?.conversationId, messagesQuery, messagesTree]); + + // if not a conversation + if (conversation?.conversationId === 'search') return null; + // if conversationId not match + if (conversation?.conversationId !== conversationId) return null; + // if conversationId is null + if (!conversationId) return null; + + return ( + <> + {conversationId === 'new' && !messagesTree?.length ? : } + + + ); +} From bd53b878d475123e3c7e232ac707e3b243466c4e Mon Sep 17 00:00:00 2001 From: Daniel D Orlando Date: Mon, 3 Apr 2023 08:28:39 -0700 Subject: [PATCH 09/36] update react-query-service --- client/src/data-provider/react-query-service.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/client/src/data-provider/react-query-service.ts b/client/src/data-provider/react-query-service.ts index cd1186c6c2..2f25033825 100644 --- a/client/src/data-provider/react-query-service.ts +++ b/client/src/data-provider/react-query-service.ts @@ -8,7 +8,6 @@ import { } from "@tanstack/react-query"; import * as t from "./types"; import * as dataService from "./data-service"; -import { useRecoilState, useResetRecoilState, useSetRecoilState } from 'recoil'; import store from '~/store'; export enum QueryKeys { @@ -68,15 +67,12 @@ export const useUpdateConversationMutation = ( t.TUpdateConversationRequest, unknown > => { - const [conversation, setConversation] = useRecoilState(store.conversation); const queryClient = useQueryClient(); return useMutation( (payload: t.TUpdateConversationRequest) => dataService.updateConversation(payload), { - onSuccess: (res) => { - console.log('res', res); - setConversation(res); + onSuccess: () => { queryClient.invalidateQueries([QueryKeys.conversation, id]); queryClient.invalidateQueries([QueryKeys.allConversations, id]); }, From 94e0636b32ea1666c1d0f09b9b4407237e9f8c59 Mon Sep 17 00:00:00 2001 From: Daniel D Orlando Date: Mon, 3 Apr 2023 12:39:00 -0700 Subject: [PATCH 10/36] add delete conversation mutation, fix withAuthentication on post requests --- .../components/Conversations/Conversation.jsx | 32 +++++++------------ .../components/Conversations/DeleteButton.jsx | 26 ++++++++++----- .../src/data-provider/react-query-service.ts | 22 +++++++++---- client/src/data-provider/request.ts | 15 ++++----- client/src/data-provider/types.ts | 22 ++++++++++--- 5 files changed, 68 insertions(+), 49 deletions(-) diff --git a/client/src/components/Conversations/Conversation.jsx b/client/src/components/Conversations/Conversation.jsx index 1ce42c85fd..543e9c9161 100644 --- a/client/src/components/Conversations/Conversation.jsx +++ b/client/src/components/Conversations/Conversation.jsx @@ -61,30 +61,20 @@ export default function Conversation({ conversation, retainView }) { if (titleInput === title) { return; } - updateConvoMutation.mutate({ arg: { conversationId, title: titleInput }}); - - // rename.trigger({ conversationId, title: titleInput }).then(() => { - // refreshConversations(); - // if (conversationId == currentConversation?.conversationId) - // setCurrentConversation(prevState => ({ - // ...prevState, - // title: titleInput - // })); - // }); + updateConvoMutation.mutate({ conversationId, title: titleInput }); }; useEffect(() => { - if (updateConvoMutation.isSuccess) { - // debugger; - refreshConversations(); - if (conversationId == currentConversation?.conversationId) - - setCurrentConversation(prevState => ({ - ...prevState, - title: titleInput - })); - } -}, [updateConvoMutation.isSuccess]); + if (updateConvoMutation.isSuccess) { + refreshConversations(); + if (conversationId == currentConversation?.conversationId) { + setCurrentConversation(prevState => ({ + ...prevState, + title: titleInput + })); + } + } + }, [updateConvoMutation.isSuccess]); const handleKeyDown = e => { if (e.key === 'Enter') { diff --git a/client/src/components/Conversations/DeleteButton.jsx b/client/src/components/Conversations/DeleteButton.jsx index 079a1e8296..5789892525 100644 --- a/client/src/components/Conversations/DeleteButton.jsx +++ b/client/src/components/Conversations/DeleteButton.jsx @@ -1,8 +1,8 @@ -import React from 'react'; +import { useEffect } from 'react'; import TrashIcon from '../svg/TrashIcon'; import CrossIcon from '../svg/CrossIcon'; -import manualSWR from '~/utils/fetchers'; import { useRecoilValue } from 'recoil'; +import { useDeleteConversationMutation } from '~/data-provider'; import store from '~/store'; @@ -10,13 +10,23 @@ export default function DeleteButton({ conversationId, renaming, cancelHandler, const currentConversation = useRecoilValue(store.conversation) || {}; const { newConversation } = store.useConversation(); const { refreshConversations } = store.useConversations(); - const { trigger } = manualSWR(`/api/convos/clear`, 'post', () => { - if (currentConversation?.conversationId == conversationId) newConversation(); - refreshConversations(); - retainView(); - }); - const clickHandler = () => trigger({ conversationId, source: 'button' }); + const deleteConvoMutation = useDeleteConversationMutation(conversationId); + + useEffect(() => { + if(deleteConvoMutation.isSuccess) { + if (currentConversation?.conversationId == conversationId) newConversation(); + + refreshConversations(); + retainView(); + } + }, [deleteConvoMutation.isSuccess]); + + + const clickHandler = () => { + deleteConvoMutation.mutate({conversationId, source: 'button' }); + }; + const handler = renaming ? cancelHandler : clickHandler; return ( diff --git a/client/src/data-provider/react-query-service.ts b/client/src/data-provider/react-query-service.ts index 2f25033825..ff05a72672 100644 --- a/client/src/data-provider/react-query-service.ts +++ b/client/src/data-provider/react-query-service.ts @@ -8,7 +8,6 @@ import { } from "@tanstack/react-query"; import * as t from "./types"; import * as dataService from "./data-service"; -import store from '~/store'; export enum QueryKeys { messages = "messsages", @@ -74,16 +73,27 @@ export const useUpdateConversationMutation = ( { onSuccess: () => { queryClient.invalidateQueries([QueryKeys.conversation, id]); - queryClient.invalidateQueries([QueryKeys.allConversations, id]); + queryClient.invalidateQueries([QueryKeys.allConversations]); }, } ); }; -// export const useDeleteConversationMutation = ( -// id: string -// ): UseMutationResult< - +export const useDeleteConversationMutation = ( + id: string +): UseMutationResult => { + const queryClient = useQueryClient(); + return useMutation( + (payload: t.TDeleteConversationRequest) => + dataService.deleteConversation(payload), + { + onSuccess: () => { + queryClient.invalidateQueries([QueryKeys.conversation, id]); + queryClient.invalidateQueries([QueryKeys.allConversations]); + }, + } + ); +}; export const useUpdateCustomGptMutation = (): UseMutationResult< t.TUpdateCustomGptResponse, diff --git a/client/src/data-provider/request.ts b/client/src/data-provider/request.ts index dcfb818d08..13ad78b2bd 100644 --- a/client/src/data-provider/request.ts +++ b/client/src/data-provider/request.ts @@ -1,13 +1,13 @@ import axios, { AxiosRequestConfig } from "axios"; async function _get(url: string, options?: AxiosRequestConfig): Promise { - const response = await axios.get(url, { withCredentials: true, ...options}); + const response = await axios.get(url, {...options}); return response.data; } -async function _post(url: string, data?: any) { - const response = await axios.post(url, JSON.stringify(data), { - withCredentials: true, +async function _post(url: string, arg?: any) { + const modifiedData = {arg, withCredentials: true} + const response = await axios.post(url, modifiedData, { headers: { "Content-Type": "application/json" }, }); return response.data; @@ -19,7 +19,6 @@ async function _postMultiPart( options?: AxiosRequestConfig ) { const response = await axios.post(url, formData, { - withCredentials: true, ...options, headers: { "Content-Type": "multipart/form-data" }, }); @@ -28,14 +27,13 @@ async function _postMultiPart( async function _put(url: string, data?: any) { const response = await axios.put(url, JSON.stringify(data), { - withCredentials: true, headers: { "Content-Type": "application/json" }, }); return response.data; } async function _delete(url: string): Promise { - const response = await axios.delete(url, { withCredentials: true }); + const response = await axios.delete(url); return response.data; } @@ -43,13 +41,12 @@ async function _deleteWithOptions( url: string, options?: AxiosRequestConfig ): Promise { - const response = await axios.delete(url, { withCredentials: true, ...options}); + const response = await axios.delete(url, {...options}); return response.data; } async function _patch(url: string, data?: any) { const response = await axios.patch(url, JSON.stringify(data), { - withCredentials: true, headers: { "Content-Type": "application/json" }, }); return response.data; diff --git a/client/src/data-provider/types.ts b/client/src/data-provider/types.ts index fb60f4e37f..59b753c832 100644 --- a/client/src/data-provider/types.ts +++ b/client/src/data-provider/types.ts @@ -106,10 +106,6 @@ export type TGetMessagesResponse = { data: TMessage[] }; -export type TDeleteConversationRequest = { - conversationId: string -}; - export type TAICompletionRequest = { chatGptLabel?: string, conversationId: string, @@ -150,8 +146,10 @@ export type TConversationUpdate = { conversationId: string, title?: string }; + export type TUpdateConversationRequest = { - arg: {}, + conversationId: string, + title: string, withCredentials?: boolean }; @@ -159,6 +157,20 @@ export type TUpdateConversationResponse = { data: TConversation }; +export type TDeleteConversationRequest = { + conversationId: string, + source: string +} + +export type TDeleteConversationResponse = { + acknowledged: boolean, + deletedCount: number, + messages: { + acknowledged: boolean, + deletedCount: number + } +}; + export type TUpdateCustomGptRequest = { value: string, chatGptLabel: string, From dd0a91a9f6a34ffeef0d72f7eb179b972d341b4c Mon Sep 17 00:00:00 2001 From: Daniel D Orlando Date: Mon, 3 Apr 2023 12:55:24 -0700 Subject: [PATCH 11/36] add RQ to clear all conversations --- client/src/components/Nav/ClearConvos.jsx | 22 +++++++------- .../src/data-provider/react-query-service.ts | 29 ++++--------------- client/src/data-provider/types.ts | 4 +-- 3 files changed, 19 insertions(+), 36 deletions(-) diff --git a/client/src/components/Nav/ClearConvos.jsx b/client/src/components/Nav/ClearConvos.jsx index 3cf6291c48..31a41e831b 100644 --- a/client/src/components/Nav/ClearConvos.jsx +++ b/client/src/components/Nav/ClearConvos.jsx @@ -1,27 +1,27 @@ -import React from 'react'; +import { useEffect } from 'react'; import store from '~/store'; import TrashIcon from '../svg/TrashIcon'; -import { useSWRConfig } from 'swr'; -import manualSWR from '~/utils/fetchers'; import { Dialog, DialogTrigger } from '../ui/Dialog.tsx'; import DialogTemplate from '../ui/DialogTemplate'; +import { useDeleteConversationMutation } from '~/data-provider'; export default function ClearConvos() { const { newConversation } = store.useConversation(); const { refreshConversations } = store.useConversations(); - const { mutate } = useSWRConfig(); - - const { trigger } = manualSWR(`/api/convos/clear`, 'post', () => { - newConversation(); - refreshConversations(); - mutate(`/api/convos`); - }); + const clearConvosMutation = useDeleteConversationMutation(); const clickHandler = () => { console.log('Clearing conversations...'); - trigger({}); + clearConvosMutation.mutate({}); }; + useEffect(() => { + if (clearConvosMutation.isSuccess) { + newConversation(); + refreshConversations(); + } + }, [clearConvosMutation.isSuccess]); + return ( diff --git a/client/src/data-provider/react-query-service.ts b/client/src/data-provider/react-query-service.ts index ff05a72672..afb05d45f8 100644 --- a/client/src/data-provider/react-query-service.ts +++ b/client/src/data-provider/react-query-service.ts @@ -61,11 +61,7 @@ export const useGetConversationByIdQuery = ( export const useUpdateConversationMutation = ( id: string ): UseMutationResult< - t.TUpdateConversationResponse, - unknown, - t.TUpdateConversationRequest, - unknown -> => { + t.TUpdateConversationResponse> => { const queryClient = useQueryClient(); return useMutation( (payload: t.TUpdateConversationRequest) => @@ -80,8 +76,8 @@ export const useUpdateConversationMutation = ( }; export const useDeleteConversationMutation = ( - id: string -): UseMutationResult => { + id?: string +): UseMutationResult => { const queryClient = useQueryClient(); return useMutation( (payload: t.TDeleteConversationRequest) => @@ -95,12 +91,7 @@ export const useDeleteConversationMutation = ( ); }; -export const useUpdateCustomGptMutation = (): UseMutationResult< - t.TUpdateCustomGptResponse, - unknown, - t.TUpdateCustomGptRequest, - unknown -> => { +export const useUpdateCustomGptMutation = (): UseMutationResult => { const queryClient = useQueryClient(); return useMutation( (payload: t.TUpdateCustomGptRequest) => @@ -124,12 +115,7 @@ export const useGetCustomGptsQuery = (): QueryObserverResult< }); }; -export const useDeleteCustomGptMutation = (): UseMutationResult< - t.TDeleteCustomGptResponse, - unknown, - t.TDeleteCustomGptRequest, - unknown -> => { +export const useDeleteCustomGptMutation = (): UseMutationResult => { const queryClient = useQueryClient(); return useMutation( (payload: t.TDeleteCustomGptRequest) => @@ -142,10 +128,7 @@ export const useDeleteCustomGptMutation = (): UseMutationResult< ); }; -export const useGetModelsQuery = (): QueryObserverResult< - t.TGetModelsResponse, - unknown -> => { +export const useGetModelsQuery = (): QueryObserverResult => { return useQuery([QueryKeys.models], () => dataService.getModels(), { refetchOnWindowFocus: false, refetchOnReconnect: false, diff --git a/client/src/data-provider/types.ts b/client/src/data-provider/types.ts index 59b753c832..d430833bd0 100644 --- a/client/src/data-provider/types.ts +++ b/client/src/data-provider/types.ts @@ -158,8 +158,8 @@ export type TUpdateConversationResponse = { }; export type TDeleteConversationRequest = { - conversationId: string, - source: string + conversationId?: string, + source?: string } export type TDeleteConversationResponse = { From 573112de7b9981a1ee1f26be9bca268404df651f Mon Sep 17 00:00:00 2001 From: Daniel D Orlando Date: Mon, 3 Apr 2023 14:57:30 -0700 Subject: [PATCH 12/36] fix: fix conversations in nav (put refreshConvoHint back) --- client/src/components/Conversations/Conversation.jsx | 1 - client/src/components/Nav/index.jsx | 8 +++++++- client/src/data-provider/data-service.ts | 8 -------- client/src/data-provider/react-query-service.ts | 3 +-- client/src/data-provider/types.ts | 4 ---- 5 files changed, 8 insertions(+), 16 deletions(-) diff --git a/client/src/components/Conversations/Conversation.jsx b/client/src/components/Conversations/Conversation.jsx index 543e9c9161..65dcdad2a2 100644 --- a/client/src/components/Conversations/Conversation.jsx +++ b/client/src/components/Conversations/Conversation.jsx @@ -4,7 +4,6 @@ import { useUpdateConversationMutation } from '~/data-provider'; import RenameButton from './RenameButton'; import DeleteButton from './DeleteButton'; import ConvoIcon from '../svg/ConvoIcon'; -import manualSWR from '~/utils/fetchers'; import store from '~/store'; diff --git a/client/src/components/Nav/index.jsx b/client/src/components/Nav/index.jsx index 4bb12082a5..6e1530cdbb 100644 --- a/client/src/components/Nav/index.jsx +++ b/client/src/components/Nav/index.jsx @@ -37,7 +37,7 @@ export default function Nav({ navVisible, setNavVisible }) { const conversation = useRecoilValue(store.conversation); const { conversationId } = conversation || {}; const setSearchResultMessages = useSetRecoilState(store.searchResultMessages); - + const refreshConversationsHint = useRecoilValue(store.refreshConversationsHint); const { refreshConversations } = store.useConversations(); const [isFetching, setIsFetching] = useState(false); @@ -112,6 +112,12 @@ export default function Nav({ navVisible, setNavVisible }) { } }, [getConversationsQuery.isSuccess, getConversationsQuery.data, isSearching, pageNumber]); + useEffect(() => { + if (!isSearching) { + getConversationsQuery.refetch(); + } + }, [pageNumber, conversationId, refreshConversationsHint]); + const moveToTop = () => { const container = containerRef.current; if (container) { diff --git a/client/src/data-provider/data-service.ts b/client/src/data-provider/data-service.ts index 621831a0ee..adb9cca9f6 100644 --- a/client/src/data-provider/data-service.ts +++ b/client/src/data-provider/data-service.ts @@ -2,10 +2,6 @@ import * as t from './types'; import request from './request'; import * as endpoints from './endpoints'; -export function getOpenAIModels(): Promise { - return request.get(endpoints.openAiModels()); -} - export function postAICompletion(payload: t.TAICompletionRequest) { return request.post(endpoints.getAICompletion(), payload); } @@ -19,10 +15,6 @@ export function deleteConversation(payload: t.TDeleteConversationRequest) { return request.post(endpoints.deleteConversation(), payload); } -export function clearAllConversations() { - return request.post(endpoints.deleteConversation()); -} - export function getMessagesByConvoId(id: string): Promise { return request.get(endpoints.getMessages(id)); } diff --git a/client/src/data-provider/react-query-service.ts b/client/src/data-provider/react-query-service.ts index afb05d45f8..a0080b3b73 100644 --- a/client/src/data-provider/react-query-service.ts +++ b/client/src/data-provider/react-query-service.ts @@ -52,7 +52,6 @@ export const useGetConversationByIdQuery = ( refetchOnWindowFocus: false, refetchOnReconnect: false, refetchOnMount: false, - enabled: false, ...config } ); @@ -148,7 +147,7 @@ export const useClearConversationsMutation = (): UseMutationResult => { export const useGetConversationsQuery = (pageNumber: string): QueryObserverResult => { return useQuery([QueryKeys.allConversations, pageNumber], () => dataService.getConversations(pageNumber), { - refetchOnWindowFocus: false, + // refetchOnWindowFocus: false, refetchOnReconnect: false, refetchOnMount: false, } diff --git a/client/src/data-provider/types.ts b/client/src/data-provider/types.ts index d430833bd0..82324be8fd 100644 --- a/client/src/data-provider/types.ts +++ b/client/src/data-provider/types.ts @@ -186,10 +186,6 @@ export type TDeleteCustomGptRequest = { export type TDeleteCustomGptResponse = {}; -export type TClearConversationsRequest = {}; - -export type TClearConversationsResponse = {}; - export type TGetCustomGptsResponse = {}; export type TSearchResults = {}; \ No newline at end of file From 1cb8ef980392a52132259d6d2f52a74f25f23074 Mon Sep 17 00:00:00 2001 From: Daniel D Orlando Date: Mon, 3 Apr 2023 16:46:35 -0700 Subject: [PATCH 13/36] feat: convert Chat.jsx to RQ --- client/src/data-provider/data-service.ts | 4 - client/src/data-provider/endpoints.ts | 3 - .../src/data-provider/react-query-service.ts | 17 ++++ client/src/data-provider/types.ts | 16 ++-- client/src/routes/Chat.jsx | 45 +++++++---- client/src/routes/ChatRQTemp.jsx | 79 ------------------- 6 files changed, 53 insertions(+), 111 deletions(-) delete mode 100644 client/src/routes/ChatRQTemp.jsx diff --git a/client/src/data-provider/data-service.ts b/client/src/data-provider/data-service.ts index adb9cca9f6..b71479affc 100644 --- a/client/src/data-provider/data-service.ts +++ b/client/src/data-provider/data-service.ts @@ -2,10 +2,6 @@ import * as t from './types'; import request from './request'; import * as endpoints from './endpoints'; -export function postAICompletion(payload: t.TAICompletionRequest) { - return request.post(endpoints.getAICompletion(), payload); -} - export function getConversations(pageNumber: string): Promise { return request.get(endpoints.getConversations(pageNumber)); } diff --git a/client/src/data-provider/endpoints.ts b/client/src/data-provider/endpoints.ts index 72f2ac608b..d67417fc30 100644 --- a/client/src/data-provider/endpoints.ts +++ b/client/src/data-provider/endpoints.ts @@ -1,6 +1,3 @@ -export const openAiModels = () => { - return `/api/open-ai-models`; -}; export const getModels = () => { return `/api/models`; diff --git a/client/src/data-provider/react-query-service.ts b/client/src/data-provider/react-query-service.ts index a0080b3b73..bdd523c46e 100644 --- a/client/src/data-provider/react-query-service.ts +++ b/client/src/data-provider/react-query-service.ts @@ -57,6 +57,23 @@ export const useGetConversationByIdQuery = ( ); } +//This isn't ideal because its just a query and we're using mutation, but it was the only way +//to make it work with how the Chat component is structured +export const useGetConversationByIdMutation = ( + id: string, + callback: (data: t.TConversation) => void +): UseMutationResult => { + const queryClient = useQueryClient(); + return useMutation(() => dataService.getConversationById(id), + { + onSuccess: (res: t.TConversation) => { + callback(res); + queryClient.invalidateQueries([QueryKeys.conversation, id]); + }, + } + ); +}; + export const useUpdateConversationMutation = ( id: string ): UseMutationResult< diff --git a/client/src/data-provider/types.ts b/client/src/data-provider/types.ts index 82324be8fd..3f3708801e 100644 --- a/client/src/data-provider/types.ts +++ b/client/src/data-provider/types.ts @@ -1,9 +1,9 @@ export type TMessage = { messageId: string, conversationId: string, - conversationSignature: string | null, + // conversationSignature: string | null, clientId: string, - invocationId: string, + // invocationId: string, parentMessageId: string, sender: string, text: string, @@ -11,12 +11,12 @@ export type TMessage = { error: boolean, createdAt: string, updatedAt: string, - searchResult: string[], - submitting: boolean, - children?: any[] | undefined, - bgColor?: string, - model?: string, - cancelled?: boolean + // searchResult: string[], + // submitting: boolean, + // children?: any[] | undefined, + // bgColor?: string, + // model?: string, + // cancelled?: boolean }; export type TMessageTreeNode = {} diff --git a/client/src/routes/Chat.jsx b/client/src/routes/Chat.jsx index 3feab50a0f..88202b8221 100644 --- a/client/src/routes/Chat.jsx +++ b/client/src/routes/Chat.jsx @@ -1,4 +1,4 @@ -import React, { useEffect } from 'react'; +import { useEffect } from 'react'; import { useNavigate, useParams } from 'react-router-dom'; import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'; @@ -7,7 +7,7 @@ import Messages from '../components/Messages'; import TextChat from '../components/Input'; import store from '~/store'; -import manualSWR from '~/utils/fetchers'; +import { useGetMessagesByConvoId, useGetConversationByIdMutation } from '~/data-provider'; export default function Chat() { const searchQuery = useRecoilValue(store.searchQuery); @@ -18,9 +18,9 @@ export default function Chat() { const { conversationId } = useParams(); const navigate = useNavigate(); - const { trigger: messagesTrigger } = manualSWR(`/api/messages/${conversation?.conversationId}`, 'get'); - - const { trigger: conversationTrigger } = manualSWR(`/api/convos/${conversationId}`, 'get'); + //disabled by default, we only enable it when messagesTree is null + const messagesQuery = useGetMessagesByConvoId(conversationId, { enabled: false }); + const getConversationMutation = useGetConversationByIdMutation(conversationId, setConversation); // when conversation changed or conversationId (in url) changed useEffect(() => { @@ -31,13 +31,7 @@ export default function Chat() { newConversation(); } else if (conversationId) { // fetch it from server - conversationTrigger() - .then(setConversation) - .catch(error => { - console.error('failed to fetch the conversation'); - console.error(error); - newConversation(); - }); + getConversationMutation.mutate(); setMessages(null); } else { navigate(`/chat/new`); @@ -51,13 +45,30 @@ export default function Chat() { } }, [conversation, conversationId]); - // when messagesTree is null (<=> messages is null) - // we need to fetch message list from server useEffect(() => { - if (messagesTree === null) { - messagesTrigger().then(setMessages); + if(getConversationMutation.isError) { + console.error('failed to fetch the conversation'); + console.error(getConversationMutation.error); + newConversation(); } - }, [conversation?.conversationId]); + }, [getConversationMutation.isError, newConversation]); + + + useEffect(() => { + if (messagesTree === null && conversation?.conversationId) { + messagesQuery.refetch(conversation?.conversationId); + } + }, [conversation?.conversationId, messagesQuery, messagesTree]); + + useEffect(() => { + if (messagesQuery.data) { + setMessages(messagesQuery.data); + } else if(messagesQuery.isError) { + console.error('failed to fetch the messages'); + console.error(messagesQuery.error); + setMessages(null); + } + }, [messagesQuery.data, messagesQuery.isError, setMessages]); // if not a conversation if (conversation?.conversationId === 'search') return null; diff --git a/client/src/routes/ChatRQTemp.jsx b/client/src/routes/ChatRQTemp.jsx deleted file mode 100644 index f2a88c2483..0000000000 --- a/client/src/routes/ChatRQTemp.jsx +++ /dev/null @@ -1,79 +0,0 @@ -import React, { useEffect } from 'react'; -import { useNavigate, useParams } from 'react-router-dom'; -import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'; - -import Landing from '../components/ui/Landing'; -import Messages from '../components/Messages'; -import TextChat from '../components/Input'; - -import store from '~/store'; -import { useGetMessagesByConvoId, useGetConversationByIdQuery } from '~/data-provider'; - -export default function Chat() { - const searchQuery = useRecoilValue(store.searchQuery); - const [conversation, setConversation] = useRecoilState(store.conversation); - const setMessages = useSetRecoilState(store.messages); - const messagesTree = useRecoilValue(store.messagesTree); - const { newConversation } = store.useConversation(); - const { conversationId } = useParams(); - const navigate = useNavigate(); - - //disabled by default, we only enable it when messagesTree is null - const messagesQuery = useGetMessagesByConvoId(conversation?.conversationId, { enabled: false }); - - const conversationQuery = useGetConversationByIdQuery( - conversation?.conversationId, { - enabled: !!conversation?.conversationId && - conversation?.conversationId !== 'search' && - conversation?.conversationId !== 'new' - }); - - // when conversation changed or conversationId (in url) changed - useEffect(() => { - if (conversation === null) { - // no current conversation, we need to do something - if (conversationId === 'new') { - // create new - newConversation(); - } else if (conversationQuery.data) { - // fetch it from server - setConversation(conversationQuery.data); - setMessages(null); - } else if (conversationQuery.isError) { - console.error('failed to fetch the conversation'); - console.error(conversationQuery.error); - newConversation(); - } else { - navigate(`/chat/new`); - } - } else if (conversation?.conversationId === 'search') { - // jump to search page - navigate(`/search/${searchQuery}`); - } else if (conversation?.conversationId !== conversationId) { - // conversationId (in url) should always follow conversation?.conversationId, unless conversation is null - navigate(`/chat/${conversation?.conversationId}`); - } - }, [conversation, conversationId, newConversation, navigate, searchQuery, setMessages, conversationQuery]); - - // when messagesTree is null (<=> messages is null) - // we need to fetch message list from server - useEffect(() => { - if (messagesTree === null) { - messagesQuery.refetch(conversation?.conversationId); - } - }, [conversation?.conversationId, messagesQuery, messagesTree]); - - // if not a conversation - if (conversation?.conversationId === 'search') return null; - // if conversationId not match - if (conversation?.conversationId !== conversationId) return null; - // if conversationId is null - if (!conversationId) return null; - - return ( - <> - {conversationId === 'new' && !messagesTree?.length ? : } - - - ); -} From 9e708225aa504f7d6c5698281e0e8fa3444e9922 Mon Sep 17 00:00:00 2001 From: Daniel D Orlando Date: Wed, 5 Apr 2023 08:44:00 -0700 Subject: [PATCH 14/36] Add preset mutation --- client/src/App.jsx | 19 +------------------ .../components/Conversations/Conversation.jsx | 2 -- .../Endpoints/SaveAsPresetDialog.jsx | 19 +++++-------------- 3 files changed, 6 insertions(+), 34 deletions(-) diff --git a/client/src/App.jsx b/client/src/App.jsx index beb796c1bf..b27a410bd3 100644 --- a/client/src/App.jsx +++ b/client/src/App.jsx @@ -6,7 +6,7 @@ import Search from './routes/Search'; import store from './store'; import { useRecoilState, useSetRecoilState } from 'recoil'; import { ScreenshotProvider } from './utils/screenshotContext.jsx'; -import { useGetSearchEnabledQuery, useGetUserQuery, useGetModelsQuery, useGetEndpointsQuery, useGetPresetsQuery} from '~/data-provider'; +import { useGetSearchEnabledQuery, useGetUserQuery, useGetEndpointsQuery, useGetPresetsQuery} from '~/data-provider'; const router = createBrowserRouter([ { @@ -42,7 +42,6 @@ const App = () => { const searchEnabledQuery = useGetSearchEnabledQuery(); const userQuery = useGetUserQuery(); - const modelsQuery = useGetModelsQuery(); const endpointsQuery = useGetEndpointsQuery(); const presetsQuery = useGetPresetsQuery(); @@ -78,22 +77,6 @@ const App = () => { } }, [userQuery.data, setUser, userQuery.error]); - useEffect(() => { - const { data, error } = modelsQuery; - if (error) { - console.error("Failed to get models", error); - } - if (data) { - const filter = { - chatgpt: data?.hasOpenAI, - chatgptCustom: data?.hasOpenAI, - bingai: data?.hasBing, - sydney: data?.hasBing, - chatgptBrowser: data?.hasChatGpt - }; - setModelsFilter(filter); - } - }, [modelsQuery.data, setModelsFilter, modelsQuery.error, modelsQuery]); if (user) return ( diff --git a/client/src/components/Conversations/Conversation.jsx b/client/src/components/Conversations/Conversation.jsx index 65dcdad2a2..ed13eea268 100644 --- a/client/src/components/Conversations/Conversation.jsx +++ b/client/src/components/Conversations/Conversation.jsx @@ -23,8 +23,6 @@ export default function Conversation({ conversation, retainView }) { const [titleInput, setTitleInput] = useState(title); - const rename = manualSWR(`/api/convos/update`, 'post'); - const clickHandler = async () => { if (currentConversation?.conversationId === conversationId) { return; diff --git a/client/src/components/Endpoints/SaveAsPresetDialog.jsx b/client/src/components/Endpoints/SaveAsPresetDialog.jsx index e06e9ad807..9fd51153e9 100644 --- a/client/src/components/Endpoints/SaveAsPresetDialog.jsx +++ b/client/src/components/Endpoints/SaveAsPresetDialog.jsx @@ -1,19 +1,18 @@ -import React, { useEffect, useState } from 'react'; -import { useSetRecoilState, useRecoilValue } from 'recoil'; -import axios from 'axios'; +import { useEffect, useState } from 'react'; +import { useRecoilValue } from 'recoil'; import DialogTemplate from '../ui/DialogTemplate'; import { Dialog } from '../ui/Dialog.tsx'; import { Input } from '../ui/Input.tsx'; import { Label } from '../ui/Label.tsx'; import { cn } from '~/utils/'; import cleanupPreset from '~/utils/cleanupPreset'; - +import { useCreatePresetMutation } from '~/data-provider'; import store from '~/store'; const SaveAsPresetDialog = ({ open, onOpenChange, preset }) => { const [title, setTitle] = useState(preset?.title || 'My Preset'); - const setPresets = useSetRecoilState(store.presets); const endpointsFilter = useRecoilValue(store.endpointsFilter); + const createPresetMutation = useCreatePresetMutation(); const defaultTextProps = 'rounded-md border border-gray-300 bg-transparent text-sm shadow-[0_0_10px_rgba(0,0,0,0.10)] outline-none placeholder:text-gray-400 focus:outline-none focus:ring-gray-400 focus:ring-opacity-20 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 dark:border-gray-400 dark:bg-gray-700 dark:text-gray-50 dark:shadow-[0_0_15px_rgba(0,0,0,0.10)] dark:focus:border-gray-400 dark:focus:outline-none dark:focus:ring-0 dark:focus:ring-gray-400 dark:focus:ring-offset-0'; @@ -26,15 +25,7 @@ const SaveAsPresetDialog = ({ open, onOpenChange, preset }) => { }, endpointsFilter }); - - axios({ - method: 'post', - url: '/api/presets', - data: _preset, - withCredentials: true - }).then(res => { - setPresets(res?.data); - }); + createPresetMutation.mutate(_preset); }; useEffect(() => { From 93b685a1a2ab5f21531eb3d6a0626b7a9649f025 Mon Sep 17 00:00:00 2001 From: Daniel D Orlando Date: Wed, 5 Apr 2023 08:44:28 -0700 Subject: [PATCH 15/36] change endpoints.ts to api-endpoints.ts --- client/src/data-provider/api-endpoints.ts | 51 +++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 client/src/data-provider/api-endpoints.ts diff --git a/client/src/data-provider/api-endpoints.ts b/client/src/data-provider/api-endpoints.ts new file mode 100644 index 0000000000..3ca970e35b --- /dev/null +++ b/client/src/data-provider/api-endpoints.ts @@ -0,0 +1,51 @@ +export const user = () => { + return `/api/me`; +}; + +export const messages = (id: string) => { + return `/api/messages/${id}`; +}; + +export const conversations = (pageNumber: string) => { + return `/api/convos?pageNumber=${pageNumber}`; +}; + +export const conversationById = (id: string) => { + return `/api/convos/${id}`; +}; + +export const updateConversation = () => { + return `/api/convos/update`; +}; + +export const deleteConversation = () => { + return `/api/convos/clear`; +}; + +export const prompts = () => { + return `/api/prompts`; +}; + +export const generateTitle = () => { + return `/api/convos/gen_title`; +}; + +export const search = (q: string, pageNumber: string) => { + return `/api/search?q=${q}&pageNumber=${pageNumber}`; +} + +export const searchEnabled = () => { + return `/api/search/enable`; +} + +export const presets = () => { + return `/api/presets`; +} + +export const deletePresets = () => { + return `/api/presets/delete`; +} + +export const aiEndpoints = () => { + return `/api/endpoints`; +} From 68041d68ae96a7f8e52a49f86ecf353fe39cb420 Mon Sep 17 00:00:00 2001 From: Daniel D Orlando Date: Wed, 5 Apr 2023 08:45:07 -0700 Subject: [PATCH 16/36] fix types --- client/src/data-provider/types.ts | 141 ++++++++++-------------------- 1 file changed, 46 insertions(+), 95 deletions(-) diff --git a/client/src/data-provider/types.ts b/client/src/data-provider/types.ts index 3f3708801e..d4cb3926c8 100644 --- a/client/src/data-provider/types.ts +++ b/client/src/data-provider/types.ts @@ -38,11 +38,15 @@ export enum EModelEndpoint { export type TConversation = { conversationId: string; title: string; - user: string | null; + user?: string; endpoint: EModelEndpoint; - model: string; // for azureOpenAI, openAI, chatGPTBrowser only, eg. gpt-3.5-turbo + suggestions?: string[]; + messages?: TMessage[]; + createdAt: string; + updatedAt: string; // for azureOpenAI, openAI only chatGptLabel?: string; + model?: string; promptPrefix?: string; temperature?: number; top_p?: number; @@ -51,40 +55,36 @@ export type TConversation = { jailbreak?: boolean; jailbreakConversationId?: string; conversationSignature?: string; + parentMessageId?: string; clientId?: string; invocationId?: string; toneStyle?: string; - suggestions?: string[]; - messages?: TMessage[]; - createdAt: string; - updatedAt: string; } -export type TPrompt = { +export type TPreset = { title: string, - prompt: string, - category: string, - createdAt: string, - updatedAt: string -}; - -export type TCustomGpt = { - chatGptLabel: string, - promptPrefix: string, - value: string, - createdAt: string, - updatedAt: string, - _id: string -}; - -export type TModel = { - _id: string, - name: string, - value: string, - model: string, + endpoint: EModelEndpoint, + conversationSignature?: string, + createdAt?: string, + updatedAt?: string, + presetId?: string, + user?: string, + // for azureOpenAI, openAI only chatGptLabel?: string, - promptPrefix?: string -}; + frequence_penalty?: number, + model?: string, + presence_penalty?: number, + promptPrefix?: string, + temperature?: number, + top_p?: number, + //for BingAI + clientId?: string, + invocationId?: number, + jailbreak?: boolean, + jailbreakPresetId?: string, + presetSignature?: string, + toneStyle?: string, +} export type TUser = { username: string, @@ -98,59 +98,9 @@ export type TGetConversationsResponse = { pages: string | number }; -export type TGetConversationResponse = { - data: TConversation -}; - -export type TGetMessagesResponse = { - data: TMessage[] -}; - -export type TAICompletionRequest = { - chatGptLabel?: string, - conversationId: string, - current: boolean, - isCreatedByUser: boolean, - model: string, - messageId: string, - parentMessageId: string, - overrideParentMessageId?: boolean, - promptPrefix?: string, - sender: string, - text: string -}; - -export type TGetModelsResponse = { - hasOpenAI: boolean, - hasChatGpt: boolean, - hasBing: boolean -}; - -export type TOpenAIModel = { - object: string, - id: string, - ready: boolean, - owner: string, - created: string | null, - permissions: string[] | null -}; - -export type TOpenAIModels = { - models: { - object: string, - data: TOpenAIModel[] - } -}; - -export type TConversationUpdate = { - conversationId: string, - title?: string -}; - export type TUpdateConversationRequest = { conversationId: string, title: string, - withCredentials?: boolean }; export type TUpdateConversationResponse = { @@ -171,21 +121,22 @@ export type TDeleteConversationResponse = { } }; -export type TUpdateCustomGptRequest = { - value: string, - chatGptLabel: string, - promptPrefix?: string, - prevLabel?: string +export type TSearchResponse = { + conversations: TConversation[], + messages: TMessage[], + pageNumber: string, + pageSize: string | number, + pages: string | number + filter: {} }; -export type TUpdateCustomGptResponse = {}; - -export type TDeleteCustomGptRequest = { - id: string -}; - -export type TDeleteCustomGptResponse = {}; - -export type TGetCustomGptsResponse = {}; - -export type TSearchResults = {}; \ No newline at end of file +export type TEndpoints = { + azureOpenAI: boolean, + bingAI: boolean, + ChatGptBrowser: { + availableModels: [] + } + OpenAI: { + availableModels: [] + } +}; \ No newline at end of file From f2d18c81fc000054670d4ed8f1216cf31e269cef Mon Sep 17 00:00:00 2001 From: Daniel D Orlando Date: Wed, 5 Apr 2023 08:45:43 -0700 Subject: [PATCH 17/36] add deletePresetMutation to NewConverationMenu --- .../Input/Endpoints/NewConversationMenu.jsx | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/client/src/components/Input/Endpoints/NewConversationMenu.jsx b/client/src/components/Input/Endpoints/NewConversationMenu.jsx index 2d5f35c85a..a35a79693c 100644 --- a/client/src/components/Input/Endpoints/NewConversationMenu.jsx +++ b/client/src/components/Input/Endpoints/NewConversationMenu.jsx @@ -1,12 +1,11 @@ -import React, { useState, useEffect } from 'react'; +import { useState, useEffect } from 'react'; import { useRecoilValue, useRecoilState } from 'recoil'; import EditPresetDialog from '../../Endpoints/EditPresetDialog'; import EndpointItems from './EndpointItems'; import PresetItems from './PresetItems'; import FileUpload from './FileUpload'; import getIcon from '~/utils/getIcon'; -import manualSWR, { handleFileSelected } from '~/utils/fetchers'; - +import { useDeleteAllPresetsMutation } from '~/data-provider'; import { Button } from '../../ui/Button.tsx'; import { DropdownMenu, @@ -22,14 +21,11 @@ import DialogTemplate from '../../ui/DialogTemplate'; import store from '~/store'; export default function NewConversationMenu() { - // const [modelSave, setModelSave] = useState(false); const [menuOpen, setMenuOpen] = useState(false); const [presetModelVisible, setPresetModelVisible] = useState(false); const [preset, setPreset] = useState(false); - // const models = useRecoilValue(store.models); const availableEndpoints = useRecoilValue(store.availableEndpoints); - // const setCustomGPTModels = useSetRecoilState(store.customGPTModels); const [presets, setPresets] = useRecoilState(store.presets); const conversation = useRecoilValue(store.conversation) || {}; @@ -37,10 +33,7 @@ export default function NewConversationMenu() { // const { model, promptPrefix, chatGptLabel, conversationId } = conversation; const { newConversation } = store.useConversation(); - const { trigger: clearPresetsTrigger } = manualSWR(`/api/presets/delete`, 'post', res => { - console.log(res); - setPresets(res.data); - }); + const deletePresetsMutation = useDeleteAllPresetsMutation(); const importPreset = jsonData => { handleFileSelected(jsonData).then(setPresets); @@ -87,7 +80,7 @@ export default function NewConversationMenu() { }; const clearPreset = () => { - clearPresetsTrigger({}); + deletePresetsMutation.mutate(); }; const icon = getIcon({ From 3d0bfaef511f9220f9f88d0bd694c923c0d91717 Mon Sep 17 00:00:00 2001 From: Daniel D Orlando Date: Wed, 5 Apr 2023 08:47:18 -0700 Subject: [PATCH 18/36] Add presets and endpoints data services --- client/src/components/Nav/ClearConvos.jsx | 6 +- client/src/data-provider/data-service.ts | 42 ++++--- client/src/data-provider/endpoints.ts | 57 --------- .../src/data-provider/react-query-service.ts | 116 ++++++++++-------- client/src/data-provider/request.ts | 7 +- 5 files changed, 102 insertions(+), 126 deletions(-) delete mode 100644 client/src/data-provider/endpoints.ts diff --git a/client/src/components/Nav/ClearConvos.jsx b/client/src/components/Nav/ClearConvos.jsx index 31a41e831b..1236631d9f 100644 --- a/client/src/components/Nav/ClearConvos.jsx +++ b/client/src/components/Nav/ClearConvos.jsx @@ -3,16 +3,16 @@ import store from '~/store'; import TrashIcon from '../svg/TrashIcon'; import { Dialog, DialogTrigger } from '../ui/Dialog.tsx'; import DialogTemplate from '../ui/DialogTemplate'; -import { useDeleteConversationMutation } from '~/data-provider'; +import { useClearConversationsMutation } from '~/data-provider'; export default function ClearConvos() { const { newConversation } = store.useConversation(); const { refreshConversations } = store.useConversations(); - const clearConvosMutation = useDeleteConversationMutation(); + const clearConvosMutation = useClearConversationsMutation(); const clickHandler = () => { console.log('Clearing conversations...'); - clearConvosMutation.mutate({}); + clearConvosMutation.mutate(); }; useEffect(() => { diff --git a/client/src/data-provider/data-service.ts b/client/src/data-provider/data-service.ts index b71479affc..40aa98a001 100644 --- a/client/src/data-provider/data-service.ts +++ b/client/src/data-provider/data-service.ts @@ -1,44 +1,48 @@ import * as t from './types'; import request from './request'; -import * as endpoints from './endpoints'; +import * as endpoints from './api-endpoints'; export function getConversations(pageNumber: string): Promise { - return request.get(endpoints.getConversations(pageNumber)); + return request.get(endpoints.conversations(pageNumber)); } export function deleteConversation(payload: t.TDeleteConversationRequest) { //todo: this should be a DELETE request - return request.post(endpoints.deleteConversation(), payload); + return request.post(endpoints.deleteConversation(), {arg: payload}); +} + +export function clearAllConversations(): Promise { + return request.post(endpoints.deleteConversation(), {arg: {}}); } export function getMessagesByConvoId(id: string): Promise { - return request.get(endpoints.getMessages(id)); + return request.get(endpoints.messages(id)); } export function getConversationById(id: string): Promise { - return request.get(endpoints.getConversationById(id)); + return request.get(endpoints.conversationById(id)); } export function updateConversation( payload: t.TUpdateConversationRequest ): Promise { - return request.post(endpoints.updateConversation(), payload); + return request.post(endpoints.updateConversation(), {arg: payload}); } -export function updateCustomGpt(payload: t.TUpdateCustomGptRequest) { - return request.post(endpoints.customGpts(), payload); +export function getPresets(): Promise { + return request.get(endpoints.presets()); } -export function getCustomGpts(): Promise { - return request.get(endpoints.customGpts()); +export function createPreset(payload: t.TPreset): Promise { + return request.post(endpoints.presets(), payload); } -export function deleteCustomGpt(payload: t.TDeleteCustomGptRequest): Promise { - return request.post(endpoints.deleteCustomGpt(), payload); +export function updatePreset(payload: t.TPreset): Promise { + return request.post(endpoints.presets(), payload); } -export function getModels(): Promise { - return request.get(endpoints.getModels()); +export function deletePresets(): Promise { + return request.post(endpoints.deletePresets(), {arg: {}}); } export function getSearchEnabled(): Promise { @@ -60,6 +64,12 @@ type TSearchFetcherProps = { callback: (data: any) => void }; +export const searchConversations = async({ q, pageNumber, callback }: TSearchFetcherProps) => { + return request.get(endpoints.search(q, pageNumber)).then(({ data }) => { + callback(data); + }); +} + export const searchFetcher = async ({ pre, q, pageNumber, callback }: TSearchFetcherProps) => { pre(); //@ts-ignore @@ -67,3 +77,7 @@ export const searchFetcher = async ({ pre, q, pageNumber, callback }: TSearchFet console.log('search data', data); callback(data); }; + +export const getAIEndpoints = () => { + return request.get(endpoints.aiEndpoints()); +} \ No newline at end of file diff --git a/client/src/data-provider/endpoints.ts b/client/src/data-provider/endpoints.ts deleted file mode 100644 index d67417fc30..0000000000 --- a/client/src/data-provider/endpoints.ts +++ /dev/null @@ -1,57 +0,0 @@ - -export const getModels = () => { - return `/api/models`; -}; - -export const getAICompletion = () => { - return `/api/ask `; -}; - -export const user = () => { - return `/api/me`; -}; - -export const getMessages = (id: string) => { - return `/api/messages/${id}`; -}; - -export const getConversations = (pageNumber: string) => { - return `/api/convos?pageNumber=${pageNumber}`; -}; - -export const getConversationById = (id: string) => { - return `/api/convos/${id}`; -}; - -export const updateConversation = () => { - return `/api/convos/update`; -}; - -export const deleteConversation = () => { - return `/api/convos/clear`; -}; - -export const prompts = () => { - return `/api/prompts`; -}; - -export const customGpts = () => { - return `/api/customGpts`; -}; - -// TODO: turn this into a DELETE instead of POST on the backend -export const deleteCustomGpt = () => { - return `/api/customGpts/delete`; -}; - -export const generateTitle = () => { - return `/api/convos/gen_title`; -}; - -export const search = (q: string, pageNumber: string) => { - return `/api/search?q=${q}&pageNumber=${pageNumber}`; -} - -export const searchEnabled = () => { - return `/api/search/enable`; -} diff --git a/client/src/data-provider/react-query-service.ts b/client/src/data-provider/react-query-service.ts index bdd523c46e..1b08d45546 100644 --- a/client/src/data-provider/react-query-service.ts +++ b/client/src/data-provider/react-query-service.ts @@ -13,10 +13,11 @@ export enum QueryKeys { messages = "messsages", allConversations = "allConversations", conversation = "conversation", - models = "models", - customGpts = "customGpts", searchEnabled = "searchEnabled", user = "user", + endpoints = "endpoints", + presets = "presets", + searchResults = "searchResults", } export const useGetUserQuery = (): QueryObserverResult => { @@ -107,51 +108,6 @@ export const useDeleteConversationMutation = ( ); }; -export const useUpdateCustomGptMutation = (): UseMutationResult => { - const queryClient = useQueryClient(); - return useMutation( - (payload: t.TUpdateCustomGptRequest) => - dataService.updateCustomGpt(payload), - { - onSuccess: () => { - queryClient.invalidateQueries([QueryKeys.customGpts]); - }, - } - ); -}; - -export const useGetCustomGptsQuery = (): QueryObserverResult< - t.TCustomGpt[], - unknown -> => { - return useQuery([QueryKeys.customGpts], () => dataService.getCustomGpts(), { - refetchOnWindowFocus: false, - refetchOnReconnect: false, - refetchOnMount: false, - }); -}; - -export const useDeleteCustomGptMutation = (): UseMutationResult => { - const queryClient = useQueryClient(); - return useMutation( - (payload: t.TDeleteCustomGptRequest) => - dataService.deleteCustomGpt(payload), - { - onSuccess: () => { - queryClient.invalidateQueries([QueryKeys.customGpts]); - }, - } - ); -}; - -export const useGetModelsQuery = (): QueryObserverResult => { - return useQuery([QueryKeys.models], () => dataService.getModels(), { - refetchOnWindowFocus: false, - refetchOnReconnect: false, - refetchOnMount: false, - }); -}; - export const useClearConversationsMutation = (): UseMutationResult => { const queryClient = useQueryClient(); return useMutation(() => dataService.clearAllConversations(), { @@ -161,7 +117,7 @@ export const useClearConversationsMutation = (): UseMutationResult => { }); }; -export const useGetConversationsQuery = (pageNumber: string): QueryObserverResult => { +export const useGetConversationsQuery = (pageNumber: string): QueryObserverResult => { return useQuery([QueryKeys.allConversations, pageNumber], () => dataService.getConversations(pageNumber), { // refetchOnWindowFocus: false, @@ -181,3 +137,67 @@ export const useGetSearchEnabledQuery = (config?: UseQueryOptions): Que } ); } + +export const useGetEndpointsQuery = (): QueryObserverResult => { + return useQuery([QueryKeys.endpoints], () => + dataService.getAIEndpoints(), { + refetchOnWindowFocus: false, + refetchOnReconnect: false, + refetchOnMount: false, + } + ); +} + +export const useCreatePresetMutation = (): UseMutationResult => { + const queryClient = useQueryClient(); + return useMutation( + (payload: t.TCreatePresetRequest) => + dataService.createPreset(payload), + { + onSuccess: () => { + queryClient.invalidateQueries([QueryKeys.presets]); + }, + } + ); +}; + +export const useUpdatePresetMutation = (): UseMutationResult => { + const queryClient = useQueryClient(); + return useMutation( + (payload: t.TUpdatePresetRequest) => + dataService.updatePreset(payload), + { + onSuccess: () => { + queryClient.invalidateQueries([QueryKeys.presets]); + }, + } + ); +}; + +export const useGetPresetsQuery = (): QueryObserverResult => { + return useQuery([QueryKeys.presets], () => dataService.getPresets(), { + refetchOnWindowFocus: false, + refetchOnReconnect: false, + refetchOnMount: false, + }); +}; + +export const useDeleteAllPresetsMutation = (): UseMutationResult => { + const queryClient = useQueryClient(); + return useMutation(() => dataService.deletePresets(), { + onSuccess: () => { + queryClient.invalidateQueries([QueryKeys.presets]); + }, + }); +} + +export const useSearchFetcher = (query: string, pageNumber: string, callback: () => void, config?: UseQueryOptions): QueryObserverResult => { + return useQuery([QueryKeys.searchResults, pageNumber, query], () => + dataService.searchConversations(query, pageNumber, callback), { + refetchOnWindowFocus: false, + refetchOnReconnect: false, + refetchOnMount: false, + ...config + } + ); +} diff --git a/client/src/data-provider/request.ts b/client/src/data-provider/request.ts index 13ad78b2bd..d79562ab34 100644 --- a/client/src/data-provider/request.ts +++ b/client/src/data-provider/request.ts @@ -1,13 +1,12 @@ import axios, { AxiosRequestConfig } from "axios"; async function _get(url: string, options?: AxiosRequestConfig): Promise { - const response = await axios.get(url, {...options}); + const response = await axios.get(url, { withCredentials: true, ...options}); return response.data; } -async function _post(url: string, arg?: any) { - const modifiedData = {arg, withCredentials: true} - const response = await axios.post(url, modifiedData, { +async function _post(url: string, data?: any) { + const response = await axios.post(url, JSON.stringify(data), { headers: { "Content-Type": "application/json" }, }); return response.data; From 61cb2858bbe83dc7f816c9bda3345109cb0f713c Mon Sep 17 00:00:00 2001 From: Daniel D Orlando Date: Thu, 6 Apr 2023 05:47:37 -0700 Subject: [PATCH 19/36] refactor and optimize search, add RQ for search --- .../components/Endpoints/BingAI/Settings.jsx | 4 +- .../components/Endpoints/OpenAI/Settings.jsx | 4 +- .../components/Input/BingAIOptions/index.jsx | 4 +- .../components/Input/ChatGPTOptions/index.jsx | 4 +- .../components/Input/OpenAIOptions/index.jsx | 4 +- client/src/components/Nav/NavLinks.jsx | 5 +-- client/src/components/Nav/SearchBar.jsx | 45 ++----------------- client/src/components/Nav/index.jsx | 36 ++++++++------- client/src/components/ui/SelectDropDown.jsx | 4 +- client/src/data-provider/data-service.ts | 25 +---------- .../src/data-provider/react-query-service.ts | 14 +++--- client/src/data-provider/types.ts | 2 +- client/src/hooks/useDebounce.js | 22 +++++++++ 13 files changed, 71 insertions(+), 102 deletions(-) create mode 100644 client/src/hooks/useDebounce.js diff --git a/client/src/components/Endpoints/BingAI/Settings.jsx b/client/src/components/Endpoints/BingAI/Settings.jsx index 09fc00951f..dc53c98bb7 100644 --- a/client/src/components/Endpoints/BingAI/Settings.jsx +++ b/client/src/components/Endpoints/BingAI/Settings.jsx @@ -3,7 +3,7 @@ import TextareaAutosize from 'react-textarea-autosize'; import { Input } from '~/components/ui/Input.tsx'; import { Label } from '~/components/ui/Label.tsx'; import { Checkbox } from '~/components/ui/Checkbox.tsx'; -import SelectDropdown from '../../ui/SelectDropDown'; +import SelectDropDown from '../../ui/SelectDropDown'; import { axiosPost } from '~/utils/fetchers.js'; import { cn } from '~/utils/'; import debounce from 'lodash/debounce'; @@ -62,7 +62,7 @@ function Settings(props) { > Tone Style (default: fast) -
- - setOption('jailbreak')(value === 'Sydney')} diff --git a/client/src/components/Input/ChatGPTOptions/index.jsx b/client/src/components/Input/ChatGPTOptions/index.jsx index 23befa36b9..951daf9b8b 100644 --- a/client/src/components/Input/ChatGPTOptions/index.jsx +++ b/client/src/components/Input/ChatGPTOptions/index.jsx @@ -1,6 +1,6 @@ import React, { useEffect } from 'react'; import { useRecoilState, useRecoilValue } from 'recoil'; -import SelectDropdown from '../../ui/SelectDropDown.jsx'; +import SelectDropDown from '../../ui/SelectDropDown'; import { cn } from '~/utils/'; import store from '~/store'; @@ -41,7 +41,7 @@ function ChatGPTOptions() { return (
- */} - {!!isSearchEnabled && ( )} diff --git a/client/src/components/Nav/SearchBar.jsx b/client/src/components/Nav/SearchBar.jsx index 0d3befc762..35daa6de05 100644 --- a/client/src/components/Nav/SearchBar.jsx +++ b/client/src/components/Nav/SearchBar.jsx @@ -1,66 +1,29 @@ -import React, { useCallback, useEffect, useState } from 'react'; -import { debounce } from 'lodash'; import { Search } from 'lucide-react'; import { useRecoilState } from 'recoil'; - import store from '~/store'; -export default function SearchBar({ fetch, clearSearch }) { - // const dispatch = useDispatch(); - const [inputValue, setInputValue] = useState(''); +export default function SearchBar({ clearSearch }) { + const [searchQuery, setSearchQuery] = useRecoilState(store.searchQuery); - // const [inputValue, setInputValue] = useState(''); - - const debouncedChangeHandler = useCallback( - debounce(q => { - setSearchQuery(q); - }, 750), - [setSearchQuery] - ); - - useEffect(() => { - if (searchQuery.length > 0) { - fetch(searchQuery, 1); - setInputValue(searchQuery); - } - }, [searchQuery]); - const handleKeyUp = e => { const { value } = e.target; if (e.keyCode === 8 && value === '') { - // Value after clearing input: "" - console.log(`Value after clearing input: "${value}"`); setSearchQuery(''); clearSearch(); } }; - const changeHandler = e => { - let q = e.target.value; - setInputValue(q); - q = q.trim(); - - if (q === '') { - setSearchQuery(''); - clearSearch(); - } else { - debouncedChangeHandler(q); - } - }; - return (
{} setSearchQuery(e.target.value)} placeholder="Search messages" onKeyUp={handleKeyUp} - // onBlur={onRename} />
); diff --git a/client/src/components/Nav/index.jsx b/client/src/components/Nav/index.jsx index 6e1530cdbb..3cb9fb4d6f 100644 --- a/client/src/components/Nav/index.jsx +++ b/client/src/components/Nav/index.jsx @@ -1,14 +1,12 @@ -import React, { useState, useEffect, useRef, useCallback } from 'react'; -import _ from 'lodash'; +import { useState, useEffect, useRef } from 'react'; import NewChat from './NewChat'; import Spinner from '../svg/Spinner'; import Pages from '../Conversations/Pages'; import Conversations from '../Conversations'; import NavLinks from './NavLinks'; -import { searchFetcher } from '~/utils/fetchers'; import { useRecoilValue, useSetRecoilState } from 'recoil'; -import { useGetConversationsQuery } from '~/data-provider'; - +import { useGetConversationsQuery, useSearchQuery } from '~/data-provider'; +import useDebounce from '~/hooks/useDebounce'; import store from '~/store'; export default function Nav({ navVisible, setNavVisible }) { @@ -17,7 +15,6 @@ export default function Nav({ navVisible, setNavVisible }) { const containerRef = useRef(null); const scrollPositionRef = useRef(null); - // const dispatch = useDispatch(); const [conversations, setConversations] = useState([]); // current page const [pageNumber, setPageNumber] = useState(1); @@ -43,6 +40,7 @@ export default function Nav({ navVisible, setNavVisible }) { const [isFetching, setIsFetching] = useState(false); const onSearchSuccess = (data, expectedPage) => { + console.log('onSearchSuccess', data, expectedPage) const res = data; setConversations(res.conversations); if (expectedPage) { @@ -55,14 +53,22 @@ export default function Nav({ navVisible, setNavVisible }) { setSearchResultMessages(res.messages); }; - // TODO: dont need this - const fetch = useCallback( - _.partialRight( - searchFetcher.bind(null, () => setIsFetching(true)), - onSearchSuccess - ), - [setIsFetching] - ); + const debouncedSearchTerm = useDebounce(searchQuery, 750); + const searchQueryFn = useSearchQuery(debouncedSearchTerm, 1, { + enabled: !!debouncedSearchTerm && + debouncedSearchTerm.length > 0 && + isSearchEnabled && + isSearching, + }); + + useEffect(() => { + if (searchQueryFn.isInitialLoading) { + setIsFetching(true); + } + else if (searchQueryFn.data) { + onSearchSuccess(searchQueryFn.data); + } + }, [searchQueryFn.data, searchQueryFn.isInitialLoading]) const clearSearch = () => { setPageNumber(1); @@ -178,8 +184,6 @@ export default function Nav({ navVisible, setNavVisible }) {
diff --git a/client/src/components/ui/SelectDropDown.jsx b/client/src/components/ui/SelectDropDown.jsx index d2e2e15f58..3ea1dd1170 100644 --- a/client/src/components/ui/SelectDropDown.jsx +++ b/client/src/components/ui/SelectDropDown.jsx @@ -3,7 +3,7 @@ import CheckMark from '../svg/CheckMark.jsx'; import { Listbox, Transition } from '@headlessui/react'; import { cn } from '~/utils/'; -function SelectDropdown({ +function SelectDropDown({ title = 'Model', value, disabled, @@ -111,4 +111,4 @@ function SelectDropdown({ ); } -export default SelectDropdown; +export default SelectDropDown; diff --git a/client/src/data-provider/data-service.ts b/client/src/data-provider/data-service.ts index 40aa98a001..db34b15d0b 100644 --- a/client/src/data-provider/data-service.ts +++ b/client/src/data-provider/data-service.ts @@ -49,35 +49,14 @@ export function getSearchEnabled(): Promise { return request.get(endpoints.searchEnabled()); } -export function getSearchResults(q: string, pageNumber: string): Promise { - return request.get(endpoints.search(q, pageNumber)); -} - export function getUser(): Promise { return request.get(endpoints.user()); } -type TSearchFetcherProps = { - pre: () => void, - q: string, - pageNumber: string, - callback: (data: any) => void -}; - -export const searchConversations = async({ q, pageNumber, callback }: TSearchFetcherProps) => { - return request.get(endpoints.search(q, pageNumber)).then(({ data }) => { - callback(data); - }); +export const searchConversations = async(q: string, pageNumber: string): Promise => { + return request.get(endpoints.search(q, pageNumber)); } -export const searchFetcher = async ({ pre, q, pageNumber, callback }: TSearchFetcherProps) => { - pre(); - //@ts-ignore - const { data } = await request.get(endpoints.search(q, pageNumber)); - console.log('search data', data); - callback(data); -}; - export const getAIEndpoints = () => { return request.get(endpoints.aiEndpoints()); } \ No newline at end of file diff --git a/client/src/data-provider/react-query-service.ts b/client/src/data-provider/react-query-service.ts index 1b08d45546..67ba0eeda0 100644 --- a/client/src/data-provider/react-query-service.ts +++ b/client/src/data-provider/react-query-service.ts @@ -120,7 +120,6 @@ export const useClearConversationsMutation = (): UseMutationResult => { export const useGetConversationsQuery = (pageNumber: string): QueryObserverResult => { return useQuery([QueryKeys.allConversations, pageNumber], () => dataService.getConversations(pageNumber), { - // refetchOnWindowFocus: false, refetchOnReconnect: false, refetchOnMount: false, } @@ -191,13 +190,18 @@ export const useDeleteAllPresetsMutation = (): UseMutationResult => { }); } -export const useSearchFetcher = (query: string, pageNumber: string, callback: () => void, config?: UseQueryOptions): QueryObserverResult => { - return useQuery([QueryKeys.searchResults, pageNumber, query], () => - dataService.searchConversations(query, pageNumber, callback), { +export const useSearchQuery = ( + searchQuery: string, + pageNumber: string, + config?: UseQueryOptions + ): QueryObserverResult => { + console.log('useSearchFetcher', searchQuery, pageNumber) + return useQuery([QueryKeys.searchResults, pageNumber, searchQuery], () => + dataService.searchConversations(searchQuery, pageNumber), { refetchOnWindowFocus: false, refetchOnReconnect: false, refetchOnMount: false, ...config } ); -} +} \ No newline at end of file diff --git a/client/src/data-provider/types.ts b/client/src/data-provider/types.ts index d4cb3926c8..ce2162c232 100644 --- a/client/src/data-provider/types.ts +++ b/client/src/data-provider/types.ts @@ -121,7 +121,7 @@ export type TDeleteConversationResponse = { } }; -export type TSearchResponse = { +export type TSearchResults = { conversations: TConversation[], messages: TMessage[], pageNumber: string, diff --git a/client/src/hooks/useDebounce.js b/client/src/hooks/useDebounce.js new file mode 100644 index 0000000000..910395fce6 --- /dev/null +++ b/client/src/hooks/useDebounce.js @@ -0,0 +1,22 @@ +import { useState, useEffect } from 'react'; + +function useDebounce(value, delay) { + const [debouncedValue, setDebouncedValue] = useState(value); + + useEffect( + () => { + const handler = setTimeout(() => { + setDebouncedValue(value); + }, delay); + + return () => { + clearTimeout(handler); + }; + }, + [value, delay] + ); + + return debouncedValue; +} + +export default useDebounce; \ No newline at end of file From fbeff7a461af5b5f83f97e48cd164d826e899926 Mon Sep 17 00:00:00 2001 From: Daniel D Orlando Date: Thu, 6 Apr 2023 06:07:16 -0700 Subject: [PATCH 20/36] Code cleanup --- .../components/Endpoints/BingAI/Settings.jsx | 21 +----- .../components/Endpoints/OpenAI/Settings.jsx | 66 ------------------- .../components/Input/BingAIOptions/index.jsx | 17 +---- .../components/Input/ChatGPTOptions/index.jsx | 10 --- .../components/Input/OpenAIOptions/index.jsx | 35 +--------- 5 files changed, 4 insertions(+), 145 deletions(-) diff --git a/client/src/components/Endpoints/BingAI/Settings.jsx b/client/src/components/Endpoints/BingAI/Settings.jsx index dc53c98bb7..db3e0b8a96 100644 --- a/client/src/components/Endpoints/BingAI/Settings.jsx +++ b/client/src/components/Endpoints/BingAI/Settings.jsx @@ -1,22 +1,14 @@ -import React, { useEffect, useState } from 'react'; +import { useEffect, useState } from 'react'; import TextareaAutosize from 'react-textarea-autosize'; -import { Input } from '~/components/ui/Input.tsx'; import { Label } from '~/components/ui/Label.tsx'; import { Checkbox } from '~/components/ui/Checkbox.tsx'; import SelectDropDown from '../../ui/SelectDropDown'; import { axiosPost } from '~/utils/fetchers.js'; import { cn } from '~/utils/'; import debounce from 'lodash/debounce'; -// import ModelDropDown from '../../ui/ModelDropDown'; -// import { Slider } from '~/components/ui/Slider.tsx'; -// import OptionHover from './OptionHover'; -// import { HoverCard, HoverCardTrigger } from '~/components/ui/HoverCard.tsx'; const defaultTextProps = 'rounded-md border border-gray-200 focus:border-slate-400 focus:bg-gray-50 bg-transparent text-sm shadow-[0_0_10px_rgba(0,0,0,0.05)] outline-none placeholder:text-gray-400 focus:outline-none focus:ring-gray-400 focus:ring-opacity-20 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 dark:border-gray-500 dark:bg-gray-700 focus:dark:bg-gray-600 dark:text-gray-50 dark:shadow-[0_0_15px_rgba(0,0,0,0.10)] dark:focus:border-gray-400 dark:focus:outline-none dark:focus:ring-0 dark:focus:ring-gray-400 dark:focus:ring-offset-0'; -const optionText = - 'p-0 shadow-none text-right pr-1 h-8 border-transparent focus:ring-[#10a37f] focus:ring-offset-0 focus:ring-opacity-100 hover:bg-gray-800/10 dark:hover:bg-white/10 focus:bg-gray-800/10 dark:focus:bg-white/10 transition-colors'; - function Settings(props) { const { readonly, context, systemMessage, jailbreak, toneStyle, setOption } = props; const [tokenCount, setTokenCount] = useState(0); @@ -27,7 +19,6 @@ function Settings(props) { const setToneStyle = value => setOption('toneStyle')(value.toLowerCase()); // useEffect to update token count - useEffect(() => { if (!context || context.trim() === '') { setTokenCount(0); @@ -49,8 +40,6 @@ function Settings(props) { return () => debouncedPost.cancel(); }, [context]); - // console.log('data', data); - return ( <>
@@ -151,14 +140,6 @@ function Settings(props) { />
)} - {/* - - - - */}
diff --git a/client/src/components/Endpoints/OpenAI/Settings.jsx b/client/src/components/Endpoints/OpenAI/Settings.jsx index 46e643ba15..2b95ea1c70 100644 --- a/client/src/components/Endpoints/OpenAI/Settings.jsx +++ b/client/src/components/Endpoints/OpenAI/Settings.jsx @@ -1,4 +1,3 @@ -import React from 'react'; import { useRecoilValue } from 'recoil'; import TextareaAutosize from 'react-textarea-autosize'; import SelectDropDown from '../../ui/SelectDropDown'; @@ -6,7 +5,6 @@ import { Input } from '~/components/ui/Input.tsx'; import { Label } from '~/components/ui/Label.tsx'; import { Slider } from '~/components/ui/Slider.tsx'; import { InputNumber } from '~/components/ui/InputNumber.tsx'; -// import { InputNumber } from '../../ui/InputNumber'; import OptionHover from './OptionHover'; import { HoverCard, HoverCardTrigger } from '~/components/ui/HoverCard.tsx'; import { cn } from '~/utils/'; @@ -49,23 +47,6 @@ function Settings(props) { )} containerClassName="flex w-full resize-none" /> - {/* - setModel(e.target.value)} - placeholder="Set a custom name for ChatGPT" - className={cn( - defaultTextProps, - 'flex h-10 max-h-10 w-full resize-none px-3 py-2 focus:outline-none focus:ring-0 focus:ring-opacity-0 focus:ring-offset-0' - )} - /> */}
@@ -160,43 +131,6 @@ function Settings(props) { side="left" /> - - {/* - -
- - setMaxTokens(e.target.value)} - className={cn( - defaultTextProps, - cn(optionText, 'h-auto w-12 border-0 group-hover/temp:border-gray-200') - )} - /> -
- setMaxTokens(value[0])} - max={2048} // should be dynamic to the currently selected model - min={1} - step={1} - className="flex h-4 w-full" - /> -
- -
*/} -
diff --git a/client/src/components/Input/BingAIOptions/index.jsx b/client/src/components/Input/BingAIOptions/index.jsx index 3ebe2c0532..a5efc1ce02 100644 --- a/client/src/components/Input/BingAIOptions/index.jsx +++ b/client/src/components/Input/BingAIOptions/index.jsx @@ -1,5 +1,5 @@ -import React, { useState, useEffect } from 'react'; -import { useRecoilValue, useRecoilState } from 'recoil'; +import { useState } from 'react'; +import { useRecoilState } from 'recoil'; import { cn } from '~/utils'; import { Button } from '../../ui/Button.tsx'; import { Settings2 } from 'lucide-react'; @@ -18,25 +18,12 @@ function BingAIOptions() { const { endpoint, conversationId } = conversation; const { toneStyle, context, systemMessage, jailbreak } = conversation; - // useEffect(() => { - // if (endpoint !== 'bingAI') return; - - // const mustInAdvancedMode = context !== null || systemMessage !== null; - - // if (mustInAdvancedMode && !advancedMode) setAdvancedMode(true); - // }, [conversation, advancedMode]); - if (endpoint !== 'bingAI') return null; if (conversationId !== 'new') return null; const triggerAdvancedMode = () => setAdvancedMode(prev => !prev); const switchToSimpleMode = () => { - // setConversation(prevState => ({ - // ...prevState, - // context: null, - // systemMessage: null - // })); setAdvancedMode(false); }; diff --git a/client/src/components/Input/ChatGPTOptions/index.jsx b/client/src/components/Input/ChatGPTOptions/index.jsx index 951daf9b8b..e0f89f15b6 100644 --- a/client/src/components/Input/ChatGPTOptions/index.jsx +++ b/client/src/components/Input/ChatGPTOptions/index.jsx @@ -12,21 +12,11 @@ function ChatGPTOptions() { const endpointsConfig = useRecoilValue(store.endpointsConfig); - // useEffect(() => { - // if (endpoint !== 'chatGPTBrowser') return; - // }, [conversation]); - if (endpoint !== 'chatGPTBrowser') return null; if (conversationId !== 'new') return null; const models = endpointsConfig?.['chatGPTBrowser']?.['availableModels'] || []; - // const modelMap = new Map([ - // ['Default (GPT-3.5)', 'text-davinci-002-render-sha'], - // ['Legacy (GPT-3.5)', 'text-davinci-002-render-paid'], - // ['GPT-4', 'gpt-4'] - // ]); - const setOption = param => newValue => { let update = {}; update[param] = newValue; diff --git a/client/src/components/Input/OpenAIOptions/index.jsx b/client/src/components/Input/OpenAIOptions/index.jsx index 3d90409adb..1167abe356 100644 --- a/client/src/components/Input/OpenAIOptions/index.jsx +++ b/client/src/components/Input/OpenAIOptions/index.jsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react'; +import { useState } from 'react'; import { Settings2 } from 'lucide-react'; import { useRecoilState, useRecoilValue } from 'recoil'; import SelectDropDown from '../../ui/SelectDropDown'; @@ -21,20 +21,6 @@ function OpenAIOptions() { const endpointsConfig = useRecoilValue(store.endpointsConfig); - // useEffect(() => { - // if (endpoint !== 'openAI') return; - - // const mustInAdvancedMode = - // chatGptLabel !== null || - // promptPrefix !== null || - // temperature !== 1 || - // top_p !== 1 || - // presence_penalty !== 0 || - // frequency_penalty !== 0; - - // if (mustInAdvancedMode && !advancedMode) setAdvancedMode(true); - // }, [conversation, advancedMode]); - if (endpoint !== 'openAI') return null; if (conversationId !== 'new') return null; @@ -43,15 +29,6 @@ function OpenAIOptions() { const triggerAdvancedMode = () => setAdvancedMode(prev => !prev); const switchToSimpleMode = () => { - // setConversation(prevState => ({ - // ...prevState, - // chatGptLabel: null, - // promptPrefix: null, - // temperature: 1, - // top_p: 1, - // presence_penalty: 0, - // frequency_penalty: 0 - // })); setAdvancedMode(false); }; @@ -79,16 +56,6 @@ function OpenAIOptions() { (!advancedMode ? ' show' : '') } > - {/* */} Date: Thu, 6 Apr 2023 08:02:08 -0700 Subject: [PATCH 21/36] Add support for deleting individual presets --- .../Input/Endpoints/NewConversationMenu.jsx | 22 ++++++------ .../components/Input/Endpoints/PresetItem.jsx | 9 ----- client/src/components/Nav/index.jsx | 1 + client/src/data-provider/api-endpoints.ts | 6 +++- client/src/data-provider/data-service.ts | 6 ++-- .../src/data-provider/react-query-service.ts | 34 +++++++++++-------- 6 files changed, 39 insertions(+), 39 deletions(-) diff --git a/client/src/components/Input/Endpoints/NewConversationMenu.jsx b/client/src/components/Input/Endpoints/NewConversationMenu.jsx index a35a79693c..72ddfc51f5 100644 --- a/client/src/components/Input/Endpoints/NewConversationMenu.jsx +++ b/client/src/components/Input/Endpoints/NewConversationMenu.jsx @@ -5,7 +5,7 @@ import EndpointItems from './EndpointItems'; import PresetItems from './PresetItems'; import FileUpload from './FileUpload'; import getIcon from '~/utils/getIcon'; -import { useDeleteAllPresetsMutation } from '~/data-provider'; +import { useDeletePresetMutation } from '~/data-provider'; import { Button } from '../../ui/Button.tsx'; import { DropdownMenu, @@ -33,7 +33,7 @@ export default function NewConversationMenu() { // const { model, promptPrefix, chatGptLabel, conversationId } = conversation; const { newConversation } = store.useConversation(); - const deletePresetsMutation = useDeleteAllPresetsMutation(); + const deletePresetsMutation = useDeletePresetMutation(); const importPreset = jsonData => { handleFileSelected(jsonData).then(setPresets); @@ -79,8 +79,12 @@ export default function NewConversationMenu() { setPreset(preset); }; - const clearPreset = () => { - deletePresetsMutation.mutate(); + const clearAllPresets = () => { + deletePresetsMutation.mutate({arg: {}}); + }; + + const onDeletePreset = preset => { + deletePresetsMutation.mutate({arg: preset}); }; const icon = getIcon({ @@ -92,9 +96,7 @@ export default function NewConversationMenu() { }); return ( - + @@ -149,7 +149,7 @@ export default function NewConversationMenu() { title="Clear presets" description="Are you sure you want to clear all presets? This is irreversible." selection={{ - selectHandler: clearPreset, + selectHandler: clearAllPresets, selectClasses: 'bg-red-600 hover:bg-red-700 dark:hover:bg-red-800 text-white', selectText: 'Clear' }} @@ -166,7 +166,7 @@ export default function NewConversationMenu() { presets={presets} onSelect={onSelectPreset} onChangePreset={onChangePreset} - onDeletePreset={clearPresetsTrigger} + onDeletePreset={onDeletePreset} /> ) : ( No preset yet. diff --git a/client/src/components/Input/Endpoints/PresetItem.jsx b/client/src/components/Input/Endpoints/PresetItem.jsx index fe8503d2cc..ad7e2e2b69 100644 --- a/client/src/components/Input/Endpoints/PresetItem.jsx +++ b/client/src/components/Input/Endpoints/PresetItem.jsx @@ -45,15 +45,6 @@ export default function PresetItem({ preset = {}, value, onSelect, onChangePrese {icon} {preset?.title} ({getPresetTitle()}) - - {/* */}
@@ -159,7 +157,7 @@ export default function NewConversationMenu() { title="Clear presets" description="Are you sure you want to clear all presets? This is irreversible." selection={{ - selectHandler: clearPreset, + selectHandler: clearAllPresets, selectClasses: 'bg-red-600 hover:bg-red-700 dark:hover:bg-red-800 text-white', selectText: 'Clear' }} @@ -176,7 +174,7 @@ export default function NewConversationMenu() { presets={presets} onSelect={onSelectPreset} onChangePreset={onChangePreset} - onDeletePreset={clearPresetsTrigger} + onDeletePreset={onDeletePreset} /> ) : ( No preset yet.