增加用户反馈功能
This commit is contained in:
parent
8094d5e128
commit
374f5171cf
@ -1,25 +1,15 @@
|
||||
import {
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import useSWR from 'swr'
|
||||
import { useLocalStorageState } from 'ahooks'
|
||||
import { produce } from 'immer'
|
||||
import type {
|
||||
Callback,
|
||||
ChatConfig,
|
||||
ChatItem,
|
||||
Feedback,
|
||||
} from '../types'
|
||||
import { CONVERSATION_ID_INFO } from '../constants'
|
||||
import { buildChatItemTree } from '../utils'
|
||||
import { addFileInfos, sortAgentSorts } from '@/app/components/tools/utils'
|
||||
import { getProcessedFilesFromResponse } from '@/app/components/base/file-uploader/utils'
|
||||
// TODO mars
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import useSWR from "swr";
|
||||
import { useLocalStorageState } from "ahooks";
|
||||
import { produce } from "immer";
|
||||
import { message } from "antd";
|
||||
import { client, getInfo } from "@/app/api/utils/common";
|
||||
import type { Callback, ChatConfig, ChatItem, Feedback } from "../types";
|
||||
import { CONVERSATION_ID_INFO } from "../constants";
|
||||
import { buildChatItemTree } from "../utils";
|
||||
import { addFileInfos, sortAgentSorts } from "@/app/components/tools/utils";
|
||||
import { getProcessedFilesFromResponse } from "@/app/components/base/file-uploader/utils";
|
||||
import {
|
||||
delConversation,
|
||||
fetchAppInfo,
|
||||
@ -32,50 +22,66 @@ import {
|
||||
renameConversation,
|
||||
unpinConversation,
|
||||
updateFeedback,
|
||||
} from '@/service/index'
|
||||
import type { InstalledApp } from '@/models/explore'
|
||||
import type {
|
||||
AppData,
|
||||
ConversationItem,
|
||||
} from '@/models/share'
|
||||
import { useToastContext } from '@/app/components/base/toast'
|
||||
import { changeLanguage } from '@/i18n/i18next-config'
|
||||
import { useAppFavicon } from '@/hooks/use-app-favicon'
|
||||
import { InputVarType } from '@/app/components/workflow/types'
|
||||
import { TransferMethod } from '@/types/app'
|
||||
} from "@/service/index";
|
||||
import type { InstalledApp } from "@/models/explore";
|
||||
import type { AppData, ConversationItem } from "@/models/share";
|
||||
import { useToastContext } from "@/app/components/base/toast";
|
||||
import { changeLanguage } from "@/i18n/i18next-config";
|
||||
import { useAppFavicon } from "@/hooks/use-app-favicon";
|
||||
import { InputVarType } from "@/app/components/workflow/types";
|
||||
import { TransferMethod } from "@/types/app";
|
||||
import FeedbackModal from "@/app/components/chat-with-history/FeedbackModal"; // 引入 FeedbackModal 组件
|
||||
|
||||
function getFormattedChatList(messages: any[]) {
|
||||
const newChatList: ChatItem[] = []
|
||||
const newChatList: ChatItem[] = [];
|
||||
messages.forEach((item) => {
|
||||
const questionFiles = item.message_files?.filter((file: any) => file.belongs_to === 'user') || []
|
||||
const questionFiles =
|
||||
item.message_files?.filter((file: any) => file.belongs_to === "user") ||
|
||||
[];
|
||||
newChatList.push({
|
||||
id: `question-${item.id}`,
|
||||
content: item.query,
|
||||
isAnswer: false,
|
||||
message_files: getProcessedFilesFromResponse(questionFiles.map((item: any) => ({ ...item, related_id: item.id }))),
|
||||
message_files: getProcessedFilesFromResponse(
|
||||
questionFiles.map((item: any) => ({ ...item, related_id: item.id }))
|
||||
),
|
||||
parentMessageId: item.parent_message_id || undefined,
|
||||
})
|
||||
const answerFiles = item.message_files?.filter((file: any) => file.belongs_to === 'assistant') || []
|
||||
});
|
||||
const answerFiles =
|
||||
item.message_files?.filter(
|
||||
(file: any) => file.belongs_to === "assistant"
|
||||
) || [];
|
||||
if (answerFiles.length > 0 && questionFiles.length > 0)
|
||||
// 获取本地文件名
|
||||
answerFiles[0].filename = questionFiles[0]?.filename
|
||||
answerFiles[0].filename = questionFiles[0]?.filename;
|
||||
newChatList.push({
|
||||
id: item.id,
|
||||
content: item.answer,
|
||||
agent_thoughts: addFileInfos(item.agent_thoughts ? sortAgentSorts(item.agent_thoughts) : item.agent_thoughts, item.message_files),
|
||||
agent_thoughts: addFileInfos(
|
||||
item.agent_thoughts
|
||||
? sortAgentSorts(item.agent_thoughts)
|
||||
: item.agent_thoughts,
|
||||
item.message_files
|
||||
),
|
||||
feedback: item.feedback,
|
||||
isAnswer: true,
|
||||
citation: item.retriever_resources,
|
||||
message_files: getProcessedFilesFromResponse(answerFiles.map((item: any) => ({ ...item, related_id: item.id }))),
|
||||
message_files: getProcessedFilesFromResponse(
|
||||
answerFiles.map((item: any) => ({ ...item, related_id: item.id }))
|
||||
),
|
||||
parentMessageId: `question-${item.id}`,
|
||||
})
|
||||
})
|
||||
return newChatList
|
||||
});
|
||||
});
|
||||
return newChatList;
|
||||
}
|
||||
|
||||
export const useChatWithHistory = (installedAppInfo?: InstalledApp) => {
|
||||
const isInstalledApp = useMemo(() => !!installedAppInfo, [installedAppInfo])
|
||||
const { data: appInfo, isLoading: appInfoLoading, error: appInfoError } = useSWR(installedAppInfo ? null : 'appInfo', fetchAppInfo)
|
||||
const isInstalledApp = useMemo(() => !!installedAppInfo, [installedAppInfo]);
|
||||
const {
|
||||
data: appInfo,
|
||||
isLoading: appInfoLoading,
|
||||
error: appInfoError,
|
||||
} = useSWR(installedAppInfo ? null : "appInfo", fetchAppInfo);
|
||||
|
||||
useAppFavicon({
|
||||
enable: !installedAppInfo,
|
||||
@ -83,11 +89,11 @@ export const useChatWithHistory = (installedAppInfo?: InstalledApp) => {
|
||||
icon: appInfo?.site?.icon,
|
||||
icon_background: appInfo?.site?.icon_background,
|
||||
icon_url: appInfo?.site?.icon_url,
|
||||
})
|
||||
});
|
||||
|
||||
const appData = useMemo(() => {
|
||||
if (isInstalledApp) {
|
||||
const { id, app } = installedAppInfo!
|
||||
const { id, app } = installedAppInfo!;
|
||||
return {
|
||||
app_id: id,
|
||||
site: {
|
||||
@ -97,326 +103,507 @@ export const useChatWithHistory = (installedAppInfo?: InstalledApp) => {
|
||||
icon_background: app.icon_background,
|
||||
icon_url: app.icon_url,
|
||||
prompt_public: false,
|
||||
copyright: '',
|
||||
copyright: "",
|
||||
show_workflow_steps: true,
|
||||
use_icon_as_answer_icon: app.use_icon_as_answer_icon,
|
||||
},
|
||||
plan: 'basic',
|
||||
} as AppData
|
||||
plan: "basic",
|
||||
} as AppData;
|
||||
}
|
||||
|
||||
return appInfo
|
||||
}, [isInstalledApp, installedAppInfo, appInfo])
|
||||
const appId = useMemo(() => appData?.app_id, [appData])
|
||||
return appInfo;
|
||||
}, [isInstalledApp, installedAppInfo, appInfo]);
|
||||
const appId = useMemo(() => appData?.app_id, [appData]);
|
||||
|
||||
useEffect(() => {
|
||||
if (appData?.site?.default_language)
|
||||
changeLanguage(appData.site?.default_language)
|
||||
}, [appData])
|
||||
changeLanguage(appData.site?.default_language);
|
||||
}, [appData]);
|
||||
|
||||
const [conversationIdInfo, setConversationIdInfo] = useLocalStorageState<Record<string, string>>(CONVERSATION_ID_INFO, {
|
||||
const [conversationIdInfo, setConversationIdInfo] = useLocalStorageState<
|
||||
Record<string, string>
|
||||
>(CONVERSATION_ID_INFO, {
|
||||
defaultValue: {},
|
||||
})
|
||||
const currentConversationId = useMemo(() => conversationIdInfo?.[appId || ''] || '', [appId, conversationIdInfo])
|
||||
const handleConversationIdInfoChange = useCallback((changeConversationId: string) => {
|
||||
if (appId) {
|
||||
setConversationIdInfo({
|
||||
...conversationIdInfo,
|
||||
[appId || '']: changeConversationId,
|
||||
})
|
||||
}
|
||||
}, [appId, conversationIdInfo, setConversationIdInfo])
|
||||
const [showConfigPanelBeforeChat, setShowConfigPanelBeforeChat] = useState(true)
|
||||
const [newConversationId, setNewConversationId] = useState('')
|
||||
});
|
||||
const currentConversationId = useMemo(
|
||||
() => conversationIdInfo?.[appId || ""] || "",
|
||||
[appId, conversationIdInfo]
|
||||
);
|
||||
const handleConversationIdInfoChange = useCallback(
|
||||
(changeConversationId: string) => {
|
||||
if (appId) {
|
||||
setConversationIdInfo({
|
||||
...conversationIdInfo,
|
||||
[appId || ""]: changeConversationId,
|
||||
});
|
||||
}
|
||||
},
|
||||
[appId, conversationIdInfo, setConversationIdInfo]
|
||||
);
|
||||
const [showConfigPanelBeforeChat, setShowConfigPanelBeforeChat] =
|
||||
useState(true);
|
||||
const [newConversationId, setNewConversationId] = useState("");
|
||||
const chatShouldReloadKey = useMemo(() => {
|
||||
if (currentConversationId === newConversationId)
|
||||
return ''
|
||||
if (currentConversationId === newConversationId) return "";
|
||||
|
||||
return currentConversationId
|
||||
}, [currentConversationId, newConversationId])
|
||||
const { data: appParams } = useSWR(['appParams', isInstalledApp, appId], () => fetchAppParams())
|
||||
const { data: appMeta } = useSWR(['appMeta', isInstalledApp, appId], () => fetchAppMeta())
|
||||
const { data: appPinnedConversationData, mutate: mutateAppPinnedConversationData } = useSWR(['appConversationData', isInstalledApp, appId, true], () => fetchConversations())
|
||||
const { data: appConversationData, isLoading: appConversationDataLoading, mutate: mutateAppConversationData } = useSWR(['appConversationData', isInstalledApp, appId, false], () => fetchConversations())
|
||||
const { data: appChatListData, isLoading: appChatListDataLoading } = useSWR(chatShouldReloadKey ? ['appChatList', chatShouldReloadKey, isInstalledApp, appId] : null, () => fetchChatList(chatShouldReloadKey))
|
||||
return currentConversationId;
|
||||
}, [currentConversationId, newConversationId]);
|
||||
const { data: appParams } = useSWR(["appParams", isInstalledApp, appId], () =>
|
||||
fetchAppParams()
|
||||
);
|
||||
const { data: appMeta } = useSWR(["appMeta", isInstalledApp, appId], () =>
|
||||
fetchAppMeta()
|
||||
);
|
||||
const {
|
||||
data: appPinnedConversationData,
|
||||
mutate: mutateAppPinnedConversationData,
|
||||
} = useSWR(["appConversationData", isInstalledApp, appId, true], () =>
|
||||
fetchConversations()
|
||||
);
|
||||
const {
|
||||
data: appConversationData,
|
||||
isLoading: appConversationDataLoading,
|
||||
mutate: mutateAppConversationData,
|
||||
} = useSWR(["appConversationData", isInstalledApp, appId, false], () =>
|
||||
fetchConversations()
|
||||
);
|
||||
const { data: appChatListData, isLoading: appChatListDataLoading } = useSWR(
|
||||
chatShouldReloadKey
|
||||
? ["appChatList", chatShouldReloadKey, isInstalledApp, appId]
|
||||
: null,
|
||||
() => fetchChatList(chatShouldReloadKey)
|
||||
);
|
||||
|
||||
const appPrevChatTree = useMemo(
|
||||
() => (currentConversationId && appChatListData?.data.length)
|
||||
? buildChatItemTree(getFormattedChatList(appChatListData.data))
|
||||
: [],
|
||||
[appChatListData, currentConversationId],
|
||||
)
|
||||
() =>
|
||||
currentConversationId && appChatListData?.data.length
|
||||
? buildChatItemTree(getFormattedChatList(appChatListData.data))
|
||||
: [],
|
||||
[appChatListData, currentConversationId]
|
||||
);
|
||||
|
||||
const [showNewConversationItemInList, setShowNewConversationItemInList] = useState(false)
|
||||
const [showNewConversationItemInList, setShowNewConversationItemInList] =
|
||||
useState(false);
|
||||
|
||||
const pinnedConversationList = useMemo(() => {
|
||||
return appPinnedConversationData?.data || []
|
||||
}, [appPinnedConversationData])
|
||||
const { t } = useTranslation()
|
||||
const newConversationInputsRef = useRef<Record<string, any>>({})
|
||||
const [newConversationInputs, setNewConversationInputs] = useState<Record<string, any>>({})
|
||||
const handleNewConversationInputsChange = useCallback((newInputs: Record<string, any>) => {
|
||||
newConversationInputsRef.current = newInputs
|
||||
setNewConversationInputs(newInputs)
|
||||
}, [])
|
||||
return appPinnedConversationData?.data || [];
|
||||
}, [appPinnedConversationData]);
|
||||
const { t } = useTranslation();
|
||||
const newConversationInputsRef = useRef<Record<string, any>>({});
|
||||
const [newConversationInputs, setNewConversationInputs] = useState<
|
||||
Record<string, any>
|
||||
>({});
|
||||
const handleNewConversationInputsChange = useCallback(
|
||||
(newInputs: Record<string, any>) => {
|
||||
newConversationInputsRef.current = newInputs;
|
||||
setNewConversationInputs(newInputs);
|
||||
},
|
||||
[]
|
||||
);
|
||||
const inputsForms = useMemo(() => {
|
||||
return (appParams?.user_input_form || []).filter((item: any) => !item.external_data_tool).map((item: any) => {
|
||||
if (item.paragraph) {
|
||||
return {
|
||||
...item.paragraph,
|
||||
type: 'paragraph',
|
||||
return (appParams?.user_input_form || [])
|
||||
.filter((item: any) => !item.external_data_tool)
|
||||
.map((item: any) => {
|
||||
if (item.paragraph) {
|
||||
return {
|
||||
...item.paragraph,
|
||||
type: "paragraph",
|
||||
};
|
||||
}
|
||||
}
|
||||
if (item.number) {
|
||||
return {
|
||||
...item.number,
|
||||
type: 'number',
|
||||
if (item.number) {
|
||||
return {
|
||||
...item.number,
|
||||
type: "number",
|
||||
};
|
||||
}
|
||||
}
|
||||
if (item.select) {
|
||||
return {
|
||||
...item.select,
|
||||
type: 'select',
|
||||
if (item.select) {
|
||||
return {
|
||||
...item.select,
|
||||
type: "select",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (item['file-list']) {
|
||||
return {
|
||||
...item['file-list'],
|
||||
type: 'file-list',
|
||||
if (item["file-list"]) {
|
||||
return {
|
||||
...item["file-list"],
|
||||
type: "file-list",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (item.file) {
|
||||
return {
|
||||
...item.file,
|
||||
type: 'file',
|
||||
if (item.file) {
|
||||
return {
|
||||
...item.file,
|
||||
type: "file",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...item['text-input'],
|
||||
type: 'text-input',
|
||||
}
|
||||
})
|
||||
}, [appParams])
|
||||
return {
|
||||
...item["text-input"],
|
||||
type: "text-input",
|
||||
};
|
||||
});
|
||||
}, [appParams]);
|
||||
useEffect(() => {
|
||||
const conversationInputs: Record<string, any> = {}
|
||||
const conversationInputs: Record<string, any> = {};
|
||||
|
||||
inputsForms.forEach((item: any) => {
|
||||
conversationInputs[item.variable] = item.default || null
|
||||
})
|
||||
handleNewConversationInputsChange(conversationInputs)
|
||||
}, [handleNewConversationInputsChange, inputsForms])
|
||||
conversationInputs[item.variable] = item.default || null;
|
||||
});
|
||||
handleNewConversationInputsChange(conversationInputs);
|
||||
}, [handleNewConversationInputsChange, inputsForms]);
|
||||
|
||||
const { data: newConversation } = useSWR(newConversationId ? [isInstalledApp, appId, newConversationId] : null, () => generationConversationName(newConversationId))
|
||||
const [originConversationList, setOriginConversationList] = useState<ConversationItem[]>([])
|
||||
const { data: newConversation } = useSWR(
|
||||
newConversationId ? [isInstalledApp, appId, newConversationId] : null,
|
||||
() => generationConversationName(newConversationId)
|
||||
);
|
||||
const [originConversationList, setOriginConversationList] = useState<
|
||||
ConversationItem[]
|
||||
>([]);
|
||||
useEffect(() => {
|
||||
if (appConversationData?.data && !appConversationDataLoading)
|
||||
setOriginConversationList(appConversationData?.data)
|
||||
}, [appConversationData, appConversationDataLoading])
|
||||
setOriginConversationList(appConversationData?.data);
|
||||
}, [appConversationData, appConversationDataLoading]);
|
||||
const conversationList = useMemo(() => {
|
||||
const data = originConversationList.slice()
|
||||
const data = originConversationList.slice();
|
||||
|
||||
if (showNewConversationItemInList && data[0]?.id !== '') {
|
||||
if (showNewConversationItemInList && data[0]?.id !== "") {
|
||||
data.unshift({
|
||||
id: '',
|
||||
name: t('share.chat.newChatDefaultName'),
|
||||
id: "",
|
||||
name: t("share.chat.newChatDefaultName"),
|
||||
inputs: {},
|
||||
introduction: '',
|
||||
})
|
||||
introduction: "",
|
||||
});
|
||||
}
|
||||
return data
|
||||
}, [originConversationList, showNewConversationItemInList, t])
|
||||
return data;
|
||||
}, [originConversationList, showNewConversationItemInList, t]);
|
||||
|
||||
useEffect(() => {
|
||||
if (newConversation) {
|
||||
setOriginConversationList(produce((draft) => {
|
||||
const index = draft.findIndex(item => item.id === newConversation.id)
|
||||
setOriginConversationList(
|
||||
produce((draft) => {
|
||||
const index = draft.findIndex(
|
||||
(item) => item.id === newConversation.id
|
||||
);
|
||||
|
||||
if (index > -1)
|
||||
draft[index] = newConversation
|
||||
else
|
||||
draft.unshift(newConversation)
|
||||
}))
|
||||
if (index > -1) draft[index] = newConversation;
|
||||
else draft.unshift(newConversation);
|
||||
})
|
||||
);
|
||||
}
|
||||
}, [newConversation])
|
||||
}, [newConversation]);
|
||||
|
||||
const currentConversationItem = useMemo(() => {
|
||||
let conversationItem = conversationList.find(item => item.id === currentConversationId)
|
||||
let conversationItem = conversationList.find(
|
||||
(item) => item.id === currentConversationId
|
||||
);
|
||||
|
||||
if (!conversationItem && pinnedConversationList.length)
|
||||
conversationItem = pinnedConversationList.find(item => item.id === currentConversationId)
|
||||
conversationItem = pinnedConversationList.find(
|
||||
(item) => item.id === currentConversationId
|
||||
);
|
||||
|
||||
return conversationItem
|
||||
}, [conversationList, currentConversationId, pinnedConversationList])
|
||||
return conversationItem;
|
||||
}, [conversationList, currentConversationId, pinnedConversationList]);
|
||||
|
||||
const { notify } = useToastContext()
|
||||
const checkInputsRequired = useCallback((silent?: boolean) => {
|
||||
let hasEmptyInput = ''
|
||||
let fileIsUploading = false
|
||||
const requiredVars = inputsForms.filter(({ required }) => required)
|
||||
if (requiredVars.length) {
|
||||
requiredVars.forEach(({ variable, label, type }) => {
|
||||
if (hasEmptyInput)
|
||||
return
|
||||
const { notify } = useToastContext();
|
||||
const checkInputsRequired = useCallback(
|
||||
(silent?: boolean) => {
|
||||
let hasEmptyInput = "";
|
||||
let fileIsUploading = false;
|
||||
const requiredVars = inputsForms.filter(({ required }) => required);
|
||||
if (requiredVars.length) {
|
||||
requiredVars.forEach(({ variable, label, type }) => {
|
||||
if (hasEmptyInput) return;
|
||||
|
||||
if (fileIsUploading)
|
||||
return
|
||||
if (fileIsUploading) return;
|
||||
|
||||
if (!newConversationInputsRef.current[variable] && !silent)
|
||||
hasEmptyInput = label as string
|
||||
if (!newConversationInputsRef.current[variable] && !silent)
|
||||
hasEmptyInput = label as string;
|
||||
|
||||
if ((type === InputVarType.singleFile || type === InputVarType.multiFiles) && newConversationInputsRef.current[variable] && !silent) {
|
||||
const files = newConversationInputsRef.current[variable]
|
||||
if (Array.isArray(files))
|
||||
fileIsUploading = files.find(item => item.transferMethod === TransferMethod.local_file && !item.uploadedId)
|
||||
else
|
||||
fileIsUploading = files.transferMethod === TransferMethod.local_file && !files.uploadedId
|
||||
}
|
||||
})
|
||||
}
|
||||
if (
|
||||
(type === InputVarType.singleFile ||
|
||||
type === InputVarType.multiFiles) &&
|
||||
newConversationInputsRef.current[variable] &&
|
||||
!silent
|
||||
) {
|
||||
const files = newConversationInputsRef.current[variable];
|
||||
if (Array.isArray(files))
|
||||
fileIsUploading = files.find(
|
||||
(item) =>
|
||||
item.transferMethod === TransferMethod.local_file &&
|
||||
!item.uploadedId
|
||||
);
|
||||
else
|
||||
fileIsUploading =
|
||||
files.transferMethod === TransferMethod.local_file &&
|
||||
!files.uploadedId;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (hasEmptyInput) {
|
||||
notify({ type: 'error', message: t('appDebug.errorMessage.valueOfVarRequired', { key: hasEmptyInput }) })
|
||||
return false
|
||||
}
|
||||
if (hasEmptyInput) {
|
||||
notify({
|
||||
type: "error",
|
||||
message: t("appDebug.errorMessage.valueOfVarRequired", {
|
||||
key: hasEmptyInput,
|
||||
}),
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fileIsUploading) {
|
||||
notify({ type: 'info', message: t('appDebug.errorMessage.waitForFileUpload') })
|
||||
return
|
||||
}
|
||||
if (fileIsUploading) {
|
||||
notify({
|
||||
type: "info",
|
||||
message: t("appDebug.errorMessage.waitForFileUpload"),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
return true
|
||||
}, [inputsForms, notify, t])
|
||||
return true;
|
||||
},
|
||||
[inputsForms, notify, t]
|
||||
);
|
||||
const handleStartChat = useCallback(() => {
|
||||
if (checkInputsRequired()) {
|
||||
setShowConfigPanelBeforeChat(false)
|
||||
setShowNewConversationItemInList(true)
|
||||
setShowConfigPanelBeforeChat(false);
|
||||
setShowNewConversationItemInList(true);
|
||||
}
|
||||
}, [setShowConfigPanelBeforeChat, setShowNewConversationItemInList, checkInputsRequired])
|
||||
const currentChatInstanceRef = useRef<{ handleStop: () => void }>({ handleStop: () => { } })
|
||||
const handleChangeConversation = useCallback((conversationId: string) => {
|
||||
currentChatInstanceRef.current.handleStop()
|
||||
setNewConversationId('')
|
||||
handleConversationIdInfoChange(conversationId)
|
||||
}, [
|
||||
setShowConfigPanelBeforeChat,
|
||||
setShowNewConversationItemInList,
|
||||
checkInputsRequired,
|
||||
]);
|
||||
const currentChatInstanceRef = useRef<{ handleStop: () => void }>({
|
||||
handleStop: () => {},
|
||||
});
|
||||
const handleChangeConversation = useCallback(
|
||||
(conversationId: string) => {
|
||||
currentChatInstanceRef.current.handleStop();
|
||||
setNewConversationId("");
|
||||
handleConversationIdInfoChange(conversationId);
|
||||
|
||||
if (conversationId === '' && !checkInputsRequired(true))
|
||||
setShowConfigPanelBeforeChat(true)
|
||||
else
|
||||
setShowConfigPanelBeforeChat(false)
|
||||
}, [handleConversationIdInfoChange, setShowConfigPanelBeforeChat, checkInputsRequired])
|
||||
if (conversationId === "" && !checkInputsRequired(true))
|
||||
setShowConfigPanelBeforeChat(true);
|
||||
else setShowConfigPanelBeforeChat(false);
|
||||
},
|
||||
[
|
||||
handleConversationIdInfoChange,
|
||||
setShowConfigPanelBeforeChat,
|
||||
checkInputsRequired,
|
||||
]
|
||||
);
|
||||
const handleNewConversation = useCallback(() => {
|
||||
currentChatInstanceRef.current.handleStop()
|
||||
setNewConversationId('')
|
||||
currentChatInstanceRef.current.handleStop();
|
||||
setNewConversationId("");
|
||||
|
||||
if (showNewConversationItemInList) {
|
||||
handleChangeConversation('')
|
||||
handleChangeConversation("");
|
||||
} else if (currentConversationId) {
|
||||
handleConversationIdInfoChange("");
|
||||
setShowConfigPanelBeforeChat(true);
|
||||
setShowNewConversationItemInList(true);
|
||||
handleNewConversationInputsChange({});
|
||||
}
|
||||
else if (currentConversationId) {
|
||||
handleConversationIdInfoChange('')
|
||||
setShowConfigPanelBeforeChat(true)
|
||||
setShowNewConversationItemInList(true)
|
||||
handleNewConversationInputsChange({})
|
||||
}
|
||||
}, [handleChangeConversation, currentConversationId, handleConversationIdInfoChange, setShowConfigPanelBeforeChat, setShowNewConversationItemInList, showNewConversationItemInList, handleNewConversationInputsChange])
|
||||
}, [
|
||||
handleChangeConversation,
|
||||
currentConversationId,
|
||||
handleConversationIdInfoChange,
|
||||
setShowConfigPanelBeforeChat,
|
||||
setShowNewConversationItemInList,
|
||||
showNewConversationItemInList,
|
||||
handleNewConversationInputsChange,
|
||||
]);
|
||||
const handleUpdateConversationList = useCallback(() => {
|
||||
mutateAppConversationData()
|
||||
mutateAppPinnedConversationData()
|
||||
}, [mutateAppConversationData, mutateAppPinnedConversationData])
|
||||
mutateAppConversationData();
|
||||
mutateAppPinnedConversationData();
|
||||
}, [mutateAppConversationData, mutateAppPinnedConversationData]);
|
||||
|
||||
const handlePinConversation = useCallback(async (conversationId: string) => {
|
||||
await pinConversation(isInstalledApp, appId, conversationId)
|
||||
notify({ type: 'success', message: t('common.api.success') })
|
||||
handleUpdateConversationList()
|
||||
}, [isInstalledApp, appId, notify, t, handleUpdateConversationList])
|
||||
const handlePinConversation = useCallback(
|
||||
async (conversationId: string) => {
|
||||
await pinConversation(isInstalledApp, appId, conversationId);
|
||||
notify({ type: "success", message: t("common.api.success") });
|
||||
handleUpdateConversationList();
|
||||
},
|
||||
[isInstalledApp, appId, notify, t, handleUpdateConversationList]
|
||||
);
|
||||
|
||||
const handleUnpinConversation = useCallback(async (conversationId: string) => {
|
||||
await unpinConversation(isInstalledApp, appId, conversationId)
|
||||
notify({ type: 'success', message: t('common.api.success') })
|
||||
handleUpdateConversationList()
|
||||
}, [isInstalledApp, appId, notify, t, handleUpdateConversationList])
|
||||
const handleUnpinConversation = useCallback(
|
||||
async (conversationId: string) => {
|
||||
await unpinConversation(isInstalledApp, appId, conversationId);
|
||||
notify({ type: "success", message: t("common.api.success") });
|
||||
handleUpdateConversationList();
|
||||
},
|
||||
[isInstalledApp, appId, notify, t, handleUpdateConversationList]
|
||||
);
|
||||
|
||||
const [conversationDeleting, setConversationDeleting] = useState(false)
|
||||
const handleDeleteConversation = useCallback(async (
|
||||
conversationId: string,
|
||||
{
|
||||
onSuccess,
|
||||
}: Callback,
|
||||
) => {
|
||||
if (conversationDeleting)
|
||||
return
|
||||
const [conversationDeleting, setConversationDeleting] = useState(false);
|
||||
const handleDeleteConversation = useCallback(
|
||||
async (conversationId: string, { onSuccess }: Callback) => {
|
||||
if (conversationDeleting) return;
|
||||
|
||||
try {
|
||||
setConversationDeleting(true)
|
||||
await delConversation(conversationId)
|
||||
notify({ type: 'success', message: t('common.api.success') })
|
||||
onSuccess()
|
||||
}
|
||||
finally {
|
||||
setConversationDeleting(false)
|
||||
}
|
||||
try {
|
||||
setConversationDeleting(true);
|
||||
await delConversation(conversationId);
|
||||
notify({ type: "success", message: t("common.api.success") });
|
||||
onSuccess();
|
||||
} finally {
|
||||
setConversationDeleting(false);
|
||||
}
|
||||
|
||||
if (conversationId === currentConversationId)
|
||||
handleNewConversation()
|
||||
if (conversationId === currentConversationId) handleNewConversation();
|
||||
|
||||
handleUpdateConversationList()
|
||||
}, [isInstalledApp, appId, notify, t, handleUpdateConversationList, handleNewConversation, currentConversationId, conversationDeleting])
|
||||
handleUpdateConversationList();
|
||||
},
|
||||
[
|
||||
isInstalledApp,
|
||||
appId,
|
||||
notify,
|
||||
t,
|
||||
handleUpdateConversationList,
|
||||
handleNewConversation,
|
||||
currentConversationId,
|
||||
conversationDeleting,
|
||||
]
|
||||
);
|
||||
|
||||
const [conversationRenaming, setConversationRenaming] = useState(false)
|
||||
const handleRenameConversation = useCallback(async (
|
||||
conversationId: string,
|
||||
newName: string,
|
||||
{
|
||||
onSuccess,
|
||||
}: Callback,
|
||||
) => {
|
||||
if (conversationRenaming)
|
||||
return
|
||||
const [conversationRenaming, setConversationRenaming] = useState(false);
|
||||
const handleRenameConversation = useCallback(
|
||||
async (
|
||||
conversationId: string,
|
||||
newName: string,
|
||||
{ onSuccess }: Callback
|
||||
) => {
|
||||
if (conversationRenaming) return;
|
||||
|
||||
if (!newName.trim()) {
|
||||
notify({
|
||||
type: 'error',
|
||||
message: t('common.chat.conversationNameCanNotEmpty'),
|
||||
})
|
||||
return
|
||||
}
|
||||
if (!newName.trim()) {
|
||||
notify({
|
||||
type: "error",
|
||||
message: t("common.chat.conversationNameCanNotEmpty"),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
setConversationRenaming(true)
|
||||
try {
|
||||
await renameConversation(conversationId, newName)
|
||||
setConversationRenaming(true);
|
||||
try {
|
||||
await renameConversation(conversationId, newName);
|
||||
|
||||
notify({
|
||||
type: 'success',
|
||||
message: t('common.actionMsg.modifiedSuccessfully'),
|
||||
})
|
||||
setOriginConversationList(produce((draft) => {
|
||||
const index = originConversationList.findIndex(item => item.id === conversationId)
|
||||
const item = draft[index]
|
||||
notify({
|
||||
type: "success",
|
||||
message: t("common.actionMsg.modifiedSuccessfully"),
|
||||
});
|
||||
setOriginConversationList(
|
||||
produce((draft) => {
|
||||
const index = originConversationList.findIndex(
|
||||
(item) => item.id === conversationId
|
||||
);
|
||||
const item = draft[index];
|
||||
|
||||
draft[index] = {
|
||||
...item,
|
||||
name: newName,
|
||||
}
|
||||
}))
|
||||
onSuccess()
|
||||
}
|
||||
finally {
|
||||
setConversationRenaming(false)
|
||||
}
|
||||
}, [isInstalledApp, appId, notify, t, conversationRenaming, originConversationList])
|
||||
draft[index] = {
|
||||
...item,
|
||||
name: newName,
|
||||
};
|
||||
})
|
||||
);
|
||||
onSuccess();
|
||||
} finally {
|
||||
setConversationRenaming(false);
|
||||
}
|
||||
},
|
||||
[
|
||||
isInstalledApp,
|
||||
appId,
|
||||
notify,
|
||||
t,
|
||||
conversationRenaming,
|
||||
originConversationList,
|
||||
]
|
||||
);
|
||||
|
||||
const handleNewConversationCompleted = useCallback((newConversationId: string) => {
|
||||
setNewConversationId(newConversationId)
|
||||
handleConversationIdInfoChange(newConversationId)
|
||||
setShowNewConversationItemInList(false)
|
||||
mutateAppConversationData()
|
||||
}, [mutateAppConversationData, handleConversationIdInfoChange])
|
||||
const handleNewConversationCompleted = useCallback(
|
||||
(newConversationId: string) => {
|
||||
setNewConversationId(newConversationId);
|
||||
handleConversationIdInfoChange(newConversationId);
|
||||
setShowNewConversationItemInList(false);
|
||||
mutateAppConversationData();
|
||||
},
|
||||
[mutateAppConversationData, handleConversationIdInfoChange]
|
||||
);
|
||||
|
||||
const handleFeedback = useCallback(async (messageId: string, feedback: Feedback) => {
|
||||
await updateFeedback({ url: `/messages/${messageId}/feedbacks`, body: { rating: feedback.rating } }, isInstalledApp, appId)
|
||||
notify({ type: 'success', message: t('common.api.success') })
|
||||
}, [isInstalledApp, appId, t, notify])
|
||||
const [isFeedbackModalVisible, setIsFeedbackModalVisible] = useState(false);
|
||||
const [currentMessageId, setCurrentMessageId] = useState<string | null>(null);
|
||||
const [currentFeedback, setCurrentFeedback] = useState<Feedback | null>(null);
|
||||
const [
|
||||
currentConversationIdForFeedback,
|
||||
setCurrentConversationIdForFeedback,
|
||||
] = useState<string | null>(null);
|
||||
|
||||
const handleFeedback = useCallback(
|
||||
(messageId: string, feedback: Feedback, conversationId: string) => {
|
||||
setCurrentMessageId(messageId);
|
||||
setCurrentFeedback(feedback);
|
||||
setCurrentConversationIdForFeedback(conversationId);
|
||||
setIsFeedbackModalVisible(true);
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
const handleFeedbackSubmit = useCallback(
|
||||
async (selectedOption: number | null, feedbackText: string) => {
|
||||
console.log(currentMessageId);
|
||||
console.log(currentFeedback);
|
||||
|
||||
try {
|
||||
// 构造请求参数
|
||||
const requestBody = {
|
||||
operationType: selectedOption, // 0 或 1
|
||||
feedbackText, // 用户反馈建议
|
||||
conversationId: '89fba8a7-e4e2-4167-8b7c-b3c103797053', // 会话 ID
|
||||
messageId: currentMessageId, // 消息 ID
|
||||
username: "user_admin", // 用户名
|
||||
};
|
||||
|
||||
// 调用 Java 接口
|
||||
const javaResponse = await fetch(
|
||||
`${process.env.NEXT_PUBLIC_BASE_API_URL}/api/conversation/feedback`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(requestBody),
|
||||
}
|
||||
);
|
||||
|
||||
// 调用原有的 updateFeedback 函数
|
||||
await updateFeedback(
|
||||
{
|
||||
url: `/messages/${currentMessageId}/feedbacks`,
|
||||
body: { rating: currentFeedback.rating },
|
||||
},
|
||||
isInstalledApp,
|
||||
appId
|
||||
);
|
||||
|
||||
// 显示成功通知
|
||||
notify({ type: "success", message: t("common.api.success") });
|
||||
|
||||
// 关闭对话框
|
||||
setIsFeedbackModalVisible(false);
|
||||
} catch (error) {
|
||||
console.error("Error:", error);
|
||||
notify({ type: "error", message: t("common.api.failed") });
|
||||
}
|
||||
},
|
||||
[
|
||||
currentMessageId,
|
||||
currentFeedback,
|
||||
currentConversationIdForFeedback,
|
||||
isInstalledApp,
|
||||
appId,
|
||||
notify,
|
||||
t,
|
||||
]
|
||||
);
|
||||
|
||||
return {
|
||||
appInfoError,
|
||||
@ -427,7 +614,7 @@ export const useChatWithHistory = (installedAppInfo?: InstalledApp) => {
|
||||
currentConversationItem,
|
||||
handleConversationIdInfoChange,
|
||||
appData,
|
||||
appParams: appParams || {} as ChatConfig,
|
||||
appParams: appParams || ({} as ChatConfig),
|
||||
appMeta,
|
||||
appPinnedConversationData,
|
||||
appConversationData,
|
||||
@ -458,5 +645,12 @@ export const useChatWithHistory = (installedAppInfo?: InstalledApp) => {
|
||||
chatShouldReloadKey,
|
||||
handleFeedback,
|
||||
currentChatInstanceRef,
|
||||
}
|
||||
}
|
||||
feedbackModal: (
|
||||
<FeedbackModal
|
||||
open={isFeedbackModalVisible}
|
||||
onOk={handleFeedbackSubmit}
|
||||
onCancel={() => setIsFeedbackModalVisible(false)}
|
||||
/>
|
||||
),
|
||||
};
|
||||
};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user