Merge pull request #6159 from escattone/fix-broken-image-upload-preview

Fix broken image upload preview
This commit is contained in:
Tasos Katsoulas 2024-08-05 12:11:35 +03:00 коммит произвёл GitHub
Родитель e35cb60c56 5ff084a78c
Коммит 464f34d24e
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
7 изменённых файлов: 143 добавлений и 122 удалений

Просмотреть файл

@ -28,34 +28,30 @@
{% macro submit_row(form, type) %}
<div class="upload-action sumo-button-wrap reverse-on-desktop align-full">
<input type="submit" class="sumo-button primary-button" name="upload" value="{{ _('Upload file') }}">
<input type="submit" class="sumo-button primary-button" name="upload" value="{{ _('Upload') }}">
<input type="submit" name="cancel" class="sumo-button kbox-cancel {% if form.instance.pk %} draft{% endif %}" data-action="{{ url('gallery.cancel_draft', media_type=type) }}" value="{{ _('Cancel') }}">
</div>
{% endmacro %}
{% macro image_preview(form, type, name) %}
{% set classes = 'preview ' + name %}
{% if form.instance.pk and type == 'image' %}
{% set html =
'<div class="preview-{type} {name}">
<img src="{thumbnail_url}">
</div>
<input type="submit" name="cancel" class="sumo-button button-sm draft link-btn"
data-name="{name}" data-action="{action}" value="{value}">'|fe(
type=type, name=name,
thumbnail_url=form.instance.thumbnail_url_if_set(),
action=url('gallery.cancel_draft', media_type=type),
value=_('Delete this image')) %}
{% set classes = classes + ' on' %}
{% else %}
{% set classes = classes + ' off' %}
{% if type == 'image' %}
<div class="field preview {{ name }} {% if form.instance.pk %}on{% else %}off{% endif %}">
<label>{{ _('Preview') }}</label>
<div class="preview-{{ type }} {{ name }}">
{% if form.instance.pk %}
<img class="preview" src="{{ form.instance.file.url }}" />
{% endif %}
</div>
<input type="submit" name="cancel" class="sumo-button button-sm draft link-btn"
data-name="{{ name }}" data-action="{{ url('gallery.cancel_draft', media_type=type) }}"
value="{{ _('Delete this image') }}" />
</div>
{% endif %}
{{ raw_row(html, classes, _('Preview')) }}
{% endmacro %}
{% macro image_fields(form, type, name) %}
{{ form_row(form, name, classes='upload-media') }}
{{ raw_row('<span></span><a class="{name}" href="{url}">{text}</a>'|fe(
{{ raw_row('<div class="progress-message"></div><a class="{name} sumo-button" data-type="{name}" href="{url}">{text}</a>'|fe(
name=name, text=_('Cancel'),
url=url('gallery.gallery', media_type=media_type)),
'progress ' + name, _('Progress')) }}
@ -79,11 +75,10 @@ TODO: better error messages ("invalid file" means too big, or extension, etc)
<div
class="gallery-upload-modal hide-until-expanded attachments-upload"
id="gallery-upload-modal"
title="{% if has_draft %}{{ _('Continue Uploading Media') }}{% else %}{{ _('Upload a New Media File') }}{% endif %}"
title="Upload Media File"
data-modal="true"
data-target="#btn-upload"
data-id="upload-kbox"
data-position="center"
data-max-image-size="{{ settings.IMAGE_MAX_FILESIZE }}">
<form id="gallery-upload-image" class="upload-form{{ ' draft' if image_form.instance.pk else '' }}"
data-post-url="{{ url('gallery.upload_async', media_type='image') }}"

Просмотреть файл

@ -28,8 +28,7 @@ import "sumo/js/kbox";
'invalid': gettext('Invalid image. Please select a valid image file.'),
'toolarge': gettext('Image too large. Please select a smaller image file.'),
'cancelled': gettext('Upload cancelled. Please select an image file.'),
'deleted': gettext('File deleted. Please select an image file.'),
'del': gettext('Delete this image')
'deleted': gettext('File deleted. Please select an image file.')
};
// Save all initial values of input details:
@ -94,12 +93,13 @@ import "sumo/js/kbox";
// Bind cancel upload event.
$('.progress a', self.$modal).on("click", function cancelUpload(ev) {
var type = $(this).attr('class');
var type = $(this).data('type');
ev.preventDefault();
self.cancelUpload($(this));
self.setInputError($(this).closest('.upload-form')
.find('input[name="' + type + '"]'),
'cancelled');
self.setInputMessage(
$(this).closest('.upload-form').find('input[name="' + type + '"]'),
'cancelled'
);
return false;
});
@ -225,8 +225,7 @@ import "sumo/js/kbox";
startUpload: function($input) {
var $form = $input.closest('.upload-form'),
filename = $input.val().split(/[\/\\]/).pop(),
$progress = $('.progress', $form)
.filter('.' + $input.attr('name'));
$progress = $('.progress', $form).filter(`.${$input.attr('name')}`);
// truncate filename
if (filename.length > CONSTANTS.maxFilenameLength) {
filename = filename.substr(0, CONSTANTS.maxFilenameLength) +
@ -234,7 +233,7 @@ import "sumo/js/kbox";
}
$form.find('.upload-media.' + $input.attr('name')).hideFade();
var message = interpolate(gettext('Uploading "%s"...'), [filename]);
$progress.filter('.row-right').find('span').text(message);
$progress.find('.progress-message').text(message);
$progress.showFade();
$form.find('.metadata').show();
return {filename: filename};
@ -273,11 +272,12 @@ import "sumo/js/kbox";
uploadSuccess: function($input, iframeJSON, filename) {
var type = $input.attr('name'),
$form = $input.closest('.upload-form'),
$cancel_btn = $('.upload-action input[name="cancel"]', $form),
$content, attrs = {},
$preview_area,
$cancel_btn = $('input[name="cancel"]', $form),
$content,
$previewArea,
$previewImage,
$previewImageContainer,
upFile = iframeJSON.file;
var message = CONSTANTS.messages[type].del;
// Upload is no longer in progress.
$form.find('.progress.' + type).hideFade();
@ -285,38 +285,36 @@ import "sumo/js/kbox";
// generate preview
if (type === 'file') {
// create thumbnail
$content = $('<img/>')
.attr({
alt: upFile.name, title: upFile.name,
width: upFile.width, height: upFile.height,
$content = $('<img/>').attr({
class: 'preview',
alt: upFile.name,
title: upFile.name,
src: upFile.thumbnail_url
})
.wrap('<div class="preview-' + type + '"/>').parent();
});
}
$previewArea = $(`.preview.${type}`, $form);
$previewImageContainer = $previewArea.find(".preview-image");
$previewImage = $previewImageContainer.find("img");
if ($previewImage.length) {
$previewImage.replaceWith($content);
} else {
$previewImageContainer.append($content);
}
$preview_area = $('.preview.' + type, $form);
$preview_area.filter('.row-right').html($content);
// Create cancel button.
attrs['data-action'] = $cancel_btn.data('action') +
'?field=' + type;
attrs['data-name'] = type;
$cancel_btn.clone().val(message).attr(attrs)
.removeClass('kbox-cancel')
.appendTo($preview_area.filter('.row-right'))
.makeCancelUpload();
$cancel_btn.makeCancelUpload();
// Show the preview area and make it a draft
$preview_area.showFade();
$previewArea.showFade();
$form.addClass('draft');
},
/*
* Little helper function to set an error next to the file input.
* Little helper function to display a message next to the file input.
* Takes the file $input and a reason.
*/
setInputError: function($input, reason) {
setInputMessage: function($input, reason) {
var type = $input.attr('name');
var message = CONSTANTS.messages[type][reason];
var $row_input = $('.upload-media.row-right.' + type);
$row_input.find('div.details').addClass('error')
.html(message);
var $uploadDetails = $(`.upload-media.${type}`).find('div.details');
$uploadDetails.html(message);
},
/*
* Fired if isValidFile or isTooLarge is false or server returned failure.
@ -329,7 +327,7 @@ import "sumo/js/kbox";
// Cancel existing upload.
$('.progress.' + type).find('a.' + type).trigger('click');
// Show an error message.
self.setInputError($input, reason);
self.setInputMessage($input, reason);
},
/*
* Fired when deleting an uploaded image.
@ -343,8 +341,7 @@ import "sumo/js/kbox";
$mediaForm = $input.closest('.upload-form'),
type = $input.data('name');
// Clean up all the preview and progress information.
$mediaForm.find('.preview.' + type).hideFade()
.filter('.row-right').html('');
$mediaForm.find('.preview.' + type).hideFade();
// Send ajax request over to remove file.
$.ajax({
@ -354,8 +351,10 @@ import "sumo/js/kbox";
dataType: 'json'
// Ignore the response, nothing to do.
});
self.setInputError($mediaForm.find('input[name="' + type + '"]'),
'deleted');
self.setInputMessage(
$mediaForm.find('input[name="' + type + '"]'),
'deleted'
);
self._reUpload($mediaForm, type);
},
/*
@ -366,7 +365,7 @@ import "sumo/js/kbox";
*/
cancelUpload: function($a) {
var self = this,
type = $a.attr('class'),
type = $a.data('type'),
$form = $a.closest('form');
var $input = $form.find('input[name="' + type + '"]');
var form_target = $input.closest('form').attr('target');
@ -452,10 +451,8 @@ import "sumo/js/kbox";
self.$modal.find('.metadata').hide();
// Clean up all the preview and progress information.
self.$modal.find('.progress,.preview').hideFade();
self.$modal.find('.preview.row-right').html('');
self.$modal.find('.progress.row-right span').html('');
// Show all the file inputs with default messages.
$uploads.filter('.row-right').each(function () {
$uploads.each(function () {
var $input = $(this).find('input[type="file"]'),
type = $input.attr('name');
$input.closest('form')[0].reset();
@ -478,7 +475,7 @@ import "sumo/js/kbox";
return true;
}
var kbox = $('#gallery-upload-modal').kbox({preClose: preClose});
var kbox = $('#gallery-upload-modal').kbox({preClose: preClose, position: 'none'});
// Open modal window from media page
if (document.location.hash === '#upload' ||

Просмотреть файл

@ -70,8 +70,10 @@
var TEMPLATE = (
'<div class="kbox-container">' +
'<a href="#close" class="kbox-close">&#x2716;</a>' +
'<div class="kbox-header">' +
'<div class="kbox-title"></div>' +
'<a href="#close" class="kbox-close">&#x2716;</a>' +
'</div>' +
'<div class="kbox-wrap"><div class="kbox-placeholder"/></div>' +
'</div>'
),
@ -214,8 +216,7 @@ KBox.prototype = {
}
},
setPosition: function (position) {
var self = this,
toX, toY, $parent, parentOffset, minX, minY, scrollL, scrollT;
var self = this;
if (!position) {
position = self.options.position;
}

Просмотреть файл

@ -111,10 +111,7 @@ jQuery.fn.ajaxSubmitInput = function (options) {
$form = '<form class="upload-input" action="' +
options.url + '" target="' + iframeName +
'" method="post" enctype="multipart/form-data"/>',
$iframe = $('<iframe hidden name="' + iframeName +
'" style="position:absolute;top:-9999px;" />')
//'" style="position:fixed;top:0px;width:500px;height:350px" />')
.appendTo('body'),
$iframe = $(`<iframe hidden name="${iframeName}" />`).appendTo('body'),
passJSON;
if (options.accept) {

Просмотреть файл

@ -118,7 +118,9 @@ $(function () {
ev.preventDefault();
var imgUrl = $(this).attr('href'),
image = new Image(),
html = '<div><img class="loading" /></div>',
currentPosX = window.scrollX,
currentPosY = window.scrollY,
html = '<img class="loading image-attachment" />',
kbox = new KBox(html, {
modal: true,
title: gettext('Image Attachment'),
@ -126,44 +128,20 @@ $(function () {
destroy: true,
position: 'none', // Disable automatic positioning
closeOnOutClick: true,
closeOnEsc: true
closeOnEsc: true,
preClose: function () {
window.scroll(currentPosX, currentPosY);
return true;
}
});
kbox.open();
let $img = $('#image-attachment-kbox img');
let $container = $('#image-attachment-kbox');
function setDimensions() {
// Calculate maximum dimensions based on 80% viewport size
let maxWidth = $(window).width() * 0.8;
let maxHeight = $(window).height() * 0.8;
let imgWidth = image.width;
let imgHeight = image.height;
// Calculate the aspect ratio
let aspectRatio = imgWidth / imgHeight;
// Resize the image maintaining the aspect ratio
if (imgWidth > maxWidth || imgHeight > maxHeight) {
if (imgWidth / maxWidth > imgHeight / maxHeight) {
imgWidth = maxWidth;
imgHeight = maxWidth / aspectRatio;
} else {
imgHeight = maxHeight;
imgWidth = maxHeight * aspectRatio;
}
}
$img.width(imgWidth);
$img.height(imgHeight);
$container.css({
'width': imgWidth,
'overflow': 'hidden'
});
function loadImage() {
$img.removeClass('loading').attr('src', imgUrl);
kbox.setPosition('center'); // Center the modal after resizing
window.scroll({top: 0});
}
image.onload = setDimensions;
image.onload = loadImage;
image.src = imgUrl;
});
}

Просмотреть файл

@ -6,4 +6,16 @@
.off {
display: none;
}
.preview-image {
width: 300px;
height: 300px;
max-width: 300px;
max-height: 300px;
img.preview {
max-width: 300px;
max-height: 300px;
object-fit: scale-down;
}
}
}

Просмотреть файл

@ -30,9 +30,7 @@
.kbox-close {
font-size: 22px !important;
position: absolute;
right: p.$spacing-sm;
top: 5px;
@include p.bidi(((margin, 0 p.$spacing-sm 0 0, 0 0 0 p.$spacing-sm),));
&:link,
&:visited,
@ -50,17 +48,21 @@
}
}
.kbox-title {
@include c.text-display-sm;
font-weight: bold;
color: var(--color-heading);
display: block;
margin: 0;
padding: p.$spacing-xl p.$spacing-xl 0;
.kbox-header {
display: flex;
justify-content: space-between;
.kbox-title {
@include c.text-display-sm;
font-weight: bold;
color: var(--color-heading);
margin: 0;
padding: p.$spacing-xl p.$spacing-xl 0;
}
}
.kbox-wrap {
padding: p.$spacing-xl;
padding: p.$spacing-lg;
.hide-until-expanded {
display: block;
@ -69,18 +71,57 @@
#kbox-overlay {
background: rgba(0, 0, 0, 0.85);
height: 100%;
left: 0;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 98;
}
#upload-kbox {
top: 10%;
left: 50%;
margin: 0 10px;
top: 10vh;
max-width: 580px;
width: calc(100% - 20px);
}
transform: translateX(-50%);
padding: p.$spacing-sm;
box-sizing: content-box;
width: 80%;
height: auto;
max-width: 560px;
@media screen and (max-width: p.$screen-sm) {
max-width: 96%;
}
@media screen and (max-width: p.$screen-xs) {
max-width: 96%;
}
}
#image-attachment-kbox {
top: 10%;
left: 50%;
transform: translateX(-50%);
padding: p.$spacing-sm;
box-sizing: content-box;
width: 70%;
max-width: 70%;
height: auto;
@media screen and (max-width: p.$screen-sm) {
width: 96%;
max-width: 96%;
}
@media screen and (max-width: p.$screen-xs) {
width: 96%;
max-width: 96%;
}
.kbox-wrap {
width: 100%;
height: 100%;
max-width: 100%;
max-height: 100%;
padding: p.$spacing-sm;
img.image-attachment {
object-fit: scale-down;
}
}
}