preland#18 #19: popup ошибки загрузки и однострочный ИИ-заголовок
deploy-preland / deploy zoomstage.online (push) Successful in 55s
deploy-preland / deploy zoomstage.online (push) Successful in 55s
This commit is contained in:
1 parent
f4ce5afdc4
commit
f30742c2fb
4 files changed
+74
-16
No files matched your search
+10
-15
@@ -1,5 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
require_once __DIR__ . '/browser-upload.php';
|
require_once __DIR__ . '/browser-upload.php';
|
||||||
|
require __DIR__ . '/upload-error.php';
|
||||||
echo '<script>', require __DIR__ . '/navigation-guard.php', '</script>';
|
echo '<script>', require __DIR__ . '/navigation-guard.php', '</script>';
|
||||||
echo '<script>', require __DIR__ . '/upload-recovery.php', '</script>';
|
echo '<script>', require __DIR__ . '/upload-recovery.php', '</script>';
|
||||||
|
|
||||||
@@ -20,7 +21,7 @@ $basePickerAccept = 'image/jpeg,image/png,image/gif,image/webp,image/bmp,image/a
|
|||||||
<div class="dropzone-form-icon">
|
<div class="dropzone-form-icon">
|
||||||
<img src="<?= $assetsUriPrefix; ?>/images/dropzone-icon.svg" alt="" height="65" width="64">
|
<img src="<?= $assetsUriPrefix; ?>/images/dropzone-icon.svg" alt="" height="65" width="64">
|
||||||
</div>
|
</div>
|
||||||
<div id="form-title" class="dropzone-form-text index-upload-heading"><?= Yii::t('app', 'Улучшить изображение') ?></div>
|
<div id="form-title" class="dropzone-form-text index-upload-heading"><?= Yii::t('app', 'ИИ улучшение изображений') ?></div>
|
||||||
<div class="dropzone-form-format">
|
<div class="dropzone-form-format">
|
||||||
<div class="dropzone-form-format-label"><?= Yii::t('app', 'Поддерживаемые форматы') ?>:</div>
|
<div class="dropzone-form-format-label"><?= Yii::t('app', 'Поддерживаемые форматы') ?>:</div>
|
||||||
<div class="dropzone-form-format-list">
|
<div class="dropzone-form-format-list">
|
||||||
@@ -242,9 +243,9 @@ $basePickerAccept = 'image/jpeg,image/png,image/gif,image/webp,image/bmp,image/a
|
|||||||
catch (_) { showUploadError(genericErrorMsg); }
|
catch (_) { showUploadError(genericErrorMsg); }
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
showUploadError(genericErrorMsg);
|
showUploadError(genericErrorMsg, false, xhr.status === 408 || xhr.status >= 500);
|
||||||
};
|
};
|
||||||
xhr.onerror = () => { if (attempt === uploadAttempt && !leavingUploadPage) showUploadError(genericErrorMsg, true); };
|
xhr.onerror = () => { if (attempt === uploadAttempt && !leavingUploadPage) showUploadError(genericErrorMsg, true, true); };
|
||||||
xhr.send(formData);
|
xhr.send(formData);
|
||||||
} catch (_) { if (attempt === uploadAttempt && !leavingUploadPage) showUploadError(genericErrorMsg); }
|
} catch (_) { if (attempt === uploadAttempt && !leavingUploadPage) showUploadError(genericErrorMsg); }
|
||||||
}
|
}
|
||||||
@@ -308,33 +309,27 @@ $basePickerAccept = 'image/jpeg,image/png,image/gif,image/webp,image/bmp,image/a
|
|||||||
try { await recovery.accepted({id: imageId}); redirectToTour(record); }
|
try { await recovery.accepted({id: imageId}); redirectToTour(record); }
|
||||||
catch (_) { showUploadError(genericErrorMsg); }
|
catch (_) { showUploadError(genericErrorMsg); }
|
||||||
} else {
|
} else {
|
||||||
showUploadError(genericErrorMsg);
|
showUploadError(genericErrorMsg, false, xhr.status === 408 || xhr.status >= 500);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
xhr.onerror = () => {
|
xhr.onerror = () => {
|
||||||
if (attempt === uploadAttempt && !leavingUploadPage) showUploadError(genericErrorMsg, true);
|
if (attempt === uploadAttempt && !leavingUploadPage) showUploadError(genericErrorMsg, true, true);
|
||||||
};
|
};
|
||||||
|
|
||||||
xhr.send(formData);
|
xhr.send(formData);
|
||||||
}
|
}
|
||||||
|
|
||||||
function showUploadError(msg, keepRecovery = false) {
|
function showUploadError(msg, keepRecovery = false, connection = false) {
|
||||||
if (leavingUploadPage) return;
|
if (leavingUploadPage) return;
|
||||||
uploadAttempt++;
|
uploadAttempt++;
|
||||||
if (!keepRecovery) clearRecovery();
|
if (!keepRecovery) clearRecovery();
|
||||||
resetUploadPending();
|
resetUploadPending();
|
||||||
form.show();
|
form.show();
|
||||||
if (typeof preview !== 'undefined' && preview) {
|
|
||||||
preview.show(); preview.showError(msg);
|
|
||||||
if (!preview.sourceEl.getAttribute('src')) preview.sourceEl.style.display = 'none';
|
|
||||||
}
|
|
||||||
// Release sentinel восстанавливает scroll history. Показываем сообщение
|
|
||||||
// после этого перехода, иначе браузер тут же возвращает верх страницы.
|
|
||||||
window.PublicNavigationGuard.allowNavigation(() => requestAnimationFrame(() => {
|
|
||||||
if (typeof preview !== 'undefined' && preview) preview.upscaleErrorOverlay.scrollIntoView({block: 'center', behavior: 'instant'});
|
|
||||||
}));
|
|
||||||
fileInput.value = '';
|
fileInput.value = '';
|
||||||
|
window.PublicNavigationGuard.allowNavigation(() => {
|
||||||
|
window.ZoomzillaUploadError.show(msg, connection);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
window.addEventListener('pagehide', () => { leavingUploadPage = true; });
|
window.addEventListener('pagehide', () => { leavingUploadPage = true; });
|
||||||
window.addEventListener('pageshow', event => {
|
window.addEventListener('pageshow', event => {
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
<?php
|
||||||
|
/** api#321: ошибка загрузки поверх страницы, без перестройки первого экрана. */
|
||||||
|
$uploadErrorCopy = json_encode([
|
||||||
|
'title' => Yii::t('app', 'Ошибка связи'),
|
||||||
|
'network' => Yii::t('app', 'Не удалось загрузить изображение. Проверьте подключение к интернету и попробуйте ещё раз.'),
|
||||||
|
'other' => Yii::t('app', 'Не удалось загрузить изображение'),
|
||||||
|
'fallback' => Yii::t('app', 'Попробуйте ещё раз.'),
|
||||||
|
], JSON_UNESCAPED_UNICODE | JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT);
|
||||||
|
?>
|
||||||
|
<style>
|
||||||
|
.upload-error-popup{position:fixed;inset:0;z-index:10000;background:rgba(0,0,0,.65);display:flex;align-items:center;justify-content:center;padding:20px;box-sizing:border-box;overflow:auto}
|
||||||
|
.upload-error-popup[hidden]{display:none!important}
|
||||||
|
.upload-error-popup .upload-error-card{position:relative;width:100%;max-width:440px;padding:38px 32px 30px;text-align:center;border-radius:24px;border:1px solid rgba(255,255,255,.15);box-shadow:0 20px 80px #0008;background:#484848 linear-gradient(to bottom,rgba(61,135,227,.25),rgba(153,6,148,.25));color:#fff;box-sizing:border-box;font-family:Inter,sans-serif;max-height:100%;overflow:auto}
|
||||||
|
.upload-error-popup .upload-error-card:focus{outline:none}
|
||||||
|
.upload-error-popup h2{font-size:24px;line-height:1.3;font-weight:600;margin:0 0 16px;color:inherit}
|
||||||
|
.upload-error-popup p{font-size:16px;line-height:24px;color:#e5e5e7;margin:0 0 24px;overflow-wrap:anywhere;white-space:pre-line}
|
||||||
|
.upload-error-popup .upload-error-close{position:absolute;top:5px;right:5px;display:flex;align-items:center;justify-content:center;width:44px;height:44px;border:0;background:none;color:#e5e5e7;cursor:pointer;font-size:28px;line-height:1}
|
||||||
|
.upload-error-popup .upload-error-ok{min-width:160px;height:48px;border-radius:16px;padding:0 28px;font:500 16px Inter,sans-serif;color:#fff;border:0;cursor:pointer;background:linear-gradient(180deg,#3d87e3 0%,#990694 100%)}
|
||||||
|
.upload-error-popup button:focus-visible{outline:2px solid #fff;outline-offset:3px}
|
||||||
|
@media(max-width:767.98px){.upload-error-popup .upload-error-card{padding:36px 24px 24px}.upload-error-popup h2{font-size:22px}.upload-error-popup p{font-size:15px;line-height:23px;margin-bottom:22px}.upload-error-popup .upload-error-ok{width:100%}}
|
||||||
|
</style>
|
||||||
|
<div class="upload-error-popup" id="upload-error-popup" hidden role="alertdialog" aria-modal="true" aria-labelledby="upload-error-title" aria-describedby="upload-error-text">
|
||||||
|
<div class="upload-error-card" tabindex="-1">
|
||||||
|
<button class="upload-error-close" type="button" aria-label="<?= htmlspecialchars(Yii::t('app', 'Закрыть'), ENT_QUOTES, 'UTF-8') ?>">×</button>
|
||||||
|
<h2 id="upload-error-title"></h2>
|
||||||
|
<p id="upload-error-text"></p>
|
||||||
|
<button class="upload-error-ok" type="button">OK</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
(() => {
|
||||||
|
const popup=document.getElementById('upload-error-popup');
|
||||||
|
if(!popup || window.ZoomzillaUploadError) return;
|
||||||
|
document.body.appendChild(popup);
|
||||||
|
const copy=<?= $uploadErrorCopy ?>, card=popup.querySelector('.upload-error-card');
|
||||||
|
const close=popup.querySelector('.upload-error-close'), ok=popup.querySelector('.upload-error-ok');
|
||||||
|
let previousOverflow='', previousFocus=null;
|
||||||
|
function hide(){
|
||||||
|
if(popup.hidden)return;
|
||||||
|
popup.hidden=true;document.body.style.overflow=previousOverflow;
|
||||||
|
const target=document.querySelector('#dropzone-form .dropzone-form-button') || previousFocus;
|
||||||
|
if(target && !target.disabled && target.isConnected)target.focus({preventScroll:true});
|
||||||
|
}
|
||||||
|
window.ZoomzillaUploadError={show(message,connection=false){
|
||||||
|
if(popup.hidden){previousOverflow=document.body.style.overflow;previousFocus=document.activeElement;}
|
||||||
|
popup.querySelector('h2').textContent=connection?copy.title:copy.other;
|
||||||
|
popup.querySelector('p').textContent=connection?copy.network:(message||copy.fallback);
|
||||||
|
popup.hidden=false;document.body.style.overflow='hidden';card.focus({preventScroll:true});
|
||||||
|
},hide};
|
||||||
|
close.addEventListener('click',hide);ok.addEventListener('click',hide);
|
||||||
|
popup.addEventListener('keydown',event=>{
|
||||||
|
if(event.key==='Escape'){event.preventDefault();hide();return;}
|
||||||
|
if(event.key==='Tab'){
|
||||||
|
event.preventDefault();
|
||||||
|
if(document.activeElement===close)ok.focus();else if(document.activeElement===ok)close.focus();else(event.shiftKey?ok:close).focus();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
@@ -18,4 +18,8 @@ return <<<'CSS'
|
|||||||
#dropzone-form [data-files-picker]::after { content: ''; position: absolute; inset: 0 -8px -14px; }
|
#dropzone-form [data-files-picker]::after { content: ''; position: absolute; inset: 0 -8px -14px; }
|
||||||
:is(#dropzone-form[data-mobile-upload], html[data-upload-device="mobile"] #dropzone-form, html[data-upload-device="android"] #dropzone-form) .dropzone-form-format-item[data-format-optional] { display: none; }
|
:is(#dropzone-form[data-mobile-upload], html[data-upload-device="mobile"] #dropzone-form, html[data-upload-device="android"] #dropzone-form) .dropzone-form-format-item[data-format-optional] { display: none; }
|
||||||
html[data-upload-device=android] #dropzone-form [data-picker-or][hidden], html[data-upload-device=android] #dropzone-form [data-files-picker][hidden] { display: block !important; }
|
html[data-upload-device=android] #dropzone-form [data-picker-or][hidden], html[data-upload-device=android] #dropzone-form [data-files-picker][hidden] { display: block !important; }
|
||||||
|
/* api#320: однострочный заголовок; правило общее для API и Preland, включая Android picker. */
|
||||||
|
@media(max-width:767.98px){
|
||||||
|
.index-mobile-fit #dropzone-form #form-title.index-upload-heading{font-size:clamp(16px,5.2vw,22px);font-weight:400;line-height:1.25;white-space:nowrap}
|
||||||
|
}
|
||||||
CSS;
|
CSS;
|
||||||
@@ -8,7 +8,7 @@ ob_start();
|
|||||||
require dirname(__DIR__) . '/components/form.php';
|
require dirname(__DIR__) . '/components/form.php';
|
||||||
$html = ob_get_clean();
|
$html = ob_get_clean();
|
||||||
$expected = [
|
$expected = [
|
||||||
'/id="form-title"[^>]*>(.*?)<\/div>/s' => 'Улучшить изображение',
|
'/id="form-title"[^>]*>(.*?)<\/div>/s' => 'ИИ улучшение изображений',
|
||||||
'/data-upload-label>(.*?)<\/span>/s' => 'Выберите фото',
|
'/data-upload-label>(.*?)<\/span>/s' => 'Выберите фото',
|
||||||
'/data-files-picker hidden>(.*?)<\/a>/s' => 'Файлы (PDF и др.)',
|
'/data-files-picker hidden>(.*?)<\/a>/s' => 'Файлы (PDF и др.)',
|
||||||
];
|
];
|
||||||
|
|||||||
Reference in new issue
Block a user