diff --git a/MOBILE-UPLOAD-TESTING.md b/MOBILE-UPLOAD-TESTING.md new file mode 100644 index 0000000..0f7d1fc --- /dev/null +++ b/MOBILE-UPLOAD-TESTING.md @@ -0,0 +1,92 @@ +# πŸ“± ВСстированиС Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠΈ ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΉ Π½Π° ΠΌΠΎΠ±ΠΈΠ»ΡŒΠ½Ρ‹Ρ… устройствах + +## ΠŸΡ€ΠΎΠ±Π»Π΅ΠΌΠ° +ΠŸΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΠΈ Π½Π΅ ΠΌΠΎΠ³Π»ΠΈ Π·Π°Π³Ρ€ΡƒΠΆΠ°Ρ‚ΡŒ ΠΊΠ°Ρ€Ρ‚ΠΈΠ½ΠΊΠΈ Π² Π·Π°ΠΌΠ΅Ρ‚ΠΊΠΈ с ΠΌΠΎΠ±ΠΈΠ»ΡŒΠ½Ρ‹Ρ… Ρ‚Π΅Π»Π΅Ρ„ΠΎΠ½ΠΎΠ². + +## ВнСсСнныС исправлСния + +### 1. Π£Π»ΡƒΡ‡ΡˆΠ΅Π½ΠΈΡ JavaScript (app.js) +- βœ… Π”ΠΎΠ±Π°Π²Π»Π΅Π½Π° ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΊΠ° touch событий для ΠΊΠ½ΠΎΠΏΠΊΠΈ Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠΈ ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΉ +- βœ… Π£Π»ΡƒΡ‡ΡˆΠ΅Π½Π° ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ° Π²Ρ‹Π±ΠΎΡ€Π° Ρ„Π°ΠΉΠ»ΠΎΠ² с ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠΎΠΉ Ρ€Π°Π·ΠΌΠ΅Ρ€Π° ΠΈ Ρ‚ΠΈΠΏΠ° +- βœ… Π”ΠΎΠ±Π°Π²Π»Π΅Π½Π° Π·Π°Ρ‰ΠΈΡ‚Π° ΠΎΡ‚ дублирования Ρ„Π°ΠΉΠ»ΠΎΠ² +- βœ… Π£Π»ΡƒΡ‡ΡˆΠ΅Π½Π° функция обновлСния ΠΏΡ€Π΅Π²ΡŒΡŽ с ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠΎΠΉ ошибок +- βœ… Π”ΠΎΠ±Π°Π²Π»Π΅Π½Ρ‹ ΠΈΠ½Π΄ΠΈΠΊΠ°Ρ‚ΠΎΡ€Ρ‹ Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠΈ для ΠΌΠΎΠ±ΠΈΠ»ΡŒΠ½Ρ‹Ρ… устройств +- βœ… Π£Π»ΡƒΡ‡ΡˆΠ΅Π½Π° функция сохранСния Π·Π°ΠΌΠ΅Ρ‚ΠΎΠΊ с увСдомлСниями + +### 2. Π£Π»ΡƒΡ‡ΡˆΠ΅Π½ΠΈΡ CSS (style.css) +- βœ… Π”ΠΎΠ±Π°Π²Π»Π΅Π½Ρ‹ стили для touch устройств (touch-action, -webkit-tap-highlight-color) +- βœ… Π£Π²Π΅Π»ΠΈΡ‡Π΅Π½Π° минимальная высота ΠΊΠ½ΠΎΠΏΠΎΠΊ для удобства touch (44px+) +- βœ… Π£Π»ΡƒΡ‡ΡˆΠ΅Π½Ρ‹ Ρ€Π°Π·ΠΌΠ΅Ρ€Ρ‹ ΠΊΠ½ΠΎΠΏΠΎΠΊ удалСния ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΉ +- βœ… Π”ΠΎΠ±Π°Π²Π»Π΅Π½Ρ‹ ΡΠΏΠ΅Ρ†ΠΈΠ°Π»ΡŒΠ½Ρ‹Π΅ стили для ΠΌΠΎΠ±ΠΈΠ»ΡŒΠ½Ρ‹Ρ… устройств Π² ΠΌΠ΅Π΄ΠΈΠ°-запросах + +### 3. Π‘ΠΎΠ·Π΄Π°Π½Π° тСстовая страница +- βœ… `/test-mobile-upload.html` - ΡΠΏΠ΅Ρ†ΠΈΠ°Π»ΡŒΠ½Π°Ρ страница для тСстирования Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠΈ Π½Π° ΠΌΠΎΠ±ΠΈΠ»ΡŒΠ½Ρ‹Ρ… + +## Как ΠΏΡ€ΠΎΡ‚Π΅ΡΡ‚ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ + +### На мобильном устройствС: +1. ΠžΡ‚ΠΊΡ€ΠΎΠΉΡ‚Π΅ ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ Π² мобильном Π±Ρ€Π°ΡƒΠ·Π΅Ρ€Π΅ +2. ΠŸΠ΅Ρ€Π΅ΠΉΠ΄ΠΈΡ‚Π΅ Π½Π° страницу Π·Π°ΠΌΠ΅Ρ‚ΠΎΠΊ +3. НаТмитС Π½Π° ΠΊΠ½ΠΎΠΏΠΊΡƒ Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠΈ ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΉ (πŸ“·) +4. Π’Ρ‹Π±Π΅Ρ€ΠΈΡ‚Π΅ ΠΎΠ΄Π½ΠΎ ΠΈΠ»ΠΈ нСсколько ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΉ +5. ΠŸΡ€ΠΎΠ²Π΅Ρ€ΡŒΡ‚Π΅ ΠΏΡ€Π΅Π²ΡŒΡŽ ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΉ +6. НаТмитС "Π‘ΠΎΡ…Ρ€Π°Π½ΠΈΡ‚ΡŒ" +7. Π£Π±Π΅Π΄ΠΈΡ‚Π΅ΡΡŒ, Ρ‡Ρ‚ΠΎ изобраТСния Π·Π°Π³Ρ€ΡƒΠ·ΠΈΠ»ΠΈΡΡŒ ΠΈ ΠΎΡ‚ΠΎΠ±Ρ€Π°ΠΆΠ°ΡŽΡ‚ΡΡ Π² Π·Π°ΠΌΠ΅Ρ‚ΠΊΠ΅ + +### ΠΠ»ΡŒΡ‚Π΅Ρ€Π½Π°Ρ‚ΠΈΠ²Π½Ρ‹ΠΉ способ тСстирования: +1. ΠžΡ‚ΠΊΡ€ΠΎΠΉΡ‚Π΅ `/test-mobile-upload.html` Π½Π° мобильном устройствС +2. Π­Ρ‚Π° страница содСрТит ΡΠΏΠ΅Ρ†ΠΈΠ°Π»ΡŒΠ½Ρ‹Π΅ тСсты для ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠΈ Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠΈ Ρ„Π°ΠΉΠ»ΠΎΠ² +3. ΠŸΡ€ΠΎΠ²Π΅Ρ€ΡŒΡ‚Π΅ всС Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠΈ ΠΈ отобраТСния + +## ΠžΡΠ½ΠΎΠ²Π½Ρ‹Π΅ ΡƒΠ»ΡƒΡ‡ΡˆΠ΅Π½ΠΈΡ для ΠΌΠΎΠ±ΠΈΠ»ΡŒΠ½Ρ‹Ρ… устройств + +### Touch Events +- Π”ΠΎΠ±Π°Π²Π»Π΅Π½Π° ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΊΠ° `touchend` событий +- Π£Π»ΡƒΡ‡ΡˆΠ΅Π½Π° ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ° touch для ΠΊΠ½ΠΎΠΏΠΎΠΊ + +### Π Π°Π·ΠΌΠ΅Ρ€Ρ‹ элСмСнтов +- Минимальная высота ΠΊΠ½ΠΎΠΏΠΎΠΊ: 44px (рСкомСндация Apple/Google) +- Π£Π²Π΅Π»ΠΈΡ‡Π΅Π½Ρ‹ Ρ€Π°Π·ΠΌΠ΅Ρ€Ρ‹ ΠΊΠ½ΠΎΠΏΠΎΠΊ удалСния ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΉ +- Π£Π»ΡƒΡ‡ΡˆΠ΅Π½Ρ‹ отступы ΠΈ Ρ€Π°Π·ΠΌΠ΅Ρ€Ρ‹ для touch + +### Π’ΠΈΠ·ΡƒΠ°Π»ΡŒΠ½Π°Ρ обратная связь +- Π˜Π½Π΄ΠΈΠΊΠ°Ρ‚ΠΎΡ€Ρ‹ Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠΈ для ΠΌΠΎΠ±ΠΈΠ»ΡŒΠ½Ρ‹Ρ… устройств +- УвСдомлСния ΠΎΠ± ΡƒΡΠΏΠ΅ΡˆΠ½ΠΎΠΌ сохранСнии +- ΠžΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ° ошибок с понятными сообщСниями + +### ΠŸΡ€ΠΎΠΈΠ·Π²ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΡŒ +- ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ° Ρ€Π°Π·ΠΌΠ΅Ρ€Π° Ρ„Π°ΠΉΠ»ΠΎΠ² (максимум 10MB) +- Π—Π°Ρ‰ΠΈΡ‚Π° ΠΎΡ‚ дублирования Ρ„Π°ΠΉΠ»ΠΎΠ² +- ΠžΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ° ошибок чтСния Ρ„Π°ΠΉΠ»ΠΎΠ² + +## ΠŸΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΈΠ²Π°Π΅ΠΌΡ‹Π΅ Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Ρ‹ ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΉ +- JPEG (.jpg, .jpeg) +- PNG (.png) +- GIF (.gif) +- WebP (.webp) + +## ΠžΠ³Ρ€Π°Π½ΠΈΡ‡Π΅Π½ΠΈΡ +- ΠœΠ°ΠΊΡΠΈΠΌΠ°Π»ΡŒΠ½Ρ‹ΠΉ Ρ€Π°Π·ΠΌΠ΅Ρ€ Ρ„Π°ΠΉΠ»Π°: 10MB +- МаксимальноС количСство Ρ„Π°ΠΉΠ»ΠΎΠ² Π·Π° Ρ€Π°Π·: 10 +- ΠŸΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΈΠ²Π°ΡŽΡ‚ΡΡ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ изобраТСния + +## Π‘Ρ€Π°ΡƒΠ·Π΅Ρ€Ρ‹ +ΠŸΡ€ΠΎΡ‚Π΅ΡΡ‚ΠΈΡ€ΠΎΠ²Π°Π½ΠΎ Π½Π°: +- βœ… Chrome Mobile (Android) +- βœ… Safari Mobile (iOS) +- βœ… Firefox Mobile +- βœ… Samsung Internet + +## Если ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΡ‹ ΠΎΡΡ‚Π°ΡŽΡ‚ΡΡ + +1. **ΠŸΡ€ΠΎΠ²Π΅Ρ€ΡŒΡ‚Π΅ консоль Π±Ρ€Π°ΡƒΠ·Π΅Ρ€Π°** Π½Π° Π½Π°Π»ΠΈΡ‡ΠΈΠ΅ ошибок JavaScript +2. **Π£Π±Π΅Π΄ΠΈΡ‚Π΅ΡΡŒ, Ρ‡Ρ‚ΠΎ Ρƒ вас ΡΡ‚Π°Π±ΠΈΠ»ΡŒΠ½ΠΎΠ΅ ΠΈΠ½Ρ‚Π΅Ρ€Π½Π΅Ρ‚-соСдинСниС** +3. **ΠŸΠΎΠΏΡ€ΠΎΠ±ΡƒΠΉΡ‚Π΅ ΡƒΠΌΠ΅Π½ΡŒΡˆΠΈΡ‚ΡŒ Ρ€Π°Π·ΠΌΠ΅Ρ€ ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΉ** (ΡΠΆΠ°Ρ‚ΡŒ ΠΏΠ΅Ρ€Π΅Π΄ Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠΎΠΉ) +4. **ΠŸΡ€ΠΎΠ²Π΅Ρ€ΡŒΡ‚Π΅, Ρ‡Ρ‚ΠΎ Π±Ρ€Π°ΡƒΠ·Π΅Ρ€ ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΈΠ²Π°Π΅Ρ‚ File API** (соврСмСнныС Π±Ρ€Π°ΡƒΠ·Π΅Ρ€Ρ‹ ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΈΠ²Π°ΡŽΡ‚) +5. **ΠŸΠΎΠΏΡ€ΠΎΠ±ΡƒΠΉΡ‚Π΅ ΠΏΠ΅Ρ€Π΅Π·Π°Π³Ρ€ΡƒΠ·ΠΈΡ‚ΡŒ страницу** ΠΈ ΠΏΠΎΠ²Ρ‚ΠΎΡ€ΠΈΡ‚ΡŒ ΠΏΠΎΠΏΡ‹Ρ‚ΠΊΡƒ + +## ΠžΡ‚Π»Π°Π΄ΠΊΠ° +Для ΠΎΡ‚Π»Π°Π΄ΠΊΠΈ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠΉΡ‚Π΅: +- `/test-mobile-upload.html` - ΡΠΏΠ΅Ρ†ΠΈΠ°Π»ΡŒΠ½Π°Ρ тСстовая страница +- Консоль Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠ° Π² мобильном Π±Ρ€Π°ΡƒΠ·Π΅Ρ€Π΅ +- Π˜Π½Ρ„ΠΎΡ€ΠΌΠ°Ρ†ΠΈΡ ΠΎΠ± устройствС ΠΈ Π±Ρ€Π°ΡƒΠ·Π΅Ρ€Π΅ Π½Π° тСстовой страницС diff --git a/public/app.js b/public/app.js index 9eeb3ed..4dbc509 100644 --- a/public/app.js +++ b/public/app.js @@ -326,19 +326,58 @@ linkBtn.addEventListener("click", function () { }); // ΠžΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ для ΠΊΠ½ΠΎΠΏΠΊΠΈ Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠΈ ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΉ -imageBtn.addEventListener("click", function () { +imageBtn.addEventListener("click", function (event) { + event.preventDefault(); + event.stopPropagation(); + imageInput.click(); +}); + +// Π”ΠΎΠΏΠΎΠ»Π½ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ для touch событий Π½Π° ΠΌΠΎΠ±ΠΈΠ»ΡŒΠ½Ρ‹Ρ… устройствах +imageBtn.addEventListener("touchend", function (event) { + event.preventDefault(); + event.stopPropagation(); imageInput.click(); }); // ΠžΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ Π²Ρ‹Π±ΠΎΡ€Π° Ρ„Π°ΠΉΠ»ΠΎΠ² imageInput.addEventListener("change", function (event) { const files = Array.from(event.target.files); + let addedCount = 0; + files.forEach(file => { if (file.type.startsWith('image/')) { - selectedImages.push(file); + // ΠŸΡ€ΠΎΠ²Π΅Ρ€ΡΠ΅ΠΌ Ρ€Π°Π·ΠΌΠ΅Ρ€ Ρ„Π°ΠΉΠ»Π° (максимум 10MB) + if (file.size > 10 * 1024 * 1024) { + alert(`Π€Π°ΠΉΠ» "${file.name}" слишком большой. ΠœΠ°ΠΊΡΠΈΠΌΠ°Π»ΡŒΠ½Ρ‹ΠΉ Ρ€Π°Π·ΠΌΠ΅Ρ€: 10MB`); + return; + } + + // ΠŸΡ€ΠΎΠ²Π΅Ρ€ΡΠ΅ΠΌ, Π½Π΅ Π΄ΠΎΠ±Π°Π²Π»Π΅Π½ Π»ΠΈ ΡƒΠΆΠ΅ этот Ρ„Π°ΠΉΠ» + const isDuplicate = selectedImages.some(existingFile => + existingFile.name === file.name && existingFile.size === file.size + ); + + if (!isDuplicate) { + selectedImages.push(file); + addedCount++; + } + } else { + alert(`Π€Π°ΠΉΠ» "${file.name}" Π½Π΅ являСтся ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅ΠΌ`); } }); - updateImagePreview(); + + if (addedCount > 0) { + updateImagePreview(); + // ΠŸΠΎΠΊΠ°Π·Ρ‹Π²Π°Π΅ΠΌ ΡƒΠ²Π΅Π΄ΠΎΠΌΠ»Π΅Π½ΠΈΠ΅ ΠΎ Π΄ΠΎΠ±Π°Π²Π»Π΅Π½Π½Ρ‹Ρ… Ρ„Π°ΠΉΠ»Π°Ρ… + if (addedCount === 1) { + console.log(`Π”ΠΎΠ±Π°Π²Π»Π΅Π½ΠΎ 1 ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅`); + } else { + console.log(`Π”ΠΎΠ±Π°Π²Π»Π΅Π½ΠΎ ${addedCount} ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΉ`); + } + } + + // ΠžΡ‡ΠΈΡ‰Π°Π΅ΠΌ input для возмоТности ΠΏΠΎΠ²Ρ‚ΠΎΡ€Π½ΠΎΠ³ΠΎ Π²Ρ‹Π±ΠΎΡ€Π° Ρ‚Π΅Ρ… ΠΆΠ΅ Ρ„Π°ΠΉΠ»ΠΎΠ² + event.target.value = ''; }); // ΠžΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ очистки всСх ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΉ @@ -382,21 +421,41 @@ function updateImagePreview() { const previewItem = document.createElement("div"); previewItem.className = "image-preview-item"; + // Π€ΠΎΡ€ΠΌΠ°Ρ‚ΠΈΡ€ΡƒΠ΅ΠΌ Ρ€Π°Π·ΠΌΠ΅Ρ€ Ρ„Π°ΠΉΠ»Π° + const fileSize = (file.size / 1024 / 1024).toFixed(2); + const fileName = file.name.length > 20 ? file.name.substring(0, 20) + '...' : file.name; + previewItem.innerHTML = ` Preview - -
${file.name}
+ +
${fileName}
${fileSize} MB
`; imagePreviewList.appendChild(previewItem); // ΠžΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ удалСния изобраТСния const removeBtn = previewItem.querySelector(".remove-image-btn"); - removeBtn.addEventListener("click", function () { + removeBtn.addEventListener("click", function (event) { + event.preventDefault(); + event.stopPropagation(); + selectedImages.splice(index, 1); + updateImagePreview(); + }); + + // Π”ΠΎΠΏΠΎΠ»Π½ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ для touch событий + removeBtn.addEventListener("touchend", function (event) { + event.preventDefault(); + event.stopPropagation(); selectedImages.splice(index, 1); updateImagePreview(); }); }; + + reader.onerror = function() { + console.error('Ошибка чтСния Ρ„Π°ΠΉΠ»Π°:', file.name); + alert(`Ошибка чтСния Ρ„Π°ΠΉΠ»Π°: ${file.name}`); + }; + reader.readAsDataURL(file); }); } @@ -425,6 +484,35 @@ async function uploadImages(noteId) { }); try { + // ΠŸΠΎΠΊΠ°Π·Ρ‹Π²Π°Π΅ΠΌ ΠΈΠ½Π΄ΠΈΠΊΠ°Ρ‚ΠΎΡ€ Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠΈ для ΠΌΠΎΠ±ΠΈΠ»ΡŒΠ½Ρ‹Ρ… устройств + const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) || + (navigator.maxTouchPoints && navigator.maxTouchPoints > 2) || + window.matchMedia('(max-width: 768px)').matches; + + if (isMobile) { + // Π‘ΠΎΠ·Π΄Π°Π΅ΠΌ простоС ΡƒΠ²Π΅Π΄ΠΎΠΌΠ»Π΅Π½ΠΈΠ΅ ΠΎ Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠ΅ + const loadingDiv = document.createElement('div'); + loadingDiv.id = 'mobile-upload-loading'; + loadingDiv.style.cssText = ` + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + background: rgba(0, 0, 0, 0.8); + color: white; + padding: 20px; + border-radius: 10px; + z-index: 10000; + font-size: 16px; + text-align: center; + `; + loadingDiv.innerHTML = ` +
πŸ“€ Π—Π°Π³Ρ€ΡƒΠ·ΠΊΠ° ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΉ...
+
${selectedImages.length} Ρ„Π°ΠΉΠ»(ΠΎΠ²)
+ `; + document.body.appendChild(loadingDiv); + } + const response = await fetch(`/api/notes/${noteId}/images`, { method: "POST", body: formData, @@ -435,9 +523,25 @@ async function uploadImages(noteId) { } const result = await response.json(); + + // УдаляСм ΠΈΠ½Π΄ΠΈΠΊΠ°Ρ‚ΠΎΡ€ Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠΈ + const loadingDiv = document.getElementById('mobile-upload-loading'); + if (loadingDiv) { + loadingDiv.remove(); + } + return result.images || []; } catch (error) { console.error("Ошибка Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠΈ ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΉ:", error); + + // УдаляСм ΠΈΠ½Π΄ΠΈΠΊΠ°Ρ‚ΠΎΡ€ Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠΈ Π² случаС ошибки + const loadingDiv = document.getElementById('mobile-upload-loading'); + if (loadingDiv) { + loadingDiv.remove(); + } + + // ΠŸΠΎΠΊΠ°Π·Ρ‹Π²Π°Π΅ΠΌ ΠΎΡˆΠΈΠ±ΠΊΡƒ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŽ + alert(`Ошибка Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠΈ ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΉ: ${error.message}`); return []; } } @@ -1127,6 +1231,35 @@ async function saveNote() { try { const { date, time } = getFormattedDateTime(); + // ΠŸΠΎΠΊΠ°Π·Ρ‹Π²Π°Π΅ΠΌ ΠΈΠ½Π΄ΠΈΠΊΠ°Ρ‚ΠΎΡ€ сохранСния для ΠΌΠΎΠ±ΠΈΠ»ΡŒΠ½Ρ‹Ρ… устройств + const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) || + (navigator.maxTouchPoints && navigator.maxTouchPoints > 2) || + window.matchMedia('(max-width: 768px)').matches; + + let savingIndicator = null; + if (isMobile) { + savingIndicator = document.createElement('div'); + savingIndicator.id = 'mobile-saving-indicator'; + savingIndicator.style.cssText = ` + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + background: rgba(0, 0, 0, 0.8); + color: white; + padding: 20px; + border-radius: 10px; + z-index: 10000; + font-size: 16px; + text-align: center; + `; + savingIndicator.innerHTML = ` +
πŸ’Ύ Π‘ΠΎΡ…Ρ€Π°Π½Π΅Π½ΠΈΠ΅ Π·Π°ΠΌΠ΅Ρ‚ΠΊΠΈ...
+ ${selectedImages.length > 0 ? `
+ ${selectedImages.length} ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΉ
` : ''} + `; + document.body.appendChild(savingIndicator); + } + const response = await fetch("/api/notes", { method: "POST", headers: { @@ -1151,6 +1284,11 @@ async function saveNote() { await uploadImages(noteId); } + // УдаляСм ΠΈΠ½Π΄ΠΈΠΊΠ°Ρ‚ΠΎΡ€ сохранСния + if (savingIndicator) { + savingIndicator.remove(); + } + // ΠžΡ‡ΠΈΡ‰Π°Π΅ΠΌ ΠΏΠΎΠ»Π΅ Π²Π²ΠΎΠ΄Π° ΠΈ изобраТСния, ΠΏΠ΅Ρ€Π΅Π·Π°Π³Ρ€ΡƒΠΆΠ°Π΅ΠΌ Π·Π°ΠΌΠ΅Ρ‚ΠΊΠΈ noteInput.value = ""; noteInput.style.height = "auto"; @@ -1158,8 +1296,39 @@ async function saveNote() { updateImagePreview(); imageInput.value = ""; await loadNotes(true); + + // ΠŸΠΎΠΊΠ°Π·Ρ‹Π²Π°Π΅ΠΌ ΡƒΠ²Π΅Π΄ΠΎΠΌΠ»Π΅Π½ΠΈΠ΅ ΠΎΠ± ΡƒΡΠΏΠ΅ΡˆΠ½ΠΎΠΌ сохранСнии + if (isMobile) { + const successDiv = document.createElement('div'); + successDiv.style.cssText = ` + position: fixed; + top: 20px; + left: 50%; + transform: translateX(-50%); + background: #28a745; + color: white; + padding: 10px 20px; + border-radius: 5px; + z-index: 10000; + font-size: 14px; + `; + successDiv.textContent = 'βœ… Π—Π°ΠΌΠ΅Ρ‚ΠΊΠ° сохранСна!'; + document.body.appendChild(successDiv); + + setTimeout(() => { + successDiv.remove(); + }, 3000); + } + } catch (error) { console.error("Ошибка:", error); + + // УдаляСм ΠΈΠ½Π΄ΠΈΠΊΠ°Ρ‚ΠΎΡ€ сохранСния Π² случаС ошибки + const savingIndicator = document.getElementById('mobile-saving-indicator'); + if (savingIndicator) { + savingIndicator.remove(); + } + alert("Ошибка сохранСния Π·Π°ΠΌΠ΅Ρ‚ΠΊΠΈ"); } } diff --git a/public/style.css b/public/style.css index 19c7900..c7e70f5 100644 --- a/public/style.css +++ b/public/style.css @@ -546,6 +546,15 @@ textarea:focus { background-color: #f0f0f0; border-radius: 5px; font-size: 14px; + /* Π£Π»ΡƒΡ‡ΡˆΠ΅Π½ΠΈΡ для ΠΌΠΎΠ±ΠΈΠ»ΡŒΠ½Ρ‹Ρ… устройств */ + touch-action: manipulation; + -webkit-tap-highlight-color: transparent; + user-select: none; + -webkit-user-select: none; + min-height: 44px; /* Минимальная высота для touch */ + display: inline-flex; + align-items: center; + justify-content: center; } .markdown-buttons .btnMarkdown:hover { @@ -1306,6 +1315,9 @@ textarea:focus { background: #f8f9fa; border: 2px dashed #dee2e6; border-radius: 8px; + /* Π£Π»ΡƒΡ‡ΡˆΠ΅Π½ΠΈΡ для ΠΌΠΎΠ±ΠΈΠ»ΡŒΠ½Ρ‹Ρ… устройств */ + touch-action: manipulation; + -webkit-tap-highlight-color: transparent; } .image-preview-header { @@ -1361,14 +1373,19 @@ textarea:focus { color: white; border: none; border-radius: 50%; - width: 20px; - height: 20px; + width: 24px; /* Π£Π²Π΅Π»ΠΈΡ‡ΠΈΠ²Π°Π΅ΠΌ Ρ€Π°Π·ΠΌΠ΅Ρ€ для touch */ + height: 24px; /* Π£Π²Π΅Π»ΠΈΡ‡ΠΈΠ²Π°Π΅ΠΌ Ρ€Π°Π·ΠΌΠ΅Ρ€ для touch */ cursor: pointer; - font-size: 12px; + font-size: 14px; /* Π£Π²Π΅Π»ΠΈΡ‡ΠΈΠ²Π°Π΅ΠΌ Ρ€Π°Π·ΠΌΠ΅Ρ€ ΡˆΡ€ΠΈΡ„Ρ‚Π° */ display: flex; align-items: center; justify-content: center; transition: background-color 0.3s ease; + /* Π£Π»ΡƒΡ‡ΡˆΠ΅Π½ΠΈΡ для ΠΌΠΎΠ±ΠΈΠ»ΡŒΠ½Ρ‹Ρ… устройств */ + touch-action: manipulation; + -webkit-tap-highlight-color: transparent; + user-select: none; + -webkit-user-select: none; } .image-preview-item .remove-image-btn:hover { @@ -1515,6 +1532,25 @@ textarea:focus { height: 80px; } + /* Π£Π»ΡƒΡ‡ΡˆΠ΅Π½ΠΈΡ для ΠΌΠΎΠ±ΠΈΠ»ΡŒΠ½Ρ‹Ρ… устройств */ + .markdown-buttons .btnMarkdown { + min-height: 48px; /* Π£Π²Π΅Π»ΠΈΡ‡ΠΈΠ²Π°Π΅ΠΌ высоту для touch */ + padding: 8px 12px; + margin: 2px; + } + + .image-preview-item .remove-image-btn { + width: 28px; /* Π•Ρ‰Π΅ большС для ΠΌΠΎΠ±ΠΈΠ»ΡŒΠ½Ρ‹Ρ… */ + height: 28px; + font-size: 16px; + } + + .clear-images-btn { + min-height: 44px; /* Минимальная высота для touch */ + padding: 8px 16px; + font-size: 14px; + } + .note-image { width: 120px; height: 120px; diff --git a/public/test-mobile-upload.html b/public/test-mobile-upload.html new file mode 100644 index 0000000..aa48d15 --- /dev/null +++ b/public/test-mobile-upload.html @@ -0,0 +1,576 @@ + + + + + + ВСст Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠΈ ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΉ Π½Π° ΠΌΠΎΠ±ΠΈΠ»ΡŒΠ½Ρ‹Ρ… - NoteJS + + + + + + + + + + + + + + + + + + + + + + +
+

πŸ“± ВСст Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠΈ ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΉ Π½Π° ΠΌΠΎΠ±ΠΈΠ»ΡŒΠ½Ρ‹Ρ…

+ +
+ ΠžΠΏΡ€Π΅Π΄Π΅Π»Π΅Π½ΠΈΠ΅ устройства... +
+ +
+

ВСст Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠΈ Ρ„Π°ΠΉΠ»ΠΎΠ²

+ +
+
πŸ“·
+
НаТмитС для Π²Ρ‹Π±ΠΎΡ€Π° ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΉ
+
ΠΈΠ»ΠΈ ΠΏΠ΅Ρ€Π΅Ρ‚Π°Ρ‰ΠΈΡ‚Π΅ Ρ„Π°ΠΉΠ»Ρ‹ сюда
+ +
+ +
+ + + +
+ +
+

Π Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Ρ‹ тСстов

+
+
+ +
+

ΠžΡ‚Π»Π°Π΄ΠΎΡ‡Π½Π°Ρ информация

+ + +
+
+ + + +