feat: неблокирующий сбор User Flow и гостевых ошибок (api#376 api#377)
deploy-preland / deploy zoomstage.online (push) Successful in 1m1s

This commit is contained in:
Panfilov Konstantin committed 2026-10-31 13:29:45 +03:00
1 parent 791761a782
commit ba33a76a34
5 files changed
+87 -12

No files matched your search

+20 -11
View File
@@ -108,7 +108,7 @@ $basePickerAccept = 'image/jpeg,image/png,image/gif,image/webp,image/bmp,image/a
let leavingUploadPage = false;
let uploadAttempt = 0;
const recovery = window.ZoomzillaUploadRecovery.create({scope: 'preland', target: createApiUrl});
const metadata = () => ({actionUrl: createApiUrl, redirectBase: redirectUrlBase, redirectUtm: redirectUrlUtm, utmDigits: {...utmDigits}});
const metadata = () => ({actionUrl: createApiUrl, redirectBase: redirectUrlBase, redirectUtm: redirectUrlUtm, utmDigits: {...utmDigits}, analytics: window.ZoomzillaAnalytics?.metadata()});
const clearRecovery = () => recovery.clear().catch(() => {});
let uploadDots = 1;
let uploadDotsTimer = null;
@@ -157,6 +157,7 @@ $basePickerAccept = 'image/jpeg,image/png,image/gif,image/webp,image/bmp,image/a
return;
}
if (e.target !== fileInput) {
if(e.isTrusted)window.ZoomzillaAnalytics?.emit('upload_click');
fileInput.click();
}
});
@@ -193,6 +194,7 @@ $basePickerAccept = 'image/jpeg,image/png,image/gif,image/webp,image/bmp,image/a
});
function handleSelectedFile(file) {
window.ZoomzillaAnalytics?.begin();
if (maxUploadBytes > 0 && file.size > maxUploadBytes) {
rejectOversizedFile(file);
return;
@@ -202,9 +204,11 @@ $basePickerAccept = 'image/jpeg,image/png,image/gif,image/webp,image/bmp,image/a
}
function redirectToTour(record) {
const azv=window.ZoomzillaAnalytics?.handoff(record.meta.analytics);
let targetUrl = record.meta.redirectBase + encodeURIComponent(record.key);
const redirectQuery = window.ZoomzillaUtmHandoff.query(record.meta, utmDigits);
if (redirectQuery) targetUrl += '&' + redirectQuery;
if(azv)targetUrl+='&azv='+encodeURIComponent(azv);
const shownTheme = window.ZoomzillaTheme.current();
targetUrl += '&shown_theme=' + encodeURIComponent(shownTheme);
window.PublicNavigationGuard.allowNavigation(() => { window.location.href = targetUrl; });
@@ -222,6 +226,7 @@ $basePickerAccept = 'image/jpeg,image/png,image/gif,image/webp,image/bmp,image/a
formData.append('original_name', file.name || 'upload');
formData.append('original_size', String(file.size || 0));
formData.append('client_key', clientKey);
window.ZoomzillaAnalytics?.append(formData,record.meta.analytics);
const xhr = new XMLHttpRequest();
xhr.open('POST', record.meta.actionUrl, true);
xhr.withCredentials = true;
@@ -230,12 +235,12 @@ $basePickerAccept = 'image/jpeg,image/png,image/gif,image/webp,image/bmp,image/a
if (attempt !== uploadAttempt || leavingUploadPage) return;
if (xhr.status >= 200 && xhr.status < 300) {
try { const result = JSON.parse(xhr.responseText); if (!result?.id) throw new Error(); await recovery.accepted({id: result.id}); redirectToTour(record); }
catch (_) { showUploadError(genericErrorMsg); }
catch (_) { showUploadError(genericErrorMsg, false, false, true, xhr); }
return;
}
showUploadError(genericErrorMsg, false, xhr.status === 408 || xhr.status >= 500);
showUploadError(genericErrorMsg, false, xhr.status === 408 || xhr.status >= 500, true, xhr);
};
xhr.onerror = () => { if (attempt === uploadAttempt && !leavingUploadPage) showUploadError(genericErrorMsg, true, true); };
xhr.onerror = () => { if (attempt === uploadAttempt && !leavingUploadPage) showUploadError(genericErrorMsg, true, true, true, xhr); };
xhr.send(formData);
} catch (_) { if (attempt === uploadAttempt && !leavingUploadPage) showUploadError(genericErrorMsg); }
}
@@ -249,7 +254,7 @@ $basePickerAccept = 'image/jpeg,image/png,image/gif,image/webp,image/bmp,image/a
const record = saved || await recovery.remember(new File([bytes], file.name, {type: file.type, lastModified: file.lastModified}), metadata());
if (attempt === uploadAttempt && !leavingUploadPage) uploadPreparedFile(file, bytes, record);
})
.catch(() => { if (attempt === uploadAttempt && !leavingUploadPage) showUploadError(genericErrorMsg); });
.catch(() => { if (attempt === uploadAttempt && !leavingUploadPage) showUploadError(genericErrorMsg, false, false, false); });
}
function uploadPreparedFile(file, bytes, record) {
const attempt = uploadAttempt;
@@ -260,6 +265,7 @@ $basePickerAccept = 'image/jpeg,image/png,image/gif,image/webp,image/bmp,image/a
const clientKey = record.key;
formData.append('file', bytes, file.name || 'upload');
formData.append('client_key', clientKey);
window.ZoomzillaAnalytics?.append(formData,record.meta.analytics);
const actionUrl = record.meta.actionUrl;
xhr.open('POST', actionUrl, true);
@@ -280,13 +286,13 @@ $basePickerAccept = 'image/jpeg,image/png,image/gif,image/webp,image/bmp,image/a
try {
response = JSON.parse(xhr.responseText);
} catch (e) {
showUploadError(genericErrorMsg);
showUploadError(genericErrorMsg, false, false, true, xhr);
return;
}
const imageId = response?.id || response?.data?.id || response?.image?.id || response?.image_id;
if (!imageId) {
showUploadError(genericErrorMsg);
showUploadError(genericErrorMsg, false, false, true, xhr);
return;
}
@@ -297,21 +303,23 @@ $basePickerAccept = 'image/jpeg,image/png,image/gif,image/webp,image/bmp,image/a
// Переход: $redirect_url_base . client_key . ($redirect_url_utm ? '&' . $redirect_url_utm : '')
try { await recovery.accepted({id: imageId}); redirectToTour(record); }
catch (_) { showUploadError(genericErrorMsg); }
catch (_) { showUploadError(genericErrorMsg, false, false, true, xhr); }
} else {
showUploadError(genericErrorMsg, false, xhr.status === 408 || xhr.status >= 500);
showUploadError(genericErrorMsg, false, xhr.status === 408 || xhr.status >= 500, true, xhr);
}
};
xhr.onerror = () => {
if (attempt === uploadAttempt && !leavingUploadPage) showUploadError(genericErrorMsg, true, true);
if (attempt === uploadAttempt && !leavingUploadPage) showUploadError(genericErrorMsg, true, true, true, xhr);
};
xhr.send(formData);
}
function showUploadError(msg, keepRecovery = false, connection = false) {
function showUploadError(msg, keepRecovery = false, connection = false, recordAnalytics = true, analyticsRequest = null) {
if (leavingUploadPage) return;
const failureProof=analyticsRequest?.getResponseHeader?.('X-Analytics-Failure');
if(recordAnalytics)window.ZoomzillaAnalytics?.emit('photo_error',{stage:'upload',reason_code:'upload_failed',...(analyticsRequest?.status>=100?{http_status:analyticsRequest.status}:{}),...(failureProof?{failure_proof:failureProof}:{})});
uploadAttempt++;
if (!keepRecovery) clearRecovery();
resetUploadPending();
@@ -333,6 +341,7 @@ $basePickerAccept = 'image/jpeg,image/png,image/gif,image/webp,image/bmp,image/a
(async () => {
try {
const saved = await recovery.load();
window.ZoomzillaAnalytics?.restore(saved?.meta?.analytics);
if (!saved) return;
if (performance.getEntriesByType('navigation')[0]?.type !== 'reload' || saved.sourceUrl !== location.href || saved.meta.actionUrl !== createApiUrl) { clearRecovery(); return; }
if (saved.result?.id) { beginUploadPending(); redirectToTour(saved); }