Skip to content

Commit

Permalink
Merge pull request #4139 from bbc/1443-add-keywords-uploadpage
Browse files Browse the repository at this point in the history
  • Loading branch information
twrichards authored Oct 9, 2023
2 parents 3d3a456 + c745b8f commit 112cd84
Show file tree
Hide file tree
Showing 10 changed files with 375 additions and 15 deletions.
30 changes: 30 additions & 0 deletions kahuna/public/js/components/gr-add-keyword/gr-add-keyword.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
gr-add-keyword{
display: flex;
height: 25px;
margin-bottom: 2px;
}

gr-add-keyword:hover {
color: white;
}

gr-add-keyword .small {
border: none;
padding: 0;
}

.gr-add-keyword__form {
display: flex;
}

.gr-add-keyword__form__input {
width: 370px;
}

.gr-add-keyword__form__buttons {
display: flex;
}

.gr-add-keyword__form__buttons__button-cancel, .gr-add-keyword__form__buttons__button-save{
margin-left: 5px;
}
51 changes: 51 additions & 0 deletions kahuna/public/js/components/gr-add-keyword/gr-add-keyword.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<button data-cy="it-add-keyword-button"
ng-class="{'small': ctrl.grSmall}"
ng-click="ctrl.active = true"
ng-disabled="ctrl.adding"
ng-if="!ctrl.active"
gr-tooltip="Add keywords"
gr-tooltip-position="left"
aria-label="Add keywords to image">

<gr-icon ng-class="{'spin': ctrl.adding}">add_box</gr-icon>
<span>
<span ng-show="ctrl.adding">Adding...</span>
</span>
</button>
<form class="gr-add-keyword__form"
ng-if="ctrl.active"
ng-submit="ctrl.save()">
<gr-datalist
class="job-info--editor__input"
gr-search="ctrl.keywordSearch(q)">

<input data-cy="keyword-input"
type="text"
class="text-input gr-add-keyword__form__input show-no-error"
placeholder="keyword1, keyword2, keyword3…"
gr-datalist-input
gr-datalist-input-on-cancel="ctrl.cancel()"
gr-datalist-input-selector="ctrl.selectLastKeyword($value)"
gr-datalist-input-updater="ctrl.keywordAppend($currentValue, $selectedValue)"
gr-auto-focus
required
ng-model="ctrl.newKeyword"
ng-disabled="ctrl.adding" />
</gr-datalist>

<span class="gr-add-keyword__form__buttons">
<button class="gr-add-keyword__form__buttons__button-cancel button-cancel" type="button" ng-click="ctrl.cancel()" title="Close">
<gr-icon-label gr-icon="close"><span ng-hide="ctrl.grSmall">Cancel</span></gr-icon-label>
</button>
<button
data-cy="save-new-keyword-button"
class="gr-add-keyword__form__buttons__button-save button-save"
type="submit"
title="Save new keyword"
ng-disabled="ctrl.adding">
<gr-icon-label gr-icon="check">
<span ng-hide="ctrl.grSmall">Save</span>
</gr-icon-label>
</button>
</span>
</form>
129 changes: 129 additions & 0 deletions kahuna/public/js/components/gr-add-keyword/gr-add-keyword.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import angular from 'angular';

import '../../services/image-accessor';
import '../../edits/service';
import '../../forms/datalist';

import './gr-add-keyword.css';
import template from './gr-add-keyword.html';

import '../../directives/gr-auto-focus';
import {overwrite} from "../../util/constants/editOptions";

export var addKeyword = angular.module('gr.addKeyword', [
'gr.image.service',
'kahuna.edits.service',
'gr.autoFocus',
'kahuna.forms.datalist'
]);

addKeyword.controller('GrAddKeywordCtrl', [
'$window', '$scope', '$q', 'imageAccessor', 'editsService',
function ($window, $scope, $q, imageAccessor, editsService) {

let ctrl = this;
ctrl.active = false;
ctrl.descriptionOption = overwrite.key;

const updateImages = (images, metadataFieldName, valueFn) => {
let uptodateImages = [];
images.map((image) => {
editsService.batchUpdateMetadataField(
[image],
metadataFieldName,
valueFn(image),
ctrl.descriptionOption
);

//-ensure metadata in image is up-to-date-
let tmpImages = $scope.$parent.ctrl.imageAsArray.filter(img => img.uri == image.uri);
if (tmpImages.length > 0) {
let uptodateImage = tmpImages[0];
uptodateImage.data.metadata.keywords = valueFn(image);
uptodateImages.push(uptodateImage);
}
});

return Promise.resolve(uptodateImages);
};

const addXToImages = (metadataFieldName, accessor) => (images, addedX) => {
return updateImages(
images,
metadataFieldName,
(image) => {
const currentXInImage = accessor(image);
return currentXInImage ? [...currentXInImage, ...addedX] : [...addedX];
}
);
};

ctrl.keywordAccessor = (image) => imageAccessor.readMetadata(image).keywords;
ctrl.addKeywordToImages = addXToImages('keywords', ctrl.keywordAccessor);

ctrl.save = () => {
let keywordList = ctrl.newKeyword.split(',').map(e => e.trim());
let imageArray = $scope.$parent.ctrl.imageAsArray;

if (keywordList) {
save(keywordList, imageArray);
}
};

ctrl.cancel = reset;

function save(keyword, imageArray) {
ctrl.adding = true;
ctrl.active = false;
ctrl.addKeywordToImages(imageArray, keyword)
.then(() => {
reset();
})
.catch(saveFailed)
.finally(() => ctrl.adding = false);
}

function saveFailed(e) {
console.error(e);
$window.alert('Something went wrong when saving, please try again!');
ctrl.active = true;
}

function reset() {
ctrl.newKeyword = '';
ctrl.active = false;
}

ctrl.keywordSearch = (q) => {
//-current search always resolves to empty but retained as possible extension point-
let a = q ? [] : [];
return $q.resolve(a);
};

ctrl.keywordAppend = (currentVal, selectedVal) => {
const beforeLastComma = currentVal.split(/, ?/).slice(0, -1);
const fullText = beforeLastComma.concat(selectedVal);
return fullText.join(', ');
};

ctrl.selectLastKeyword = (value) => {
const afterComma = value.split(',').slice(-1)[0].trim();
return afterComma;
};

}
]);

addKeyword.directive('grAddKeyword', [function () {
return {
restrict: 'E',
scope: {
grSmall: '=?',
active: '=?'
},
controller: 'GrAddKeywordCtrl',
controllerAs: 'ctrl',
bindToController: true,
template: template
};
}]);
27 changes: 27 additions & 0 deletions kahuna/public/js/edits/image-editor.html
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,7 @@ <h1>Organisation and Grouping</h1>
with-batch="ctrl.withBatch"
add-to-images="ctrl.addLabelToImages"
remove-from-images="ctrl.removeLabelFromImages"
remove-as-array="false"
accessor="ctrl.labelAccessor"
query-filter="queryLabelFilter"
element-name="label"
Expand All @@ -278,6 +279,32 @@ <h1>Organisation and Grouping</h1>
</div>
</div>

<div class="result-editor__field-container flex-container flex-center">
<span class="result-editor__field-label flex-no-shrink text-small">
Keywords
</span>
<div class="result-editor__field-container__labels" ng-class="{'result-editor__field-container__labels--hidden' : ctrl.inputtingKeyword}">
<gr-add-keyword
images="[ctrl.image]"
active="ctrl.inputtingKeyword"
class="result-editor__field-container__add-button">
</gr-add-keyword>
<dd class="image-info__pills">
<ui-list-editor-upload
images="ctrl.imageAsArray"
with-batch="ctrl.withBatch"
add-to-images="ctrl.addKeywordToImages"
remove-from-images="ctrl.removeKeywordFromImages"
remove-as-array="true"
accessor="ctrl.keywordAccessor"
query-filter="queryFilter:'keyword'"
element-name="keyword"
element-name-plural="keywords">
</ui-list-editor-upload>
</dd>
</div>
</div>

<div class="result-editor__field-container flex-container flex-center">
<span class="result-editor__field-label flex-no-shrink text-small">
Photoshoot
Expand Down
73 changes: 71 additions & 2 deletions kahuna/public/js/edits/image-editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {archiver} from '../components/gr-archiver-status/gr-archiver-status';
import {collectionsApi} from '../services/api/collections-api';
import {rememberScrollTop} from '../directives/gr-remember-scroll-top';
import '../util/storage';
import {overwrite} from "../util/constants/editOptions";

export var imageEditor = angular.module('kahuna.edits.imageEditor', [
service.name,
Expand Down Expand Up @@ -56,6 +57,7 @@ imageEditor.controller('ImageEditorCtrl', [
var ctrl = this;
ctrl.canUndelete = false;
ctrl.isDeleted = false;

ctrl.displayMetadataTemplates = window._clientConfig.metadataTemplates !== undefined && window._clientConfig.metadataTemplates.length > 0;

ctrl.$onInit = () => {
Expand All @@ -79,15 +81,82 @@ imageEditor.controller('ImageEditorCtrl', [
ctrl.usageRights = imageService(ctrl.image).usageRights;
ctrl.invalidReasons = ctrl.image.data.invalidReasons;
ctrl.systemName = window._clientConfig.systemName;
ctrl.descriptionOption = overwrite.key;

ctrl.undelete = undelete;

ctrl.imageAsArray = [ctrl.image];

ctrl.addLabelToImages = labelService.batchAdd;
ctrl.removeLabelFromImages = labelService.batchRemove;
const updateImages = (images, metadataFieldName, valueFn) => {
images.map((image) => {
editsService.batchUpdateMetadataField(
[image],
metadataFieldName,
valueFn(image),
ctrl.descriptionOption
);
});
return Promise.resolve(ctrl.imageAsArray);
};

const removeXFromImages = (metadataFieldName, accessor) => (images, removedX, fieldName) => {
if (fieldName && fieldName !== metadataFieldName) {
return Promise.resolve(ctrl.imageAsArray);
}

var removedArr = Array.isArray(removedX) ? removedX : [removedX];
return updateImages(
images,
metadataFieldName,
(image) => accessor(image)?.filter((x) => !removedArr.includes(x)) || []
);
};

const addXToImages = (metadataFieldName, accessor) => (images, addedX, fieldName, removedElements = []) => {
if (fieldName && fieldName !== metadataFieldName) {
return Promise.resolve(ctrl.imageAsArray);
}

return updateImages(
images,
metadataFieldName,
(image) => {
const currentXInImage = accessor(image);
let tempElements = currentXInImage ? [...currentXInImage, ...addedX] : [...addedX];
tempElements = tempElements.filter(e => !removedElements.includes(e));
return tempElements;
}
);
};

//-labels-
function addLabelToImagesFn(images, addedX, fieldName, removedElements = []) {
if (fieldName && fieldName !== "labels") {
return Promise.resolve(ctrl.imageAsArray);
}

if (removedElements.length > 0) {
removedElements.forEach(element => labelService.batchRemove(images, element));
}
return labelService.batchAdd(images, addedX);
}

function removeLabelFromImagesFn(images, removedX, fieldName) {
if (fieldName && fieldName !== "labels") {
return Promise.resolve(ctrl.imageAsArray);
}
return labelService.batchRemove(images, removedX);
}

ctrl.addLabelToImages = addLabelToImagesFn;
ctrl.removeLabelFromImages = removeLabelFromImagesFn;
ctrl.labelAccessor = (image) => imageAccessor.readLabels(image).map(label => label.data);

//-keywords-
ctrl.keywordAccessor = (image) => imageAccessor.readMetadata(image).keywords;
ctrl.addKeywordToImages = addXToImages('keywords', ctrl.keywordAccessor);
ctrl.removeKeywordFromImages = removeXFromImages('keywords', ctrl.keywordAccessor);

//TODO put collections in their own directive
ctrl.addCollection = false;
ctrl.addToCollection = addToCollection;
Expand Down
4 changes: 2 additions & 2 deletions kahuna/public/js/edits/list-editor-upload.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
<button
title="Apply these {{ctrl.elementNamePlural}} to all your current uploads"
ng-if="!ctrl.confirmDelete"
ng-click="ctrl.batchApply()"
ng-click="ctrl.batchApply(ctrl.elementNamePlural)"
></button>

<button title="Remove ALL {{ctrl.elementNamePlural}}"
class="button button--confirm-delete"
ng-if="ctrl.confirmDelete"
ng-click="ctrl.batchRemove()">
ng-click="ctrl.batchRemove(ctrl.elementNamePlural)">
<gr-icon>warning</gr-icon>Remove ALL {{ctrl.elementNamePlural}} in job?
</button>
</span>
Expand Down
Loading

0 comments on commit 112cd84

Please sign in to comment.