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