diff --git a/contentcuration/contentcuration/frontend/channelEdit/components/ContentNodeEditListItem.vue b/contentcuration/contentcuration/frontend/channelEdit/components/ContentNodeEditListItem.vue index 3e57f48587..8ed1adce13 100644 --- a/contentcuration/contentcuration/frontend/channelEdit/components/ContentNodeEditListItem.vue +++ b/contentcuration/contentcuration/frontend/channelEdit/components/ContentNodeEditListItem.vue @@ -64,6 +64,22 @@ + + + + + + + + + + - import { mapGetters } from 'vuex'; + import { mapGetters, mapActions } from 'vuex'; import ContentNodeListItem from './ContentNodeListItem'; import ContentNodeOptions from './ContentNodeOptions'; @@ -89,9 +105,11 @@ import Checkbox from 'shared/views/form/Checkbox'; import IconButton from 'shared/views/IconButton'; import DraggableItem from 'shared/views/draggable/DraggableItem'; - import { COPYING_FLAG } from 'shared/data/constants'; + import { ContentNode } from 'shared/data/resources'; import { DragEffect, DropEffect, EffectAllowed } from 'shared/mixins/draggable/constants'; import { DraggableRegions } from 'frontend/channelEdit/constants'; + import { withChangeTracker } from 'shared/data/changes'; + import { COPYING_STATUS, COPYING_STATUS_VALUES } from 'shared/data/constants'; export default { name: 'ContentNodeEditListItem', @@ -141,7 +159,11 @@ }, computed: { ...mapGetters('currentChannel', ['canEdit']), - ...mapGetters('contentNode', ['getContentNode']), + ...mapGetters('contentNode', [ + 'getContentNode', + 'isNodeInCopyingState', + 'hasNodeCopyingErrored', + ]), ...mapGetters('draggable', ['activeDraggableRegionId']), selected: { get() { @@ -163,7 +185,10 @@ return !this.copying; }, copying() { - return this.contentNode[COPYING_FLAG]; + return this.isNodeInCopyingState(this.nodeId); + }, + hasCopyingErrored() { + return this.hasNodeCopyingErrored(this.nodeId); }, dragEffect() { return DragEffect.SORT; @@ -206,18 +231,57 @@ this.selected = false; } }, + methods: { + ...mapActions(['showSnackbar', 'clearSnackbar']), + ...mapActions('contentNode', [ + 'updateContentNode', + 'waitForCopyingStatus', + 'deleteContentNode', + ]), + retryFailedCopy: withChangeTracker(function(changeTracker) { + this.updateContentNode({ + id: this.nodeId, + [COPYING_STATUS]: COPYING_STATUS_VALUES.COPYING, + }); + + this.showSnackbar({ + duration: null, + text: this.$tr('creatingCopies'), + // TODO: determine how to cancel copying while it's in progress, + // TODO: if that's something we want + // actionText: this.$tr('cancel'), + // actionCallback: () => changeTracker.revert(), + }); + + ContentNode.retryCopyChange(this.nodeId); + + return this.waitForCopyingStatus({ + contentNodeId: this.nodeId, + startingRev: changeTracker._startingRev, + }) + .then(() => { + this.showSnackbar({ + text: this.$tr('copiedSnackbar'), + actionText: this.$tr('undo'), + actionCallback: () => changeTracker.revert(), + }).then(() => changeTracker.cleanUp()); + }) + .catch(() => { + this.clearSnackbar(); + changeTracker.cleanUp(); + }); + }), + removeFailedCopyNode() { + return this.deleteContentNode(this.nodeId); + }, + }, $trs: { optionsTooltip: 'Options', - /* eslint-disable kolibri/vue-no-unused-translations */ - /** - * Strings for handling copy failures - */ removeNode: 'Remove', retryCopy: 'Retry', creatingCopies: 'Copying...', copiedSnackbar: 'Copy operation complete', undo: 'Undo', - /* eslint-enable kolibri/vue-no-unused-translations */ }, }; @@ -298,4 +362,8 @@ justify-content: center; } + .copy-retry-btn { + font-size: inherit; + } + diff --git a/contentcuration/contentcuration/frontend/channelEdit/components/ContentNodeListItem/index.spec.js b/contentcuration/contentcuration/frontend/channelEdit/components/ContentNodeListItem/index.spec.js index 54e1547717..89c7f526fb 100644 --- a/contentcuration/contentcuration/frontend/channelEdit/components/ContentNodeListItem/index.spec.js +++ b/contentcuration/contentcuration/frontend/channelEdit/components/ContentNodeListItem/index.spec.js @@ -30,7 +30,16 @@ const TOPIC_NODE = { function mountComponent(opts = {}) { return mount(ContentNodeListItem, { - store: createStore(), + store: createStore({ + modules: { + contentNode: { + namespaced: true, + getters: { + isNodeInCopyingState: () => jest.fn(), + }, + }, + }, + }), ...opts, }); } diff --git a/contentcuration/contentcuration/frontend/channelEdit/components/ContentNodeListItem/index.vue b/contentcuration/contentcuration/frontend/channelEdit/components/ContentNodeListItem/index.vue index ba6d555b85..ef85e548b0 100644 --- a/contentcuration/contentcuration/frontend/channelEdit/components/ContentNodeListItem/index.vue +++ b/contentcuration/contentcuration/frontend/channelEdit/components/ContentNodeListItem/index.vue @@ -167,10 +167,20 @@ - - {{ $tr("copyingTask") }} + + + {{ copyingMessage }} + + - + + @@ -190,6 +200,7 @@
- {{ $tr("copyingTask") }} +
+ + {{ copyingMessage }} + +