Review AB11194 — Improve media selector UX (#10157)

* set input file accept

* Use PreValue file extensions for limiting the files to be chosen in file input

* Current state for Warren to review

* This should fix up what you need Niels

* update csproj

* use empty string if fileExtensions is undefined

* public interface

* initial work

* local crops

* translations

* translation correction

* fix misspeling

* some progress

* filter media picker

* align media card grid items correctly

* responsive media cropper

* always be able to scale 3 times smallest scale

* making image cropper property editor responsive

* scroll to scale

* adjust slider look

* rearrange parts of mediaentryeditor

* test helper

* styling

* move controls inside umb-image-crop

* seperate umg-cropper-gravity styling

* corrected layout

* more ui refinement

* keep the idea of mandatory out for now.

* remove double ;

* removed testing code

* JSON Property Value Convertor now has an array of property editors to exclude

* Property Value Convertor for Media Picker 3 aka Media Picker with Local Crops

* Experimenting on best approach to retrieve local crop in razor view when iterating over picked media items

* Update ValueConvertor to use ImageCropperValue as part of the model for views as alot of existing CropUrls can then use it

* Update extension methods to take an ImageCropperValue model (localCropData)

* Forgot to update CSProj for new ValueConvertor

* New GetCropUrl @Url.GetCropUrl(crop.Alias, media.LocalCrops) as oppposed to @Url.GetCropUrl(media.LocalCrops, cropAlias:crop.Alias, useCropDimensions: true)

* Remove dupe item in CSProj

* Use a contains as an opposed to Array.IndexOf

* various corrections, SingleMode based on max 1, remove double checkerBackground, enforce validation for Crops, changed error indication

* mediapicker v3

* correct version

* fixing file ext label text color

* clipboard features for MediaPicker v3

* highlight not allowed types

* highlight trashed as an error

* Media Types Video, Sound, Document and Vector Image

* Rename to Audio and VectorGraphics

* Add (SVG) in the name for Vector Graphics

* adding CSV to Documents

* remove this commented code.

* remove this commented code

* number range should not go below 0, at-least as default until we make that configurable.

* use min not ng-min

* description for local crops

* Error/Limits highlighting reactive

* visual adjustments

* Enabling opening filtered folders + corrected select hover states

* Varous fixes to resolve issues with unit tests.

* Refactor MediaType Documents to only contain Article file type

* mark as build-in

* predefined MediaPicker3 DataTypes, renaming v2 to "old"

* set scale bar current value after min and max has been set

* added missing }

* update when focal point is dragged

* adjusted styling for Image Cropper property editor

* correcting comment

* remove todo - message for trashed media items works

* Changed parameter ordering

* Introduced new extension method on MediaWithCrops to get croppings urls in with full path

* Reintroducing Single Item Mode

* use Multiple instead of SingleMode

* renaming and adding multiple to preconfigured datatypes

* Change existing media picker to use the Clipboard type MEDIA, enabling shared functionality.

* clean up unused clipboard parts

* adjusted to new amount

* correcting test

* Fix unit test

* Move MediaWithCrops to separate file and move to Core.Models

* parseContentForPaste

* clean up

* ensure crops is an array.

* actively enable focal points, so we dont set focal points that aren't used.

* only accept files that matches file extensions from Umbraco Settings

* Cleanup

* Add references from MediaPicker3 to media

* corrections from various feedback

* remove comment

* correct wording

* use windowResizeListener

* Show Media Type Selector if there is multiple types that matches the current upload batch

* clean-up and refactoring

* auto pick option

Co-authored-by: Warren Buckley <warren@umbraco.com>
Co-authored-by: Niels Lyngsø <nsl@umbraco.com>
Co-authored-by: Mads Rasmussen <madsr@hey.com>
Co-authored-by: Andy Butland <abutland73@gmail.com>
Co-authored-by: Bjarke Berg <mail@bergmania.dk>
Co-authored-by: Sebastiaan Janssen <sebastiaan@umbraco.com>
Co-authored-by: Elitsa Marinovska <elm@umbraco.dk>
This commit is contained in:
Niels Lyngsø
2021-05-04 11:06:52 +02:00
committed by GitHub
parent ce34165e9d
commit c8a98a670c
5 changed files with 87 additions and 50 deletions

View File

@@ -20,7 +20,7 @@ TODO
angular.module("umbraco.directives")
.directive('umbFileDropzone',
function ($timeout, Upload, localizationService, umbRequestHelper, overlayService) {
function ($timeout, Upload, localizationService, umbRequestHelper, overlayService, mediaHelper, mediaTypeHelper) {
return {
restrict: 'E',
replace: true,
@@ -88,21 +88,12 @@ angular.module("umbraco.directives")
});
scope.queue = [];
}
// One allowed type
if (scope.acceptedMediatypes && scope.acceptedMediatypes.length === 1) {
// Standard setup - set alias to auto select to let the server best decide which media type to use
if (scope.acceptedMediatypes[0].alias === 'Image') {
scope.contentTypeAlias = "umbracoAutoSelect";
} else {
scope.contentTypeAlias = scope.acceptedMediatypes[0].alias;
}
// If we have Accepted Media Types, we will ask to choose Media Type, if Choose Media Type returns false, it only had one choice and therefor no reason to
if (scope.acceptedMediatypes && _requestChooseMediaTypeDialog() === false) {
scope.contentTypeAlias = "umbracoAutoSelect";
_processQueueItem();
}
// More than one, open dialog
if (scope.acceptedMediatypes && scope.acceptedMediatypes.length > 1) {
_chooseMediaType();
}
}
}
@@ -146,8 +137,8 @@ angular.module("umbraco.directives")
// set percentage property on file
file.uploadProgress = progressPercentage;
// set uploading status on file
file.uploadStatus = "uploading";
}
file.uploadStatus = "uploading";
}
})
.success(function(data, status, headers, config) {
if (data.notifications && data.notifications.length > 0) {
@@ -195,35 +186,61 @@ angular.module("umbraco.directives")
});
}
function _chooseMediaType() {
function _requestChooseMediaTypeDialog() {
const dialog = {
view: "itempicker",
filter: scope.acceptedMediatypes.length > 15,
availableItems: scope.acceptedMediatypes,
submit: function (model) {
scope.contentTypeAlias = model.selectedItem.alias;
_processQueueItem();
if (scope.acceptedMediatypes.length === 1) {
// if only one accepted type, then we wont ask to choose.
return false;
}
overlayService.close();
},
close: function () {
var uploadFileExtensions = scope.queue.map(file => mediaHelper.getFileExtension(file.name));
scope.queue.map(function (file) {
file.uploadStatus = "error";
file.serverErrorMessage = "Cannot upload this file, no mediatype selected";
scope.rejected.push(file);
});
scope.queue = [];
var filteredMediaTypes = mediaTypeHelper.getTypeAcceptingFileExtensions(scope.acceptedMediatypes, uploadFileExtensions);
overlayService.close();
}
};
var mediaTypesNotFile = filteredMediaTypes.filter(mediaType => mediaType.alias !== "File");
localizationService.localize("defaultdialogs_selectMediaType").then(value => {
dialog.title = value;
if (mediaTypesNotFile.length <= 1) {
// if only one or less accepted types when we have filtered type 'file' out, then we wont ask to choose.
return false;
}
localizationService.localizeMany(["defaultdialogs_selectMediaType", "mediaType_autoPickMediaType"]).then(function (translations) {
filteredMediaTypes.push({
alias: "umbracoAutoSelect",
name: translations[1],
icon: "icon-wand"
});
const dialog = {
view: "itempicker",
filter: filteredMediaTypes.length > 8,
availableItems: filteredMediaTypes,
submit: function (model) {
scope.contentTypeAlias = model.selectedItem.alias;
_processQueueItem();
overlayService.close();
},
close: function () {
scope.queue.map(function (file) {
file.uploadStatus = "error";
file.serverErrorMessage = "No files uploaded, no mediatype selected";
scope.rejected.push(file);
});
scope.queue = [];
overlayService.close();
}
};
dialog.title = translations[0];
overlayService.open(dialog);
});
return true;// yes, we did open the choose-media dialog, therefor we return true.
}
scope.handleFiles = function(files, event) {

View File

@@ -23,15 +23,15 @@ function mediaTypeHelper(mediaTypeResource, $q) {
getAllowedImagetypes: function (mediaId){
// TODO: This is horribly inneficient - why make one request per type!?
//This should make a call to c# to get exactly what it's looking for instead of returning every single media type and doing
//This should make a call to c# to get exactly what it's looking for instead of returning every single media type and doing
//some filtering on the client side.
//This is also called multiple times when it's not needed! Example, when launching the media picker, this will be called twice
//This is also called multiple times when it's not needed! Example, when launching the media picker, this will be called twice
//which means we'll be making at least 6 REST calls to fetch each media type
// Get All allowedTypes
return mediaTypeResource.getAllowedTypes(mediaId)
.then(function(types){
var allowedQ = types.map(function(type){
return mediaTypeResource.getById(type.id);
});
@@ -39,16 +39,8 @@ function mediaTypeHelper(mediaTypeResource, $q) {
// Get full list
return $q.all(allowedQ).then(function(fullTypes){
// Find all the media types with an Image Cropper property editor
var filteredTypes = mediaTypeHelperService.getTypeWithEditor(fullTypes, ['Umbraco.ImageCropper']);
// If there is only one media type with an Image Cropper we will return this one
if(filteredTypes.length === 1) {
return filteredTypes;
// If there is more than one Image cropper, custom media types have been added, and we return all media types with and Image cropper or UploadField
} else {
return mediaTypeHelperService.getTypeWithEditor(fullTypes, ['Umbraco.ImageCropper', 'Umbraco.UploadField']);
}
// Find all the media types with an Image Cropper or Upload Field property editor
return mediaTypeHelperService.getTypeWithEditor(fullTypes, ['Umbraco.ImageCropper', 'Umbraco.UploadField']);
});
});
@@ -68,6 +60,31 @@ function mediaTypeHelper(mediaTypeResource, $q) {
}
});
},
getTypeAcceptingFileExtensions: function (mediaTypes, fileExtensions) {
return mediaTypes.filter(mediaType => {
var uploadProperty;
mediaType.groups.forEach(group => {
var foundProperty = group.properties.find(property => property.alias === "umbracoFile");
if(foundProperty) {
uploadProperty = foundProperty;
}
});
if(uploadProperty) {
var acceptedFileExtensions;
if(uploadProperty.editor === "Umbraco.ImageCropper") {
acceptedFileExtensions = Umbraco.Sys.ServerVariables.umbracoSettings.imageFileTypes;
} else if(uploadProperty.editor === "Umbraco.UploadField") {
acceptedFileExtensions = (uploadProperty.config.fileExtensions && uploadProperty.config.fileExtensions.length > 0) ? uploadProperty.config.fileExtensions.map(x => x.value) : null;
}
if(acceptedFileExtensions && acceptedFileExtensions.length > 0) {
return fileExtensions.length === fileExtensions.filter(fileExt => acceptedFileExtensions.includes(fileExt)).length;
}
return true;
}
return false;
});
}
};

View File

@@ -343,6 +343,7 @@
<area alias="mediaType">
<key alias="copyFailed">Kopiering af medietypen fejlede</key>
<key alias="moveFailed">Flytning af medietypen fejlede</key>
<key alias="autoPickMediaType">Auto vælg</key>
</area>
<area alias="memberType">
<key alias="copyFailed">Kopiering af medlemstypen fejlede</key>

View File

@@ -362,6 +362,7 @@
<area alias="mediaType">
<key alias="copyFailed">Failed to copy media type</key>
<key alias="moveFailed">Failed to move media type</key>
<key alias="autoPickMediaType">Auto pick</key>
</area>
<area alias="memberType">
<key alias="copyFailed">Failed to copy member type</key>

View File

@@ -369,6 +369,7 @@
<area alias="mediaType">
<key alias="copyFailed">Failed to copy media type</key>
<key alias="moveFailed">Failed to move media type</key>
<key alias="autoPickMediaType">Auto pick</key>
</area>
<area alias="memberType">
<key alias="copyFailed">Failed to copy member type</key>