diff --git a/kahuna/public/js/components/gr-add-label/gr-add-label.js b/kahuna/public/js/components/gr-add-label/gr-add-label.js index e4e0dcc706..e4ef83f22f 100644 --- a/kahuna/public/js/components/gr-add-label/gr-add-label.js +++ b/kahuna/public/js/components/gr-add-label/gr-add-label.js @@ -15,8 +15,8 @@ export var addLabel = angular.module('gr.addLabel', [ ]); addLabel.controller('GrAddLabelCtrl', [ - '$window', '$q', 'labelService', 'mediaApi', - function ($window, $q, labelService, mediaApi) { + '$scope', '$window', '$q', 'labelService', 'mediaApi', + function ($scope, $window, $q, labelService, mediaApi) { let ctrl = this; @@ -42,7 +42,10 @@ addLabel.controller('GrAddLabelCtrl', [ reset(); }) .catch(saveFailed) - .finally(() => ctrl.adding = false); + .finally(() => { + ctrl.adding = false; + $scope.$apply(); + }); } diff --git a/kahuna/public/js/components/gr-image-metadata/gr-image-metadata.html b/kahuna/public/js/components/gr-image-metadata/gr-image-metadata.html index 78a58b1d5b..37bf72995c 100644 --- a/kahuna/public/js/components/gr-image-metadata/gr-image-metadata.html +++ b/kahuna/public/js/components/gr-image-metadata/gr-image-metadata.html @@ -358,8 +358,8 @@ -
People
-
+
People
+
@@ -449,22 +448,12 @@
-
  • - - {{label.data}} - -
  • + +
    @@ -494,12 +483,11 @@
    Keywords
    diff --git a/kahuna/public/js/components/gr-image-metadata/gr-image-metadata.js b/kahuna/public/js/components/gr-image-metadata/gr-image-metadata.js index e45a3b71eb..618c48179f 100644 --- a/kahuna/public/js/components/gr-image-metadata/gr-image-metadata.js +++ b/kahuna/public/js/components/gr-image-metadata/gr-image-metadata.js @@ -6,7 +6,7 @@ import template from './gr-image-metadata.html'; import '../../image/service'; import '../../edits/service'; import '../gr-description-warning/gr-description-warning'; -import { editOptions, overwrite } from '../../util/constants/editOptions'; +import { editOptions, overwrite, append } from '../../util/constants/editOptions'; import '../../services/image-accessor'; import '../../services/image-list'; import '../../services/label'; @@ -100,15 +100,39 @@ module.controller('grImageMetadataCtrl', [ ); }; - ctrl.addLabel = function (label) { - var imageArray = Array.from(ctrl.selectedImages); - labelService.batchAdd(imageArray, [label]); + ctrl.addLabelToImages = labelService.batchAdd; + ctrl.removeLabelFromImages = labelService.batchRemove; + ctrl.labelAccessor = (image) => imageAccessor.readLabels(image).map(label => label.data); + + ctrl.peopleAccessor = (image) => imageAccessor.readPeopleInImage(image); + ctrl.removePersonFromImages = (images, removedPerson) => { + images.map((image) => { + const maybeNewPeopleInImage = ctrl.peopleAccessor(image)?.filter((person) => person !== removedPerson); + const newPeopleInImage = maybeNewPeopleInImage ? maybeNewPeopleInImage : []; + editsService.batchUpdateMetadataField( + [image], + 'peopleInImage', + newPeopleInImage, + ctrl.descriptionOption + ); + }); + return Promise.resolve(ctrl.selectedImages); }; + ctrl.addPersonToImages = (images, addedPerson) => { + images.map((image) => { + const currentPeopleInImage = ctrl.peopleAccessor(image); + const newPeopleInImage = currentPeopleInImage ? [...currentPeopleInImage, addedPerson] : [addedPerson]; + editsService.batchUpdateMetadataField( + [image], + 'peopleInImage', + newPeopleInImage, + ctrl.descriptionOption + ); + }); + return Promise.resolve(ctrl.selectedImages); + } - ctrl.removeLabel = function (label) { - var imageArray = Array.from(ctrl.selectedImages); - labelService.batchRemove(imageArray, label); - }; + ctrl.keywordAccessor = (image) => imageAccessor.readMetadata(image).keywords; const ignoredMetadata = [ 'title', 'description', 'copyright', 'keywords', 'byline', diff --git a/kahuna/public/js/edits/image-editor.html b/kahuna/public/js/edits/image-editor.html index 34fe919bb2..b7d251e0e9 100644 --- a/kahuna/public/js/edits/image-editor.html +++ b/kahuna/public/js/edits/image-editor.html @@ -207,10 +207,13 @@

    Organisation and Grouping

    active="ctrl.inputtingLabel" class="result-editor__field-container__add-button"> - - + + diff --git a/kahuna/public/js/edits/image-editor.js b/kahuna/public/js/edits/image-editor.js index 44c92112e9..5b65cd1324 100644 --- a/kahuna/public/js/edits/image-editor.js +++ b/kahuna/public/js/edits/image-editor.js @@ -4,16 +4,22 @@ import './image-editor.css'; import {service} from './service'; import {imageService} from '../image/service'; +import '../services/label'; +import {imageAccessor} from '../services/image-accessor' import {usageRightsEditor} from '../usage-rights/usage-rights-editor'; import {leases} from '../leases/leases'; 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 {List} from 'immutable'; + export var imageEditor = angular.module('kahuna.edits.imageEditor', [ service.name, imageService.name, + "kahuna.services.label", + imageAccessor.name, usageRightsEditor.name, archiver.name, collectionsApi.name, @@ -28,6 +34,8 @@ imageEditor.controller('ImageEditorCtrl', [ 'editsService', 'editsApi', 'imageService', + 'labelService', + 'imageAccessor', 'collections', function($rootScope, @@ -36,6 +44,8 @@ imageEditor.controller('ImageEditorCtrl', [ editsService, editsApi, imageService, + labelService, + imageAccessor, collections) { var ctrl = this; @@ -56,6 +66,11 @@ imageEditor.controller('ImageEditorCtrl', [ ctrl.invalidReasons = ctrl.image.data.invalidReasons; ctrl.systemName = window._clientConfig.systemName; + ctrl.addLabelToImages = labelService.batchAdd; + ctrl.removeLabelFromImages = labelService.batchRemove; + ctrl.labelAccessor = (image) => imageAccessor.readLabels(image).map(label => label.data); + ctrl.labelSref = (element) => {debugger; return search.results({query:(element | queryLabelFilter)})}; + //TODO put collections in their own directive ctrl.addCollection = false; ctrl.addToCollection = addToCollection; diff --git a/kahuna/public/js/edits/index.js b/kahuna/public/js/edits/index.js index 95040f8f08..6913ff8adf 100644 --- a/kahuna/public/js/edits/index.js +++ b/kahuna/public/js/edits/index.js @@ -1,7 +1,7 @@ import angular from 'angular'; -import './labeller'; +import './list-editor'; export var edits = angular.module('kahuna.edits', [ - 'kahuna.edits.labeller' + 'kahuna.edits.listEditor' ]); diff --git a/kahuna/public/js/edits/list-editor-compact.html b/kahuna/public/js/edits/list-editor-compact.html new file mode 100644 index 0000000000..e2c59cdc86 --- /dev/null +++ b/kahuna/public/js/edits/list-editor-compact.html @@ -0,0 +1,16 @@ +
    + +
    diff --git a/kahuna/public/js/edits/list-editor-info-panel.html b/kahuna/public/js/edits/list-editor-info-panel.html new file mode 100644 index 0000000000..3ff63cd2d8 --- /dev/null +++ b/kahuna/public/js/edits/list-editor-info-panel.html @@ -0,0 +1,16 @@ +
  • + + {{element.data}} + +
  • diff --git a/kahuna/public/js/edits/list-editor-upload.html b/kahuna/public/js/edits/list-editor-upload.html new file mode 100644 index 0000000000..0928275c26 --- /dev/null +++ b/kahuna/public/js/edits/list-editor-upload.html @@ -0,0 +1,32 @@ +
    + + + + + + + +
    diff --git a/kahuna/public/js/edits/list-editor.css b/kahuna/public/js/edits/list-editor.css new file mode 100644 index 0000000000..0bc5766fee --- /dev/null +++ b/kahuna/public/js/edits/list-editor.css @@ -0,0 +1,93 @@ +ui-list-editor, +ui-list-editor-compact { + display: block; +} + +.labeller { + font-size: 1.3rem; + display: flex; +} + +.element { + color: white; + display: inline-flex; + justify-content: space-between; + margin: 0 5px 5px 0; +} + +.element--removing { + background-color: #99ddff; +} + +/* +element--partial is part of gr-panel. +*/ +.element--partial .element__value, +.element--partial .element__add, +.element--partial .element__remove { + background-color: white; + color: #00adee; +} + +.element__value, +.element__remove { + color: white; + background-color: #00adee; +} + +.element__link:hover, +.element__remove:hover { + color: #00adee; + background-color: white; +} + +.element__add, +.element__value { + border-top-left-radius: 2px; + border-bottom-left-radius: 2px; +} + +.element--partial .element__value { + border-radius: 0px; +} + +.element__value { + padding: 0 5px; +} + +.element__value--compact { + border-radius: 2px; +} + +.element__remove { + border-top-right-radius: 2px; + border-bottom-right-radius: 2px; +} + +.element button:hover gr-icon { + color: #333; +} + +.element button:active gr-icon { + background-color: #008fc5; + color: #333; +} + +.element--partial button:active gr-icon { + background-color: #CFCFCF; + color: #333; +} + +.labeller__apply-all { + margin-left: 10px; + line-height: 20px; + font-size: 1.6rem; +} + +.labeller__apply-all:hover { + color: #FFF; +} + +.image-info__keyword .element__value { + background-color: #222; +} \ No newline at end of file diff --git a/kahuna/public/js/edits/list-editor.js b/kahuna/public/js/edits/list-editor.js new file mode 100644 index 0000000000..c0b3909f9a --- /dev/null +++ b/kahuna/public/js/edits/list-editor.js @@ -0,0 +1,176 @@ +import angular from 'angular'; +import templateUpload from './list-editor-upload.html'; +import templateCompact from './list-editor-compact.html'; +import templateInfoPanel from './list-editor-info-panel.html'; +import {List} from 'immutable'; +import './list-editor.css'; +import '../services/image-list'; + +import '../search/query-filter'; + +export var listEditor = angular.module('kahuna.edits.listEditor', [ + 'kahuna.search.filters.query', + 'kahuna.services.image-logic', +]); + +listEditor.controller('ListEditorCtrl', [ + '$rootScope', + '$scope', + '$window', + '$timeout', + 'imageLogic', + 'imageList', + function($rootScope, + $scope, + $window, + $timeout, + imageLogic, + imageList) { + var ctrl = this; +// debugger; +// ctrl.sref("test-sref") + + + const retrieveElementsWithOccurrences = (images) => imageList.getOccurrences(images.flatMap(img => ctrl.accessor(img))); + + $scope.$watchCollection('ctrl.images', updatedImages => updateHandler(updatedImages)); + + const updateHandler = (maybeUpdatedImages) => { + const updatedImages = ctrl.images.map(img => maybeUpdatedImages.find(x => imageLogic.isSameImage(x, img)) || img); + ctrl.listWithOccurrences = retrieveElementsWithOccurrences(updatedImages); + ctrl.plainList = ctrl.listWithOccurrences.map(x => x.data); + + }; + + const updateListener = $rootScope.$on('images-updated', (e, updatedImages) => { + updateHandler(updatedImages); + }); + + ctrl.listWithOccurrences = retrieveElementsWithOccurrences(ctrl.images); + ctrl.plainList = ctrl.listWithOccurrences.map(x => x.data); + + function saveFailed(e) { + console.error(e); + $window.alert('Something went wrong when saving, please try again!'); + } + + ctrl.addElements = elements => { + ctrl.adding = true; + + ctrl.addToImages(ctrl.images, elements) + .then(imgs => { + updateHandler(imgs); + }) + .catch(saveFailed) + .finally(() => { + ctrl.adding = false; + }); + }; + + ctrl.elementsBeingRemoved = new Set(); + ctrl.removeElement = element => { + ctrl.elementsBeingRemoved.add(element); + + ctrl.removeFromImages(ctrl.images, element) + .then(imgs => { + updateHandler(imgs); + }) + .catch(saveFailed) + .finally(() => { + ctrl.elementsBeingRemoved.delete(element); + }); + }; + + ctrl.removeAll = () => { + ctrl.plainList.forEach(element => ctrl.removeFromImages(ctrl.images, element)); + }; + + const batchAddEvent = 'events:batch-apply:add-all'; + const batchRemoveEvent = 'events:batch-apply:remove-all'; + + if (Boolean(ctrl.withBatch)) { + $scope.$on(batchAddEvent, (e, elements) => ctrl.addElements(elements)); + $scope.$on(batchRemoveEvent, () => ctrl.removeAll()); + + ctrl.batchApply = () => { + var elements = ctrl.plainList; + + if (elements.length > 0) { + $rootScope.$broadcast(batchAddEvent, elements); + } else { + ctrl.confirmDelete = true; + + $timeout(() => { + ctrl.confirmDelete = false; + }, 5000); + } + }; + + ctrl.batchRemove = () => { + ctrl.confirmDelete = false; + $rootScope.$broadcast(batchRemoveEvent); + }; + } + + $scope.$on('$destroy', function() { + updateListener(); + }); +}]); + +listEditor.directive('uiListEditorUpload', [function() { + return { + restrict: 'E', + scope: { + // Annoying that we can't make a uni-directional binding + // as we don't really want to modify the original + images: '<', + withBatch: '=?', + addToImages: '=', + removeFromImages: '=', + accessor: '=' + }, + controller: 'ListEditorCtrl', + controllerAs: 'ctrl', + bindToController: true, + template: templateUpload + }; +}]); + +listEditor.directive('uiListEditorCompact', [function() { + return { + restrict: 'E', + scope: { + // Annoying that we can't make a uni-directional binding + // as we don't really want to modify the original + images: '<', + disabled: '=', + removeFromImages: '=', + accessor: '=', + sref: '=', + element: '=' + }, + controller: 'ListEditorCtrl', + controllerAs: 'ctrl', + bindToController: true, + template: templateCompact + }; +}]); + +listEditor.directive('uiListEditorInfoPanel', [function() { + return { + restrict: 'E', + scope: { + // Annoying that we can't make a uni-directional binding + // as we don't really want to modify the original + images: '<', + disabled: '<', + addToImages: '<', + removeFromImages: '<', + accessor: '<' + }, + controller: 'ListEditorCtrl', + controllerAs: 'ctrl', + bindToController: true, + template: templateInfoPanel + }; +}]); diff --git a/kahuna/public/js/preview/image-large.html b/kahuna/public/js/preview/image-large.html index 25a7e04e49..c47fe86556 100644 --- a/kahuna/public/js/preview/image-large.html +++ b/kahuna/public/js/preview/image-large.html @@ -70,15 +70,20 @@ - - + ng-if="!ctrl.inputtingLabel" + add-to-images="ctrl.addLabelToImages" + remove-from-images="ctrl.removeLabelFromImages" + element="ctrl.xyz" + accessor="ctrl.labelAccessor" + sref="ctrl.labelSref"> + diff --git a/kahuna/public/js/preview/image.html b/kahuna/public/js/preview/image.html index 433ec5e8cd..02dd191adc 100644 --- a/kahuna/public/js/preview/image.html +++ b/kahuna/public/js/preview/image.html @@ -62,15 +62,19 @@ - - + ng-if="!ctrl.inputtingLabel" + add-to-images="ctrl.addLabelToImages" + remove-from-images="ctrl.removeLabelFromImages" + accessor="ctrl.labelAccessor" + sref="ctrl.labelSref"> + diff --git a/kahuna/public/js/preview/image.js b/kahuna/public/js/preview/image.js index 5ce7f15fd4..0d8ed95f0d 100644 --- a/kahuna/public/js/preview/image.js +++ b/kahuna/public/js/preview/image.js @@ -6,16 +6,24 @@ import '../util/rx'; import template from './image.html'; import templateLarge from './image-large.html'; +import '../search/query-filter'; + +import {List} from 'immutable'; import '../image/service'; import '../imgops/service'; import '../services/image/usages'; +import '../services/label'; +import '../services/image-accessor'; import '../components/gr-add-label/gr-add-label'; import '../components/gr-archiver-status/gr-archiver-status'; import '../components/gr-syndication-icon/gr-syndication-icon'; export var image = angular.module('kahuna.preview.image', [ + 'kahuna.search.filters.query', 'gr.image.service', 'gr.image-usages.service', + 'kahuna.services.label', + 'kahuna.services.image-accessor', 'gr.addLabel', 'gr.archiverStatus', 'gr.syndicationIcon', @@ -30,19 +38,40 @@ image.controller('uiPreviewImageCtrl', [ '$window', 'imageService', 'imageUsagesService', + 'labelService', + 'imageAccessor', function ( $scope, inject$, $rootScope, $window, imageService, - imageUsagesService) { + imageUsagesService, + labelService, + imageAccessor) { var ctrl = this; + $scope.$watch(() => ctrl.image, (newImage) => { + ctrl.imageAsArray = [newImage]; + }) + + ctrl.addLabelToImages = labelService.batchAdd; + ctrl.removeLabelFromImages = labelService.batchRemove; + ctrl.labelAccessor = (image) => imageAccessor.readLabels(image).map(label => label.data); + ctrl.labelSref = (element) => { + console.log("***********image.js ctrl.labelSref *************") + console.log(element) +// debugger; + const result = `(${element} | queryLabelFilter)` + return result + }; + ctrl.imageAsArray = [ctrl.image]; + const updateImage = (updatedImage) => { ctrl.states = imageService(updatedImage).states; ctrl.image = updatedImage; ctrl.flagState = ctrl.states.costState; + ctrl.imageAsArray = [updatedImage]; }; const freeImagesUpdateListener = $rootScope.$on('images-updated', (e, updatedImages) => { diff --git a/kahuna/public/js/search/query-filter.js b/kahuna/public/js/search/query-filter.js index a9ff57c3a2..200525c902 100644 --- a/kahuna/public/js/search/query-filter.js +++ b/kahuna/public/js/search/query-filter.js @@ -3,7 +3,7 @@ import angular from 'angular'; import {getCollection} from '../search-query/query-syntax'; export var queryFilters = angular.module('kahuna.search.filters.query', []); - +debugger; var containsSpace = s => / /.test(s); var stripDoubleQuotes = s => s.replace(/"/g, ''); @@ -14,7 +14,6 @@ export function maybeQuoted(value) { return value; } } - export function fieldFilter(field, value) { const cleanValue = stripDoubleQuotes(value); const valueMaybeQuoted = maybeQuoted(cleanValue); @@ -22,10 +21,12 @@ export function fieldFilter(field, value) { } queryFilters.filter('queryFilter', function() { + debugger; return (value, field) => fieldFilter(field, value); }); queryFilters.filter('queryLabelFilter', function() { + debugger; return (value) => { const cleanValue = stripDoubleQuotes(value); if (containsSpace(cleanValue)) { diff --git a/kahuna/public/js/services/image-accessor.js b/kahuna/public/js/services/image-accessor.js index ba833deee2..b226600da8 100644 --- a/kahuna/public/js/services/image-accessor.js +++ b/kahuna/public/js/services/image-accessor.js @@ -15,6 +15,9 @@ imageAccessor.factory('imageAccessor', function() { /* == Readers == (return data) */ + function readId(image) { + return image.data.id; + } function readCost(image) { return image.data.cost; @@ -25,6 +28,10 @@ imageAccessor.factory('imageAccessor', function() { return userMetadata.data.labels.data; } + function readPeopleInImage(image) { + return image.data.metadata.peopleInImage; + } + function readLeases(image) { return image.data.leases.data; } @@ -80,6 +87,7 @@ imageAccessor.factory('imageAccessor', function() { } return { + readId, readCost, readLabels, readLeases, @@ -87,6 +95,7 @@ imageAccessor.factory('imageAccessor', function() { readExtraInfo, readUsageRights, readPersistedReasons, + readPeopleInImage, isPersisted, isArchived, readCollections, diff --git a/kahuna/public/js/services/image-logic.js b/kahuna/public/js/services/image-logic.js index d39d1d0dbe..eebb38fe09 100644 --- a/kahuna/public/js/services/image-logic.js +++ b/kahuna/public/js/services/image-logic.js @@ -11,6 +11,10 @@ export const imageLogic = angular.module('kahuna.services.image-logic', [ */ imageLogic.factory('imageLogic', ['imageAccessor', function(imageAccessor) { + function isSameImage(image1, image2) { + return imageAccessor.readId(image1) === imageAccessor.readId(image2); + } + function canBeDeleted(image) { return image.getAction('delete').then(action => !! action); } @@ -101,6 +105,7 @@ imageLogic.factory('imageLogic', ['imageAccessor', function(imageAccessor) { } return { + isSameImage, canBeDeleted, canBeArchived, getArchivedState, diff --git a/kahuna/public/stylesheets/main.css b/kahuna/public/stylesheets/main.css index 0d828d8fdb..eb947ef1d6 100644 --- a/kahuna/public/stylesheets/main.css +++ b/kahuna/public/stylesheets/main.css @@ -1696,7 +1696,7 @@ FIXME: what to do with touch devices float: left; } -.image-info__keyword { +.image-info__keyword li { display: inline-block; background-color: #222; color: #999; @@ -1707,7 +1707,7 @@ FIXME: what to do with touch devices font-size: 1.3rem; } -.image-info__keyword a { +.image-info__keyword li a { color: inherit; } @@ -2413,7 +2413,7 @@ FIXME: what to do with touch devices .image-info__description, .image-info__special-instructions, .label__value, - .image-info__keyword { + .image-info__keyword li { color: black !important; } @@ -2434,7 +2434,7 @@ FIXME: what to do with touch devices width: auto; } - .image-info__description, .image-info__keyword { + .image-info__description, .image-info__keyword li { max-height: 80px; overflow: hidden; }