diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index 7e80711..e2c8786 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -12,6 +12,8 @@
"@emotion/styled": "^11.14.1",
"@mui/icons-material": "^7.3.5",
"@mui/material": "^7.3.5",
+ "@tanstack/react-query": "^5.90.11",
+ "@tanstack/react-query-devtools": "^5.91.1",
"axios": "^1.8.1",
"dotenv": "^16.4.7",
"framer-motion": "^12.23.24",
@@ -1669,6 +1671,59 @@
"win32"
]
},
+ "node_modules/@tanstack/query-core": {
+ "version": "5.90.11",
+ "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.90.11.tgz",
+ "integrity": "sha512-f9z/nXhCgWDF4lHqgIE30jxLe4sYv15QodfdPDKYAk7nAEjNcndy4dHz3ezhdUaR23BpWa4I2EH4/DZ0//Uf8A==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ }
+ },
+ "node_modules/@tanstack/query-devtools": {
+ "version": "5.91.1",
+ "resolved": "https://registry.npmjs.org/@tanstack/query-devtools/-/query-devtools-5.91.1.tgz",
+ "integrity": "sha512-l8bxjk6BMsCaVQH6NzQEE/bEgFy1hAs5qbgXl0xhzezlaQbPk6Mgz9BqEg2vTLPOHD8N4k+w/gdgCbEzecGyNg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ }
+ },
+ "node_modules/@tanstack/react-query": {
+ "version": "5.90.11",
+ "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.90.11.tgz",
+ "integrity": "sha512-3uyzz01D1fkTLXuxF3JfoJoHQMU2fxsfJwE+6N5hHy0dVNoZOvwKP8Z2k7k1KDeD54N20apcJnG75TBAStIrBA==",
+ "license": "MIT",
+ "dependencies": {
+ "@tanstack/query-core": "5.90.11"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ },
+ "peerDependencies": {
+ "react": "^18 || ^19"
+ }
+ },
+ "node_modules/@tanstack/react-query-devtools": {
+ "version": "5.91.1",
+ "resolved": "https://registry.npmjs.org/@tanstack/react-query-devtools/-/react-query-devtools-5.91.1.tgz",
+ "integrity": "sha512-tRnJYwEbH0kAOuToy8Ew7bJw1lX3AjkkgSlf/vzb+NpnqmHPdWM+lA2DSdGQSLi1SU0PDRrrCI1vnZnci96CsQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@tanstack/query-devtools": "5.91.1"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ },
+ "peerDependencies": {
+ "@tanstack/react-query": "^5.90.10",
+ "react": "^18 || ^19"
+ }
+ },
"node_modules/@types/babel__core": {
"version": "7.20.5",
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
diff --git a/frontend/package.json b/frontend/package.json
index c6da8e1..e18a788 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -14,6 +14,8 @@
"@emotion/styled": "^11.14.1",
"@mui/icons-material": "^7.3.5",
"@mui/material": "^7.3.5",
+ "@tanstack/react-query": "^5.90.11",
+ "@tanstack/react-query-devtools": "^5.91.1",
"axios": "^1.8.1",
"dotenv": "^16.4.7",
"framer-motion": "^12.23.24",
diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx
index 414df9d..acf2829 100644
--- a/frontend/src/App.tsx
+++ b/frontend/src/App.tsx
@@ -194,19 +194,25 @@ function AppContent() {
);
}
+import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
+
+const queryClient = new QueryClient();
+
function App() {
return (
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
);
}
diff --git a/frontend/src/components/Header.tsx b/frontend/src/components/Header.tsx
index 306987f..b8e24d3 100644
--- a/frontend/src/components/Header.tsx
+++ b/frontend/src/components/Header.tsx
@@ -93,7 +93,7 @@ const Header: React.FC = ({
const { availableTags, selectedTags, handleTagToggle } = useVideo();
- const isDownloading = activeDownloads.length > 0 || queuedDownloads.length > 0;
+
useEffect(() => {
console.log('Header props:', { activeDownloads, queuedDownloads });
diff --git a/frontend/src/components/UploadModal.tsx b/frontend/src/components/UploadModal.tsx
index a1aa76e..c014091 100644
--- a/frontend/src/components/UploadModal.tsx
+++ b/frontend/src/components/UploadModal.tsx
@@ -12,6 +12,7 @@ import {
TextField,
Typography
} from '@mui/material';
+import { useMutation } from '@tanstack/react-query';
import axios from 'axios';
import { useState } from 'react';
import { useLanguage } from '../contexts/LanguageContext';
@@ -29,7 +30,6 @@ const UploadModal: React.FC = ({ open, onClose, onUploadSucces
const [file, setFile] = useState(null);
const [title, setTitle] = useState('');
const [author, setAuthor] = useState('Admin');
- const [uploading, setUploading] = useState(false);
const [progress, setProgress] = useState(0);
const [error, setError] = useState('');
@@ -43,22 +43,8 @@ const UploadModal: React.FC = ({ open, onClose, onUploadSucces
}
};
- const handleUpload = async () => {
- if (!file) {
- setError(t('pleaseSelectVideo'));
- return;
- }
-
- setUploading(true);
- setError('');
- setProgress(0);
-
- const formData = new FormData();
- formData.append('video', file);
- formData.append('title', title);
- formData.append('author', author);
-
- try {
+ const uploadMutation = useMutation({
+ mutationFn: async (formData: FormData) => {
await axios.post(`${API_URL}/upload`, formData, {
headers: {
'Content-Type': 'multipart/form-data',
@@ -68,15 +54,32 @@ const UploadModal: React.FC = ({ open, onClose, onUploadSucces
setProgress(percentCompleted);
},
});
-
+ },
+ onSuccess: () => {
onUploadSuccess();
handleClose();
- } catch (err: any) {
+ },
+ onError: (err: any) => {
console.error('Upload failed:', err);
setError(err.response?.data?.error || t('failedToUpload'));
- } finally {
- setUploading(false);
}
+ });
+
+ const handleUpload = () => {
+ if (!file) {
+ setError(t('pleaseSelectVideo'));
+ return;
+ }
+
+ setError('');
+ setProgress(0);
+
+ const formData = new FormData();
+ formData.append('video', file);
+ formData.append('title', title);
+ formData.append('author', author);
+
+ uploadMutation.mutate(formData);
};
const handleClose = () => {
@@ -89,7 +92,7 @@ const UploadModal: React.FC = ({ open, onClose, onUploadSucces
};
return (
-