From 08e23c4ae3c69928f9e5fb14ff634f0cc773e84a Mon Sep 17 00:00:00 2001 From: liuyangjuncong20202570 Date: Sun, 17 Aug 2025 14:07:03 +0800 Subject: [PATCH 1/2] chore:adjust the logic of polling --- .../MrView/components/Checks/cpns/Task.tsx | 77 +++++++++++-------- .../MrView/components/Checks/cpns/store.ts | 10 ++- .../MrView/components/Checks/index.tsx | 12 ++- .../apps/web/components/MrView/hook/useSSM.ts | 4 +- moon/apps/web/hooks/SSE/ssmRequest.ts | 41 ++++++++++ moon/apps/web/hooks/SSE/useGetMrTask.ts | 5 +- moon/apps/web/hooks/SSE/useGetMrTaskStatus.ts | 28 +++++++ moon/apps/web/hooks/SSE/useGetTaskStatus.ts | 16 ++++ moon/apps/web/hooks/SSE/useRequest.ts | 13 ---- 9 files changed, 156 insertions(+), 50 deletions(-) create mode 100644 moon/apps/web/hooks/SSE/ssmRequest.ts create mode 100644 moon/apps/web/hooks/SSE/useGetMrTaskStatus.ts create mode 100644 moon/apps/web/hooks/SSE/useGetTaskStatus.ts delete mode 100644 moon/apps/web/hooks/SSE/useRequest.ts diff --git a/moon/apps/web/components/MrView/components/Checks/cpns/Task.tsx b/moon/apps/web/components/MrView/components/Checks/cpns/Task.tsx index e9d6b3f9e..09f4585e4 100644 --- a/moon/apps/web/components/MrView/components/Checks/cpns/Task.tsx +++ b/moon/apps/web/components/MrView/components/Checks/cpns/Task.tsx @@ -7,12 +7,12 @@ import { LoadingSpinner } from '@gitmono/ui/Spinner' import { buildId } from '@/components/Issues/utils/store' import { TaskResult } from '@/hooks/SSE/useGetMrTask' -import { loadingAtom, Status, statusAtom } from './store' +import { loadingAtom, Status, statusMapAtom } from './store' export const mocks = [ { arguments: '--env=prod --force', - build_id: 'BUILD_20250813001', + build_id: '0198b32b-6ede-7be2-99dc-aee8c7ef358d', end_at: '2025-08-13T16:20:00Z', exit_code: 0, mr: 'MR-125', @@ -49,30 +49,9 @@ export const Task = ({ list }: { list: TaskResult[] }) => { const [extend, setExtend] = useState(false) const [_, setBuildId] = useAtom(buildId) const [_loading, setLoading] = useAtom(loadingAtom) - const [status] = useAtom(statusAtom) list = mocks - const handleClick = (build_id: string) => { - // 此处建立连接 - setLoading(true) - setBuildId(build_id) - // if (eventSourcesRef.current[build_id]) return - // setEventSource(build_id) - } - - const identifyStatus = (status: string) => { - switch (status) { - case Status.Success: - return - case Status.Fail: - return - - default: - return - } - } - return ( <>
{ {!extend && list && (
{list.map((i) => ( -
handleClick(i.build_id)} - className='!fz-[14px] flex !h-[37px] items-center gap-2' - key={i.build_id} - > - {identifyStatus(status[i.build_id])} - {i.mr} -
+ ))}
)} ) } + +export const TaskItem = ({ task }: { task: TaskResult }) => { + const [_loading, setLoading] = useAtom(loadingAtom) + const [statusMap] = useAtom(statusMapAtom) + + const [_, setBuildId] = useAtom(buildId) + const handleClick = (build_id: string) => { + // 此处建立连接 + setLoading(true) + setBuildId(build_id) + // if (eventSourcesRef.current[build_id]) return + // setEventSource(build_id) + } + + return ( + <> +
handleClick(task.build_id)} + className='!fz-[14px] flex !h-[37px] items-center gap-2' + key={task.build_id} + > + {identifyStatus(statusMap.get(task.build_id)?.status || Status.NotFound)} + {task.mr} +
+ + ) +} + +export const identifyStatus = (status: Status[keyof Status]) => { + switch (status) { + case Status.Completed: + return + case Status.Failed: + return + case Status.Building: + return + case Status.Pending: + return + + default: + return + } +} diff --git a/moon/apps/web/components/MrView/components/Checks/cpns/store.ts b/moon/apps/web/components/MrView/components/Checks/cpns/store.ts index aca7a61a1..1bc0defec 100644 --- a/moon/apps/web/components/MrView/components/Checks/cpns/store.ts +++ b/moon/apps/web/components/MrView/components/Checks/cpns/store.ts @@ -1,11 +1,17 @@ import { atom } from 'jotai' +import { MRTaskStatus } from '@/hooks/SSE/useGetMrTaskStatus' + export enum Status { Pending = 'pending', - Success = 'success', - Fail = 'fail' + Completed = 'completed', + Failed = 'failed', + Building = 'building', + Interrupted = 'interrupted', + NotFound = 'notfound' } export const logsAtom = atom>({}) export const statusAtom = atom>({}) export const loadingAtom = atom(false) +export const statusMapAtom = atom>(new Map()) diff --git a/moon/apps/web/components/MrView/components/Checks/index.tsx b/moon/apps/web/components/MrView/components/Checks/index.tsx index 0cda08734..c407b078c 100644 --- a/moon/apps/web/components/MrView/components/Checks/index.tsx +++ b/moon/apps/web/components/MrView/components/Checks/index.tsx @@ -6,9 +6,10 @@ import { LoadingSpinner } from '@gitmono/ui/Spinner' import { buildId } from '@/components/Issues/utils/store' import { TaskResult, useGetMrTask } from '@/hooks/SSE/useGetMrTask' +import { useGetMrTaskStatus } from '@/hooks/SSE/useGetMrTaskStatus' import { useTaskSSE } from '../../hook/useSSM' -import { loadingAtom } from './cpns/store' +import { loadingAtom, statusMapAtom } from './cpns/store' import { mocks, Task } from './cpns/Task' const Checks = ({ mr }: { mr: string }) => { @@ -16,6 +17,15 @@ const Checks = ({ mr }: { mr: string }) => { const [buildid, setBuildId] = useAtom(buildId) const { logsMap, setEventSource } = useTaskSSE() const [loading] = useAtom(loadingAtom) + const [statusMap, _setStatusMap] = useAtom(statusMapAtom) + const { data: status } = useGetMrTaskStatus(mr) + + useEffect(() => { + status && + status.map((i) => { + statusMap.set(i.build_id, i) + }) + }, [status, statusMap]) // 页面加载时建立连接 useEffect(() => { diff --git a/moon/apps/web/components/MrView/hook/useSSM.ts b/moon/apps/web/components/MrView/hook/useSSM.ts index a1631a9ed..a1a98f18d 100644 --- a/moon/apps/web/components/MrView/hook/useSSM.ts +++ b/moon/apps/web/components/MrView/hook/useSSM.ts @@ -38,8 +38,8 @@ export const useTaskSSE = () => { const [_status, setStatus] = useAtom(statusAtom) const setEventSource: (taskId: string) => void = (taskId: string) => { - // const es = new EventSource(`/sse/task-output/${taskId}`) - const es = new EventSource(`/api/event?id=${taskId}`) + const es = new EventSource(`/sse/task-output/${taskId}`) + // const es = new EventSource(`/api/event?id=${taskId}`) es.onmessage = (e) => { setLogsMap((prev) => { diff --git a/moon/apps/web/hooks/SSE/ssmRequest.ts b/moon/apps/web/hooks/SSE/ssmRequest.ts new file mode 100644 index 000000000..c8a4c0ce8 --- /dev/null +++ b/moon/apps/web/hooks/SSE/ssmRequest.ts @@ -0,0 +1,41 @@ +export const fetchTask = async (mr: string) => { + const res = await fetch(`/sse/mr-task/${mr}`, { + method: 'GET', + headers: { + 'Content-Type': 'application/json' + } + }) + + if (!res.ok) { + throw new Error(`HTTP error ${res.status}`) + } + return res.json() +} + +export const taskStatus = async (taskId: string) => { + const res = await fetch(`/sse/task-status/${taskId}`, { + method: 'GET', + headers: { + 'Content-Type': 'application/json' + } + }) + + if (!res.ok) { + throw new Error(`HTTP error ${res.status}`) + } + return res.json() +} + +export const MrTaskStatus = async (mr: string) => { + const res = await fetch(`/sse/tasks/${mr}`, { + method: 'GET', + headers: { + 'Content-Type': 'application/json' + } + }) + + if (!res.ok) { + throw new Error(`HTTP error ${res.status}`) + } + return res.json() +} diff --git a/moon/apps/web/hooks/SSE/useGetMrTask.ts b/moon/apps/web/hooks/SSE/useGetMrTask.ts index 3aea405fe..de5805179 100644 --- a/moon/apps/web/hooks/SSE/useGetMrTask.ts +++ b/moon/apps/web/hooks/SSE/useGetMrTask.ts @@ -1,6 +1,6 @@ import { useQuery } from '@tanstack/react-query' -import { fetchTask } from './useRequest' +import { fetchTask } from './ssmRequest' export interface TaskResult { arguments: string @@ -18,5 +18,8 @@ export function useGetMrTask(mr: string) { return useQuery({ queryKey: [mr], queryFn: () => fetchTask(mr) + // refetchInterval: 15000, + // refetchIntervalInBackground: true, + // enabled: !!mr }) } diff --git a/moon/apps/web/hooks/SSE/useGetMrTaskStatus.ts b/moon/apps/web/hooks/SSE/useGetMrTaskStatus.ts new file mode 100644 index 000000000..490bab38b --- /dev/null +++ b/moon/apps/web/hooks/SSE/useGetMrTaskStatus.ts @@ -0,0 +1,28 @@ +import { useQuery } from '@tanstack/react-query' + +import { Status } from '@/components/MrView/components/Checks/cpns/store' + +import { MrTaskStatus } from './ssmRequest' + +export interface MRTaskStatus { + arguments: string + build_id: string + end_at: string + exit_code: number + mr: string + output_file: string + repo_name: string + start_at: string + status: Status + target: string +} + +export function useGetMrTaskStatus(mr: string) { + return useQuery({ + queryKey: [mr], + queryFn: () => MrTaskStatus(mr), + refetchInterval: 15000, + refetchIntervalInBackground: true, + enabled: !!mr + }) +} diff --git a/moon/apps/web/hooks/SSE/useGetTaskStatus.ts b/moon/apps/web/hooks/SSE/useGetTaskStatus.ts new file mode 100644 index 000000000..03275d904 --- /dev/null +++ b/moon/apps/web/hooks/SSE/useGetTaskStatus.ts @@ -0,0 +1,16 @@ +import { useQuery } from '@tanstack/react-query' + +import { taskStatus } from './ssmRequest' + +export interface TaskStatus { + exit_code: number + message: string + status: string +} + +export function useGetTaskStatus(taskId: string) { + return useQuery({ + queryKey: [taskId], + queryFn: () => taskStatus(taskId) + }) +} diff --git a/moon/apps/web/hooks/SSE/useRequest.ts b/moon/apps/web/hooks/SSE/useRequest.ts deleted file mode 100644 index 3651aae82..000000000 --- a/moon/apps/web/hooks/SSE/useRequest.ts +++ /dev/null @@ -1,13 +0,0 @@ -export const fetchTask = async (mr: string) => { - const res = await fetch(`/sse/mr-task/${mr}`, { - method: 'GET', - headers: { - 'Content-Type': 'application/json' - } - }) - - if (!res.ok) { - throw new Error(`HTTP error ${res.status}`) - } - return res.json() -} From 0866487a5ac60efb8006c74c1b45b57b28e63241 Mon Sep 17 00:00:00 2001 From: liuyangjuncong20202570 Date: Sat, 23 Aug 2025 20:36:48 +0800 Subject: [PATCH 2/2] feat(UI):sse logs complete & dynamic import of cmps --- .../web/components/Issues/IssueDetailPage.tsx | 4 +- .../web/components/Issues/utils/store.tsx | 2 +- moon/apps/web/components/Layout/TabLayout.tsx | 36 ++ .../MrView/components/Checks/cpns/Task.tsx | 17 +- .../MrView/components/Checks/cpns/store.ts | 17 +- .../MrView/components/Checks/index.tsx | 98 +++-- .../apps/web/components/MrView/hook/useSSM.ts | 16 +- moon/apps/web/hooks/SSE/ssmRequest.ts | 16 + moon/apps/web/hooks/SSE/useGetHTTPLog.ts | 23 ++ moon/apps/web/hooks/SSE/useGetMrTask.ts | 2 +- moon/apps/web/pages/[org]/mr/[link]/index.tsx | 355 +++++++++--------- 11 files changed, 350 insertions(+), 236 deletions(-) create mode 100644 moon/apps/web/components/Layout/TabLayout.tsx create mode 100644 moon/apps/web/hooks/SSE/useGetHTTPLog.ts diff --git a/moon/apps/web/components/Issues/IssueDetailPage.tsx b/moon/apps/web/components/Issues/IssueDetailPage.tsx index 3438f4666..a3a84c87c 100644 --- a/moon/apps/web/components/Issues/IssueDetailPage.tsx +++ b/moon/apps/web/components/Issues/IssueDetailPage.tsx @@ -326,7 +326,7 @@ export default function IssueDetailPage({ link }: { link: string }) { {`${issueDetail?.title || ''}`}   - ${issueDetail?.id} + ${link}
) : ( - + )} {info && info.status === 'open' && ( diff --git a/moon/apps/web/components/Issues/utils/store.tsx b/moon/apps/web/components/Issues/utils/store.tsx index 80b3d5a16..1877c1bc2 100644 --- a/moon/apps/web/components/Issues/utils/store.tsx +++ b/moon/apps/web/components/Issues/utils/store.tsx @@ -48,4 +48,4 @@ export const editIdAtom = atom(0) export const refreshAtom = atom(0) -export const buildId = atom('') +export const buildIdAtom = atom('') diff --git a/moon/apps/web/components/Layout/TabLayout.tsx b/moon/apps/web/components/Layout/TabLayout.tsx new file mode 100644 index 000000000..ea52f8f6e --- /dev/null +++ b/moon/apps/web/components/Layout/TabLayout.tsx @@ -0,0 +1,36 @@ +import { PropsWithChildren } from 'react' +import { ChecklistIcon, CommentDiscussionIcon, FileDiffIcon } from '@primer/octicons-react' +import { UnderlineNav } from '@primer/react' +import { useAtom } from 'jotai' + +import { tabAtom } from '../MrView/components/Checks/cpns/store' + +export const TabLayout = ({ children }: PropsWithChildren) => { + const [tab, setTab] = useAtom(tabAtom) + + return ( + <> + + setTab('conversation')} + icon={CommentDiscussionIcon} + // aria-current='page' + > + Conversation + + setTab('check')} icon={ChecklistIcon}> + Checks + + setTab('filechange')} + icon={FileDiffIcon} + > + Files Changed + + + {children} + + ) +} diff --git a/moon/apps/web/components/MrView/components/Checks/cpns/Task.tsx b/moon/apps/web/components/MrView/components/Checks/cpns/Task.tsx index 09f4585e4..e6eadb0ba 100644 --- a/moon/apps/web/components/MrView/components/Checks/cpns/Task.tsx +++ b/moon/apps/web/components/MrView/components/Checks/cpns/Task.tsx @@ -4,10 +4,10 @@ import { useAtom } from 'jotai' import { LoadingSpinner } from '@gitmono/ui/Spinner' -import { buildId } from '@/components/Issues/utils/store' +import { buildIdAtom } from '@/components/Issues/utils/store' import { TaskResult } from '@/hooks/SSE/useGetMrTask' -import { loadingAtom, Status, statusMapAtom } from './store' +import { Status, statusMapAtom } from './store' export const mocks = [ { @@ -47,10 +47,8 @@ export const mocks = [ export const Task = ({ list }: { list: TaskResult[] }) => { const [extend, setExtend] = useState(false) - const [_, setBuildId] = useAtom(buildId) - const [_loading, setLoading] = useAtom(loadingAtom) - list = mocks + // list = mocks return ( <> @@ -77,13 +75,12 @@ export const Task = ({ list }: { list: TaskResult[] }) => { } export const TaskItem = ({ task }: { task: TaskResult }) => { - const [_loading, setLoading] = useAtom(loadingAtom) const [statusMap] = useAtom(statusMapAtom) - const [_, setBuildId] = useAtom(buildId) + const [_, setBuildId] = useAtom(buildIdAtom) const handleClick = (build_id: string) => { // 此处建立连接 - setLoading(true) + // setLoading(true) setBuildId(build_id) // if (eventSourcesRef.current[build_id]) return // setEventSource(build_id) @@ -97,7 +94,7 @@ export const TaskItem = ({ task }: { task: TaskResult }) => { key={task.build_id} > {identifyStatus(statusMap.get(task.build_id)?.status || Status.NotFound)} - {task.mr} + {task.build_id} ) @@ -113,6 +110,8 @@ export const identifyStatus = (status: Status[keyof Status]) => { return case Status.Pending: return + case Status.NotFound: + return default: return diff --git a/moon/apps/web/components/MrView/components/Checks/cpns/store.ts b/moon/apps/web/components/MrView/components/Checks/cpns/store.ts index 1bc0defec..9757c19c6 100644 --- a/moon/apps/web/components/MrView/components/Checks/cpns/store.ts +++ b/moon/apps/web/components/MrView/components/Checks/cpns/store.ts @@ -3,15 +3,16 @@ import { atom } from 'jotai' import { MRTaskStatus } from '@/hooks/SSE/useGetMrTaskStatus' export enum Status { - Pending = 'pending', - Completed = 'completed', - Failed = 'failed', - Building = 'building', - Interrupted = 'interrupted', - NotFound = 'notfound' + Pending = 'Pending', + Completed = 'Completed', + Failed = 'Failed', + Building = 'Building', + Interrupted = 'Interrupted', + NotFound = 'NotFound' } -export const logsAtom = atom>({}) +export const logsAtom = atom>({}) export const statusAtom = atom>({}) -export const loadingAtom = atom(false) +export const loadingAtom = atom(true) export const statusMapAtom = atom>(new Map()) +export const tabAtom = atom<'conversation' | 'check' | 'filechange'>('conversation') diff --git a/moon/apps/web/components/MrView/components/Checks/index.tsx b/moon/apps/web/components/MrView/components/Checks/index.tsx index c407b078c..190c33663 100644 --- a/moon/apps/web/components/MrView/components/Checks/index.tsx +++ b/moon/apps/web/components/MrView/components/Checks/index.tsx @@ -1,42 +1,67 @@ -import { memo, useEffect } from 'react' +import { useEffect } from 'react' import { LazyLog } from '@melloware/react-logviewer' import { useAtom } from 'jotai' import { LoadingSpinner } from '@gitmono/ui/Spinner' -import { buildId } from '@/components/Issues/utils/store' -import { TaskResult, useGetMrTask } from '@/hooks/SSE/useGetMrTask' +import { buildIdAtom } from '@/components/Issues/utils/store' +import { HttpTaskRes } from '@/hooks/SSE/ssmRequest' +// import { TaskResult } from '@/hooks/SSE/useGetMrTask' import { useGetMrTaskStatus } from '@/hooks/SSE/useGetMrTaskStatus' import { useTaskSSE } from '../../hook/useSSM' -import { loadingAtom, statusMapAtom } from './cpns/store' -import { mocks, Task } from './cpns/Task' +import { statusMapAtom } from './cpns/store' +import { Task } from './cpns/Task' const Checks = ({ mr }: { mr: string }) => { - const { data } = useGetMrTask(mr) - const [buildid, setBuildId] = useAtom(buildId) - const { logsMap, setEventSource } = useTaskSSE() - const [loading] = useAtom(loadingAtom) + // const { data } = useGetMrTask(mr) + const [buildid, setBuildId] = useAtom(buildIdAtom) + const { logsMap, setEventSource, eventSourcesRef, setLogsMap } = useTaskSSE() const [statusMap, _setStatusMap] = useAtom(statusMapAtom) + // 获取所有构建任务 const { data: status } = useGetMrTaskStatus(mr) useEffect(() => { - status && - status.map((i) => { - statusMap.set(i.build_id, i) - }) - }, [status, statusMap]) + // 构建日志id与日志映射,同时获取已存在日志 + if (!status) return + // setBuildId((prev) => prev ?? status[0].build_id) + const fetchLogs = async () => { + const logsResult = await Promise.allSettled( + status.map(async (i) => { + statusMap.set(i.build_id, i) + const res = await HttpTaskRes(i.build_id, 0, 4096) + + return res + }) + ) + const newLogsMap = logsResult.reduce( + (acc, i) => { + if (i.status === 'fulfilled' && i.value) { + acc[i.value.task_id] = i.value.data === '' ? 'empty logs, please check it later' : i.value.data + } + return acc + }, + { ...logsMap } + ) + + setLogsMap(newLogsMap) + } + + fetchLogs() + setBuildId(status[0].build_id) + return () => { + statusMap.clear() + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [status]) // 页面加载时建立连接 useEffect(() => { - if (data) { - setBuildId(data[0].build_id) - data.map((i) => setEventSource(i.build_id)) + if (status?.length) { + status.map((i) => setEventSource(i.build_id)) } - setBuildId(mocks[0].build_id) - mocks.map((i) => setEventSource(i.build_id)) // eslint-disable-next-line react-hooks/exhaustive-deps - }, []) + }, [status]) return ( <> @@ -49,27 +74,29 @@ const Checks = ({ mr }: { mr: string }) => {
{/* left side */}
- {/* {data && } */} - + {status && } + {/* */}
{/* right side */}
- {logsMap[buildid] ? ( - - ) : ( - loading && ( + { + logsMap[buildid] && eventSourcesRef.current[buildid] ? ( + + ) : eventSourcesRef.current[buildid] ? ( +
+ ) : ( + //
) - )} + + // loading && ( + //
+ // + //
+ // ) + }
@@ -77,4 +104,5 @@ const Checks = ({ mr }: { mr: string }) => { ) } -export default memo(Checks) +// export default memo(Checks) +export default Checks diff --git a/moon/apps/web/components/MrView/hook/useSSM.ts b/moon/apps/web/components/MrView/hook/useSSM.ts index a1a98f18d..96b78b739 100644 --- a/moon/apps/web/components/MrView/hook/useSSM.ts +++ b/moon/apps/web/components/MrView/hook/useSSM.ts @@ -38,18 +38,27 @@ export const useTaskSSE = () => { const [_status, setStatus] = useAtom(statusAtom) const setEventSource: (taskId: string) => void = (taskId: string) => { + if (eventSourcesRef.current[taskId]) return const es = new EventSource(`/sse/task-output/${taskId}`) // const es = new EventSource(`/api/event?id=${taskId}`) es.onmessage = (e) => { setLogsMap((prev) => { - const prevLogs = prev[taskId] ?? [] + const prevLogs = prev[taskId] ?? '' + const newLog = e.data + '\n' + + // 判断最后一条是否重复 + if (prevLogs.endsWith(newLog)) { + return prev // 重复就直接返回原对象 + } return { ...prev, - [taskId]: [...prevLogs, e.data] // 每条消息生成新数组 + [taskId]: prevLogs + newLog } }) + + setLoading(false) } // status @@ -78,11 +87,12 @@ export const useTaskSSE = () => { Object.values(eventSourcesRef.current).forEach((es) => es.close()) eventSourcesRef.current = {} setLoading(false) + setLogsMap({}) } // eslint-disable-next-line react-hooks/exhaustive-deps }, []) - return { eventSourcesRef, setEventSource, logsMap } + return { eventSourcesRef, setEventSource, logsMap, setLogsMap } } export const useMultiTaskSSE = (taskIds: string[]) => { diff --git a/moon/apps/web/hooks/SSE/ssmRequest.ts b/moon/apps/web/hooks/SSE/ssmRequest.ts index c8a4c0ce8..4c66b6aa0 100644 --- a/moon/apps/web/hooks/SSE/ssmRequest.ts +++ b/moon/apps/web/hooks/SSE/ssmRequest.ts @@ -1,3 +1,5 @@ +import { HTTPLogRes } from './useGetHTTPLog' + export const fetchTask = async (mr: string) => { const res = await fetch(`/sse/mr-task/${mr}`, { method: 'GET', @@ -39,3 +41,17 @@ export const MrTaskStatus = async (mr: string) => { } return res.json() } + +export const HttpTaskRes = async (taskId: string, offset: number, len: number): Promise => { + const res = await fetch(`/sse/task-output-segment/${taskId}?offset=${offset}&len=${len}`, { + method: 'GET', + headers: { + 'Content-Type': 'application/json' + } + }) + + if (!res.ok) { + throw new Error(`HTTP error ${res.status}`) + } + return res.json() +} diff --git a/moon/apps/web/hooks/SSE/useGetHTTPLog.ts b/moon/apps/web/hooks/SSE/useGetHTTPLog.ts new file mode 100644 index 000000000..d9537cdfd --- /dev/null +++ b/moon/apps/web/hooks/SSE/useGetHTTPLog.ts @@ -0,0 +1,23 @@ +import { useQuery } from '@tanstack/react-query' + +import { HttpTaskRes } from './ssmRequest' + +export interface HTTPLogRes { + task_id: string + offset: number + len: number + data: string + next_offset: number + file_size: number + eof: boolean +} + +export function useGetHTTPLog(taskId: string) { + return useQuery({ + queryKey: [taskId], + queryFn: () => HttpTaskRes(taskId, 0, 4096) + // refetchInterval: 15000, + // refetchIntervalInBackground: true, + // enabled: !!mr + }) +} diff --git a/moon/apps/web/hooks/SSE/useGetMrTask.ts b/moon/apps/web/hooks/SSE/useGetMrTask.ts index de5805179..3c0dbaf7c 100644 --- a/moon/apps/web/hooks/SSE/useGetMrTask.ts +++ b/moon/apps/web/hooks/SSE/useGetMrTask.ts @@ -17,7 +17,7 @@ export interface TaskResult { export function useGetMrTask(mr: string) { return useQuery({ queryKey: [mr], - queryFn: () => fetchTask(mr) + queryFn: () => fetchTask(mr), // refetchInterval: 15000, // refetchIntervalInBackground: true, // enabled: !!mr diff --git a/moon/apps/web/pages/[org]/mr/[link]/index.tsx b/moon/apps/web/pages/[org]/mr/[link]/index.tsx index 27c67258d..d909fcfec 100644 --- a/moon/apps/web/pages/[org]/mr/[link]/index.tsx +++ b/moon/apps/web/pages/[org]/mr/[link]/index.tsx @@ -1,9 +1,9 @@ 'use client' import React, { useEffect, useRef, useState } from 'react' -import { ChecklistIcon, CommentDiscussionIcon, FileDiffIcon } from '@primer/octicons-react' import { BaseStyles, TextInput, ThemeProvider } from '@primer/react' import { useAtom } from 'jotai' +import dynamic from 'next/dynamic' import { useRouter } from 'next/router' import { toast } from 'react-hot-toast' @@ -26,8 +26,9 @@ import { } from '@/components/Issues/utils/sideEffect' import { editIdAtom, FALSE_EDIT_VAL, mridAtom, refreshAtom } from '@/components/Issues/utils/store' import { AppLayout } from '@/components/Layout/AppLayout' +import { TabLayout } from '@/components/Layout/TabLayout' import { MemberAvatar } from '@/components/MemberAvatar' -import Checks from '@/components/MrView/components/Checks' +import { tabAtom } from '@/components/MrView/components/Checks/cpns/store' import TimelineItems from '@/components/MrView/TimelineItems' import { useHandleBottomScrollOffset } from '@/components/NoteEditor/useHandleBottomScrollOffset' import AuthAppProviders from '@/components/Providers/AuthAppProviders' @@ -48,8 +49,6 @@ import { apiErrorToast } from '@/utils/apiErrorToast' import { trimHtml } from '@/utils/trimHtml' import { PageWithLayout } from '@/utils/types' -const { UnderlinePanels } = require('@primer/react/experimental') - export interface MRDetail { status: string conversations: ConversationItem[] @@ -72,6 +71,7 @@ const MRDetailPage: PageWithLayout = () => { const [isEdit, setIsEdit] = useState(false) const [editTitle, setEditTitle] = useState(mrDetail?.title) const [loading, setLoading] = useState(false) + const Checks = dynamic(() => import('@/components/MrView/components/Checks')) if (mrDetail && typeof mrDetail.status === 'string') { mrDetail.status = mrDetail.status.toLowerCase() @@ -235,6 +235,175 @@ const MRDetailPage: PageWithLayout = () => { ) } + const [tab] = useAtom(tabAtom) + const Conversation = () => ( +
+
+ {detailIsLoading ? ( +
+ +
+ ) : ( + mrDetail && + )} +
+
+ {mrDetail && mrDetail.status === 'open' && ( + + )} +
+

Add a comment

+ +
+ handleChange(html)} + /> +
+
+ {mrDetail && mrDetail.status === 'open' && ( + + )} + {mrDetail && mrDetail.status === 'closed' && ( + + )} + +
+
+
+ {/* */} +
+ handleAssignees(selected)} + open={open} + // eslint-disable-next-line react-hooks/rules-of-hooks + onOpenChange={(open) => handleOpenChange(open)} + selected={fetchSelected} + > + {(el) => { + const names = Array.from(new Set(splitFun(el))) + + return ( + <> + {names.map((i, index) => ( + // eslint-disable-next-line react/no-array-index-key +
+ + {i} +
+ ))} + + ) + }} +
+ handleLabels(selected)} + open={label_open} + onOpenChange={(open) => label_handleOpenChange(open)} + selected={label_fetchSelected} + > + {(el) => { + const names = splitFun(el) + + return ( + <> +
+ {names.map((i, index) => { + const label = labelMap.get(i) ?? {} + + return ( + // eslint-disable-next-line react/no-array-index-key +
+
+ {label.name} +
+
+ ) + })} +
+ + ) + }} +
+ + + +
+
+ ) + const FileChange = () => ( + <> + {fileChgIsLoading ? ( +
+ +
+ ) : MrFilesChangedData?.data?.content ? ( + // +
files
+ ) : ( +
No files changed
+ )} + + ) + return ( @@ -277,179 +446,11 @@ const MRDetailPage: PageWithLayout = () => { )}
- - Conversation - Checks - Files Changed - -
-
- {detailIsLoading ? ( -
- -
- ) : ( - mrDetail && - )} -
-
- {mrDetail && mrDetail.status === 'open' && ( - - )} -
-

Add a comment

- -
- handleChange(html)} - /> -
-
- {mrDetail && mrDetail.status === 'open' && ( - - )} - {mrDetail && mrDetail.status === 'closed' && ( - - )} - -
-
-
- {/* */} -
- handleAssignees(selected)} - open={open} - // eslint-disable-next-line react-hooks/rules-of-hooks - onOpenChange={(open) => handleOpenChange(open)} - selected={fetchSelected} - > - {(el) => { - const names = Array.from(new Set(splitFun(el))) - - return ( - <> - {names.map((i, index) => ( - // eslint-disable-next-line react/no-array-index-key -
- - {i} -
- ))} - - ) - }} -
- handleLabels(selected)} - open={label_open} - onOpenChange={(open) => label_handleOpenChange(open)} - selected={label_fetchSelected} - > - {(el) => { - const names = splitFun(el) - - return ( - <> -
- {names.map((i, index) => { - const label = labelMap.get(i) ?? {} - - return ( - // eslint-disable-next-line react/no-array-index-key -
-
- {label.name} -
-
- ) - })} -
- - ) - }} -
- - - -
-
-
- - - - - {fileChgIsLoading ? ( -
- -
- ) : MrFilesChangedData?.data?.content ? ( - // -
files
- ) : ( -
No files changed
- )} -
-
+ + {tab === 'check' && } + {tab === 'conversation' && } + {tab === 'filechange' && } +