Compare commits
2 Commits
845b330242
...
04d26600fd
| Author | SHA1 | Date | |
|---|---|---|---|
| 04d26600fd | |||
| 52fd0ebd63 |
@ -12,10 +12,21 @@ const FilePreview = ({ file, style = {} }) => {
|
||||
|
||||
const [fileObjectUrl, setFileObjectUrl] = useState(null)
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [error, setError] = useState(null)
|
||||
|
||||
const fetchPreview = useCallback(async () => {
|
||||
if (error != null) {
|
||||
return
|
||||
}
|
||||
setLoading(true)
|
||||
console.log('fetching file content', file)
|
||||
const objectUrl = await fetchFileContent(file, false)
|
||||
if (objectUrl == null) {
|
||||
setLoading(false)
|
||||
console.error('Failed to fetch file content', file)
|
||||
setError('Failed to fetch file content')
|
||||
return
|
||||
}
|
||||
setFileObjectUrl(objectUrl)
|
||||
setLoading(false)
|
||||
}, [file, fetchFileContent])
|
||||
@ -30,6 +41,10 @@ const FilePreview = ({ file, style = {} }) => {
|
||||
return <LoadingPlaceholder message={'Loading file preview...'} />
|
||||
}
|
||||
|
||||
if (error != null) {
|
||||
return <div style={{ color: 'red' }}>{error}</div>
|
||||
}
|
||||
|
||||
const isGcode = ['.g', '.gcode'].includes(
|
||||
(file?.extension || '').toLowerCase()
|
||||
)
|
||||
|
||||
@ -7,12 +7,14 @@ import ObjectSelect from './ObjectSelect'
|
||||
import FileList from './FileList'
|
||||
import PlusIcon from '../../Icons/PlusIcon'
|
||||
import { LoadingOutlined } from '@ant-design/icons'
|
||||
import FileIcon from '../../Icons/FileIcon'
|
||||
|
||||
const { Text } = Typography
|
||||
|
||||
const FileUpload = ({
|
||||
value,
|
||||
onChange,
|
||||
minimal = false,
|
||||
multiple = true,
|
||||
defaultPreviewOpen = false,
|
||||
showPreview = true,
|
||||
@ -96,6 +98,15 @@ const FileUpload = ({
|
||||
}
|
||||
}
|
||||
|
||||
if (minimal == true) {
|
||||
return (
|
||||
<Flex gap={'small'} vertical>
|
||||
<FileIcon file={currentFiles[0]} style={{ fontSize: '24px' }} />
|
||||
<Text>{currentFiles[0]?.name}</Text>
|
||||
</Flex>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Flex gap={'small'} vertical>
|
||||
{hasNoItems && uploading == false ? (
|
||||
@ -170,7 +181,8 @@ FileUpload.propTypes = {
|
||||
multiple: PropTypes.bool,
|
||||
showPreview: PropTypes.bool,
|
||||
showInfo: PropTypes.bool,
|
||||
defaultPreviewOpen: PropTypes.bool
|
||||
defaultPreviewOpen: PropTypes.bool,
|
||||
minimal: PropTypes.bool
|
||||
}
|
||||
|
||||
export default FileUpload
|
||||
|
||||
@ -559,6 +559,7 @@ const ObjectProperty = ({
|
||||
return (
|
||||
<FileList
|
||||
files={value}
|
||||
minimal={minimal}
|
||||
multiple={false}
|
||||
card={false}
|
||||
defaultPreviewOpen={previewOpen}
|
||||
@ -572,6 +573,7 @@ const ObjectProperty = ({
|
||||
return (
|
||||
<FileList
|
||||
files={value}
|
||||
minimal={minimal}
|
||||
multiple={true}
|
||||
defaultPreviewOpen={previewOpen}
|
||||
showPreview={showPreview}
|
||||
@ -801,6 +803,7 @@ const ObjectProperty = ({
|
||||
return (
|
||||
<FileUpload
|
||||
multiple={false}
|
||||
minimal={minimal}
|
||||
defaultPreviewOpen={previewOpen}
|
||||
showPreview={showPreview}
|
||||
showInfo={showHyperlink}
|
||||
@ -811,6 +814,7 @@ const ObjectProperty = ({
|
||||
return (
|
||||
<FileUpload
|
||||
multiple={true}
|
||||
minimal={minimal}
|
||||
defaultPreviewOpen={previewOpen}
|
||||
showPreview={showPreview}
|
||||
showInfo={showHyperlink}
|
||||
|
||||
@ -57,6 +57,7 @@ const PropertyChanges = ({ type, value }) => {
|
||||
{...changeProperty}
|
||||
longId={false}
|
||||
minimal={true}
|
||||
showPreview={false}
|
||||
objectData={value?.old}
|
||||
maxWidth='200px'
|
||||
/>
|
||||
@ -71,6 +72,7 @@ const PropertyChanges = ({ type, value }) => {
|
||||
{...changeProperty}
|
||||
longId={false}
|
||||
minimal={true}
|
||||
showPreview={false}
|
||||
objectData={value?.new}
|
||||
maxWidth='200px'
|
||||
/>
|
||||
|
||||
@ -42,6 +42,19 @@ const AuthContext = createContext()
|
||||
|
||||
const { Text } = Typography
|
||||
|
||||
const extractUserFromAuthData = (authData) => {
|
||||
if (!authData || typeof authData !== 'object') return null
|
||||
if (authData.user && typeof authData.user === 'object') return authData.user
|
||||
|
||||
// Some endpoints may return the "user" fields at the top-level. Only treat it
|
||||
// as a user object if it looks like one (avoid confusing token-only payloads).
|
||||
const looksLikeUser =
|
||||
authData._id || authData.username || authData.email || authData.name
|
||||
if (looksLikeUser) return authData
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
const AuthProvider = ({ children }) => {
|
||||
const [messageApi, contextHolder] = message.useMessage()
|
||||
const [notificationApi, notificationContextHolder] =
|
||||
@ -80,19 +93,6 @@ const AuthProvider = ({ children }) => {
|
||||
redirectType = 'app-scheme'
|
||||
}
|
||||
|
||||
const extractUserFromAuthData = (authData) => {
|
||||
if (!authData || typeof authData !== 'object') return null
|
||||
if (authData.user && typeof authData.user === 'object') return authData.user
|
||||
|
||||
// Some endpoints may return the "user" fields at the top-level. Only treat it
|
||||
// as a user object if it looks like one (avoid confusing token-only payloads).
|
||||
const looksLikeUser =
|
||||
authData._id || authData.username || authData.email || authData.name
|
||||
if (looksLikeUser) return authData
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
const isSessionExpired = (session) => {
|
||||
if (!session?.expiresAt) return true
|
||||
const now = new Date()
|
||||
@ -100,6 +100,32 @@ const AuthProvider = ({ children }) => {
|
||||
return expirationDate <= now
|
||||
}
|
||||
|
||||
const getUserInfo = useCallback(
|
||||
async (token, getIsCancelled = () => false) => {
|
||||
try {
|
||||
const response = await axios.get(`${config.backendUrl}/auth/user`, {
|
||||
headers: { Authorization: `Bearer ${token}` }
|
||||
})
|
||||
if (!getIsCancelled() && response.status === 200 && response.data) {
|
||||
const nextUser = extractUserFromAuthData(response.data)
|
||||
if (nextUser) setUserProfile(nextUser)
|
||||
}
|
||||
} catch (err) {
|
||||
if (!getIsCancelled()) {
|
||||
logger.debug('Failed to refresh user from API:', err)
|
||||
if (err.response?.status === 401) {
|
||||
setAuthenticated(false)
|
||||
setToken(null)
|
||||
setUserProfile(null)
|
||||
setExpiresAt(null)
|
||||
setShowUnauthorizedModal(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
[]
|
||||
)
|
||||
|
||||
const persistSession = useCallback(
|
||||
async ({ token: nextToken, expiresAt: nextExpiresAt, user: nextUser }) => {
|
||||
if (isElectron) {
|
||||
@ -154,6 +180,10 @@ const AuthProvider = ({ children }) => {
|
||||
setUserProfile(session.user)
|
||||
setExpiresAt(session.expiresAt)
|
||||
setAuthenticated(true)
|
||||
|
||||
if (session.user) {
|
||||
getUserInfo(session.token, () => cancelled)
|
||||
}
|
||||
} else if (!cancelled) {
|
||||
setAuthenticated(false)
|
||||
setUserProfile(null)
|
||||
@ -173,6 +203,10 @@ const AuthProvider = ({ children }) => {
|
||||
setUserProfile(storedUser)
|
||||
setExpiresAt(storedExpiresAt)
|
||||
setAuthenticated(true)
|
||||
|
||||
if (storedToken && storedUser) {
|
||||
getUserInfo(storedToken, () => cancelled)
|
||||
}
|
||||
}
|
||||
} else if (!cancelled) {
|
||||
setAuthenticated(false)
|
||||
@ -197,7 +231,7 @@ const AuthProvider = ({ children }) => {
|
||||
return () => {
|
||||
cancelled = true
|
||||
}
|
||||
}, [isElectron, getAuthSession, clearPersistedSession])
|
||||
}, [isElectron, getAuthSession, clearPersistedSession, getUserInfo])
|
||||
|
||||
// Set up cookie synchronization between tabs
|
||||
useEffect(() => {
|
||||
|
||||
@ -113,17 +113,11 @@ const NotificationProvider = ({ children }) => {
|
||||
const unreadCount = notifications.filter((n) => !n.read).length
|
||||
|
||||
useEffect(() => {
|
||||
if (authenticated && notificationCenterVisible) {
|
||||
if (authenticated == true && notificationCenterVisible == true) {
|
||||
fetchNotifications()
|
||||
}
|
||||
}, [authenticated, notificationCenterVisible, fetchNotifications])
|
||||
|
||||
useEffect(() => {
|
||||
if (authenticated) {
|
||||
fetchNotifications()
|
||||
}
|
||||
}, [authenticated, fetchNotifications])
|
||||
|
||||
useEffect(() => {
|
||||
setNotificationCenterVisible(false)
|
||||
}, [location.pathname])
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user