Compare commits

..

4 Commits
0.2.5 ... 0.2.6

Author SHA1 Message Date
Stefan Zermatten
43c4122fe3 Fixed stack dragging within the same container 2015-05-11 17:10:58 +02:00
Stefan Zermatten
3f4dcc146a fixed charId's being out of date after re-parenting 2015-05-11 17:10:28 +02:00
Stefan Zermatten
e4600decd0 Added more things to except list for need sign in 2015-05-11 16:51:17 +02:00
Stefan Zermatten
f6df716870 Rewrote how item drag and drop works. Need to update charId's to keep up. 2015-05-11 16:51:02 +02:00
8 changed files with 260 additions and 165 deletions

View File

@@ -19,6 +19,156 @@ Schemas.Item = new SimpleSchema({
Items.attachSchema(Schemas.Item);
var checkMovePermission = function(itemId, parent) {
var item = Items.findOne(itemId);
if (!item)
throw new Meteor.Error("No such item",
"An item could not be found to move");
//handle permissions
var permission = Meteor.call("canWriteCharacter", item.charId);
if (!permission){
throw new Meteor.Error("Access denied",
"Not permitted to move items from this character");
}
if (parent.collection === "Characters"){
permission = Meteor.call("canWriteCharacter", parent.id);
if (!permission){
throw new Meteor.Error("Access denied",
"Not permitted to move items to this character");
}
} else {
var parentCollectionObject = global[parent.collection];
var parentObject = null;
if (parentCollectionObject)
parentObject = parentCollectionObject.findOne(
parent.id, {fields: {_id: 1, charId: 1}}
);
if (!parentObject) throw new Meteor.Error(
"Invalid parent",
"The destination parent " + parent.id +
" does not exist in the collection " + parent.collection
);
if (parentObject.charId){
permission = Meteor.call("canWriteCharacter", parentObject.charId);
if (!permission){
throw new Meteor.Error("Access denied",
"Not permitted to move items to this character");
}
}
}
};
var moveItem = function(itemId, enable, parentCollection, parentId) {
var item = Items.findOne(itemId);
if (!item) return;
parentCollection = parentCollection || item.parent.collection;
parentId = parentId || item.parent.id;
if (Meteor.isServer) {
checkMovePermission(itemId, {collection: parentCollection, id: parentId});
}
//update the item provided the update will actually change something
if (
item.parent.collection !== parentCollection ||
item.parent.id !== parentId ||
item.enabled !== enable
){
Items.update(
itemId,
{$set: {
"parent.collection": parentCollection,
"parent.id": parentId,
enabled: enable,
}}
);
}
};
Meteor.methods({
moveItemToParent: function(itemId, parent) {
check(itemId, String);
check(parent, {collection: String, id: String});
moveItem(itemId, false, parent.collection, parent.id);
},
moveItemToCharacter: function(itemId, charId) {
check(itemId, String);
check(charId, String);
moveItem(itemId, false, "Characters", charId);
},
moveItemToContainer: function(itemId, containerId) {
check(itemId, String);
check(containerId, String);
moveItem(itemId, false, "Containers", containerId);
},
equipItem: function(itemId, charId){
check(itemId, String);
check(charId, String);
moveItem(itemId, true, "Characters", charId);
},
unequipItem: function(itemId, charId){
check(itemId, String);
check(charId, String);
moveItem(itemId, false, "Characters", charId);
},
splitItemToParent: function(itemId, moveQuantity, parent){
check(itemId, String);
check(moveQuantity, Number);
check(parent, {id: String, collection: String});
//get the item
var item = Items.findOne(itemId);
if (!item) return;
//don't bother moving nothing
if (moveQuantity <= 0 || item.quantity <= 0){
return;
}
//ensure we are only moving up to the current stack size
if (item.quantity < moveQuantity){
moveQuantity = this.quantity;
}
if (Meteor.isServer) {
checkMovePermission(itemId, parent);
}
//create a new item stack
var newStack = _.omit(EJSON.clone(item), "_id");
newStack.parent = parent;
newStack.quantity = moveQuantity;
//find out if we have an exact replica in the destination
var query = _.omit(newStack, ["parent", "quantity"]);
query["parent.collection"] = newStack.parent.collection;
query["parent.id"] = newStack.parent.id;
query._id = {$ne: itemId}; //make sure we don't join it to itself
var existingStack = Items.findOne(query);
if (existingStack){
//increase the existing stack's size
Items.update(
existingStack._id,
{$inc: {quantity: moveQuantity}}
);
} else {
//insert the new stack
Items.insert(newStack, function(err, id){
if (err) throw err;
//copy the children also
Meteor.call("cloneChildren", item._id, {collection: "Items", id: id});
});
}
//reduce the old stack's size
var oldQuantity = item.quantity - moveQuantity;
if (oldQuantity === 0){
Items.remove(itemId);
} else {
Items.update(itemId, {$set: {quantity: oldQuantity}});
}
},
});
Items.helpers({
totalValue: function(){
return this.value * this.quantity;
@@ -33,103 +183,6 @@ Items.helpers({
return this.name;
}
},
equip: function(characterId){
var charId = characterId || this.charId;
if (!charId || !Characters.findOne(charId)) throw "Invalid character";
if (this.parent.collection === "Characters" &&
this.parent.id === charId &&
this.enabled) {
return;
}
Items.update(
this._id,
{$set: {
"parent.collection": "Characters",
"parent.id": charId,
enabled: true,
}}
);
},
unequip: function(){
if (!this.enabled) return;
Items.update(this._id, {$set: {enabled: false}});
},
moveToContainer: function(containerId){
if (!containerId || !Containers.findOne(containerId)){
throw "Invalid container";
}
if (this.parent.collection === "Containers" &&
this.parent.id === containerId &&
!this.enabled) return;
Items.update(
this._id,
{$set: {
"parent.collection": "Containers",
"parent.id": containerId,
enabled: false,
}}
);
},
moveToCharacter: function(characterId){
if (!characterId || !Characters.findOne(characterId)) {
throw "Invalid character";
}
if (this.parent.collection === "Characters" &&
this.parent.id === characterId &&
!this.enabled) return;
Items.update(
this._id,
{$set: {
"parent.collection": "Characters",
"parent.id": characterId,
charId: characterId,
enabled: false,
}}
);
},
splitToParent: function(parent, moveQuantity){
check(parent, {id: String, collection: String});
check(moveQuantity, Number);
var parentCollection = Meteor.isClient ?
window[parent.collection] : global[parent.collection];
if (!parent.id || !parentCollection.findOne(parent.id)){
throw "Invalid parent";
}
var oldStack = this;
//we can only move as much as we have, leaving 0 behind at worst
if (oldStack.quantity < moveQuantity) moveQuantity = oldStack.quantity;
var oldQuantity = oldStack.quantity - moveQuantity;
var newStack = _.pick(oldStack, Schemas.Item.objectKeys());
newStack.parent = parent;
newStack.quantity = moveQuantity;
var existingStack = Items.findOne(_.omit(newStack, "quantity"));
var updateStackSize = function(){
if (oldQuantity > 0){
Items.update(oldStack._id, {$set: {quantity: oldQuantity}});
} else {
Items.remove(oldStack._id);
}
};
if (existingStack){
Items.update(
existingStack._id,
{$inc: {quantity: moveQuantity}},
{},
function(){
updateStackSize();
}
);
} else {
Items.insert(newStack, function(err, id){
if (err) throw err;
updateStackSize();
//copy the children also
Meteor.call("cloneChildren", oldStack._id, {collection: "Items", id: id});
});
}
},
});
Items.before.update(function(userId, doc, fieldNames, modifier, options){

View File

@@ -4,7 +4,18 @@ Router.configure({
});
Router.plugin("ensureSignedIn", {
except: ["home", "atSignIn", "atSignUp", "atForgotPassword", "notFound"]
except: [
"home",
"atSignIn",
"atSignUp",
"atForgotPassword",
"atResetPwd",
"atEnrollAccount",
"atVerifyEmail",
"atresendVerificationEmail",
"loginButtons",
"notFound",
]
});
Router.plugin("dataNotFound", {notFoundTemplate: "notFound"});

View File

@@ -174,71 +174,93 @@ Template.inventoryItem.helpers({
Template.layout.events({
"dragstart .inventoryItem": function(event, instance){
event.originalEvent.dataTransfer.setData("dicecloud-id/items", this._id);
Session.set("inventory.dragItemId", this._id);
Session.set("inventory.dragItemOriginalContainer", this.container);
Session.set("inventory.dragItemOriginalCharacter", this.charId);
},
"dragover .itemContainer, dragenter .itemContainer":
function(event, instance){
if (_.contains(event.originalEvent.dataTransfer.types, "dicecloud-id/items")){
event.preventDefault();
}
},
"dragover .equipmentContainer, dragenter .equipmentContainer":
function(event, instance){
if (_.contains(event.originalEvent.dataTransfer.types, "dicecloud-id/items")){
event.preventDefault();
}
},
"dragover .carriedContainer, dragenter .carriedContainer":
function(event, instance){
if (_.contains(event.originalEvent.dataTransfer.types, "dicecloud-id/items")){
event.preventDefault();
}
},
"dragover .characterRepresentative, dragenter .characterRepresentative":
function(event, instance){
if (_.contains(event.originalEvent.dataTransfer.types, "dicecloud-id/items")){
event.preventDefault();
}
},
"dragend .inventoryItem": function(event, instance){
resetInvetorySession(); //this is a valid drop zone
Session.set("inventory.dragItemId", null);
},
"dragover .itemContainer": function(event, instance){
event.preventDefault();
},
"dragover .equipmentContainer": function(event, instance){
event.preventDefault();
},
"dragover .carriedContainer": function(event, instance){
event.preventDefault();
},
"drop .itemContainer": function(event, instacne){
var item = Items.findOne(Session.get("inventory.dragItemId"));
"drop .itemContainer": function(event, instance){
var itemId = event.originalEvent.dataTransfer.getData("dicecloud-id/items");
if (event.ctrlKey){
//split the stack to the container
GlobalUI.showDialog({
template: "splitStackDialog",
data: {
id: item._id,
id: itemId,
parentCollection: "Containers",
parentId: this._id,
},
});
} else {
//move item to the container
item.moveToContainer(this._id);
Meteor.call("moveItemToContainer", itemId, this._id);
}
resetInvetorySession();
Session.set("inventory.dragItemId", null);
},
"drop .equipmentContainer": function(event, instance){
var charId = Session.get("inventory.dragItemOriginalCharacter");
var item = Items.findOne(Session.get("inventory.dragItemId"));
item.equip(charId);
resetInvetorySession();
var itemId = event.originalEvent.dataTransfer.getData("dicecloud-id/items");
Meteor.call("equipItem", itemId, this._id);
Session.set("inventory.dragItemId", null);
},
"drop .carriedContainer": function(event, instance){
var charId = Session.get("inventory.dragItemOriginalCharacter");
var item = Items.findOne(Session.get("inventory.dragItemId"));
var itemId = event.originalEvent.dataTransfer.getData("dicecloud-id/items");
if (event.ctrlKey){
//split the stack to the container
GlobalUI.showDialog({
template: "splitStackDialog",
data: {
id: item._id,
id: itemId,
parentCollection: "Characters",
parentId: this._id,
},
});
} else {
//move item to the character
item.moveToCharacter(this._id);
Meteor.call("moveItemToCharacter", itemId, this._id);
}
resetInvetorySession();
Session.set("inventory.dragItemId", null);
},
"drop .characterRepresentative": function(event, instance) {
var itemId = event.originalEvent.dataTransfer.getData("dicecloud-id/items");
if (event.ctrlKey){
//split the stack to the container
GlobalUI.showDialog({
template: "splitStackDialog",
data: {
id: itemId,
parentCollection: "Characters",
parentId: this._id,
},
});
} else {
//move item to the character
Meteor.call("moveItemToCharacter", itemId, this._id);
}
Session.set("inventory.dragItemId", null);
},
});
var resetInvetorySession = function(){
_.defer(function(){
Session.set("inventory.dragItemId", null);
Session.set("inventory.dragItemOriginalContainer", null);
Session.set("inventory.dragItemOriginalCharacter", null);
});
};

View File

@@ -81,13 +81,10 @@ Template.itemEdit.events({
},
"change #equippedInput": function(event){
var equipped = Template.instance().find("#equippedInput").checked;
var item = Items.findOne(this._id);
if (item){
if (equipped){
item.equip();
} else {
item.unequip();
}
if (equipped){
Meteor.call("equipItem", this._id, this.charId);
} else {
Meteor.call("unequipItem", this._id, this.charId);
}
},
"change #attunementCheckbox": function(event){
@@ -107,7 +104,6 @@ Template.containerDropdown.events({
var detail = event.originalEvent.detail;
if (!detail.isSelected) return;
var containerId = detail.item.getAttribute("name");
var item = Items.findOne(Template.currentData()._id);
item.moveToContainer(containerId);
Meteor.call("moveItemToContainer", Template.currentData()._id, containerId);
}
});

View File

@@ -7,13 +7,12 @@ Template.splitStackDialog.helpers({
Template.splitStackDialog.events({
"tap #moveButton": function(event, instance){
var item = Items.findOne(this.id);
if (item){
item.splitToParent(
{collection: this.parentCollection , id: this.parentId},
+instance.find("#quantityInput").value
);
}
Meteor.call(
"splitItemToParent",
this.id,
+instance.find("#quantityInput").value,
{collection: this.parentCollection , id: this.parentId}
);
},
"tap #oneButton":function(event, instance){
instance.find("#quantityInput").value = 1;

View File

@@ -3,7 +3,7 @@
{{#if characters.count}}
<div>
{{#each characters}}
<div class="singleLineItem">{{name}}</div>
<div class="singleLineItem characterRepresentative">{{name}}</div>
{{/each}}
</div>
{{/if}}

View File

@@ -1,3 +1,14 @@
Meteor.methods({
canWriteCharacter: function(charId) {
var userId = this.userId;
var char = Characters.findOne(
charId,
{fields: {owner: 1, writers: 1}}
);
return (userId && char.owner === userId || _.contains(char.writers, userId));
},
});
CHARACTER_SUBSCHEMA_ALLOW = {
// the user must be logged in, and the user must be a writer of the character
insert: function(userId, doc) {

View File

@@ -42,6 +42,12 @@ var inheritParentProperties = function(doc, collection){
"Document's parent does not exist"
);
var handMeDowns = _.pick(parent, collection.inheritedKeys);
if (
_.contains(collection.inheritedKeys, "charId") &&
doc.parent.collection === "Characters"
){
handMeDowns.charId = doc.parent.id;
}
if (_.isEmpty(handMeDowns)) return;
collection.update(doc._id, {$set: handMeDowns});
};
@@ -76,14 +82,13 @@ makeChild = function(collection, inheritedKeys){
}
});
if (Meteor.isClient) {
collection.after.update(function(userId, doc, fieldNames, modifier, options) {
if (modifier && modifier.$set && modifier.$set.parent){
//when we change parents, inherit its properties
inheritParentProperties(doc, collection);
}
});
}
collection.after.update(function(userId, doc, fieldNames, modifier, options) {
if (modifier && modifier.$set && modifier.$set["parent.id"]){
//when we change parents, inherit its properties
console.log("re-inheriting")
inheritParentProperties(doc, collection);
}
});
collection.softRemoveNode = collection.softRemoveNode || function(id){
collection.softRemove(id);
@@ -102,14 +107,12 @@ makeParent = function(collection, donatedKeys){
donatedKeys = joinWithDefaultKeys(donatedKeys);
var collectionName = collection._collection.name;
//after changing, push the changes to all children
if (Meteor.isClient) {
collection.after.update(function(userId, doc, fieldNames, modifier, options) {
modifier = limitModifierToKeys(modifier, donatedKeys);
doc = _.pick(doc, ["_id", "charId"]);
if (!modifier) return;
Meteor.call("updateChildren", doc, modifier, true);
});
}
collection.after.update(function(userId, doc, fieldNames, modifier, options) {
modifier = limitModifierToKeys(modifier, donatedKeys);
doc = _.pick(doc, ["_id", "charId"]);
if (!modifier) return;
Meteor.call("updateChildren", doc, modifier, true);
});
collection.softRemoveNode = function(id){
Meteor.call("softRemoveNode", collectionName, id);
};