Compare commits

...

2 Commits

6 changed files with 83 additions and 22 deletions

View File

@ -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()
)

View File

@ -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

View File

@ -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}

View File

@ -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'
/>

View File

@ -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(() => {

View File

@ -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])