Skip to content
This repository has been archived by the owner on Dec 13, 2021. It is now read-only.

Copy & Paste feature #44

Merged
merged 7 commits into from
Jul 2, 2018
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,10 @@
color: #00aea2;
}

.stacked-content .placeholder a:hover {
text-decoration: none;
}

.stack__buttons {
position: absolute;
right: -12px;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,40 +3,40 @@
"$scope",
"editorState",
"notificationsService",
"localStorageService",
"innerContentService",
"Our.Umbraco.StackedContent.Resources.StackedContentResources",

function ($scope, editorState, notificationsService, innerContentService, scResources) {
function ($scope, editorState, notificationsService, localStorageService, innerContentService, scResources) {

$scope.inited = false;
$scope.markup = {};
$scope.prompts = {};
$scope.model.value = $scope.model.value || [];

$scope.contentTypeGuids = _.uniq($scope.model.config.contentTypes.map(function (itm) {
return itm.icContentTypeGuid;
}));

$scope.canAdd = function () {
return (!$scope.model.config.maxItems || $scope.model.config.maxItems === 0 || $scope.model.value.length < $scope.model.config.maxItems) && $scope.model.config.singleItemMode !== "1";
return (!$scope.model.config.maxItems || $scope.model.config.maxItems === "0" || $scope.model.value.length < $scope.model.config.maxItems) && $scope.model.config.singleItemMode !== "1";

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this always going to be string? Should we intParse then comapre?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe that prevalues will be strings. This wasn't an issue previously, as we were using double-equals, so the JS was evaluating it. I'd tried to follow the ESLint rules by using the triple-equals, but the value was a string (based on my tests).

};

$scope.canDelete = function () {
return $scope.model.config.singleItemMode !== "1";
};

$scope.canCopy = function () {
var test = "test";
try {
window.localStorage.setItem(test, test);
window.localStorage.removeItem(test);
return true;
} catch (e) {
return false;
}
}
// TODO: Move this to InnerContent Service
return localStorageService.isSupported;
};

$scope.canPaste = function () {
var stackedContentItem = JSON.parse(window.localStorage.getItem("StackedContentCopy"));
if (stackedContentItem && validateModel(stackedContentItem)) return true;
if ($scope.canCopy() && $scope.canAdd()) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe canAdd should include a ls.isSupported check so you only need to check canAdd? Seems odd to check canCopy

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My thinking was that you can't paste without copying it first. But yeah, we can replace it with isSupported.

return allowPaste;
}
return false;
}
};

$scope.addContent = function (evt, idx) {
$scope.overlayConfig.event = evt;
Expand All @@ -55,34 +55,35 @@
setDirty();
};

$scope.copyToLocalStorage = function (evt, idx) {
var stackedContentItem = JSON.parse(JSON.stringify($scope.model.value[idx]));
stackedContentItem.key = "";
delete stackedContentItem.$$hashKey;

if (validateModel(stackedContentItem)) {
window.localStorage.setItem("StackedContentCopy", JSON.stringify(stackedContentItem));
notificationsService.success("Stacked Content", "Copied to clipboard.");
return;
$scope.copyContent = function (evt, idx) {
// TODO: Move this to InnerContent Service
var item = $scope.model.value[idx];

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this need an ls.isSupperted check?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd thought about it, but the copy feature wouldn't be available if canCopy was false.
Unless if a dev were to hack the API themselves?

if (item && item.icContentTypeGuid) {
localStorageService.set("icContentJson", JSON.stringify(item, function (k, v) {
if (k === "key" || k === "$$hashKey") {
return undefined;
}
return v;
}));
allowPaste = true;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not convinced the allowPaste local variable is fool proof. Is there as possibility this could be false/true but there is something pastable int he clipboard? How expensive is the pastedAllowed() call?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It felt pretty expensive. I'd added a console.log in canPaste - I had 5 StackedContent fields on a page, (with at least 10 blocks between them) - meaning it was being called for each field, then each row.

It felt overkill to get the data from localStorage, parse it and loop over the contentType GUIDs each time.

There's mostly a clever way to handle this?

notificationsService.success("Content", "The content block has been copied.");
} else {
notificationsService.error("Stacked Content", "Sorry, something went wrong.");
notificationsService.error("Content", "Unfortunately, the content block was not able to be copied.");
}
}
};

$scope.pasteFromLocalStorage = function (evt, idx) {
var stackedContentItem = JSON.parse(window.localStorage.getItem("StackedContentCopy"));
stackedContentItem.key = innerContentService.generateUid();
if (!stackedContentItem) {
notificationsService.error("Stacked Content", "You need to copy content first.");
return;
}
if (validateModel(stackedContentItem)) {
$scope.overlayConfig.callback({ model: stackedContentItem, idx: idx, action: "add" });
return;
$scope.pasteContent = function (evt, idx) {
// TODO: Move this to InnerContent Service
var item = JSON.parse(localStorageService.get("icContentJson"));
item.key = innerContentService.generateUid();

if (contentValid(item)) {
$scope.overlayConfig.callback({ model: item, idx: idx, action: "add" });
setDirty();
} else {
notificationsService.error("Stacked Content", "Sorry, this content is not allowed here.");
notificationsService.error("Content", "Unfortunately, the content block is not allowed to be pasted here.");
}
}
};

$scope.sortableOptions = {
axis: 'y',
Expand Down Expand Up @@ -123,15 +124,22 @@
}
};

var validateModel = function (model) {
try {
if (!model || !model.icContentTypeGuid) return false;
if (!$scope.model.config.contentTypes.filter(x => x.icContentTypeGuid === model.icContentTypeGuid).length) return false;
return true;
} catch (e) {
return false;
var contentValid = function (itm) {
return !!itm && !!itm.icContentTypeGuid && _.contains($scope.contentTypeGuids, itm.icContentTypeGuid);
};

var pasteAllowed = function () {
// TODO: Move this to InnerContent Service
var json = localStorageService.get("icContentJson");
if (json !== null) {
var item = JSON.parse(json);
return item && contentValid(item);
}
}
return false;
};

// Storing the 'canPaste' check in a local variable, so that it doesn't need to be re-eval'd every time
var allowPaste = pasteAllowed();

// Set overlay config
$scope.overlayConfig = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@

<div ng-show="inited">

<a ng-if="!model.value || model.value.length == 0" ng-click="addContent($event, 0)" class="placeholder" title="Add content">
<div ng-if="!model.value || model.value.length == 0"ng-click="addContent($event, 0)" class="placeholder" title="Add content">

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something is wrong here. This should be two links side by side, but it appears the paste link is inside the add link. Needs refactoring.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd played around with various options - tried 2 <a> side-by-side, we'd lose the functionality to click anywhere inside the "Add content" panel - users would have to click the "icon-umb-contour" icon.

<i class="icon icon-umb-contour"></i>
</a>
<a ng-click="pasteContent($event, 0)" ng-if="canPaste()" title="Paste content"><i class="icon icon-paste-in"></i></a>
</div>

<div ng-if="model.value && model.value.length > 0" class="stack__wrapper">

Expand All @@ -15,7 +16,7 @@
<div class="stack__item" ng-repeat="itm in model.value">
<div class="stack__add-bar stack__add-bar--top" ng-if="model.config.singleItemMode !== '1'">
<a ng-click="addContent($event, $index)" class="stack__add-button" ng-if="canAdd()" title="Add"><i class="icon icon-add"></i></a>
<a ng-click="pasteFromLocalStorage($event, $index)" class="stack__paste-button" ng-if="canPaste()" title="Paste"><i class="icon icon-paste-in"></i></a>
<a ng-click="pasteContent($event, $index)" class="stack__paste-button" ng-if="canPaste()" title="Paste"><i class="icon icon-paste-in"></i></a>
</div>
<div class="stack__preview-wrapper">
<a ng-click="editContent($event, $index, itm)" class="stack__preview stack__preview--default" ng-if="!markup[itm.key]">
Expand All @@ -27,7 +28,7 @@ <h3>{{itm.name}}</h3>
</a>
<div class="stack__buttons" ng-if="canDelete()" ng-mousedown="$event.stopPropagation();">
<div class="no-overflow">
<a ng-click="copyToLocalStorage($event, $index)" ng-if="canCopy()" class="stack__button" title="Copy"><i class="icon icon-documents"></i></a>
<a ng-click="copyContent($event, $index)" ng-if="canCopy() && !prompts[itm.key]" class="stack__button" title="Copy"><i class="icon icon-documents"></i></a>
<umb-confirm-action ng-if="prompts[itm.key]"
direction="left"
on-confirm="deleteContent($event, $index)"
Expand All @@ -43,7 +44,7 @@ <h3>{{itm.name}}</h3>

<div class="stack__add-bar stack__add-bar--bottom" ng-if="model.config.singleItemMode !== '1'">
<a ng-click="addContent($event, model.value.length)" class="stack__add-button" ng-if="canAdd()" title="Add"><i class="icon icon-add"></i></a>
<a ng-click="pasteFromLocalStorage($event, model.value.length)" class="stack__paste-button" ng-if="canPaste()" title="Paste"><i class="icon icon-paste-in"></i></a>
<a ng-click="pasteContent($event, model.value.length)" class="stack__paste-button" ng-if="canPaste()" title="Paste"><i class="icon icon-paste-in"></i></a>
</div>

</div>
Expand Down