Publish #1

Merged
IgorVolochay merged 29 commits from dev into main 2026-01-10 16:02:13 +00:00
3 changed files with 266 additions and 18 deletions
Showing only changes of commit 7ec0acf09b - Show all commits
+8 -2
View File
@@ -4,7 +4,7 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Аналог DropMeFiles</title>
<link rel="stylesheet" href="/style.css">
<link rel="stylesheet" href="./style.css">
</head>
<body>
<div id="uploadPage">
@@ -34,6 +34,12 @@
</div>
</div>
<div class="status" id="status"></div>
<div id="uploadProgress" class="download-progress" style="display: none;">
<div class="progress-bar-container">
<div class="progress-bar" id="uploadProgressBar"></div>
</div>
<p class="progress-text" id="uploadProgressText">0%</p>
</div>
</div>
</div>
</div>
@@ -54,6 +60,6 @@
</div>
</div>
<script src="/script.js"></script>
<script src="./script.js"></script>
</body>
</html>
+53 -16
View File
@@ -366,25 +366,57 @@ function initUploadPage() {
uploadData.fields["key"] = fileUuid;
delete uploadData.fields["Content-Type"];
setStatus('Загрузка файла на сервер...', '');
// Hide status, show progress bar
statusDiv.style.display = 'none';
const uploadProgress = document.getElementById('uploadProgress');
const uploadProgressBar = document.getElementById('uploadProgressBar');
const uploadProgressText = document.getElementById('uploadProgressText');
uploadProgress.style.display = 'block';
uploadProgressBar.style.width = '0%';
uploadProgressText.textContent = '0%';
// Step 2: Upload to S3 using form-data
// Add all fields from the API response (order matters for S3, file should be last)
const formData = new FormData();
Object.keys(uploadData.fields).forEach(key => {
formData.append(key, uploadData.fields[key]);
});
// File must be appended last
formData.append('file', file);
const uploadResponse = await fetch(uploadData.url, {
method: 'POST',
body: formData
// Step 2: Upload to S3 using XMLHttpRequest for progress tracking
await new Promise((resolve, reject) => {
const formData = new FormData();
Object.keys(uploadData.fields).forEach(key => {
formData.append(key, uploadData.fields[key]);
});
// File must be appended last
formData.append('file', file);
const xhr = new XMLHttpRequest();
// Track upload progress
xhr.upload.addEventListener('progress', (event) => {
if (event.lengthComputable) {
const percent = Math.round((event.loaded / event.total) * 100);
uploadProgressBar.style.width = percent + '%';
uploadProgressText.textContent = `${percent}% (${formatBytes(event.loaded)} / ${formatBytes(event.total)})`;
}
});
xhr.addEventListener('load', () => {
if (xhr.status >= 200 && xhr.status < 300) {
resolve();
} else {
reject(new Error(`Ошибка загрузки: ${xhr.status}`));
}
});
xhr.addEventListener('error', () => {
reject(new Error('Ошибка при загрузке файла на сервер'));
});
xhr.addEventListener('abort', () => {
reject(new Error('Загрузка прервана'));
});
xhr.open('POST', uploadData.url);
xhr.send(formData);
});
if (!uploadResponse.ok) {
throw new Error('Ошибка при загрузке файла на сервер');
}
// Hide progress bar after successful upload
uploadProgress.style.display = 'none';
// Success!
const downloadUrl = `${window.location.origin}/get/${fileUuid}`;
@@ -405,6 +437,11 @@ function initUploadPage() {
}
showErrorPopup(errorMessage);
setStatus('', '');
// Hide progress bar on error
const uploadProgress = document.getElementById('uploadProgress');
if (uploadProgress) {
uploadProgress.style.display = 'none';
}
} finally {
uploadButton.disabled = false;
}
+205
View File
@@ -15,6 +15,13 @@ body {
padding: 24px;
}
@media (max-width: 768px) {
body {
padding: 16px;
justify-content: center;
}
}
.page-content {
display: flex;
flex-direction: column;
@@ -30,6 +37,13 @@ h1 {
width: 100%;
}
@media (max-width: 768px) {
h1 {
font-size: clamp(24px, 6vw, 32px);
margin: 0 0 24px 0;
}
}
.upload-container {
width: min(640px, 100%);
background: #ffffffdd;
@@ -42,6 +56,14 @@ h1 {
backdrop-filter: blur(8px);
}
@media (max-width: 768px) {
.upload-container {
padding: 20px;
gap: 16px;
border-radius: 16px;
}
}
.dropzone {
border: 2px dashed #6366f1;
border-radius: 16px;
@@ -55,6 +77,13 @@ h1 {
overflow: hidden;
}
@media (max-width: 768px) {
.dropzone {
padding: 32px 16px;
border-radius: 12px;
}
}
.dropzone:focus,
.dropzone:focus-within {
outline: none;
@@ -130,6 +159,17 @@ button {
font-weight: 600;
cursor: pointer;
transition: transform 0.2s ease, box-shadow 0.2s ease;
touch-action: manipulation;
-webkit-tap-highlight-color: transparent;
}
@media (max-width: 768px) {
button {
padding: 16px 24px;
font-size: 16px;
min-height: 48px;
width: 100%;
}
}
button:hover:not(:disabled) {
@@ -204,6 +244,16 @@ button:disabled {
cursor: pointer;
transition: transform 0.2s ease, box-shadow 0.2s ease;
font-family: inherit;
touch-action: manipulation;
-webkit-tap-highlight-color: transparent;
}
@media (max-width: 768px) {
.download-button {
padding: 16px 24px;
width: 100%;
min-height: 48px;
}
}
.download-button:hover {
@@ -246,6 +296,53 @@ button:disabled {
}
}
@media (max-width: 768px) {
.error-popup {
top: 10px;
left: 10px;
right: 10px;
transform: none;
max-width: none;
padding: 12px 16px;
font-size: 14px;
}
@keyframes slideDown {
from {
opacity: 0;
transform: translateY(-20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.status {
font-size: 14px;
}
.status.error {
font-size: 16px;
}
.file-info h2 {
font-size: 20px;
}
.file-size {
font-size: 14px;
}
.dropzone p {
font-size: 14px;
}
.dropzone p.strong {
font-size: 16px;
}
}
/* Success content */
.success-content {
animation: fadeIn 0.3s ease-in;
@@ -282,6 +379,15 @@ button:disabled {
cursor: text;
}
@media (max-width: 768px) {
.download-link-input {
width: 100%;
min-width: 0;
font-size: 12px;
padding: 14px;
}
}
.download-link-input:focus {
outline: none;
border-color: #6366f1;
@@ -299,6 +405,16 @@ button:disabled {
cursor: pointer;
transition: transform 0.2s ease, box-shadow 0.2s ease;
white-space: nowrap;
touch-action: manipulation;
-webkit-tap-highlight-color: transparent;
}
@media (max-width: 768px) {
.copy-link-button {
width: 100%;
padding: 14px 24px;
min-height: 48px;
}
}
.copy-link-button:hover {
@@ -325,6 +441,16 @@ button:disabled {
font-weight: 600;
cursor: pointer;
transition: all 0.2s ease;
touch-action: manipulation;
-webkit-tap-highlight-color: transparent;
}
@media (max-width: 768px) {
.upload-another-button {
width: 100%;
padding: 14px 24px;
min-height: 48px;
}
}
.upload-another-button:hover {
@@ -363,3 +489,82 @@ button:disabled {
margin: 0;
font-weight: 500;
}
@media (max-width: 768px) {
.status {
font-size: 14px;
}
.status.error {
font-size: 16px;
}
.file-info h2 {
font-size: 20px;
}
.file-size {
font-size: 14px;
}
.dropzone p {
font-size: 14px;
}
.dropzone p.strong {
font-size: 16px;
}
.error-popup {
top: 10px;
left: 10px;
right: 10px;
transform: none;
max-width: none;
padding: 12px 16px;
font-size: 14px;
}
@keyframes slideDown {
from {
opacity: 0;
transform: translateY(-20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.progress-text {
font-size: 12px;
}
.button-container {
margin-top: 16px;
}
}
@media (max-width: 480px) {
body {
padding: 12px;
}
h1 {
font-size: 22px;
margin: 0 0 20px 0;
}
.upload-container {
padding: 16px;
gap: 12px;
}
.dropzone {
padding: 24px 12px;
}
.file-info {
padding: 16px;
}
}