@@ -43,18 +43,6 @@ async function getModels(token?: string): Promise { } } -type LangParam = T extends true ? LangFull : LangShort - -type LangReturn = T extends true ? LangShort : LangFull - -const convertLang = (toShort: T, lang: LangParam): LangReturn => { - const arrLang = Object.entries(languages) as [LangFull, LangShort][] - if (toShort) { - return arrLang.find(([key]) => lang === key)![1] as LangReturn - } - return arrLang.find(([_, value]) => lang === value)![0] as LangReturn -} - const InfoBar: React.FC = ({ device }) => { const theme = useAppSelector((state) => state.theme.theme) @@ -72,16 +60,6 @@ const InfoBar: React.FC = ({ device }) => { const dispatch = useAppDispatch() - const [search, setSearch] = useState('') - - const [searchOpen, setSearchOpen] = useState(false) - - const [models, setModels] = useState([]) - - useEffect(() => { - getModels(data?.access).then((res) => setModels(res)) - }, [data?.access]) - const [anchorEl, setAnchorEl] = React.useState(null) const [anchorEl2, setAnchorEl2] = React.useState(null) @@ -90,10 +68,6 @@ const InfoBar: React.FC = ({ device }) => { setAnchorEl(event.currentTarget) } - const handleClick2 = (event: React.MouseEvent) => { - setAnchorEl2(event.currentTarget) - } - const handleClose = () => { setAnchorEl(null) } @@ -1,153 +0,0 @@ -import React, { useState } from 'react' -import { Box } from '@mui/material' -import { ThemeProvider } from '@mui/material/styles' -import Head from 'next/head' -import { useRouter } from 'next/router' -import { useSession } from 'next-auth/react' - -import { getUserBalance } from '#/entities/balance' -import { useTheme } from '#/entities/theme' -import { getAllInfo } from '#/entities/user-account' -import { isSettingExist } from '#/entities/user-account/lib/helpers/is-setting-exist' -import { getUserAccountSettings, setSettings } from '#/entities/user-account/model/settings' -import InfoBar from '#/app/layout/ui/info-bar' -import { useAppDispatch, useAppSelector } from '#/app/store/store' -import { Loader } from '#/shared' -import { pingFangFont } from '#/shared/lib/constants/font/font' -import { Device } from '#/shared/lib/types/entities' -import { MainMenuMobile } from '#/widgets/main-menu' -import { SideMenu } from '#/widgets/side-menu' -import { getDeviceType } from '#/shared/lib/helpers' - -const freeRoutes = ['/login', '/register', '/reset', '/change-password'] - -interface Props { - children: React.ReactNode - titlePage: string - isAuthPage?: boolean - title?: string | React.ReactNode | null - isLoader?: boolean -} - -export const Layout: React.FC = ({ children, isAuthPage = false, titlePage, title = titlePage, isLoader }) => { - const { data: sessionData } = useSession() - const appState = useAppSelector((state) => state) - const dispatch = useAppDispatch() - - const device = getDeviceType() - - const desktop = device === 'desktop' - - const [sidemenuDefaultOpen, setSidemenuDefaultOpen] = useState(true) - - useTheme() - - const { data, status } = useSession() - - const router = useRouter() - - React.useEffect(() => { - dispatch(getUserBalance(data?.access)) - dispatch(getAllInfo(data?.access)) - }, [data]) - - React.useEffect(() => { - if ( - (sessionData && !sessionStorage.getItem('firstRender')) || - (sessionData && !localStorage.getItem('global_settings')) - ) { - dispatch(getUserAccountSettings(sessionData.access)) - sessionStorage.setItem('firstRender', 'true') - } - }, [sessionData]) - - React.useEffect(() => { - const lsData = localStorage.getItem('global_settings') - if (lsData !== null) { - dispatch(setSettings(JSON.parse(lsData))) - } - - window.addEventListener('beforeunload', () => { - sessionStorage.removeItem('firstRender') - sessionStorage.clear() - }) - }, []) - - React.useEffect(() => { - if (appState.settings.state !== null && device) { - let setting = isSettingExist({ - settings: appState.settings.state, - targetDevice: device, - targetType: 'sidemenu', - }) - if (setting) - setting?.value?.sidemenu_state === 'opened' - ? setSidemenuDefaultOpen(true) - : setSidemenuDefaultOpen(false) - } - }, [appState.settings.state]) - - if (status === 'loading' || isLoader) { - return ( - - - - ) - } - - return ( - <> - - {titlePage} - - - - - - - - - - {!isAuthPage ? ( - desktop ? ( - - - - - - {children} - - - - ) : ( - - - {children} - - ) - ) : ( - <>{children} - )} - - - - - ) -} @@ -8,6 +8,8 @@ import { useChatBot } from '#/entities/model-entity' import { useShowDataStore } from '#/shared/lib/hooks/use-show-data' import { useChatBotMessages } from './chat-bot-messages.store' import { useCurrentChat } from '#/features/chats' +import { useModelInputStore } from '#/features/model-input/model' +import { useChatWindowContext } from '#/widgets/chat-window/model' export function useUserMessageActions(message: Message) { const includeParams = useAppSelector((state) => state.params.params) @@ -16,35 +18,29 @@ export function useUserMessageActions(message: Message) { const { currentChat } = useCurrentChat() - const { setMessages, setLoading } = useChatBotMessages() + const {setFile, setValueInput} = useChatWindowContext() const { inference } = useChatBot() - const resend = makePrivateRequest(async () => { + const resend = async () => { if (!currentChat || !inference) return const { file, content } = message - const request: MessageSend = { - content, - info: { ...includeParams, inference: inference.slug }, + if (!content && !file) { + return showMessage('Нет данных для повторной отправки сообщения') + } + if (file && typeof file === 'string') { + const newFile = await getBlobFromUrl(file) + setFile(newFile ? newFile : null) + } else if (file && file instanceof File) { + setFile(file) + } else { + setFile(null) } - if (file) request.file = await getBlobFromUrl(file as string) - - setLoading(true) - - const { data, status } = await postMessage(currentChat, request) - - setLoading(false) - - if (status !== 201) - return showMessage(`Ошибка при повторной отправке сообщений, повторите позже`) - - const { messages } = useChatBotMessages.getState() - - setMessages([...messages.filter((m) => m.uid !== 'new-send'), ...(data as Message[])]) - }) + setValueInput(content) + } return { resend, @@ -33,7 +33,10 @@ export const BotMessage = memo( const popup = getPopupById(message.uid) return ( -
+
@@ -0,0 +1 @@ +export * from './settings.routes' \ No newline at end of file @@ -0,0 +1,16 @@ +import { api } from '#/shared/api' +import { API_URL } from '#/shared/lib/constants' +import { Agent } from 'https' +import { IUserSetting } from '../model/types' + +export function getUserSettings() { + return api.get('/api/users/settings/') +} + +export function postUserSettings(option: Omit) { + return api.post('/api/users/settings/', option) +} + +export function updateUserSettings(id: string, value: any) { + return api.put(`/api/users/settings/${id}`, { value }) +} @@ -0,0 +1,2 @@ +export * from './settings-context' +export * from './use-global-settings' \ No newline at end of file @@ -0,0 +1,9 @@ +import { createUseContext } from '#/shared' +import { createContext } from 'react' +import { useGlobalSettings } from './use-global-settings' + +export const UserSettingsContext = createContext | null>(null) + +export const useUserSettingsContext = createUseContext(UserSettingsContext) + +export const UserSettingsContextProvider = UserSettingsContext.Provider @@ -6,6 +6,7 @@ import { updateUserSettings } from '../api/update-user-settings' import { getUpdatedSettingsLocal } from '../lib/helpers/update-setting-local' import { IUserSetting, SettingValueType } from './types' +import { useLocalStorageSave } from '#/shared/lib/helpers/local-storage-helper' interface IAddSettings { token: string | undefined | null @@ -2,7 +2,7 @@ import { Device } from '#/shared/lib/types/entities' export type AccountType = 'regular' | 'business_host' | 'business_account' -export type SettingType = 'sidemenu' +export type SettingType = 'sidemenu' | string export type SettingValueType = { sidemenu_state?: 'opened' | 'closed' @@ -12,5 +12,5 @@ export interface IUserSetting { id: string device: Device type: SettingType - value: SettingValueType + value: SettingValueType | any } @@ -0,0 +1,65 @@ +import { useLocalStorage } from 'usehooks-ts' +import { IUserSetting } from './types' +import { makePrivateRequest } from '#/shared/api' +import { getUserSettings, postUserSettings, updateUserSettings } from '../api' +import { useShowDataStore } from '#/shared/lib/hooks' +import { getDeviceType } from '#/shared' +import { useCallback, useEffect, useRef, useState } from 'react' +import { useSession } from 'next-auth/react' + +export function useGlobalSettings() { + const [settings, setSettings] = useLocalStorage('global_settings', []) + + const { showMessage } = useShowDataStore() + + const device = getDeviceType() + + const { data } = useSession() + + const addSettings = makePrivateRequest(async (type: string, value: any) => { + const { status, data } = await postUserSettings({ device, type, value }) + + if (status !== 200) return showMessage('Ошибка создания настроек') + + setSettings((s) => [...s, data]) + }) + + const updateSettings = useCallback( + makePrivateRequest(async (type: string, value: any) => { + const option = settings.find((x) => x.type === type) + + if (!option) return showMessage('Ошибка присвоения настроек') + + const { status } = await updateUserSettings(option.id, value) + + if (status !== 204) return showMessage('Ошибка создания настроек') + + setSettings((s) => [...s.filter((x) => x.type !== type), { ...option, value }]) + }), + [settings, data] + ) + + const fetchUserSettings = makePrivateRequest(async () => { + const { status, data } = await getUserSettings() + + const device = getDeviceType() + + if (status !== 200) return showMessage('Ошибка загрузки данных пользователя') + + setSettings(data.filter((s) => s.device === device)) + }) + + const getOptionValue = (type: string, initial: any) => { + const option = settings.find((o) => o.type === type) + return option ? option.value : initial + } + + return { + settings, + setSettings, + addSettings, + fetchUserSettings, + updateSettings, + getOptionValue, + } +} @@ -1 +1,2 @@ export { getAllInfo, userSlice } from './model/user-type-slice' +export * from './model' \ No newline at end of file @@ -10,18 +10,19 @@ export function useChatBotInput( ) { const { showMessage } = useShowDataStore() const [file, setFile] = useState(null) - const { inferenceParams, infrerenceValues } = useChatBotParams() + const { infrerenceValues } = useChatBotParams() + + const [valueInput, setValueInput] = useState('') const onSendMessage = async (content: string, required: (string | null)[]) => { if (required.includes('text') && content === '') return showMessage('Введите сообщение!') - if (required.includes('image') && file === null) - return showMessage('Прикрепите изображение!') + if (required.includes('image') && file === null) return showMessage('Прикрепите изображение!') if (!inference) return showMessage('Не выбрана версия модели') await sendMessage({ - content, + content, file, info: { inference: inference.slug, @@ -34,5 +35,7 @@ export function useChatBotInput( onSendMessage, file, setFile, + valueInput, + setValueInput } } @@ -44,7 +44,11 @@ export function useChatBotPagination() { setMessages([]) offset.current = 0 setLoaded(false) - fetchMessages() + if (currentChat && observer.current) { + observer.current.disconnect() + observer.current = null + onObserverMounted() + } if (!scrollContainer.current) return scrollContainer.current.scrollToBottom() }, [currentChat]) @@ -86,6 +90,8 @@ export function useChatBotPagination() { setLoaded(true) + console.info('ОН ФЕТЧИТ БЛЯТЬ', answer, response) + if (response.status >= 400 || !Array.isArray(answer)) return showMessage('Ошибка загрузки чата') const { messages } = useChatBotMessages.getState() @@ -21,13 +21,7 @@ export const ChatItemActionsPopup = ({ id, setRename, ...props }: ChatItemAction const renamePopup = getPopupById(`rename-${id}`) return ( - +