add navagation handler while file upload
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
import ProtectionSummary from '@/app/ui/marking-page/protection-summary';
|
||||
import FilesTable from '@/app/ui/dashboard/files-table';
|
||||
import PageTitle from '@/app/ui/page-title';
|
||||
import UploadSectionFile from '@/app/components/upload-section-file';
|
||||
|
||||
const ALLOWED_EXTENSIONS: string[] = ['jpg', 'jpeg', 'png', 'gif', 'bmp'];
|
||||
const FILE_TYPE = "VIDEO"
|
||||
const FILE_TYPE = "AUDIO"
|
||||
const MAX_SIZE = 1048576000;
|
||||
|
||||
export default function Page() {
|
||||
@@ -12,6 +13,7 @@ export default function Page() {
|
||||
<div>
|
||||
<PageTitle title="audio-protection" />
|
||||
<ProtectionSummary />
|
||||
<UploadSectionFile fileType={FILE_TYPE} allowedExtensions={ALLOWED_EXTENSIONS} maxFileSize={MAX_SIZE} />
|
||||
{/* <TestSection /> */}
|
||||
<FilesTable />
|
||||
</div>
|
||||
|
||||
@@ -66,6 +66,7 @@ export async function fileUpload(messageBody: initMessageBody) {
|
||||
|
||||
export async function cancelUpload(udloadId: string) {
|
||||
try {
|
||||
console.log('cancelUpload start');
|
||||
const response = await fetch(`${API_BASE_URL}/api/v1/data`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
@@ -93,6 +94,7 @@ export async function cancelUpload(udloadId: string) {
|
||||
};
|
||||
|
||||
if (parsed.message_desc === 'Upload cancelled successfully') {
|
||||
console.log('cancelUpload done');
|
||||
return parsed.message_body;
|
||||
} else {
|
||||
throw parsed;
|
||||
@@ -101,6 +103,7 @@ export async function cancelUpload(udloadId: string) {
|
||||
throw (`${response.status}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('cancelUpload error');
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
'use client'
|
||||
|
||||
import React, { useRef, useState, useCallback, ChangeEvent, DragEvent } from 'react';
|
||||
import { useRef, useState, useCallback, ChangeEvent, DragEvent, useEffect } from 'react';
|
||||
import { IconShieldAdd } from '@/app/ui/icons/icons';
|
||||
import { fileUpload, cancelUpload, chunkUpload, checkChunkStatus } from '@/app/actions/file-upload';
|
||||
import { useTranslations } from 'next-intl';
|
||||
|
||||
import { useNavigationBlocker } from '@/app/hooks/useNavigationBlocker';
|
||||
interface SelectedFile {
|
||||
file: File;
|
||||
preview: string;
|
||||
@@ -94,7 +94,6 @@ export default function UploadSectionFile({ fileType, allowedExtensions, maxFile
|
||||
|
||||
const ALLOWED_EXTENSIONS: string[] = allowedExtensions;
|
||||
|
||||
|
||||
const validateFile = (file: File): { isValid: boolean; errorMessage?: string } => {
|
||||
if (!SUPPORTED_FORMATS.includes(file.type as string)) {
|
||||
const extension = file.name.split('.').pop()?.toLowerCase();
|
||||
@@ -308,6 +307,42 @@ export default function UploadSectionFile({ fileType, allowedExtensions, maxFile
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
// Обработка закрытия вкладки
|
||||
// Тут можно попробовать изучить вопрос с navigator.sendBeacon('', blob)
|
||||
// он нужен для того что бы послать запрос на бек при закрытии вкладки,
|
||||
// но у него есть свои особенности и стандартные запросы не работают.
|
||||
const handleUnload = () => {
|
||||
if (uploadId) {
|
||||
//const data = JSON.stringify({ uploadId, reason: 'tab_closed' });
|
||||
//const blob = new Blob([data], { type: 'application/json' });
|
||||
//navigator.sendBeacon('/api/v1/data/cancel', blob);
|
||||
console.log('sendBeacon');
|
||||
}
|
||||
};
|
||||
if (uploadId) {
|
||||
window.addEventListener('unload', handleUnload);
|
||||
}
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('unload', handleUnload);
|
||||
};
|
||||
}, [uploadId]);
|
||||
|
||||
useNavigationBlocker({
|
||||
shouldBlock: !!uploadId,
|
||||
message: t('have-unsaved-changes'),
|
||||
onConfirm: async () => {
|
||||
console.log('User confirmed navigation');
|
||||
if (uploadId) {
|
||||
await cancelUpload(uploadId);
|
||||
}
|
||||
},
|
||||
onCancel: () => {
|
||||
console.log('User cancelled navigation');
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="upload-section">
|
||||
<h3>
|
||||
@@ -354,14 +389,16 @@ export default function UploadSectionFile({ fileType, allowedExtensions, maxFile
|
||||
{t('select-files-to-protect')}
|
||||
</button>
|
||||
|
||||
{fileType === "IMAGE" && (
|
||||
<p className="description">
|
||||
Разрешение изображение: 100x100 - 10000x10000
|
||||
</p>
|
||||
)}
|
||||
<p className="description">
|
||||
Разрешение изображение: 100x100 - 10000x10000
|
||||
Размер файла: до {(maxFileSize / 1024 / 1024)} МБ
|
||||
</p>
|
||||
<p className="description">
|
||||
Размер файла: до 20 МБ
|
||||
</p>
|
||||
<p className="description">
|
||||
Формат файла: JPG, PNG, GIF, BMP (WEBP НЕ поддерживается)
|
||||
Формат файла: {ALLOWED_EXTENSIONS.join(', ')}
|
||||
</p>
|
||||
|
||||
{error && (
|
||||
@@ -411,17 +448,6 @@ export default function UploadSectionFile({ fileType, allowedExtensions, maxFile
|
||||
/>
|
||||
)}
|
||||
|
||||
{fileType === 'VIDEO' && (
|
||||
<video
|
||||
src={selectedFile.preview}
|
||||
className="uploaded-image"
|
||||
controls
|
||||
muted
|
||||
onError={() => setError('Не удалось загрузить превью видео')}
|
||||
onLoadedData={() => console.log('Видео загружено')}
|
||||
/>
|
||||
)}
|
||||
|
||||
<div className="mt-4 flex gap-3 justify-center relative">
|
||||
<button
|
||||
className="btn btn-cancel"
|
||||
@@ -440,19 +466,6 @@ export default function UploadSectionFile({ fileType, allowedExtensions, maxFile
|
||||
>
|
||||
{t('upload-file')}
|
||||
</button>
|
||||
<button
|
||||
className="btn btn-cancel"
|
||||
type="button"
|
||||
onClick={
|
||||
async () => {
|
||||
if (uploadId) {
|
||||
await checkChunkStatus(uploadId);
|
||||
}
|
||||
}
|
||||
}
|
||||
>
|
||||
dev-test-button
|
||||
</button>
|
||||
{uploadProgress !== 0 && (
|
||||
<div
|
||||
className={`absolute top-5 right-5 font-medium ${isFileUploaded ? "text-green-600" : ""}`}
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
'use client';
|
||||
|
||||
import { useEffect } from 'react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
|
||||
|
||||
interface UseNavigationBlockerProps {
|
||||
shouldBlock: boolean;
|
||||
message?: string;
|
||||
onConfirm?: () => void;
|
||||
onCancel?: () => void;
|
||||
}
|
||||
|
||||
export const useNavigationBlocker = ({
|
||||
shouldBlock,
|
||||
message,
|
||||
onConfirm,
|
||||
onCancel
|
||||
}: UseNavigationBlockerProps) => {
|
||||
const router = useRouter();
|
||||
|
||||
useEffect(() => {
|
||||
if (!shouldBlock) return;
|
||||
|
||||
const handleBeforeUnload = (e: BeforeUnloadEvent) => {
|
||||
e.preventDefault();
|
||||
e.returnValue = message;
|
||||
return message;
|
||||
};
|
||||
|
||||
const handleClick = (e: MouseEvent) => {
|
||||
const target = e.target as HTMLElement;
|
||||
const link = target.closest('a');
|
||||
|
||||
if (link && link.href) {
|
||||
const currentUrl = window.location.href;
|
||||
if (link.href !== currentUrl && !link.href.startsWith('#')) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
const confirmed = window.confirm(message);
|
||||
if (confirmed) {
|
||||
onConfirm?.();
|
||||
window.location.href = link.href;
|
||||
} else {
|
||||
onCancel?.();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('beforeunload', handleBeforeUnload);
|
||||
document.addEventListener('click', handleClick, true);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('beforeunload', handleBeforeUnload);
|
||||
document.removeEventListener('click', handleClick, true);
|
||||
};
|
||||
}, [shouldBlock, message, onConfirm, onCancel]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!shouldBlock) return;
|
||||
|
||||
const originalPush = router.push;
|
||||
|
||||
router.push = async (...args) => {
|
||||
const confirmed = window.confirm(message);
|
||||
if (confirmed) {
|
||||
onConfirm?.();
|
||||
return originalPush.apply(router, args);
|
||||
} else {
|
||||
onCancel?.();
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
};
|
||||
|
||||
return () => {
|
||||
router.push = originalPush;
|
||||
};
|
||||
}, [shouldBlock, message, router, onConfirm, onCancel]);
|
||||
};
|
||||
@@ -128,7 +128,8 @@
|
||||
"select-files-to-protect": "Select files to protect",
|
||||
"upload-for-protection": "Upload {fileType} for protection",
|
||||
"image": "image",
|
||||
"file-has-no-extension": "File has no extension"
|
||||
"file-has-no-extension": "File has no extension",
|
||||
"have-unsaved-changes": "You have unsaved changes. Are you sure you want to delete this page?"
|
||||
},
|
||||
"Login-register-form": {
|
||||
"and": "and",
|
||||
|
||||
@@ -128,7 +128,8 @@
|
||||
"select-files-to-protect": "Выбрать файлы для защиты",
|
||||
"upload-for-protection": "Загрузить {fileType} для защиты",
|
||||
"image": "изображений",
|
||||
"file-has-no-extension": "Файл не имеет расширения"
|
||||
"file-has-no-extension": "Файл не имеет расширения",
|
||||
"have-unsaved-changes": "У вас есть несохраненные изменения. Вы уверены, что хотите покинуть страницу?"
|
||||
},
|
||||
"Login-register-form": {
|
||||
"and": "и",
|
||||
|
||||
Reference in New Issue
Block a user