mirror of
https://github.com/FunnyWolf/agentic-soc-platform.git
synced 2026-08-22 13:12:56 +02:00
Fix frontend runtime warnings
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -75,7 +75,7 @@ export default function AlertBasicView({ record, onOpenResource }: AlertBasicVie
|
||||
<Section title="Correlation">
|
||||
<Descriptions.Item label="Rule ID">{mono(stringValue(record, 'rule_id'))}</Descriptions.Item>
|
||||
<Descriptions.Item label="Rule Name">{stringValue(record, 'rule_name')}</Descriptions.Item>
|
||||
<Descriptions.Item label="Correlation UID">{mono(stringValue(record, 'correlation_uid'))}</Descriptions.Item>
|
||||
<Descriptions.Item label="Correlation UID" span="filled">{mono(stringValue(record, 'correlation_uid'))}</Descriptions.Item>
|
||||
</Section>
|
||||
|
||||
<Section title="MITRE ATT&CK and ATLAS">
|
||||
@@ -86,10 +86,10 @@ export default function AlertBasicView({ record, onOpenResource }: AlertBasicVie
|
||||
</Section>
|
||||
|
||||
<Section title="Status & Disposition & Action & Remediation">
|
||||
<Descriptions.Item label="Status">{statusTag(String(value(record, 'status') || ''))}</Descriptions.Item>
|
||||
<Descriptions.Item label="Status Detail" span={4}>{block(stringValue(record, 'status_detail'))}</Descriptions.Item>
|
||||
<Descriptions.Item label="Disposition">{alertDispositionTag(String(value(record, 'disposition') || ''))}</Descriptions.Item>
|
||||
<Descriptions.Item label="Action">{alertActionTag(String(value(record, 'action') || ''))}</Descriptions.Item>
|
||||
<Descriptions.Item label="Status" span="filled">{statusTag(String(value(record, 'status') || ''))}</Descriptions.Item>
|
||||
<Descriptions.Item label="Status Detail" span={4}>{block(stringValue(record, 'status_detail'))}</Descriptions.Item>
|
||||
<Descriptions.Item label="Remediation" span={4}>{block(stringValue(record, 'remediation'))}</Descriptions.Item>
|
||||
</Section>
|
||||
|
||||
@@ -106,7 +106,7 @@ export default function AlertBasicView({ record, onOpenResource }: AlertBasicVie
|
||||
<Section title="Analytic & Policy">
|
||||
<Descriptions.Item label="Analytic Name">{stringValue(record, 'analytic_name')}</Descriptions.Item>
|
||||
<Descriptions.Item label="Analytic Type">{alertAnalyticTypeTag(String(value(record, 'analytic_type') || ''))}</Descriptions.Item>
|
||||
<Descriptions.Item label="Analytic State">{choiceTag(String(value(record, 'analytic_state') || ''), 'green')}</Descriptions.Item>
|
||||
<Descriptions.Item label="Analytic State" span="filled">{choiceTag(String(value(record, 'analytic_state') || ''), 'green')}</Descriptions.Item>
|
||||
<Descriptions.Item label="Analytic Description" span={4}>{block(stringValue(record, 'analytic_desc'))}</Descriptions.Item>
|
||||
<Descriptions.Item label="Policy Name">{stringValue(record, 'policy_name')}</Descriptions.Item>
|
||||
<Descriptions.Item label="Policy Type">{choiceTag(String(value(record, 'policy_type') || ''), 'volcano')}</Descriptions.Item>
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import {useLayoutEffect} from 'react'
|
||||
import {App as AntApp} from 'antd'
|
||||
import {clearAppMessage, setAppMessage} from '../utils/appMessage'
|
||||
|
||||
export default function AppMessageProvider() {
|
||||
const {message} = AntApp.useApp()
|
||||
|
||||
useLayoutEffect(() => {
|
||||
setAppMessage(message)
|
||||
return () => clearAppMessage(message)
|
||||
}, [message])
|
||||
|
||||
return null
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import type {UploadProps} from 'antd'
|
||||
import {message, Upload} from 'antd'
|
||||
import {Upload} from 'antd'
|
||||
import {message} from '../utils/appMessage'
|
||||
import {CameraOutlined} from '@ant-design/icons'
|
||||
import ImgCrop from 'antd-img-crop'
|
||||
import client from '../api/client'
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import {useCallback, useEffect, useMemo, useRef, useState} from 'react'
|
||||
import {Empty, message, Spin, theme} from 'antd'
|
||||
import {Empty, Spin, theme} from 'antd'
|
||||
import {message} from '../utils/appMessage'
|
||||
import client from '../api/client'
|
||||
import type {EditableFieldConfig, FieldEditingController, OpenResourceOptions} from '../types/records'
|
||||
import DetailDraftActionBar from './DetailDraftActionBar'
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import {type CSSProperties, useCallback, useEffect, useMemo, useRef, useState} from 'react'
|
||||
import {Alert, Button, Empty, Input, message, Modal, Space, Table, Tag, theme, Tooltip, Typography} from 'antd'
|
||||
import {Alert, Button, Empty, Input, Modal, Space, Table, Tag, theme, Tooltip, Typography} from 'antd'
|
||||
import {message} from '../utils/appMessage'
|
||||
import {ReloadOutlined, SearchOutlined, ThunderboltOutlined} from '@ant-design/icons'
|
||||
import type {ColumnsType} from 'antd/es/table'
|
||||
import client from '../api/client'
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type {Key} from 'react'
|
||||
import {useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState} from 'react'
|
||||
import {Button, Checkbox, Divider, Input, message, Pagination, Popconfirm, Popover, Select, Space, Table, Tooltip} from 'antd'
|
||||
import {Button, Checkbox, Divider, Input, Pagination, Popconfirm, Popover, Select, Space, Table, Tooltip} from 'antd'
|
||||
import {message} from '../utils/appMessage'
|
||||
import {ClearOutlined, CloseOutlined, DeleteOutlined, FilterOutlined, HolderOutlined, PushpinFilled, PushpinOutlined, ReloadOutlined, SettingOutlined} from '@ant-design/icons'
|
||||
import {closestCenter, DndContext, type DragEndEvent, PointerSensor, useSensor, useSensors} from '@dnd-kit/core'
|
||||
import {arrayMove, SortableContext, useSortable, verticalListSortingStrategy} from '@dnd-kit/sortable'
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import {useCallback, useEffect, useMemo, useState} from 'react'
|
||||
import {Alert, Button, Input, List, message, Popconfirm, Space, Tooltip} from 'antd'
|
||||
import {Alert, Button, Input, List, Popconfirm, Space, Tooltip} from 'antd'
|
||||
import {message} from '../utils/appMessage'
|
||||
import {MessageOutlined, ReloadOutlined, SearchOutlined} from '@ant-design/icons'
|
||||
import dayjs from 'dayjs'
|
||||
import relativeTime from 'dayjs/plugin/relativeTime'
|
||||
@@ -170,7 +171,6 @@ export default function DiscussionThread({ contentType, objectId }: DiscussionTh
|
||||
|
||||
useEffect(() => {
|
||||
if (reconnectToken === 0) return
|
||||
// eslint-disable-next-line react-hooks/set-state-in-effect
|
||||
refreshComments()
|
||||
}, [reconnectToken, refreshComments])
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import {useEffect, useState} from 'react'
|
||||
import {ExperimentOutlined} from '@ant-design/icons'
|
||||
import {Form, Input, message, Modal, Select, theme} from 'antd'
|
||||
import {Form, Input, Modal, Select, theme} from 'antd'
|
||||
import {message} from '../utils/appMessage'
|
||||
import client from '../api/client'
|
||||
import {fetchResourceMetadata} from '../api/metadata'
|
||||
import type {ChoiceOption} from '../types/records'
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import {useCallback, useEffect, useState} from 'react'
|
||||
import {Alert, Badge, Button, Drawer, Empty, List, message, Popconfirm, Segmented, Space, Spin, Tag, Tooltip, Typography} from 'antd'
|
||||
import {Alert, Badge, Button, Drawer, Empty, List, Popconfirm, Segmented, Space, Spin, Tag, Tooltip, Typography} from 'antd'
|
||||
import {message} from '../utils/appMessage'
|
||||
import {CheckCircleOutlined, CloseOutlined, InboxOutlined, MailOutlined, MessageOutlined, ReloadOutlined} from '@ant-design/icons'
|
||||
import dayjs from 'dayjs'
|
||||
import relativeTime from 'dayjs/plugin/relativeTime'
|
||||
@@ -222,7 +223,6 @@ export default function InboxDrawer({ onOpenResource }: InboxDrawerProps) {
|
||||
|
||||
useEffect(() => {
|
||||
if (!open || reconnectToken === 0) return
|
||||
// eslint-disable-next-line react-hooks/set-state-in-effect
|
||||
refreshMessages()
|
||||
}, [open, reconnectToken, refreshMessages])
|
||||
|
||||
@@ -379,7 +379,7 @@ export default function InboxDrawer({ onOpenResource }: InboxDrawerProps) {
|
||||
</Badge>
|
||||
)}
|
||||
title={(
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', gap: 8 }}>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', gap: 8, marginBottom: 4 }}>
|
||||
<Space size={8} wrap>
|
||||
<Tag {...comfortableTagProps} color={row.kind === 'system' ? 'purple' : 'blue'} style={{ marginInlineEnd: 0 }}>
|
||||
{row.kind}
|
||||
@@ -423,7 +423,7 @@ export default function InboxDrawer({ onOpenResource }: InboxDrawerProps) {
|
||||
</div>
|
||||
)}
|
||||
</List.Item>
|
||||
)}
|
||||
)}
|
||||
/>
|
||||
</>
|
||||
) : loadError ? (
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import {useState} from 'react'
|
||||
import type {SelectProps} from 'antd'
|
||||
import {DatePicker, Form, Input, message, Modal, Select, Tag, theme, Typography} from 'antd'
|
||||
import {DatePicker, Form, Input, Modal, Select, Tag, theme, Typography} from 'antd'
|
||||
import {message} from '../utils/appMessage'
|
||||
import {BookOutlined} from '@ant-design/icons'
|
||||
import type {Dayjs} from 'dayjs'
|
||||
import client from '../api/client'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {type ClipboardEvent, useState} from 'react'
|
||||
import axios from 'axios'
|
||||
import {message} from 'antd'
|
||||
import {message} from '../utils/appMessage'
|
||||
import type {PreviewType} from '@uiw/react-md-editor'
|
||||
import MDEditor from '@uiw/react-md-editor'
|
||||
import client from '../api/client'
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import {useMemo, useState} from 'react'
|
||||
import type {UploadProps} from 'antd'
|
||||
import {Alert, Button, Mentions, message, Tooltip, Upload} from 'antd'
|
||||
import {Alert, Button, Mentions, Tooltip, Upload} from 'antd'
|
||||
import {message} from '../utils/appMessage'
|
||||
import {PaperClipOutlined, SendOutlined} from '@ant-design/icons'
|
||||
import {type Attachment, uploadAttachment} from '../api/attachments'
|
||||
import {fetchMentionUsers, type MentionUser} from '../api/comments'
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import {useEffect, useState} from 'react'
|
||||
import {Button, Col, DatePicker, Form, Input, message, Modal, Popconfirm, Row, Space, Switch, Table, Tabs, Tag, Typography} from 'antd'
|
||||
import {Button, Col, DatePicker, Form, Input, Modal, Popconfirm, Row, Space, Switch, Table, Tabs, Tag, Typography} from 'antd'
|
||||
import {message} from '../utils/appMessage'
|
||||
import {ApiOutlined, CopyOutlined, DeleteOutlined, KeyOutlined, LockOutlined, ReloadOutlined, SettingOutlined, UserOutlined} from '@ant-design/icons'
|
||||
import type {ColumnsType} from 'antd/es/table'
|
||||
import type {Dayjs} from 'dayjs'
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import {useCallback, useEffect, useMemo, useRef, useState} from 'react'
|
||||
import {Button, Divider, Empty, message, Modal, Spin, theme, Tooltip} from 'antd'
|
||||
import {Button, Divider, Empty, Modal, Spin, theme, Tooltip} from 'antd'
|
||||
import {message} from '../utils/appMessage'
|
||||
import {CloseOutlined, FileTextOutlined, HistoryOutlined, MenuFoldOutlined, MenuUnfoldOutlined, MessageOutlined, ReloadOutlined, ShareAltOutlined} from '@ant-design/icons'
|
||||
import client from '../api/client'
|
||||
import type {FieldEditingController, OpenResourceOptions, ResourceConfig} from '../types/records'
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {type ReactNode, useCallback, useEffect, useMemo, useState} from 'react'
|
||||
import {message} from 'antd'
|
||||
import {message} from '../utils/appMessage'
|
||||
import DataTable from './DataTable'
|
||||
import {fetchResourceMetadata} from '../api/metadata'
|
||||
import type {AdvancedFilterFieldConfig, MetadataResponse, OpenResourceOptions, ResourceColumn, ResourceFilterConfig} from '../types/records'
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import {Fragment} from 'react'
|
||||
import {choiceTag} from '../utils/recordDisplay'
|
||||
|
||||
interface TagListProps {
|
||||
@@ -10,7 +11,11 @@ export default function TagList({ items, color = 'blue' }: TagListProps) {
|
||||
|
||||
return (
|
||||
<span style={{ display: 'inline-flex', flexWrap: 'wrap', gap: 4 }}>
|
||||
{items.map((item) => choiceTag(String(item), color))}
|
||||
{items.map((item, index) => (
|
||||
<Fragment key={`${String(item)}-${index}`}>
|
||||
{choiceTag(String(item), color)}
|
||||
</Fragment>
|
||||
))}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import ReactDOM from 'react-dom/client'
|
||||
import {BrowserRouter} from 'react-router-dom'
|
||||
import {App as AntApp, ConfigProvider} from 'antd'
|
||||
import App from './App'
|
||||
import AppMessageProvider from './components/AppMessageProvider'
|
||||
import themeConfig from './theme'
|
||||
import './index.css'
|
||||
|
||||
@@ -11,6 +12,7 @@ ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||
<BrowserRouter>
|
||||
<ConfigProvider theme={themeConfig}>
|
||||
<AntApp>
|
||||
<AppMessageProvider />
|
||||
<App />
|
||||
</AntApp>
|
||||
</ConfigProvider>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import {useCallback, useEffect, useMemo, useState} from 'react'
|
||||
import {Alert, Button, Descriptions, Drawer, Empty, Flex, Input, InputNumber, List, message, Select, Space, Table, Tabs, Tag, Typography} from 'antd'
|
||||
import {Alert, Button, Descriptions, Drawer, Empty, Flex, Input, InputNumber, List, Select, Space, Table, Tabs, Tag, Typography} from 'antd'
|
||||
import {message} from '../utils/appMessage'
|
||||
import {ReloadOutlined} from '@ant-design/icons'
|
||||
import type {ColumnsType} from 'antd/es/table'
|
||||
import {Boxes, BrainCircuit, DatabaseZap} from 'lucide-react'
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import {useCallback, useEffect, useState} from 'react'
|
||||
import {Button, Card, Col, Form, Input, message, Row, Space, Switch} from 'antd'
|
||||
import {Button, Card, Col, Form, Input, Row, Space, Switch} from 'antd'
|
||||
import {message} from '../utils/appMessage'
|
||||
import client from '../api/client'
|
||||
|
||||
interface LDAPConfig {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import {useMemo, useState} from 'react'
|
||||
import {Button, Form, Input, InputNumber, message, Modal, Popconfirm, Select, Space, Switch} from 'antd'
|
||||
import {Button, Form, Input, InputNumber, Modal, Popconfirm, Select, Space, Switch} from 'antd'
|
||||
import {message} from '../utils/appMessage'
|
||||
import {CheckCircleOutlined, EditOutlined, PlusOutlined, StopOutlined, ThunderboltOutlined} from '@ant-design/icons'
|
||||
import client from '../api/client'
|
||||
import DataTable from '../components/DataTable'
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import {useState} from 'react'
|
||||
import {useNavigate, useSearchParams} from 'react-router-dom'
|
||||
import {BorderBeam, Button, Card, Form, Input, message, Segmented} from 'antd'
|
||||
import {BorderBeam, Button, Card, Form, Input, Segmented} from 'antd'
|
||||
import {message} from '../utils/appMessage'
|
||||
import {LockOutlined, UserOutlined} from '@ant-design/icons'
|
||||
import type {AuthType} from '../api/auth'
|
||||
import {login} from '../api/auth'
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import {useCallback, useEffect, useState} from 'react'
|
||||
import {Button, Card, Col, Divider, Form, InputNumber, message, Row, Select, Space, Tooltip, Typography} from 'antd'
|
||||
import {Button, Card, Col, Divider, Form, InputNumber, Row, Select, Space, Tooltip, Typography} from 'antd'
|
||||
import {message} from '../utils/appMessage'
|
||||
import {QuestionCircleOutlined} from '@ant-design/icons'
|
||||
import client from '../api/client'
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import {useCallback, useEffect, useState} from 'react'
|
||||
import {Button, Card, Col, Form, Input, InputNumber, message, Row, Select, Space, Switch, Tabs} from 'antd'
|
||||
import {Button, Card, Col, Form, Input, InputNumber, Row, Select, Space, Switch, Tabs} from 'antd'
|
||||
import {message} from '../utils/appMessage'
|
||||
import {Layers, Search} from 'lucide-react'
|
||||
import client from '../api/client'
|
||||
import IconTabLabel from '../components/IconTabLabel'
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import {useCallback, useEffect, useState} from 'react'
|
||||
import {Button, Card, Form, Input, InputNumber, message, Space, Switch, Tabs} from 'antd'
|
||||
import {Button, Card, Form, Input, InputNumber, Space, Switch, Tabs} from 'antd'
|
||||
import {message} from '../utils/appMessage'
|
||||
import {ChartNetwork, SatelliteDish} from 'lucide-react'
|
||||
import client from '../api/client'
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import {Col, Divider, Form, Input, message, Modal, Row, Select, Typography} from 'antd'
|
||||
import {Col, Divider, Form, Input, Modal, Row, Select, Typography} from 'antd'
|
||||
import {message} from '../utils/appMessage'
|
||||
import client from '../api/client'
|
||||
import type {AuthType, UserMutationResponse} from '../api/auth'
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import {useEffect, useState} from 'react'
|
||||
import {Button, Col, Divider, Form, Input, message, Modal, Row, Select, Space, theme, Typography} from 'antd'
|
||||
import {Button, Col, Divider, Form, Input, Modal, Row, Select, Space, theme, Typography} from 'antd'
|
||||
import {message} from '../utils/appMessage'
|
||||
import client from '../api/client'
|
||||
import type {AuthUser} from '../stores/auth'
|
||||
import AvatarUpload from '../components/AvatarUpload'
|
||||
@@ -27,13 +28,8 @@ export default function UserDetailModal({ userId, open, onClose, onSaved }: User
|
||||
const [saving, setSaving] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
if (!open || userId === null) {
|
||||
// eslint-disable-next-line react-hooks/set-state-in-effect
|
||||
setUser(null)
|
||||
form.resetFields()
|
||||
return
|
||||
}
|
||||
setLoading(true)
|
||||
if (!open || userId === null) return
|
||||
|
||||
client.get<AuthUser>(`/auth/users/${userId}/`)
|
||||
.then(({ data }) => {
|
||||
setUser(data)
|
||||
@@ -89,6 +85,14 @@ export default function UserDetailModal({ userId, open, onClose, onSaved }: User
|
||||
footer: { background: token.colorBgContainer },
|
||||
}}
|
||||
destroyOnHidden
|
||||
afterOpenChange={(nextOpen) => {
|
||||
if (nextOpen) {
|
||||
if (userId !== null) setLoading(true)
|
||||
return
|
||||
}
|
||||
setUser(null)
|
||||
form.resetFields()
|
||||
}}
|
||||
>
|
||||
<Form form={form} layout="vertical" disabled={loading} style={{ paddingTop: 8 }}>
|
||||
<Row gutter={16}>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import {useEffect, useMemo, useState} from 'react'
|
||||
import {Button, Input, message, Modal, Popconfirm, Space, Tooltip} from 'antd'
|
||||
import {Button, Input, Modal, Popconfirm, Space, Tooltip} from 'antd'
|
||||
import {message} from '../utils/appMessage'
|
||||
import {CheckCircleOutlined, DeleteOutlined, KeyOutlined, PlusOutlined, StopOutlined} from '@ant-design/icons'
|
||||
import client from '../api/client'
|
||||
import DataTable from '../components/DataTable'
|
||||
|
||||
@@ -7,6 +7,20 @@ function websocketUrl(token: string) {
|
||||
return `${protocol}//${window.location.host}/ws/events/?token=${encodeURIComponent(token)}`
|
||||
}
|
||||
|
||||
function closeSocket(socket: WebSocket) {
|
||||
if (socket.readyState === WebSocket.CONNECTING) {
|
||||
socket.onopen = () => socket.close()
|
||||
socket.onmessage = null
|
||||
socket.onerror = null
|
||||
socket.onclose = null
|
||||
return
|
||||
}
|
||||
|
||||
if (socket.readyState === WebSocket.OPEN) {
|
||||
socket.close()
|
||||
}
|
||||
}
|
||||
|
||||
export function RealtimeProvider({ children }: { children: React.ReactNode }) {
|
||||
const token = useAuthStore((state) => state.token)
|
||||
const [status, setStatus] = useState<RealtimeStatus>('disconnected')
|
||||
@@ -32,8 +46,9 @@ export function RealtimeProvider({ children }: { children: React.ReactNode }) {
|
||||
useEffect(() => {
|
||||
if (!token) {
|
||||
shouldReconnectRef.current = false
|
||||
socketRef.current?.close()
|
||||
const socket = socketRef.current
|
||||
socketRef.current = null
|
||||
if (socket) closeSocket(socket)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -53,6 +68,10 @@ export function RealtimeProvider({ children }: { children: React.ReactNode }) {
|
||||
socketRef.current = socket
|
||||
|
||||
socket.onopen = () => {
|
||||
if (socketRef.current !== socket) {
|
||||
socket.close()
|
||||
return
|
||||
}
|
||||
reconnectAttemptRef.current = 0
|
||||
setStatus('connected')
|
||||
setReconnectToken((current) => current + 1)
|
||||
@@ -62,6 +81,7 @@ export function RealtimeProvider({ children }: { children: React.ReactNode }) {
|
||||
}
|
||||
|
||||
socket.onmessage = (event) => {
|
||||
if (socketRef.current !== socket) return
|
||||
let data: unknown
|
||||
try {
|
||||
data = JSON.parse(event.data)
|
||||
@@ -75,9 +95,8 @@ export function RealtimeProvider({ children }: { children: React.ReactNode }) {
|
||||
}
|
||||
|
||||
socket.onclose = () => {
|
||||
if (socketRef.current === socket) {
|
||||
socketRef.current = null
|
||||
}
|
||||
if (socketRef.current !== socket) return
|
||||
socketRef.current = null
|
||||
setStatus('disconnected')
|
||||
if (!shouldReconnectRef.current) return
|
||||
const attempt = reconnectAttemptRef.current + 1
|
||||
@@ -87,17 +106,19 @@ export function RealtimeProvider({ children }: { children: React.ReactNode }) {
|
||||
}
|
||||
|
||||
socket.onerror = () => {
|
||||
socket.close()
|
||||
if (socketRef.current !== socket) return
|
||||
if (socket.readyState === WebSocket.OPEN) socket.close()
|
||||
}
|
||||
}
|
||||
|
||||
connect()
|
||||
reconnectTimerRef.current = window.setTimeout(connect, 0)
|
||||
|
||||
return () => {
|
||||
shouldReconnectRef.current = false
|
||||
clearReconnectTimer()
|
||||
socketRef.current?.close()
|
||||
const socket = socketRef.current
|
||||
socketRef.current = null
|
||||
if (socket) closeSocket(socket)
|
||||
}
|
||||
}, [sendCommentSubscription, token])
|
||||
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import {App as AntApp} from 'antd'
|
||||
|
||||
type MessageApi = ReturnType<typeof AntApp.useApp>['message']
|
||||
|
||||
let activeMessage: MessageApi | null = null
|
||||
|
||||
export function setAppMessage(message: MessageApi) {
|
||||
activeMessage = message
|
||||
}
|
||||
|
||||
export function clearAppMessage(message: MessageApi) {
|
||||
if (activeMessage === message) activeMessage = null
|
||||
}
|
||||
|
||||
function getMessage() {
|
||||
if (!activeMessage) {
|
||||
throw new Error('Ant Design message API is not ready')
|
||||
}
|
||||
return activeMessage
|
||||
}
|
||||
|
||||
export const message = new Proxy({} as MessageApi, {
|
||||
get(_target, property: keyof MessageApi) {
|
||||
const api = getMessage()
|
||||
const value = api[property]
|
||||
return typeof value === 'function' ? value.bind(api) : value
|
||||
},
|
||||
})
|
||||
Reference in New Issue
Block a user