1088 lines
31 KiB
JavaScript

// src/contexts/ApiServerContext.js
import {
createContext,
useEffect,
useState,
useContext,
useRef,
useCallback
} from 'react'
import io from 'socket.io-client'
import { message, notification, Modal, Space, Button } from 'antd'
import PropTypes from 'prop-types'
import { AuthContext } from './AuthContext'
import axios from 'axios'
import ExclamationOctagonIcon from '../../Icons/ExclamationOctagonIcon'
import ReloadIcon from '../../Icons/ReloadIcon'
import config from '../../../config'
import loglevel from 'loglevel'
const logger = loglevel.getLogger('ApiServerContext')
logger.setLevel(config.logLevel)
const ApiServerContext = createContext()
const ApiServerProvider = ({ children }) => {
const { token, userProfile, authenticated, setUnauthenticated } =
useContext(AuthContext)
const socketRef = useRef(null)
const [connected, setConnected] = useState(false)
const [connecting, setConnecting] = useState(false)
const [error, setError] = useState(null)
const [messageApi, contextHolder] = message.useMessage()
const [notificationApi] = notification.useNotification()
const [fetchLoading, setFetchLoading] = useState(false)
const [showErrorModal, setShowErrorModal] = useState(false)
const [errorModalContent, setErrorModalContent] = useState('')
const [retryCallback, setRetryCallback] = useState(null)
const subscribedCallbacksRef = useRef(new Map())
const subscribedLockCallbacksRef = useRef(new Map())
const handleLockUpdate = useCallback(
async (lockData) => {
logger.debug('Notifying lock update:', lockData)
const objectId = lockData._id || lockData.id
if (
objectId &&
subscribedLockCallbacksRef.current.has(objectId) &&
lockData.user != userProfile?._id
) {
const callbacks = subscribedLockCallbacksRef.current.get(objectId)
logger.debug(
`Calling ${callbacks.length} lock callbacks for object:`,
objectId
)
callbacks.forEach((callback) => {
try {
callback(lockData)
} catch (error) {
logger.error('Error in lock update callback:', error)
}
})
} else {
logger.debug(
`No lock callbacks found for object: ${objectId}, subscribed lock callbacks:`,
Array.from(subscribedLockCallbacksRef.current.keys())
)
}
},
[userProfile?._id]
)
const connectToServer = useCallback(() => {
if (token && authenticated == true) {
logger.debug('Token is available, connecting to api server...')
const newSocket = io(config.apiServerUrl, {
reconnectionAttempts: 3,
timeout: 3000,
auth: { type: 'user' }
})
setConnecting(true)
newSocket.on('connect', () => {
logger.debug('Api Server connected')
newSocket.emit('authenticate', { token: token }, (result) => {
console.log('Auth result', result)
setConnecting(false)
setConnected(true)
setError(null)
})
})
newSocket.on('objectUpdate', handleObjectUpdate)
newSocket.on('objectEvent', handleObjectEvent)
newSocket.on('objectNew', handleObjectNew)
newSocket.on('objectDelete', handleObjectDelete)
newSocket.on('lockUpdate', handleLockUpdate)
newSocket.on('disconnect', () => {
logger.debug('Api Server disconnected')
setError('Api Server disconnected')
setConnected(false)
})
newSocket.on('connect_error', (err) => {
logger.error('Api Server connection error:', err)
messageApi.error('Api Server connection error: ' + err.message)
setError('Api Server connection error')
setConnected(false)
})
newSocket.on('bridge.notification', (data) => {
notificationApi[data.type]({
title: data.title,
message: data.message
})
})
newSocket.on('error', (err) => {
logger.error('Api Server error:', err)
setError('Api Server error')
})
socketRef.current = newSocket
}
}, [token, authenticated, messageApi, notificationApi, handleLockUpdate])
useEffect(() => {
if (token && authenticated == true) {
connectToServer()
} else if (!token && socketRef.current) {
logger.debug('Token not available, disconnecting api server...')
socketRef.current.disconnect()
socketRef.current = null
}
// Clean up function
return () => {
if (socketRef.current) {
logger.debug('Cleaning up api server connection...')
socketRef.current.disconnect()
socketRef.current = null
}
}
}, [token, authenticated, connectToServer])
const lockObject = (id, type) => {
logger.debug('Locking ' + id)
if (socketRef.current && socketRef.current.connected) {
socketRef.current.emit('lock', { _id: id, type: type })
logger.debug('Sent lock command for object:', id)
}
}
const unlockObject = (id, type) => {
logger.debug('Unlocking ' + id)
if (socketRef.current && socketRef.current.connected == true) {
socketRef.current.emit('unlock', { _id: id, type: type })
logger.debug('Sent unlock command for object:', id)
}
}
const fetchObjectLock = async (id, type) => {
if (socketRef.current && socketRef.current.connected == true) {
logger.debug('Fetching lock status for ' + id)
return new Promise((resolve) => {
socketRef.current.emit(
'getLock',
{
_id: id,
type: type
},
(lockEvent) => {
logger.debug('Received lock status for object:', id, lockEvent)
if (lockEvent.user != userProfile?._id) {
resolve(lockEvent)
} else {
resolve(null)
}
}
)
logger.debug('Sent fetch lock command for object:', id)
})
}
}
const handleObjectUpdate = async (data) => {
logger.debug('Notifying object update:', data)
const id = data._id
const objectType = data.objectType
const callbacksRefKey = `${objectType}:${id}`
if (
id &&
objectType &&
subscribedCallbacksRef.current.has(callbacksRefKey)
) {
const callbacks = subscribedCallbacksRef.current.get(callbacksRefKey)
logger.debug(
`Calling ${callbacks.length} callbacks for object:`,
callbacksRefKey
)
callbacks.forEach((callback) => {
try {
callback(data.object)
} catch (error) {
logger.error('Error in object update callback:', error)
}
})
} else {
logger.debug(
`No callbacks found for object: ${callbacksRefKey}, subscribed callbacks:`,
Array.from(subscribedCallbacksRef.current.keys())
)
}
}
const handleObjectEvent = async (data) => {
const id = data._id
const objectType = data.objectType
const callbacksRefKey = `${objectType}:${id}:events:${data.event.type}`
logger.debug('Notifying object event:', data)
if (
id &&
objectType &&
subscribedCallbacksRef.current.has(callbacksRefKey)
) {
const callbacks = subscribedCallbacksRef.current.get(callbacksRefKey)
logger.debug(
`Calling ${callbacks.length} callbacks for object:`,
callbacksRefKey
)
callbacks.forEach((callback) => {
try {
callback(data.event)
} catch (error) {
logger.error('Error in object event callback:', error)
}
})
} else {
logger.debug(
`No callbacks found for object: ${callbacksRefKey}, subscribed callbacks:`,
Array.from(subscribedCallbacksRef.current.keys())
)
}
}
const handleObjectNew = async (data) => {
logger.debug('Notifying object new:', data)
const objectType = data.objectType || 'unknown'
if (objectType && subscribedCallbacksRef.current.has(objectType)) {
const callbacks = subscribedCallbacksRef.current.get(objectType)
logger.debug(
`Calling ${callbacks.length} callbacks for type:`,
objectType
)
callbacks.forEach((callback) => {
try {
callback(data.object)
} catch (error) {
logger.error('Error in object new callback:', error)
}
})
} else {
logger.debug(
`No callbacks found for object: ${objectType}, subscribed callbacks:`,
Array.from(subscribedCallbacksRef.current.keys())
)
}
}
const handleObjectDelete = async (data) => {
logger.debug('Notifying object delete:', data)
const objectType = data.objectType || 'unknown'
if (objectType && subscribedCallbacksRef.current.has(objectType)) {
const callbacks = subscribedCallbacksRef.current.get(objectType)
logger.debug(
`Calling ${callbacks.length} callbacks for type:`,
objectType
)
callbacks.forEach((callback) => {
try {
callback(data.object)
} catch (error) {
logger.error('Error in object new callback:', error)
}
})
} else {
logger.debug(
`No callbacks found for object: ${objectType}, subscribed callbacks:`,
Array.from(subscribedCallbacksRef.current.keys())
)
}
}
const offObjectUpdatesEvent = useCallback((id, objectType, callback) => {
if (socketRef.current && socketRef.current.connected == true) {
const callbacksRefKey = `${objectType}:${id}`
// Remove callback from the subscribed callbacks map
if (subscribedCallbacksRef.current.has(callbacksRefKey)) {
const callbacks = subscribedCallbacksRef.current
.get(callbacksRefKey)
.filter((cb) => cb !== callback)
if (callbacks.length === 0) {
logger.debug(
'No callbacks found for object:',
callbacksRefKey,
'unsubscribing from object update...'
)
subscribedCallbacksRef.current.delete(callbacksRefKey)
socketRef.current.emit('unsubscribeObjectUpdate', {
_id: id,
objectType: objectType
})
} else {
subscribedCallbacksRef.current.set(callbacksRefKey, callbacks)
}
}
}
}, [])
const offObjectTypeUpdatesEvent = useCallback((objectType, callback) => {
if (socketRef.current && socketRef.current.connected == true) {
// Remove callback from the subscribed callbacks map
console.log(
'Unsubscribing from type',
objectType,
subscribedCallbacksRef.current.has(objectType)
)
if (subscribedCallbacksRef.current.has(objectType)) {
const callbacks = subscribedCallbacksRef.current
.get(objectType)
.filter((cb) => cb !== callback)
console.log('API: CALLBACKS', callbacks)
if (callbacks.length === 0) {
subscribedCallbacksRef.current.delete(objectType)
socketRef.current.emit('unsubscribeObjectTypeUpdate', {
objectType: objectType
})
} else {
subscribedCallbacksRef.current.set(objectType, callbacks)
}
}
}
}, [])
const subscribeToObjectUpdates = useCallback(
(id, objectType, callback) => {
if (socketRef.current && socketRef.current.connected == true) {
const callbacksRefKey = `${objectType}:${id}`
// Add callback to the subscribed callbacks map immediately
if (!subscribedCallbacksRef.current.has(callbacksRefKey)) {
subscribedCallbacksRef.current.set(callbacksRefKey, [])
}
const callbacksLength =
subscribedCallbacksRef.current.get(callbacksRefKey).length
if (callbacksLength <= 0) {
socketRef.current.emit(
'subscribeToObjectUpdate',
{
_id: id,
objectType: objectType
},
(result) => {
if (result.success) {
logger.info('Subscribed to id:', id, 'objectType:', objectType)
}
}
)
}
logger.info(
'Adding callback id:',
id,
'objectType:',
objectType,
'callbacks length:',
callbacksLength + 1
)
subscribedCallbacksRef.current.get(callbacksRefKey).push(callback)
// Return cleanup function
return () => offObjectUpdatesEvent(id, objectType, callback)
}
},
[offObjectUpdatesEvent]
)
const subscribeToObjectTypeUpdates = useCallback(
(objectType, callback) => {
logger.debug('Subscribing to type updates:', objectType)
if (socketRef.current && socketRef.current.connected == true) {
// Add callback to the subscribed callbacks map immediately
if (!subscribedCallbacksRef.current.has(objectType)) {
subscribedCallbacksRef.current.set(objectType, [])
}
subscribedCallbacksRef.current.get(objectType).push(callback)
logger.debug(
`Added callback for type ${objectType}, total callbacks: ${subscribedCallbacksRef.current.get(objectType).length}`
)
socketRef.current.emit(
'subscribeToObjectTypeUpdate',
{ objectType: objectType },
(result) => {
if (result.success) {
logger.info('Subscribed to objectType:', objectType)
}
}
)
logger.debug('Registered type event listener for object:', objectType)
// Return cleanup function
return () => offObjectTypeUpdatesEvent(objectType, callback)
}
},
[offObjectTypeUpdatesEvent]
)
const offLockEvent = useCallback((id, callback) => {
if (socketRef.current && socketRef.current.connected == true) {
// Remove callback from the subscribed lock callbacks map
if (subscribedLockCallbacksRef.current.has(id)) {
const callbacks = subscribedLockCallbacksRef.current
.get(id)
.filter((cb) => cb !== callback)
if (callbacks.length === 0) {
subscribedLockCallbacksRef.current.delete(id)
} else {
subscribedLockCallbacksRef.current.set(id, callbacks)
}
}
logger.debug('Removed lock event listener for object:', id)
}
}, [])
const offObjectEventEvent = useCallback(
(id, objectType, eventType, callback) => {
if (socketRef.current && socketRef.current.connected == true) {
const callbacksRefKey = `${objectType}:${id}:events:${eventType}`
// Remove callback from the subscribed callbacks map
if (subscribedCallbacksRef.current.has(callbacksRefKey)) {
const callbacks = subscribedCallbacksRef.current
.get(callbacksRefKey)
.filter((cb) => cb !== callback)
if (callbacks.length === 0) {
subscribedCallbacksRef.current.delete(callbacksRefKey)
socketRef.current.emit('unsubscribeObjectEvent', {
_id: id,
objectType,
eventType
})
} else {
subscribedCallbacksRef.current.set(callbacksRefKey, callbacks)
}
}
}
},
[]
)
const subscribeToObjectEvent = useCallback(
(id, objectType, eventType, callback) => {
if (socketRef.current && socketRef.current.connected == true) {
const callbacksRefKey = `${objectType}:${id}:events:${eventType}`
// Add callback to the subscribed callbacks map immediately
if (!subscribedCallbacksRef.current.has(callbacksRefKey)) {
subscribedCallbacksRef.current.set(callbacksRefKey, [])
}
const callbacksLength =
subscribedCallbacksRef.current.get(callbacksRefKey).length
if (callbacksLength <= 0) {
socketRef.current.emit(
'subscribeToObjectEvent',
{
_id: id,
objectType: objectType,
eventType: eventType
},
(result) => {
if (result.success) {
logger.info(
'Subscribed to event id:',
id,
'objectType:',
objectType,
'eventType:',
eventType
)
}
}
)
}
logger.info(
'Adding event callback id:',
id,
'objectType:',
objectType,
'eventType:',
eventType,
'callbacks length:',
callbacksLength + 1
)
subscribedCallbacksRef.current.get(callbacksRefKey).push(callback)
// Return cleanup function
return () => offObjectEventEvent(id, objectType, eventType, callback)
}
},
[offObjectUpdatesEvent]
)
const subscribeToObjectLock = useCallback(
(id, type, callback) => {
logger.debug('Subscribing to lock for object:', id, 'type:', type)
if (socketRef.current && socketRef.current.connected == true) {
// Add callback to the subscribed lock callbacks map immediately
if (!subscribedLockCallbacksRef.current.has(id)) {
subscribedLockCallbacksRef.current.set(id, [])
}
subscribedLockCallbacksRef.current.get(id).push(callback)
logger.debug(
`Added lock callback for object ${id}, total lock callbacks: ${subscribedLockCallbacksRef.current.get(id).length}`
)
socketRef.current.emit('subscribe_lock', { _id: id, objectType: type })
logger.debug('Registered lock event listener for object:', id)
// Return cleanup function
return () => offLockEvent(id, callback)
}
},
[offLockEvent]
)
const showError = (error, callback = null) => {
const code = error.response.data.code || 'UNKNOWN'
if (code == 'UNAUTHORIZED') {
setUnauthenticated()
return
}
var content = `Error ${error.code} (${error.status}): ${error.message}`
if (error.response?.data?.error) {
content = `${error.response?.data?.error} (${error.status})`
}
setErrorModalContent(content)
setRetryCallback(() => callback)
setShowErrorModal(true)
}
const handleRetry = () => {
setShowErrorModal(false)
setErrorModalContent('')
if (retryCallback) {
retryCallback()
}
setRetryCallback(null)
}
// Generalized fetchObject function
const fetchObject = async (id, type) => {
const fetchUrl = `${config.backendUrl}/${type}s/${id}`
setFetchLoading(true)
logger.debug('Fetching from ' + fetchUrl)
try {
const response = await axios.get(fetchUrl, {
headers: {
Accept: 'application/json',
Authorization: `Bearer ${token}`
}
})
setFetchLoading(false)
return response.data
} catch (err) {
console.error(err)
showError(err, () => {
fetchObject(id, type)
})
return {}
}
}
// Fetch table data with pagination, filtering, and sorting
const fetchObjects = async (type, params = {}) => {
const {
page = 1,
limit = 25,
filter = {},
sorter = {},
onDataChange
} = params
logger.debug('Fetching table data from:', type, {
page,
limit,
filter,
sorter
})
try {
const response = await axios.get(
`${config.backendUrl}/${type.toLowerCase()}s`,
{
params: {
page,
limit,
...filter,
sort: sorter.field,
order: sorter.order
},
headers: {
Accept: 'application/json',
Authorization: `Bearer ${token}`
}
}
)
const newData = response.data
const totalCount = parseInt(response.headers['x-total-count'] || '0', 10)
const totalPages = Math.ceil(totalCount / limit)
const hasMore = newData.length >= limit
if (onDataChange) {
onDataChange(newData)
}
return {
data: newData,
totalCount,
totalPages,
hasMore,
page
}
} catch (err) {
console.error(err)
showError(err, () => {
fetchObjects(type, params)
})
return []
}
}
// Fetch table data with pagination, filtering, and sorting
const fetchObjectsByProperty = async (type, params = {}) => {
const { filter = {}, properties = [], masterFilter = {} } = params
logger.debug('Fetching property object data from:', type, {
properties,
filter
})
try {
const response = await axios.get(
`${config.backendUrl}/${type.toLowerCase()}s/properties`,
{
params: {
...filter,
properties: properties.join(','), // Convert array to comma-separated string
masterFilter: JSON.stringify(masterFilter)
},
headers: {
Accept: 'application/json',
Authorization: `Bearer ${token}`
}
}
)
const newData = response.data
return newData
} catch (err) {
console.error(err)
showError(err, () => {
fetchObjects(type, params)
})
return []
}
}
// Update filament information
const updateObject = async (id, type, value) => {
const updateUrl = `${config.backendUrl}/${type.toLowerCase()}s/${id}`
logger.debug('Updating info for ' + id)
try {
const response = await axios.put(updateUrl, value, {
headers: {
Accept: 'application/json',
Authorization: `Bearer ${token}`
}
})
logger.debug('Object updated successfully')
return response.data
} catch (err) {
console.error(err)
setError(err, () => {
updateObject(id, type, value)
})
return {}
}
}
// Update filament information
const deleteObject = async (id, type) => {
const deleteUrl = `${config.backendUrl}/${type.toLowerCase()}s/${id}`
logger.debug('Deleting object ID: ' + id)
try {
const response = await axios.delete(deleteUrl, {
headers: {
Accept: 'application/json',
Authorization: `Bearer ${token}`
}
})
logger.debug('Object deleted successfully')
return response.data
} catch (err) {
console.error(err)
showError(err, () => {
deleteObject(id, type)
})
return {}
}
}
// Update filament information
const createObject = async (type, value) => {
const createUrl = `${config.backendUrl}/${type.toLowerCase()}s`
logger.debug('Creating object...')
try {
const response = await axios.post(createUrl, value, {
headers: {
Accept: 'application/json',
Authorization: `Bearer ${token}`
}
})
return response.data
} catch (err) {
console.error(err)
showError(err, () => {
createObject(type, value)
})
return {}
}
}
// Download GCode file content
const fetchFileContent = async (file, download = false) => {
try {
const response = await axios.get(
`${config.backendUrl}/files/${file._id}/content`,
{
headers: {
Accept: '*/*',
Authorization: `Bearer ${token}`
},
responseType: 'blob'
}
)
const blob = new Blob([response.data], {
type: response.headers['content-type']
})
const fileURL = window.URL.createObjectURL(blob)
if (download == true) {
const fileLink = document.createElement('a')
fileLink.href = fileURL
fileLink.setAttribute('download', `${file.name}${file.extension}`)
document.body.appendChild(fileLink)
fileLink.click()
fileLink.parentNode.removeChild(fileLink)
return
}
return fileURL
} catch (err) {
console.error(err)
showError(err, () => {
fetchFileContent(file, download)
})
}
}
// Fetch notes for a specific parent
const fetchNotes = async (parentId) => {
logger.debug('Fetching notes for parent:', parentId)
try {
const response = await axios.get(`${config.backendUrl}/notes`, {
params: {
'parent._id': parentId,
sort: 'createdAt',
order: 'ascend'
},
headers: {
Accept: 'application/json',
Authorization: `Bearer ${token}`
}
})
const notesData = response.data || []
logger.debug('Fetched notes:', notesData.length)
return notesData
} catch (err) {
console.error(err)
showError(err, () => {
fetchNotes(parentId)
})
}
}
const fetchSpotlightData = async (query) => {
logger.debug('Fetching spotlight data with query:', query)
try {
const response = await axios.get(
`${config.backendUrl}/spotlight/${query}`,
{
headers: {
Accept: 'application/json',
Authorization: `Bearer ${token}`
}
}
)
return response.data
} catch (err) {
console.error(err)
showError(err, () => {
fetchSpotlightData(query)
})
}
}
const fetchTemplatePreview = async (
id,
content,
testObject,
scale,
callback
) => {
logger.debug('Fetching preview...')
if (socketRef.current && socketRef.current.connected) {
return socketRef.current.emit(
'previewTemplate',
{
_id: id,
content: content,
testObject: testObject,
scale: scale
},
callback
)
}
}
const fetchTemplatePDF = async (id, content, testObject, callback) => {
logger.debug('Fetching pdf template...')
if (socketRef.current && socketRef.current.connected) {
return socketRef.current.emit(
'renderTemplatePDF',
{
_id: id,
content: content,
object: testObject
},
callback
)
}
}
const downloadTemplatePDF = async (
id,
content,
object,
filename,
callback
) => {
logger.debug('Downloading template PDF...')
fetchTemplatePDF(id, content, object, (result) => {
logger.debug('Downloading template PDF result:', result)
if (result?.error) {
console.error(result.error)
if (callback) {
callback(result.error)
}
} else {
const pdfBlob = new Blob([result.pdf], { type: 'application/pdf' })
const pdfUrl = URL.createObjectURL(pdfBlob)
const fileLink = document.createElement('a')
fileLink.href = pdfUrl
fileLink.setAttribute('download', `${filename}.pdf`)
document.body.appendChild(fileLink)
fileLink.click()
fileLink.parentNode.removeChild(fileLink)
if (callback) {
callback()
}
}
})
}
const fetchHostOTP = async (id, callback) => {
logger.debug('Fetching host OTP...')
if (socketRef.current && socketRef.current.connected) {
return socketRef.current.emit(
'generateHostOtp',
{
_id: id
},
callback
)
}
}
const sendObjectAction = async (id, objectType, action, callback) => {
logger.debug('Sending object action...')
if (socketRef.current && socketRef.current.connected) {
return socketRef.current.emit(
'objectAction',
{
_id: id,
objectType: objectType,
action: action
},
callback
)
}
}
// Upload file to the API
const uploadFile = async (file, additionalData = {}) => {
const uploadUrl = `${config.backendUrl}/files`
logger.debug('Uploading file:', file.name, 'to:', uploadUrl)
try {
const formData = new FormData()
formData.append('file', file)
// Add any additional data to the form
Object.keys(additionalData).forEach((key) => {
formData.append(key, additionalData[key])
})
const response = await axios.post(uploadUrl, formData, {
headers: {
'Content-Type': 'multipart/form-data',
Authorization: `Bearer ${token}`
},
onUploadProgress: (progressEvent) => {
const percentCompleted = Math.round(
(progressEvent.loaded * 100) / progressEvent.total
)
logger.debug(`Upload progress: ${percentCompleted}%`)
}
})
logger.debug('File uploaded successfully:', response.data)
return response.data
} catch (err) {
console.error('File upload error:', err)
showError(err, () => {
uploadFile(file, additionalData)
})
return null
}
}
const flushFile = async (id) => {
logger.debug('Flushing file...')
try {
const response = await axios.delete(
`${config.backendUrl}/files/${id}/flush`,
{
headers: {
Accept: 'application/json',
Authorization: `Bearer ${token}`
}
}
)
logger.debug('Flushed file:', response.data)
return true
} catch (err) {
console.error(err)
showError(err, () => {
flushFile(id)
})
}
}
// Sanitize a string so it is safe to use as a filename on most file systems
const formatFileName = (name) => {
if (!name || typeof name !== 'string') {
return ''
}
// Remove characters that are problematic on most common file systems
const cleaned = name.replace(/[^a-zA-Z0-9.\-_\s]/g, '')
// Normalize whitespace to single underscores
const normalized = cleaned.trim().replace(/\s+/g, '_')
// Most file systems limit filenames to 255 characters
return normalized.slice(0, 255)
}
return (
<ApiServerContext.Provider
value={{
apiServer: socketRef.current,
error,
connecting,
connected,
lockObject,
unlockObject,
fetchObjectLock,
updateObject,
createObject,
deleteObject,
subscribeToObjectUpdates,
subscribeToObjectEvent,
subscribeToObjectTypeUpdates,
subscribeToObjectLock,
fetchObject,
fetchObjects,
fetchObjectsByProperty,
fetchSpotlightData,
fetchLoading,
showError,
fetchFileContent,
fetchTemplatePreview,
fetchTemplatePDF,
fetchNotes,
downloadTemplatePDF,
fetchHostOTP,
sendObjectAction,
uploadFile,
flushFile,
formatFileName
}}
>
{contextHolder}
{children}
<Modal
title={
<Space size={'middle'}>
<ExclamationOctagonIcon />
Error
</Space>
}
open={showErrorModal}
okText='OK'
style={{ maxWidth: 430 }}
closable={false}
centered
maskClosable={true}
footer={[
<Button
key='retry'
onClick={() => {
setShowErrorModal(false)
}}
>
Close
</Button>,
<Button key='retry' icon={<ReloadIcon />} onClick={handleRetry}>
Retry
</Button>
]}
>
{errorModalContent}
</Modal>
</ApiServerContext.Provider>
)
}
ApiServerProvider.propTypes = {
children: PropTypes.node.isRequired
}
export { ApiServerContext, ApiServerProvider }