Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fcae3056de | ||
|
|
7d364c80c0 | ||
|
|
0ff6c08abd | ||
|
|
1c95336843 | ||
|
|
b36720511b | ||
|
|
261220fdd5 | ||
|
|
64edc52cca | ||
|
|
56f1bd2829 | ||
|
|
3b669fd2f9 | ||
|
|
933878e158 | ||
|
|
0e6ca56316 | ||
|
|
6599fe1ef8 | ||
|
|
f39baf43a1 | ||
|
|
96f4e35e25 | ||
|
|
e17dbf6601 | ||
|
|
3f81d419f7 | ||
|
|
1c00f5aa04 | ||
|
|
f5a32cb50a | ||
|
|
f4d3368fb4 | ||
|
|
1de9fb558a | ||
|
|
06ffc94b4c | ||
|
|
74c6a423ee |
1
app/.gitignore
vendored
1
app/.gitignore
vendored
@@ -4,6 +4,7 @@
|
|||||||
settings.json
|
settings.json
|
||||||
public/components
|
public/components
|
||||||
public/_imports.html
|
public/_imports.html
|
||||||
|
private/oldClient
|
||||||
nohup.out
|
nohup.out
|
||||||
node_modules
|
node_modules
|
||||||
dump
|
dump
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
# but you can also edit it by hand.
|
# but you can also edit it by hand.
|
||||||
|
|
||||||
accounts-password@1.5.1
|
accounts-password@1.5.1
|
||||||
accounts-ui@1.3.0
|
accounts-ui@1.3.1
|
||||||
random@1.1.0
|
random@1.1.0
|
||||||
dburles:collection-helpers
|
dburles:collection-helpers
|
||||||
reactive-var@1.0.11
|
reactive-var@1.0.11
|
||||||
@@ -17,7 +17,7 @@ dburles:mongo-collection-instances
|
|||||||
percolate:migrations
|
percolate:migrations
|
||||||
ecwyne:mathjs
|
ecwyne:mathjs
|
||||||
useraccounts:polymer
|
useraccounts:polymer
|
||||||
accounts-google@1.3.1
|
accounts-google@1.3.2
|
||||||
splendido:accounts-meld
|
splendido:accounts-meld
|
||||||
email@1.2.3
|
email@1.2.3
|
||||||
meteorhacks:subs-manager
|
meteorhacks:subs-manager
|
||||||
@@ -25,9 +25,9 @@ chuangbo:marked
|
|||||||
reywood:iron-router-ga
|
reywood:iron-router-ga
|
||||||
meteor-base@1.4.0
|
meteor-base@1.4.0
|
||||||
mobile-experience@1.0.5
|
mobile-experience@1.0.5
|
||||||
mongo@1.5.0
|
mongo@1.6.0
|
||||||
blaze-html-templates
|
blaze-html-templates
|
||||||
session@1.1.7
|
session@1.1.8
|
||||||
jquery@1.11.10
|
jquery@1.11.10
|
||||||
tracker@1.2.0
|
tracker@1.2.0
|
||||||
logging@1.1.20
|
logging@1.1.20
|
||||||
@@ -37,20 +37,20 @@ spacebars
|
|||||||
check@1.3.1
|
check@1.3.1
|
||||||
useraccounts:iron-routing
|
useraccounts:iron-routing
|
||||||
wizonesolutions:canonical
|
wizonesolutions:canonical
|
||||||
standard-minifier-js@2.3.4
|
standard-minifier-js@2.4.0
|
||||||
shell-server@0.3.1
|
shell-server@0.4.0
|
||||||
seba:minifiers-autoprefixer
|
seba:minifiers-autoprefixer
|
||||||
nikogosovd:multiple-uihooks
|
nikogosovd:multiple-uihooks
|
||||||
templates:array
|
templates:array
|
||||||
ecmascript@0.11.1
|
ecmascript@0.12.0
|
||||||
es5-shim@4.8.0
|
es5-shim@4.8.0
|
||||||
differential:vulcanize
|
differential:vulcanize
|
||||||
reactive-dict@1.2.0
|
reactive-dict@1.2.1
|
||||||
percolate:synced-cron
|
|
||||||
ongoworks:speakingurl
|
ongoworks:speakingurl
|
||||||
service-configuration@1.0.11
|
service-configuration@1.0.11
|
||||||
google-config-ui@1.0.0
|
google-config-ui@1.0.1
|
||||||
dynamic-import@0.4.0
|
dynamic-import@0.5.0
|
||||||
ddp-rate-limiter@1.0.7
|
ddp-rate-limiter@1.0.7
|
||||||
rate-limit@1.0.9
|
rate-limit@1.0.9
|
||||||
iron:router
|
iron:router
|
||||||
|
littledata:synced-cron
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
METEOR@1.7.0.3
|
METEOR@1.8
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
accounts-base@1.4.2
|
accounts-base@1.4.3
|
||||||
accounts-google@1.3.1
|
accounts-google@1.3.2
|
||||||
accounts-oauth@1.1.15
|
accounts-oauth@1.1.16
|
||||||
accounts-password@1.5.1
|
accounts-password@1.5.1
|
||||||
accounts-ui@1.3.0
|
accounts-ui@1.3.1
|
||||||
accounts-ui-unstyled@1.4.1
|
accounts-ui-unstyled@1.4.1
|
||||||
aldeed:collection2@2.10.0
|
aldeed:collection2@2.10.0
|
||||||
aldeed:collection2-core@1.2.0
|
aldeed:collection2-core@1.2.0
|
||||||
@@ -10,16 +10,16 @@ aldeed:schema-deny@1.1.0
|
|||||||
aldeed:schema-index@1.1.1
|
aldeed:schema-index@1.1.1
|
||||||
aldeed:simple-schema@1.5.4
|
aldeed:simple-schema@1.5.4
|
||||||
allow-deny@1.1.0
|
allow-deny@1.1.0
|
||||||
autoupdate@1.4.1
|
autoupdate@1.5.0
|
||||||
babel-compiler@7.1.1
|
babel-compiler@7.2.0
|
||||||
babel-runtime@1.2.2
|
babel-runtime@1.3.0
|
||||||
base64@1.0.11
|
base64@1.0.11
|
||||||
binary-heap@1.0.10
|
binary-heap@1.0.11
|
||||||
blaze@2.3.2
|
blaze@2.3.3
|
||||||
blaze-html-templates@1.1.2
|
blaze-html-templates@1.1.2
|
||||||
blaze-tools@1.0.10
|
blaze-tools@1.0.10
|
||||||
boilerplate-generator@1.5.0
|
boilerplate-generator@1.6.0
|
||||||
caching-compiler@1.1.12
|
caching-compiler@1.2.0
|
||||||
caching-html-compiler@1.1.3
|
caching-html-compiler@1.1.3
|
||||||
callback-hook@1.1.0
|
callback-hook@1.1.0
|
||||||
check@1.3.1
|
check@1.3.1
|
||||||
@@ -35,23 +35,25 @@ ddp-server@2.2.0
|
|||||||
deps@1.0.12
|
deps@1.0.12
|
||||||
diff-sequence@1.1.0
|
diff-sequence@1.1.0
|
||||||
differential:vulcanize@3.0.0
|
differential:vulcanize@3.0.0
|
||||||
dynamic-import@0.4.1
|
dynamic-import@0.5.0
|
||||||
ecmascript@0.11.1
|
ecmascript@0.12.0
|
||||||
ecmascript-runtime@0.7.0
|
ecmascript-runtime@0.7.0
|
||||||
ecmascript-runtime-client@0.7.1
|
ecmascript-runtime-client@0.8.0
|
||||||
ecmascript-runtime-server@0.7.0
|
ecmascript-runtime-server@0.7.1
|
||||||
ecwyne:mathjs@0.25.0
|
ecwyne:mathjs@0.25.0
|
||||||
ejson@1.1.0
|
ejson@1.1.0
|
||||||
email@1.2.3
|
email@1.2.3
|
||||||
es5-shim@4.8.0
|
es5-shim@4.8.0
|
||||||
|
fetch@0.1.0
|
||||||
geojson-utils@1.0.10
|
geojson-utils@1.0.10
|
||||||
google-config-ui@1.0.0
|
google-config-ui@1.0.1
|
||||||
google-oauth@1.2.5
|
google-oauth@1.2.6
|
||||||
hot-code-push@1.0.4
|
hot-code-push@1.0.4
|
||||||
html-tools@1.0.11
|
html-tools@1.0.11
|
||||||
htmljs@1.0.11
|
htmljs@1.0.11
|
||||||
http@1.4.1
|
http@1.4.1
|
||||||
id-map@1.1.0
|
id-map@1.1.0
|
||||||
|
inter-process-messaging@0.1.0
|
||||||
iron:controller@1.0.12
|
iron:controller@1.0.12
|
||||||
iron:core@1.0.11
|
iron:core@1.0.11
|
||||||
iron:dynamic-template@1.0.12
|
iron:dynamic-template@1.0.12
|
||||||
@@ -63,7 +65,8 @@ iron:url@1.1.0
|
|||||||
jquery@1.11.11
|
jquery@1.11.11
|
||||||
lai:collection-extensions@0.2.1_1
|
lai:collection-extensions@0.2.1_1
|
||||||
launch-screen@1.1.1
|
launch-screen@1.1.1
|
||||||
less@2.7.12
|
less@2.8.0
|
||||||
|
littledata:synced-cron@1.5.1
|
||||||
livedata@1.0.18
|
livedata@1.0.18
|
||||||
localstorage@1.2.0
|
localstorage@1.2.0
|
||||||
logging@1.1.20
|
logging@1.1.20
|
||||||
@@ -72,51 +75,51 @@ mdg:validation-error@0.5.1
|
|||||||
meteor@1.9.2
|
meteor@1.9.2
|
||||||
meteor-base@1.4.0
|
meteor-base@1.4.0
|
||||||
meteorhacks:subs-manager@1.6.4
|
meteorhacks:subs-manager@1.6.4
|
||||||
minifier-css@1.3.1
|
minifier-css@1.4.0
|
||||||
minifier-js@2.3.5
|
minifier-js@2.4.0
|
||||||
minimongo@1.4.4
|
minimongo@1.4.5
|
||||||
mobile-experience@1.0.5
|
mobile-experience@1.0.5
|
||||||
mobile-status-bar@1.0.14
|
mobile-status-bar@1.0.14
|
||||||
modern-browsers@0.1.2
|
modern-browsers@0.1.2
|
||||||
modules@0.12.2
|
modules@0.13.0
|
||||||
modules-runtime@0.10.2
|
modules-runtime@0.10.2
|
||||||
momentjs:moment@2.22.2
|
momentjs:moment@2.22.2
|
||||||
mongo@1.5.1
|
mongo@1.6.0
|
||||||
|
mongo-decimal@0.1.0
|
||||||
mongo-dev-server@1.1.0
|
mongo-dev-server@1.1.0
|
||||||
mongo-id@1.0.7
|
mongo-id@1.0.7
|
||||||
nikogosovd:multiple-uihooks@0.1.8
|
nikogosovd:multiple-uihooks@0.1.8
|
||||||
npm-bcrypt@0.9.3
|
npm-bcrypt@0.9.3
|
||||||
npm-mongo@3.0.11
|
npm-mongo@3.1.1
|
||||||
oauth@1.2.3
|
oauth@1.2.3
|
||||||
oauth2@1.2.0
|
oauth2@1.2.1
|
||||||
observe-sequence@1.0.16
|
observe-sequence@1.0.16
|
||||||
ongoworks:speakingurl@9.0.0
|
ongoworks:speakingurl@9.0.0
|
||||||
ordered-dict@1.1.0
|
ordered-dict@1.1.0
|
||||||
percolate:migrations@0.9.8
|
percolate:migrations@0.9.8
|
||||||
percolate:synced-cron@1.3.2
|
|
||||||
promise@0.11.1
|
promise@0.11.1
|
||||||
raix:eventemitter@0.1.3
|
raix:eventemitter@0.1.3
|
||||||
random@1.1.0
|
random@1.1.0
|
||||||
rate-limit@1.0.9
|
rate-limit@1.0.9
|
||||||
reactive-dict@1.2.0
|
reactive-dict@1.2.1
|
||||||
reactive-var@1.0.11
|
reactive-var@1.0.11
|
||||||
reload@1.2.0
|
reload@1.2.0
|
||||||
retry@1.1.0
|
retry@1.1.0
|
||||||
reywood:iron-router-ga@0.7.1
|
reywood:iron-router-ga@0.7.1
|
||||||
routepolicy@1.0.13
|
routepolicy@1.1.0
|
||||||
seba:minifiers-autoprefixer@1.0.1
|
seba:minifiers-autoprefixer@1.1.1
|
||||||
service-configuration@1.0.11
|
service-configuration@1.0.11
|
||||||
session@1.1.7
|
session@1.1.8
|
||||||
sha@1.0.9
|
sha@1.0.9
|
||||||
shell-server@0.3.1
|
shell-server@0.4.0
|
||||||
socket-stream-client@0.2.2
|
socket-stream-client@0.2.2
|
||||||
softwarerero:accounts-t9n@1.3.11
|
softwarerero:accounts-t9n@1.3.11
|
||||||
spacebars@1.0.15
|
spacebars@1.0.15
|
||||||
spacebars-compiler@1.1.3
|
spacebars-compiler@1.1.3
|
||||||
splendido:accounts-emails-field@1.2.0
|
splendido:accounts-emails-field@1.2.0
|
||||||
splendido:accounts-meld@1.3.1
|
splendido:accounts-meld@1.3.1
|
||||||
srp@1.0.10
|
srp@1.0.12
|
||||||
standard-minifier-js@2.3.4
|
standard-minifier-js@2.4.0
|
||||||
templates:array@1.0.3
|
templates:array@1.0.3
|
||||||
templating@1.3.2
|
templating@1.3.2
|
||||||
templating-compiler@1.3.3
|
templating-compiler@1.3.3
|
||||||
@@ -129,7 +132,7 @@ url@1.2.0
|
|||||||
useraccounts:core@1.14.2
|
useraccounts:core@1.14.2
|
||||||
useraccounts:iron-routing@1.14.2
|
useraccounts:iron-routing@1.14.2
|
||||||
useraccounts:polymer@1.14.2
|
useraccounts:polymer@1.14.2
|
||||||
webapp@1.6.2
|
webapp@1.7.0
|
||||||
webapp-hashing@1.0.9
|
webapp-hashing@1.0.9
|
||||||
wizonesolutions:canonical@0.0.5
|
wizonesolutions:canonical@0.0.5
|
||||||
zimme:collection-behaviours@1.1.3
|
zimme:collection-behaviours@1.1.3
|
||||||
|
|||||||
@@ -65,3 +65,37 @@ makeParent(Buffs, ["name", "enabled"]); //parents of effects, attacks, proficien
|
|||||||
|
|
||||||
Buffs.allow(CHARACTER_SUBSCHEMA_ALLOW);
|
Buffs.allow(CHARACTER_SUBSCHEMA_ALLOW);
|
||||||
Buffs.deny(CHARACTER_SUBSCHEMA_DENY);
|
Buffs.deny(CHARACTER_SUBSCHEMA_DENY);
|
||||||
|
|
||||||
|
Meteor.methods({
|
||||||
|
applyBuff: function(buffId, targetId){
|
||||||
|
if (!Meteor.call("canWriteCharacter", targetId)){
|
||||||
|
throw new Meteor.Error(
|
||||||
|
"Access denied",
|
||||||
|
"You do not have permission to buff this character"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let buff = CustomBuffs.findOne(buffId);
|
||||||
|
if (!buff) return;
|
||||||
|
|
||||||
|
var parentCol = Meteor.isClient ?
|
||||||
|
window[buff.parent.collection] : global[buff.parent.collection]
|
||||||
|
var parent = parentCol.findOne(buff.parent.id);
|
||||||
|
|
||||||
|
//insert new buff
|
||||||
|
newBuffId = Buffs.insert({
|
||||||
|
charId: targetId,
|
||||||
|
name: buff.name,
|
||||||
|
description: buff.description,
|
||||||
|
lifeTime: {total: buff.lifeTime.total},
|
||||||
|
type: "custom",
|
||||||
|
|
||||||
|
appliedBy: buff.charId,
|
||||||
|
appliedByDetails: {
|
||||||
|
name: parent && parent.name || "",
|
||||||
|
collection: buff.parent.collection,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Meteor.call("cloneChildren", buffId, {id: newBuffId, collection: "Buffs"})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|||||||
@@ -582,8 +582,8 @@ Characters.allow({
|
|||||||
});
|
});
|
||||||
|
|
||||||
Characters.deny({
|
Characters.deny({
|
||||||
update: function(userId, docs, fields, modifier) {
|
update: function(userId, doc, fields, modifier) {
|
||||||
// can't change owners
|
// can't change owners unless you are the current owner
|
||||||
return _.contains(fields, "owner");
|
return _.contains(fields, "owner") && doc.owner !== userId;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,14 +2,38 @@ Libraries = new Mongo.Collection("library");
|
|||||||
|
|
||||||
Schemas.Library = new SimpleSchema({
|
Schemas.Library = new SimpleSchema({
|
||||||
name: {type: String},
|
name: {type: String},
|
||||||
owner: {type: String, regEx: SimpleSchema.RegEx.Id},
|
owner: {type: String, regEx: SimpleSchema.RegEx.Id, index: 1},
|
||||||
readers: {type: [String], regEx: SimpleSchema.RegEx.Id, defaultValue: []},
|
readers: {type: [String], regEx: SimpleSchema.RegEx.Id, defaultValue: [], index: 1},
|
||||||
writers: {type: [String], regEx: SimpleSchema.RegEx.Id, defaultValue: []},
|
writers: {type: [String], regEx: SimpleSchema.RegEx.Id, defaultValue: [], index: 1},
|
||||||
public: {type: Boolean, defaultValue: false},
|
public: {type: Boolean, defaultValue: false, index: 1},
|
||||||
});
|
});
|
||||||
|
|
||||||
Libraries.attachSchema(Schemas.Library);
|
Libraries.attachSchema(Schemas.Library);
|
||||||
|
|
||||||
|
Libraries.after.remove(function(userId, library) {
|
||||||
|
LibraryItems.remove({library: library._id});
|
||||||
|
LibrarySpells.remove({library: library._id});
|
||||||
|
});
|
||||||
|
|
||||||
|
Meteor.methods({
|
||||||
|
removeLibrary: function(libraryId) {
|
||||||
|
let library = Libraries.findOne(libraryId);
|
||||||
|
let userId = Meteor.userId();
|
||||||
|
|
||||||
|
if (!library) return;
|
||||||
|
if (library.owner === userId){
|
||||||
|
Libraries.remove(libraryId);
|
||||||
|
} else {
|
||||||
|
if (_.contains(library.readers, userId)){
|
||||||
|
Libraries.update(libraryId, {$pull: {"readers": userId}});
|
||||||
|
}
|
||||||
|
if (_.contains(library.writers, userId)){
|
||||||
|
Libraries.update(libraryId, {$pull: {"writers": userId}});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
Libraries.allow({
|
Libraries.allow({
|
||||||
insert(userId, doc) {
|
insert(userId, doc) {
|
||||||
return userId && doc.owner === userId;
|
return userId && doc.owner === userId;
|
||||||
@@ -18,16 +42,14 @@ Libraries.allow({
|
|||||||
return canEdit(userId, doc);
|
return canEdit(userId, doc);
|
||||||
},
|
},
|
||||||
remove(userId, doc) {
|
remove(userId, doc) {
|
||||||
return canEdit(userId, doc);
|
return userId && doc.owner === userId;
|
||||||
},
|
},
|
||||||
fetch: ["owner", "writers"],
|
fetch: ["owner", "writers"],
|
||||||
});
|
});
|
||||||
|
|
||||||
Libraries.deny({
|
Libraries.deny({
|
||||||
// For now, only admins can manage libraries
|
|
||||||
insert(userId, doc){
|
insert(userId, doc){
|
||||||
var user = Meteor.users.findOne(userId);
|
return !Meteor.users.findOne(userId);
|
||||||
return !user || !_.contains(user.roles, "admin");
|
|
||||||
},
|
},
|
||||||
update(userId, doc, fields, modifier) {
|
update(userId, doc, fields, modifier) {
|
||||||
// Can't change owners
|
// Can't change owners
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
Schemas.LibraryAttacks = new SimpleSchema({
|
Schemas.LibraryAttacks = new SimpleSchema({
|
||||||
name: {
|
name: {
|
||||||
type: String,
|
type: String,
|
||||||
defaultValue: "New Attack",
|
optional: true,
|
||||||
trim: false,
|
trim: false,
|
||||||
},
|
},
|
||||||
details: {
|
details: {
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ Schemas.LibraryEffects = new SimpleSchema({
|
|||||||
defaultValue: "add",
|
defaultValue: "add",
|
||||||
allowedValues: [
|
allowedValues: [
|
||||||
"base",
|
"base",
|
||||||
"proficiency",
|
|
||||||
"add",
|
"add",
|
||||||
"mul",
|
"mul",
|
||||||
"min",
|
"min",
|
||||||
|
|||||||
@@ -42,3 +42,50 @@ LibraryItems.allow({
|
|||||||
},
|
},
|
||||||
fetch: ["library"],
|
fetch: ["library"],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Meteor.methods({
|
||||||
|
updateLibraryItemEffect: function({itemId, effectIndex, field, value, unsetField}){
|
||||||
|
let libraryId = LibraryItems.findOne(itemId).library;
|
||||||
|
let userId = Meteor.userId();
|
||||||
|
if (!Libraries.canEdit(userId, libraryId)) return;
|
||||||
|
let modifier = {
|
||||||
|
$set: {
|
||||||
|
[`effects.${effectIndex}.${field}`]: value,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (unsetField){
|
||||||
|
modifier.$unset = {
|
||||||
|
[`effects.${effectIndex}.${unsetField}`]: 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LibraryItems.update(itemId, modifier);
|
||||||
|
},
|
||||||
|
removeLibraryItemEffect: function({itemId, effectIndex}){
|
||||||
|
let libraryId = LibraryItems.findOne(itemId).library;
|
||||||
|
let userId = Meteor.userId();
|
||||||
|
if (!Libraries.canEdit(userId, libraryId)) return;
|
||||||
|
LibraryItems.update(itemId, {$unset : {
|
||||||
|
[`effects.${effectIndex}`] : 1,
|
||||||
|
}});
|
||||||
|
LibraryItems.update(itemId, {$pull : {"effects" : null}});
|
||||||
|
},
|
||||||
|
updateLibraryItemAttack: function({itemId, attackIndex, field, value}){
|
||||||
|
let libraryId = LibraryItems.findOne(itemId).library;
|
||||||
|
let userId = Meteor.userId();
|
||||||
|
if (!Libraries.canEdit(userId, libraryId)) return;
|
||||||
|
LibraryItems.update(itemId, {
|
||||||
|
$set: {
|
||||||
|
[`attacks.${attackIndex}.${field}`]: value,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
removeLibraryItemAttack: function({itemId, attackIndex}){
|
||||||
|
let libraryId = LibraryItems.findOne(itemId).library;
|
||||||
|
let userId = Meteor.userId();
|
||||||
|
if (!Libraries.canEdit(userId, libraryId)) return;
|
||||||
|
LibraryItems.update(itemId, {$unset : {
|
||||||
|
[`attacks.${attackIndex}`] : 1,
|
||||||
|
}});
|
||||||
|
LibraryItems.update(itemId, {$pull : {"attacks" : null}});
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|||||||
@@ -3,6 +3,10 @@ Schemas.UserProfile = new SimpleSchema({
|
|||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
},
|
},
|
||||||
|
librarySubscriptions: {
|
||||||
|
type: [String],
|
||||||
|
defaultValue: [],
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
Schemas.User = new SimpleSchema({
|
Schemas.User = new SimpleSchema({
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ Router.map(function() {
|
|||||||
this.route("library", {
|
this.route("library", {
|
||||||
path: "/library",
|
path: "/library",
|
||||||
waitOn: function(){
|
waitOn: function(){
|
||||||
return subsManager.subscribe("standardLibraries");
|
return subsManager.subscribe("customLibraries");
|
||||||
},
|
},
|
||||||
onAfterAction: function() {
|
onAfterAction: function() {
|
||||||
document.title = appName + " - Library";
|
document.title = appName + " - Library";
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "RPG Docs",
|
"name": "dicecloud",
|
||||||
"version": "0.0.0",
|
"version": "1",
|
||||||
"homepage": "",
|
"homepage": "",
|
||||||
"authors": [
|
"authors": [
|
||||||
"Stefan Zermatten"
|
"Stefan Zermatten"
|
||||||
|
|||||||
5
app/client/lib/fixPasswordButton.js
Normal file
5
app/client/lib/fixPasswordButton.js
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
let pwdFormSubmit = AccountsTemplates.atPwdFormEvents["submit #at-pwd-form"]
|
||||||
|
|
||||||
|
Template.atPwdForm.events({
|
||||||
|
"click .at-btn.submit": pwdFormSubmit,
|
||||||
|
});
|
||||||
8
app/client/style/iosButtonFix.css
Normal file
8
app/client/style/iosButtonFix.css
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
/*
|
||||||
|
* iOS doens't believe in click events for some elements.
|
||||||
|
* This is here to convince it to allow buttons to be clickable
|
||||||
|
*/
|
||||||
|
button {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,47 +1,5 @@
|
|||||||
const applyBuff = function(targetId, buff) {
|
const applyBuff = function(targetId, buff) {
|
||||||
var parent = global[buff.parent.collection].findOne(buff.parent.id);
|
Meteor.call("applyBuff", buff._id, targetId)
|
||||||
|
|
||||||
//insert new buff
|
|
||||||
newBuffId = Buffs.insert({
|
|
||||||
charId: targetId,
|
|
||||||
name: buff.name,
|
|
||||||
description: buff.description,
|
|
||||||
lifeTime: {total: buff.lifeTime.total},
|
|
||||||
type: "custom",
|
|
||||||
|
|
||||||
appliedBy: buff.charId,
|
|
||||||
appliedByDetails: {
|
|
||||||
name: parent.name,
|
|
||||||
collection: buff.parent.collection,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
//insert children
|
|
||||||
Attacks.find({"parent.id": buff._id}).forEach(function(doc){
|
|
||||||
temp = _.clone(doc);
|
|
||||||
temp.parent.id = newBuffId;
|
|
||||||
temp.parent.collection = "Buffs";
|
|
||||||
delete temp._id;
|
|
||||||
|
|
||||||
Attacks.insert(temp);
|
|
||||||
});
|
|
||||||
Effects.find({"parent.id": buff._id}).forEach(function(doc){
|
|
||||||
temp = _.clone(doc);
|
|
||||||
temp.parent.id = newBuffId;
|
|
||||||
temp.parent.collection = "Buffs";
|
|
||||||
delete temp._id;
|
|
||||||
|
|
||||||
Effects.insert(temp);
|
|
||||||
});
|
|
||||||
Proficiencies.find({"parent.id": buff._id}).forEach(function(doc){
|
|
||||||
temp = _.clone(doc);
|
|
||||||
temp.parent.id = newBuffId;
|
|
||||||
temp.parent.collection = "Buffs";
|
|
||||||
delete temp._id;
|
|
||||||
|
|
||||||
Proficiencies.insert(temp);
|
|
||||||
});
|
|
||||||
|
|
||||||
let target;
|
let target;
|
||||||
if (targetId == buff.charId) {
|
if (targetId == buff.charId) {
|
||||||
target = "self";
|
target = "self";
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ Template.characterSettings.events({
|
|||||||
"change #hideSpellcasting": function(event, instance){
|
"change #hideSpellcasting": function(event, instance){
|
||||||
var value = instance.find("#hideSpellcasting").checked;
|
var value = instance.find("#hideSpellcasting").checked;
|
||||||
if (this.settings.hideSpellcasting !== value){
|
if (this.settings.hideSpellcasting !== value){
|
||||||
|
Session.set(this._id + ".selectedTab", "0");
|
||||||
Characters.update(
|
Characters.update(
|
||||||
this._id,
|
this._id,
|
||||||
{$set: {"settings.hideSpellcasting": value}}
|
{$set: {"settings.hideSpellcasting": value}}
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ Template.shareDialog.events({
|
|||||||
Characters.update(this._id, {$set: {"settings.viewPermission": value}});
|
Characters.update(this._id, {$set: {"settings.viewPermission": value}});
|
||||||
},
|
},
|
||||||
"input #userNameOrEmailInput":
|
"input #userNameOrEmailInput":
|
||||||
function(event, instance){
|
_.debounce(function(event, instance){
|
||||||
var userName = instance.find("#userNameOrEmailInput").value;
|
var userName = instance.find("#userNameOrEmailInput").value;
|
||||||
instance.userId.set(undefined);
|
instance.userId.set(undefined);
|
||||||
Meteor.call("getUserId", userName, function(err, result) {
|
Meteor.call("getUserId", userName, function(err, result) {
|
||||||
@@ -64,7 +64,7 @@ Template.shareDialog.events({
|
|||||||
instance.userId.set(result);
|
instance.userId.set(result);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
}, 300),
|
||||||
"click #shareButton": function(event, instance){
|
"click #shareButton": function(event, instance){
|
||||||
var self = this;
|
var self = this;
|
||||||
var permission = instance.find("#accessLevelMenu").selected;
|
var permission = instance.find("#accessLevelMenu").selected;
|
||||||
|
|||||||
@@ -48,6 +48,25 @@
|
|||||||
{{/unless}}
|
{{/unless}}
|
||||||
</iron-collapse>
|
</iron-collapse>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
|
{{#each customLibraries}}
|
||||||
|
<div class="paper-font-body2 category-header clickable">
|
||||||
|
<iron-icon icon="chevron-right" class="{{#if isOpen _id}}open{{/if}}">
|
||||||
|
</iron-icon>
|
||||||
|
{{name}}
|
||||||
|
</div>
|
||||||
|
<iron-collapse opened={{isOpen _id}}>
|
||||||
|
<table style="width: 100%">
|
||||||
|
<tbody>
|
||||||
|
{{#each item in (itemsInLibrary _id)}}
|
||||||
|
{{>libraryItem item=item selected=(isSelected item)}}
|
||||||
|
{{/each}}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{{#unless ready _id}}
|
||||||
|
<paper-spinner active></paper-spinner>
|
||||||
|
{{/unless}}
|
||||||
|
</iron-collapse>
|
||||||
|
{{/each}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -7,6 +7,13 @@ const categories = [
|
|||||||
{name: "Tools", key: "tools"},
|
{name: "Tools", key: "tools"},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const categoryKeys = [
|
||||||
|
"weapons",
|
||||||
|
"armor",
|
||||||
|
"adventuringGear",
|
||||||
|
"tools",
|
||||||
|
];
|
||||||
|
|
||||||
Template.itemLibraryDialog.onCreated(function(){
|
Template.itemLibraryDialog.onCreated(function(){
|
||||||
this.selectedItem = new ReactiveVar();
|
this.selectedItem = new ReactiveVar();
|
||||||
this.searchTerm = new ReactiveVar();
|
this.searchTerm = new ReactiveVar();
|
||||||
@@ -14,10 +21,17 @@ Template.itemLibraryDialog.onCreated(function(){
|
|||||||
this.readyDict = new ReactiveDict();
|
this.readyDict = new ReactiveDict();
|
||||||
this.searchReady = new ReactiveVar();
|
this.searchReady = new ReactiveVar();
|
||||||
librarySubs.subscribe("standardLibraries");
|
librarySubs.subscribe("standardLibraries");
|
||||||
|
librarySubs.subscribe("customLibraries");
|
||||||
|
|
||||||
this.autorun(() => {
|
this.autorun(() => {
|
||||||
// Subscribe to all open categories
|
// Subscribe to all open categories
|
||||||
_.each(this.categoriesOpen.get(), (key) => {
|
_.each(this.categoriesOpen.get(), (key) => {
|
||||||
var handle = librarySubs.subscribe("standardLibraryItems", key);
|
let handle;
|
||||||
|
if (_.contains(categoryKeys, key)){
|
||||||
|
handle = librarySubs.subscribe("standardLibraryItems", key);
|
||||||
|
} else {
|
||||||
|
handle = librarySubs.subscribe("libraryItems", key);
|
||||||
|
}
|
||||||
this.autorun(() => {
|
this.autorun(() => {
|
||||||
this.readyDict.set(key, handle.ready());
|
this.readyDict.set(key, handle.ready());
|
||||||
});
|
});
|
||||||
@@ -70,12 +84,29 @@ Template.itemLibraryDialog.helpers({
|
|||||||
const searchTerm = Template.instance().searchTerm.get();
|
const searchTerm = Template.instance().searchTerm.get();
|
||||||
if (!searchTerm) return;
|
if (!searchTerm) return;
|
||||||
return LibraryItems.find({
|
return LibraryItems.find({
|
||||||
library: "SRDLibraryGA3XWsd",
|
|
||||||
name: {
|
name: {
|
||||||
$regex: new RegExp(".*" + searchTerm + ".*", "gi")
|
$regex: new RegExp(".*" + searchTerm + ".*", "gi")
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
customLibraries(){
|
||||||
|
let userId = Meteor.userId();
|
||||||
|
return Libraries.find({
|
||||||
|
$or: [
|
||||||
|
{readers: userId},
|
||||||
|
{writers: userId},
|
||||||
|
{owner: userId},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
},
|
||||||
|
itemsInLibrary(libraryId){
|
||||||
|
return LibraryItems.find({
|
||||||
|
library: libraryId,
|
||||||
|
}, {
|
||||||
|
sort: {name: 1},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Template.itemLibraryDialog.events({
|
Template.itemLibraryDialog.events({
|
||||||
@@ -93,7 +124,7 @@ Template.itemLibraryDialog.events({
|
|||||||
},
|
},
|
||||||
"click .category-header": function(event, template){
|
"click .category-header": function(event, template){
|
||||||
let cats = template.categoriesOpen.get();
|
let cats = template.categoriesOpen.get();
|
||||||
const key = this.key;
|
const key = this.key || this._id;
|
||||||
// Toggle whether this key is in the array or not
|
// Toggle whether this key is in the array or not
|
||||||
if (_.contains(cats, key)){
|
if (_.contains(cats, key)){
|
||||||
cats = _.without(cats, key);
|
cats = _.without(cats, key);
|
||||||
|
|||||||
@@ -11,16 +11,20 @@
|
|||||||
<div class="bottom list">
|
<div class="bottom list">
|
||||||
{{#each levels}}{{#if showSlots ..}}
|
{{#each levels}}{{#if showSlots ..}}
|
||||||
<div class="item-slot">
|
<div class="item-slot">
|
||||||
<div class="item spellSlot layout horizontal center">
|
<div class="white spellSlot layout horizontal center ">
|
||||||
<div style="margin-right: 16px">
|
<div class="spellLevelName" style="margin-right: 16px; margin-left: 8px; cursor: pointer;">
|
||||||
{{name}}
|
{{name}}
|
||||||
</div>
|
</div>
|
||||||
<div class="flex layout horizontal center">
|
<div class="flex layout horizontal center wrap">
|
||||||
{{#each slotBubbles ..}}
|
{{#each slotBubbles ..}}
|
||||||
<paper-icon-button class="slotBubble"
|
{{#unless overflow}}
|
||||||
icon={{icon}}
|
<paper-icon-button class="slotBubble"
|
||||||
disabled={{disabled}}>
|
icon={{icon}}
|
||||||
</paper-icon-button>
|
disabled={{disabled}}>
|
||||||
|
</paper-icon-button>
|
||||||
|
{{else}}
|
||||||
|
<div class="paper-font-subhead">+{{overflow}}</div>
|
||||||
|
{{/unless}}
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -122,6 +122,7 @@ Template.spells.helpers({
|
|||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
slotBubbles: function(char){
|
slotBubbles: function(char){
|
||||||
|
const MAX_SLOTS = 10;
|
||||||
var baseSlots = Characters.calculate.attributeBase(
|
var baseSlots = Characters.calculate.attributeBase(
|
||||||
char._id, "level" + this.level + "SpellSlots"
|
char._id, "level" + this.level + "SpellSlots"
|
||||||
);
|
);
|
||||||
@@ -130,16 +131,28 @@ Template.spells.helpers({
|
|||||||
);
|
);
|
||||||
var slotsUsed = baseSlots - currentSlots;
|
var slotsUsed = baseSlots - currentSlots;
|
||||||
var bubbles = [];
|
var bubbles = [];
|
||||||
var i;
|
var i, overflowFilled, overflowEmpty;
|
||||||
for (i = 0; i < currentSlots; i++){
|
var filledSlots = currentSlots;
|
||||||
|
var maxEmptySlots = Math.max(MAX_SLOTS - filledSlots + 1, 1);
|
||||||
|
var emptySlots = slotsUsed;
|
||||||
|
if (baseSlots > MAX_SLOTS){
|
||||||
|
filledSlots = Math.min(MAX_SLOTS, filledSlots);
|
||||||
|
overflowFilled = Math.max(currentSlots - MAX_SLOTS, 0);
|
||||||
|
emptySlots = Math.min(maxEmptySlots, emptySlots);
|
||||||
|
overflowEmpty = Math.max(slotsUsed - maxEmptySlots, 0);
|
||||||
|
}
|
||||||
|
for (i = 0; i < filledSlots; i++){
|
||||||
bubbles.push({
|
bubbles.push({
|
||||||
icon: "radio-button-checked",
|
icon: "radio-button-checked",
|
||||||
disabled: i !== currentSlots - 1 || !canEditCharacter(char._id), //last full slot not disabled
|
disabled: i !== filledSlots - 1 || !canEditCharacter(char._id), //last full slot not disabled
|
||||||
attribute: "level" + this.level + "SpellSlots",
|
attribute: "level" + this.level + "SpellSlots",
|
||||||
charId: char._id,
|
charId: char._id,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
for (i = 0; i < slotsUsed; i++){
|
if (overflowFilled){
|
||||||
|
bubbles.push({overflow: overflowFilled});
|
||||||
|
}
|
||||||
|
for (i = 0; i < emptySlots; i++){
|
||||||
bubbles.push({
|
bubbles.push({
|
||||||
icon: "radio-button-unchecked",
|
icon: "radio-button-unchecked",
|
||||||
disabled: i !== 0 || !canEditCharacter(char._id), //first empty slot not disabled
|
disabled: i !== 0 || !canEditCharacter(char._id), //first empty slot not disabled
|
||||||
@@ -147,6 +160,9 @@ Template.spells.helpers({
|
|||||||
charId: char._id,
|
charId: char._id,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (overflowEmpty){
|
||||||
|
bubbles.push({overflow: overflowEmpty});
|
||||||
|
}
|
||||||
return bubbles;
|
return bubbles;
|
||||||
},
|
},
|
||||||
slotStatName: function() {
|
slotStatName: function() {
|
||||||
@@ -178,7 +194,7 @@ Template.spells.events({
|
|||||||
}
|
}
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
},
|
},
|
||||||
"click .spellSlot": function(event, instance) {
|
"click .spellLevelName": function(event, instance) {
|
||||||
var name = "Level " + this.level + " Spell Slots";
|
var name = "Level " + this.level + " Spell Slots";
|
||||||
var stat = "level" + this.level + "SpellSlots";
|
var stat = "level" + this.level + "SpellSlots";
|
||||||
var charId = instance.data._id;
|
var charId = instance.data._id;
|
||||||
|
|||||||
@@ -1,5 +1,29 @@
|
|||||||
<template name="itemLibrary">
|
<template name="itemLibrary">
|
||||||
{{#each items}}
|
{{#each libraries}}
|
||||||
{{> libraryItem}}
|
<div class="paper-font-subhead library-header layout horizontal center" data-id={{_id}} style="height: 40px;">
|
||||||
|
<iron-icon icon="chevron-right" class="{{#if isOpen _id}}open{{/if}}">
|
||||||
|
</iron-icon>
|
||||||
|
<div class="flex">{{name}}</div>
|
||||||
|
{{#if isOpen _id}}
|
||||||
|
<div class="relative">
|
||||||
|
<paper-icon-button icon="create" class="editLibrary"></paper-icon-button>
|
||||||
|
{{#simpleTooltip}}Edit Library{{/simpleTooltip}}
|
||||||
|
</div>
|
||||||
|
<div class="relative">
|
||||||
|
<paper-icon-button icon="add" class="addItem"></paper-icon-button>
|
||||||
|
{{#simpleTooltip}}Add Item{{/simpleTooltip}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
<iron-collapse opened={{isOpen _id}}>
|
||||||
|
{{#each libraryItems}}
|
||||||
|
<paper-item class="short item-name" data-id={{_id}}>
|
||||||
|
{{name}}
|
||||||
|
</paper-item>
|
||||||
|
{{/each}}
|
||||||
|
{{#unless ready _id}}
|
||||||
|
<paper-spinner active></paper-spinner>
|
||||||
|
{{/unless}}
|
||||||
|
</iron-collapse>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,7 +1,94 @@
|
|||||||
|
const librarySubs = new SubsManager();
|
||||||
|
|
||||||
|
Template.itemLibrary.onCreated(function(){
|
||||||
|
this.selectedTab = new ReactiveVar("0");
|
||||||
|
this.librariesOpen = new ReactiveVar([]);
|
||||||
|
this.readyDict = new ReactiveDict();
|
||||||
|
this.autorun(() => {
|
||||||
|
// Subscribe to all open libraries
|
||||||
|
_.each(this.librariesOpen.get(), (libraryId) => {
|
||||||
|
var handle = librarySubs.subscribe("libraryItems", libraryId);
|
||||||
|
this.autorun(() => {
|
||||||
|
this.readyDict.set(libraryId, handle.ready());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
Template.itemLibrary.helpers({
|
Template.itemLibrary.helpers({
|
||||||
items(){
|
selectedTab(){
|
||||||
return Items.find({charId: {$in: [
|
return Template.instance().selectedTab.get();
|
||||||
"SRDLibrary",
|
},
|
||||||
]}});
|
libraries(){
|
||||||
|
let userId = Meteor.userId();
|
||||||
|
return Libraries.find({
|
||||||
|
$or: [
|
||||||
|
{readers: userId},
|
||||||
|
{writers: userId},
|
||||||
|
{owner: userId},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
},
|
||||||
|
libraryItems(){
|
||||||
|
return LibraryItems.find({
|
||||||
|
library: this._id
|
||||||
|
},{
|
||||||
|
sort: {name: 1}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
ready(libraryId){
|
||||||
|
return Template.instance().readyDict.get(libraryId);
|
||||||
|
},
|
||||||
|
isOpen(libraryId){
|
||||||
|
const librariesOpen = Template.instance().librariesOpen.get();
|
||||||
|
return _.contains(librariesOpen, libraryId);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Template.itemLibrary.events({
|
||||||
|
"click .library-header": function(event, template){
|
||||||
|
let libs = template.librariesOpen.get();
|
||||||
|
const libraryId = this._id;
|
||||||
|
// Toggle whether this key is in the array or not
|
||||||
|
if (_.contains(libs, libraryId)){
|
||||||
|
libs = _.without(libs, libraryId);
|
||||||
|
} else {
|
||||||
|
libs.push(libraryId);
|
||||||
|
}
|
||||||
|
template.librariesOpen.set(libs);
|
||||||
|
},
|
||||||
|
"click .editLibrary": function(event, instance){
|
||||||
|
event.stopPropagation();
|
||||||
|
var libraryId = this._id;
|
||||||
|
pushDialogStack({
|
||||||
|
template: "libraryDialog",
|
||||||
|
data: {libraryId},
|
||||||
|
element: event.currentTarget.parentElement.parentElement,
|
||||||
|
returnElement: () => instance.find(`.library-header[data-id='${libraryId}']`),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
"click .addItem": function(event, instance){
|
||||||
|
event.stopPropagation();
|
||||||
|
var libraryId = this._id;
|
||||||
|
var itemId = LibraryItems.insert({
|
||||||
|
name: "New Library Item",
|
||||||
|
library: libraryId,
|
||||||
|
});
|
||||||
|
pushDialogStack({
|
||||||
|
template: "libraryItemDialog",
|
||||||
|
data: {itemId},
|
||||||
|
element: event.currentTarget,
|
||||||
|
returnElement: () => instance.find(`.item-name[data-id='${itemId}']`),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
"click .item-name": function(event, instance){
|
||||||
|
event.stopPropagation();
|
||||||
|
var itemId = this._id;
|
||||||
|
pushDialogStack({
|
||||||
|
template: "libraryItemDialog",
|
||||||
|
data: {itemId},
|
||||||
|
element: event.currentTarget,
|
||||||
|
returnElement: () => instance.find(`.item-name[data-id='${itemId}']`),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|||||||
@@ -0,0 +1,88 @@
|
|||||||
|
<template name="libraryItemDialog">
|
||||||
|
<div class="fit base-dialog layout vertical">
|
||||||
|
<app-toolbar>
|
||||||
|
<paper-icon-button id="backButton"
|
||||||
|
icon="arrow-back">
|
||||||
|
</paper-icon-button>
|
||||||
|
<div main-title>{{item.name}}</div>
|
||||||
|
<paper-icon-button id="deleteButton"
|
||||||
|
role="button"
|
||||||
|
tabindex="0"
|
||||||
|
icon="delete">
|
||||||
|
</paper-icon-button>
|
||||||
|
</app-toolbar>
|
||||||
|
<div class="form flex scroll-y" style="position: relative;">
|
||||||
|
<paper-input id="libraryItemLibraryNameInput" class="fullwidth" label="Library name (optional)" value={{item.libraryName}}></paper-input>
|
||||||
|
<paper-input id="libraryItemNameInput" class="fullwidth" label="Item name" value={{item.name}}></paper-input>
|
||||||
|
<div class="layout horizontal center wrap">
|
||||||
|
<paper-input id="libraryItemPluralInput" class="flex" label="Plural name" value={{item.plural}}></paper-input>
|
||||||
|
<paper-input id="libraryItemQuantityInput" class="flex" label="Quantity" type="number" value={{item.quantity}}></paper-input>
|
||||||
|
<paper-checkbox id="incrementCheckbox" class="flex" checked={{item.settings.showIncrement}}>
|
||||||
|
Show Increment
|
||||||
|
</paper-checkbox>
|
||||||
|
</div>
|
||||||
|
<div class="layout horizontal center wrap">
|
||||||
|
<paper-input id="libraryItemValueInput" class="flex" label="Value" type="number" value={{item.value}}></paper-input>
|
||||||
|
<paper-input id="libraryItemWeightInput" class="flex" label="Weight" type="number" value={{item.weight}}></paper-input>
|
||||||
|
<paper-checkbox id="attunementCheckbox" class="flex" checked={{item.requiresAttunement}}>
|
||||||
|
Requires Attunement
|
||||||
|
</paper-checkbox>
|
||||||
|
</div>
|
||||||
|
<paper-textarea id="libraryItemDescriptionInput" label="Description" value={{item.description}}></paper-textarea>
|
||||||
|
<div style="margin-top: 8px;">
|
||||||
|
<div class="paper-font-subhead">Effects</div>
|
||||||
|
{{#each indexedEffects}}
|
||||||
|
<div class="effect layout horizontal center wrap">
|
||||||
|
<paper-dropdown-menu label="Operation" class="operationMenu">
|
||||||
|
<paper-listbox class="dropdown-content" selected={{operationIndex operation}}>
|
||||||
|
<paper-item label="Base Value" name="base"> Base Value </paper-item>
|
||||||
|
<paper-item label="Add" name="add"> Add </paper-item>
|
||||||
|
<paper-item label="Multiply" name="mul"> Multiply </paper-item>
|
||||||
|
<paper-item label="Min" name="min"> Min </paper-item>
|
||||||
|
<paper-item label="Max" name="max"> Max </paper-item>
|
||||||
|
<paper-item label="Advantage" name="advantage"> Advantage </paper-item>
|
||||||
|
<paper-item label="Disadvantage" name="disadvantage"> Disadvantage </paper-item>
|
||||||
|
<paper-item label="PassiveAdd" name="passiveAdd"> PassiveAdd </paper-item>
|
||||||
|
<paper-item label="Fail" name="fail"> Fail </paper-item>
|
||||||
|
<paper-item label="Conditional" name="conditional"> Conditional </paper-item>
|
||||||
|
</paper-listbox>
|
||||||
|
</paper-dropdown-menu>
|
||||||
|
<paper-input class="LibraryItemEffectStat flex" label="Attribute" value={{stat}}></paper-input>
|
||||||
|
<paper-input class="LibraryItemEffectValue flex" label="Value" value={{calculationOrValue}}></paper-input>
|
||||||
|
<paper-icon-button icon="delete" class="deleteEffect"></paper-icon-button>
|
||||||
|
</div>
|
||||||
|
{{/each}}
|
||||||
|
<paper-button id="addEffect" class="red-button">Add Effect</paper-button>
|
||||||
|
</div>
|
||||||
|
<div style="margin-top: 8px;">
|
||||||
|
<div class="paper-font-subhead">Attacks</div>
|
||||||
|
{{#each indexedAttacks}}
|
||||||
|
<div class="effect layout horizontal center wrap">
|
||||||
|
<paper-input class="LibraryItemAttackBonusInput flex" label="Attack Bonus" value={{attackBonus}}></paper-input>
|
||||||
|
<paper-input class="LibraryItemAttackDamageInput flex" label="Damage" value={{damage}}></paper-input>
|
||||||
|
<paper-input class="LibraryItemAttackDetailsInput flex" label="Details" value={{details}}></paper-input>
|
||||||
|
<paper-dropdown-menu label="Damage Type" class="damageTypeMenu">
|
||||||
|
<paper-listbox class="dropdown-content" selected={{damageTypeIndex damageType}}>
|
||||||
|
<paper-item label="Bludgeoning" name="bludgeoning"> Bludgeoning </paper-item>
|
||||||
|
<paper-item label="Piercing" name="piercing"> Piercing </paper-item>
|
||||||
|
<paper-item label="Slashing" name="slashing"> Slashing </paper-item>
|
||||||
|
<paper-item label="Acid" name="acid"> Acid </paper-item>
|
||||||
|
<paper-item label="Cold" name="cold"> Cold </paper-item>
|
||||||
|
<paper-item label="Fire" name="fire"> Fire </paper-item>
|
||||||
|
<paper-item label="Force" name="force"> Force </paper-item>
|
||||||
|
<paper-item label="Lightning" name="lightning"> Lightning </paper-item>
|
||||||
|
<paper-item label="Necrotic" name="necrotic"> Necrotic </paper-item>
|
||||||
|
<paper-item label="Poison" name="poison"> Poison </paper-item>
|
||||||
|
<paper-item label="Psychic" name="psychic"> Psychic </paper-item>
|
||||||
|
<paper-item label="Radiant" name="radiant"> Radiant </paper-item>
|
||||||
|
<paper-item label="Thunder" name="thunder"> Thunder </paper-item>
|
||||||
|
</paper-listbox>
|
||||||
|
</paper-dropdown-menu>
|
||||||
|
<paper-icon-button icon="delete" class="deleteAttack"></paper-icon-button>
|
||||||
|
</div>
|
||||||
|
{{/each}}
|
||||||
|
<paper-button id="addAttack" class="red-button">Add Attack</paper-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,204 @@
|
|||||||
|
Template.libraryItemDialog.helpers({
|
||||||
|
item(){
|
||||||
|
return LibraryItems.findOne(this.itemId);
|
||||||
|
},
|
||||||
|
calculationOrValue(){
|
||||||
|
return this.calculation || this.value;
|
||||||
|
},
|
||||||
|
indexedEffects(){
|
||||||
|
let item = LibraryItems.findOne(this.itemId);
|
||||||
|
if (!item) return;
|
||||||
|
return _.map(item.effects, (effect, index) => {
|
||||||
|
if (!effect) return;
|
||||||
|
effect.index = index;
|
||||||
|
return effect;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
indexedAttacks(){
|
||||||
|
let item = LibraryItems.findOne(this.itemId);
|
||||||
|
if (!item) return;
|
||||||
|
return _.map(item.attacks, (attack, index) => {
|
||||||
|
if (!attack) return;
|
||||||
|
attack.index = index;
|
||||||
|
return attack;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
operationIndex(operation){
|
||||||
|
const ref = {
|
||||||
|
base: 0,
|
||||||
|
add: 1,
|
||||||
|
mul: 2,
|
||||||
|
min: 3,
|
||||||
|
max: 4,
|
||||||
|
advantage: 5,
|
||||||
|
disadvantage: 6,
|
||||||
|
passiveAdd: 7,
|
||||||
|
fail: 8,
|
||||||
|
conditional: 9,
|
||||||
|
};
|
||||||
|
return ref[operation];
|
||||||
|
},
|
||||||
|
damageTypeIndex(damageType){
|
||||||
|
const ref = {
|
||||||
|
bludgeoning: 0,
|
||||||
|
piercing: 1,
|
||||||
|
slashing: 2,
|
||||||
|
acid: 3,
|
||||||
|
cold: 4,
|
||||||
|
fire: 5,
|
||||||
|
force: 6,
|
||||||
|
lightning: 7,
|
||||||
|
necrotic: 8,
|
||||||
|
poison: 9,
|
||||||
|
psychic: 10,
|
||||||
|
radiant: 11,
|
||||||
|
thunder: 12,
|
||||||
|
};
|
||||||
|
return ref[damageType];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const bind = function(field){
|
||||||
|
return _.debounce(function(event){
|
||||||
|
const input = event.currentTarget;
|
||||||
|
var value = input.value;
|
||||||
|
LibraryItems.update(this.itemId, {
|
||||||
|
$set: {[field]: value}
|
||||||
|
}, {
|
||||||
|
removeEmptyStrings: false,
|
||||||
|
trimStrings: false,
|
||||||
|
});
|
||||||
|
}, 300);
|
||||||
|
};
|
||||||
|
|
||||||
|
Template.libraryItemDialog.events({
|
||||||
|
"click #backButton": function(){
|
||||||
|
popDialogStack();
|
||||||
|
},
|
||||||
|
"click #deleteButton": function(){
|
||||||
|
LibraryItems.remove(this.itemId);
|
||||||
|
popDialogStack();
|
||||||
|
},
|
||||||
|
"input #libraryItemLibraryNameInput": bind("libraryName"),
|
||||||
|
"input #libraryItemNameInput": bind("name"),
|
||||||
|
"input #libraryItemPluralInput": bind("plural"),
|
||||||
|
"input #libraryItemQuantityInput": bind("quantity"),
|
||||||
|
"input #libraryItemValueInput": bind("value"),
|
||||||
|
"input #libraryItemWeightInput": bind("weight"),
|
||||||
|
"change #attunementCheckbox": function(event){
|
||||||
|
LibraryItems.update(this.itemId, {
|
||||||
|
$set: {requiresAttunement: event.currentTarget.checked}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
"change #incrementCheckbox": function(event){
|
||||||
|
LibraryItems.update(this.itemId, {
|
||||||
|
$set: {"settings.showIncrement": event.currentTarget.checked}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
"input #libraryItemDescriptionInput": bind("description"),
|
||||||
|
|
||||||
|
// Effects
|
||||||
|
"click #addEffect": function(event, template){
|
||||||
|
LibraryItems.update(template.data.itemId, {
|
||||||
|
$push: {
|
||||||
|
effects: {operation: "add"}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
"iron-select .operationMenu": function(event, template){
|
||||||
|
var detail = event.originalEvent.detail;
|
||||||
|
var opName = detail.item.getAttribute("name");
|
||||||
|
if (opName == this.operation) return;
|
||||||
|
Meteor.call("updateLibraryItemEffect", {
|
||||||
|
itemId: template.data.itemId,
|
||||||
|
effectIndex: this.index,
|
||||||
|
field: "operation",
|
||||||
|
value: opName,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
"input .LibraryItemEffectStat": _.debounce(function(event, template){
|
||||||
|
Meteor.call("updateLibraryItemEffect", {
|
||||||
|
itemId: template.data.itemId,
|
||||||
|
effectIndex: this.index,
|
||||||
|
field: "stat",
|
||||||
|
value: event.currentTarget.value,
|
||||||
|
});
|
||||||
|
}, 300),
|
||||||
|
"input .LibraryItemEffectValue": _.debounce(function(event, template){
|
||||||
|
let value = event.currentTarget.value;
|
||||||
|
if (value && _.isFinite(+value)){
|
||||||
|
Meteor.call("updateLibraryItemEffect", {
|
||||||
|
itemId: template.data.itemId,
|
||||||
|
effectIndex: this.index,
|
||||||
|
field: "value",
|
||||||
|
unsetField: "calculation",
|
||||||
|
value,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Meteor.call("updateLibraryItemEffect", {
|
||||||
|
itemId: template.data.itemId,
|
||||||
|
effectIndex: this.index,
|
||||||
|
field: "calculation",
|
||||||
|
unsetField: "value",
|
||||||
|
value,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, 300),
|
||||||
|
"click .deleteEffect": function (event, template) {
|
||||||
|
Meteor.call("removeLibraryItemEffect", {
|
||||||
|
itemId: template.data.itemId,
|
||||||
|
effectIndex: this.index,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// Attacks
|
||||||
|
"click #addAttack": function(event, template){
|
||||||
|
LibraryItems.update(template.data.itemId, {
|
||||||
|
$push: {
|
||||||
|
attacks: {damageType: "slashing"}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
"iron-select .damageTypeMenu": function(event, template){
|
||||||
|
var detail = event.originalEvent.detail;
|
||||||
|
var damageType = detail.item.getAttribute("name");
|
||||||
|
if (damageType == this.damageType) return;
|
||||||
|
Meteor.call("updateLibraryItemAttack", {
|
||||||
|
itemId: template.data.itemId,
|
||||||
|
attackIndex: this.index,
|
||||||
|
field: "damageType",
|
||||||
|
value: damageType,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
"input .LibraryItemAttackBonusInput": _.debounce(function(event, template){
|
||||||
|
Meteor.call("updateLibraryItemAttack", {
|
||||||
|
itemId: template.data.itemId,
|
||||||
|
attackIndex: this.index,
|
||||||
|
field: "attackBonus",
|
||||||
|
value: event.currentTarget.value,
|
||||||
|
});
|
||||||
|
}, 300),
|
||||||
|
"input .LibraryItemAttackDamageInput": _.debounce(function(event, template){
|
||||||
|
Meteor.call("updateLibraryItemAttack", {
|
||||||
|
itemId: template.data.itemId,
|
||||||
|
attackIndex: this.index,
|
||||||
|
field: "damage",
|
||||||
|
value: event.currentTarget.value,
|
||||||
|
});
|
||||||
|
}, 300),
|
||||||
|
"input .LibraryItemAttackDetailsInput": _.debounce(function(event, template){
|
||||||
|
Meteor.call("updateLibraryItemAttack", {
|
||||||
|
itemId: template.data.itemId,
|
||||||
|
attackIndex: this.index,
|
||||||
|
field: "details",
|
||||||
|
value: event.currentTarget.value,
|
||||||
|
});
|
||||||
|
}, 300),
|
||||||
|
|
||||||
|
"click .deleteAttack": function (event, template) {
|
||||||
|
Meteor.call("removeLibraryItemAttack", {
|
||||||
|
itemId: template.data.itemId,
|
||||||
|
attackIndex: this.index,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
19
app/client/views/library/library.css
Normal file
19
app/client/views/library/library.css
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
.library .item-name {
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.library .library-header {
|
||||||
|
font-weight: 500;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.library .library-header iron-icon {
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.library .library-header iron-icon.open {
|
||||||
|
transform: rotate(90deg);
|
||||||
|
}
|
||||||
@@ -1,26 +1,32 @@
|
|||||||
<template name="library">
|
<template name="library">
|
||||||
<div class="fit layout vertical character-sheet">
|
<div class="fit layout vertical library">
|
||||||
<app-header fixed effects="waterfall">
|
<app-header fixed effects="waterfall">
|
||||||
<app-toolbar class="medium-tall app-grey white-text">
|
<app-toolbar class="medium-tall app-grey white-text">
|
||||||
<div top-item class="layout horizontal center">
|
<div top-item class="layout horizontal center">
|
||||||
<paper-icon-button icon="menu" drawer-toggle></paper-icon-button>
|
<paper-icon-button icon="menu" drawer-toggle></paper-icon-button>
|
||||||
<div class="flex">
|
<div class="flex layout horizontal center" style="height: 40px; margin-left: 8px;">
|
||||||
Library
|
Item Library
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!--
|
||||||
<div bottom-item>
|
<div bottom-item>
|
||||||
<paper-tabs id="characterSheetTabs" selected={{selectedTab}} class="app-grey white-text">
|
<paper-tabs id="libraryTabs" selected={{selectedTab}} class="app-grey white-text">
|
||||||
<paper-tab name="items">Items</paper-tab>
|
<paper-tab name="items">Items</paper-tab>
|
||||||
<paper-tab name="spells">Spells</paper-tab>
|
<paper-tab name="spells">Spells</paper-tab>
|
||||||
</paper-tabs>
|
</paper-tabs>
|
||||||
</div>
|
</div>
|
||||||
|
-->
|
||||||
</app-toolbar>
|
</app-toolbar>
|
||||||
</app-header>
|
</app-header>
|
||||||
<div class="flex" style="position: relative;">
|
<div class="flex" style="position: relative;">
|
||||||
<iron-pages id="tabPages" class="fit" selected={{selectedTab}}>
|
<!-- <iron-pages id="tabPages" class="fit" selected={{selectedTab}}> -->
|
||||||
<div name="items" class="tab-page fit">{{> itemLibrary}}</div>
|
<div name="items" class="tab-page fit">{{> itemLibrary}}</div>
|
||||||
<div name="spells" class="tab-page fit">{{! {{> spellLibrary}} }}</div>
|
<!-- <div name="spells" class="tab-page fit">{{! {{> spellLibrary}} }}</div>
|
||||||
</iron-pages>
|
</iron-pages> -->
|
||||||
|
</div>
|
||||||
|
<div class="floatyButton">
|
||||||
|
<paper-fab id="addLibrary" icon="add"></paper-fab>
|
||||||
|
{{#simpleTooltip}}Add Library{{/simpleTooltip}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
29
app/client/views/library/library.js
Normal file
29
app/client/views/library/library.js
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
const librarySubs = new SubsManager();
|
||||||
|
|
||||||
|
Template.library.onCreated(function(){
|
||||||
|
this.selectedTab = new ReactiveVar("0");
|
||||||
|
});
|
||||||
|
|
||||||
|
Template.library.helpers({
|
||||||
|
selectedTab(){
|
||||||
|
return Template.instance().selectedTab.get();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Template.library.events({
|
||||||
|
"iron-select #libraryTabs": function(event, instance){
|
||||||
|
instance.selectedTab.set(event.target.selected);
|
||||||
|
},
|
||||||
|
"click #addLibrary": function(event, instance){
|
||||||
|
var libraryId = Libraries.insert({
|
||||||
|
name: "New Library",
|
||||||
|
owner: Meteor.userId(),
|
||||||
|
});
|
||||||
|
pushDialogStack({
|
||||||
|
template: "libraryDialog",
|
||||||
|
data: {libraryId},
|
||||||
|
element: event.currentTarget,
|
||||||
|
returnElement: () => instance.find(`.library-header[data-id='${libraryId}']`),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
})
|
||||||
58
app/client/views/library/libraryDialog/libraryDialog.html
Normal file
58
app/client/views/library/libraryDialog/libraryDialog.html
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
<template name="libraryDialog">
|
||||||
|
<div class="fit base-dialog layout vertical">
|
||||||
|
<app-toolbar>
|
||||||
|
<div main-title>{{library.name}}</div>
|
||||||
|
<paper-icon-button id="deleteButton"
|
||||||
|
role="button"
|
||||||
|
tabindex="0"
|
||||||
|
icon="delete">
|
||||||
|
</paper-icon-button>
|
||||||
|
</app-toolbar>
|
||||||
|
<div class="form flex scroll-y" style="position: relative;">
|
||||||
|
<paper-input id="libraryNameInput" class="fullwidth" label="Name" value={{library.name}}></paper-input>
|
||||||
|
<div class="layout horizontal center wrap">
|
||||||
|
<paper-input class="flex" id="userNameOrEmailInput" label="Share with username or email" floatinglabel></paper-input>
|
||||||
|
<paper-button id="shareButton"
|
||||||
|
class="red-button"
|
||||||
|
style="width: 80px; height: 37px; margin-top: 16px;"
|
||||||
|
raised
|
||||||
|
disabled={{shareButtonDisabled}}>Share</paper-button>
|
||||||
|
<paper-radio-group id="accessLevelMenu" selected="read">
|
||||||
|
<paper-radio-button name="read">View Only</paper-radio-button>
|
||||||
|
<paper-radio-button name="write">Can Edit</paper-radio-button>
|
||||||
|
</paper-radio-group>
|
||||||
|
</div>
|
||||||
|
<p style="color: red;">{{userFindError}}</p>
|
||||||
|
<div>
|
||||||
|
{{#if readers.length}}
|
||||||
|
<div class="paper-font-subhead">
|
||||||
|
Can View
|
||||||
|
</div>
|
||||||
|
{{#each id in readers}}
|
||||||
|
<div class="layout horizontal center">
|
||||||
|
{{#with id=id}}
|
||||||
|
<paper-icon-button class="deleteShare" icon="delete">
|
||||||
|
</paper-icon-button>
|
||||||
|
{{/with}}
|
||||||
|
<div class="flex">{{username id}}</div>
|
||||||
|
</div>
|
||||||
|
{{/each}}
|
||||||
|
{{/if}}
|
||||||
|
{{#if writers.length}}
|
||||||
|
<div class="paper-font-subhead">
|
||||||
|
Can Edit
|
||||||
|
</div>
|
||||||
|
{{#each id in writers}}
|
||||||
|
<div class="layout horizontal center">
|
||||||
|
{{#with id=id}}
|
||||||
|
<paper-icon-button class="deleteShare" icon="delete">
|
||||||
|
</paper-icon-button>
|
||||||
|
{{/with}}
|
||||||
|
<div class="flex">{{username id}}</div>
|
||||||
|
</div>
|
||||||
|
{{/each}}
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
95
app/client/views/library/libraryDialog/libraryDialog.js
Normal file
95
app/client/views/library/libraryDialog/libraryDialog.js
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
Template.libraryDialog.onCreated(function(){
|
||||||
|
this.userId = new ReactiveVar();
|
||||||
|
this.autorun(() => {
|
||||||
|
var library = Libraries.findOne(Template.currentData().libraryId, {
|
||||||
|
fields: {readers: 1, writers: 1, owner: 1}
|
||||||
|
});
|
||||||
|
if (!library) return;
|
||||||
|
this.subscribe("userNames", _.union(library.readers, library.writers, [library.owner]));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
Template.libraryDialog.helpers({
|
||||||
|
library(){
|
||||||
|
return Libraries.findOne(this.libraryId);
|
||||||
|
},
|
||||||
|
readers: function(){
|
||||||
|
var library = Libraries.findOne(this.libraryId, {fields: {readers: 1}});
|
||||||
|
return library && library.readers;
|
||||||
|
},
|
||||||
|
writers: function(){
|
||||||
|
var library = Libraries.findOne(this.libraryId, {fields: {writers: 1}});
|
||||||
|
return library && library.writers
|
||||||
|
},
|
||||||
|
username: function(id){
|
||||||
|
const user = Meteor.users.findOne(id);
|
||||||
|
return user && user.username || "user: " + id;
|
||||||
|
},
|
||||||
|
shareButtonDisabled: function(){
|
||||||
|
return !Template.instance().userId.get();
|
||||||
|
},
|
||||||
|
userFindError: function(){
|
||||||
|
if (!Template.instance().userId.get()){
|
||||||
|
return "User not found";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Template.libraryDialog.events({
|
||||||
|
"input #libraryNameInput": _.debounce(function(event){
|
||||||
|
const input = event.currentTarget;
|
||||||
|
var name = input.value;
|
||||||
|
if (!name){
|
||||||
|
input.invalid = true;
|
||||||
|
input.errorMessage = "Name is required";
|
||||||
|
} else {
|
||||||
|
input.invalid = false;
|
||||||
|
Libraries.update(this.libraryId, {
|
||||||
|
$set: {name}
|
||||||
|
}, {
|
||||||
|
removeEmptyStrings: false,
|
||||||
|
trimStrings: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, 300),
|
||||||
|
"click #deleteButton": function(){
|
||||||
|
Meteor.call("removeLibrary", this.libraryId);
|
||||||
|
popDialogStack();
|
||||||
|
},
|
||||||
|
"input #userNameOrEmailInput":
|
||||||
|
function(event, instance){
|
||||||
|
var userName = instance.find("#userNameOrEmailInput").value;
|
||||||
|
instance.userId.set(undefined);
|
||||||
|
Meteor.call("getUserId", userName, function(err, result) {
|
||||||
|
if (err){
|
||||||
|
console.error(err);
|
||||||
|
} else {
|
||||||
|
console.log(result);
|
||||||
|
instance.userId.set(result);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
"click #shareButton": function(event, instance){
|
||||||
|
var self = this;
|
||||||
|
var permission = instance.find("#accessLevelMenu").selected;
|
||||||
|
if (!permission) throw "no permission set";
|
||||||
|
var userId = instance.userId.get();
|
||||||
|
if (!userId) return;
|
||||||
|
if (permission === "write"){
|
||||||
|
Libraries.update(self.libraryId, {
|
||||||
|
$addToSet: {writers: userId},
|
||||||
|
$pull: {readers: userId},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Libraries.update(self.libraryId, {
|
||||||
|
$addToSet: {readers: userId},
|
||||||
|
$pull: {writers: userId},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"click .deleteShare": function(event, instance) {
|
||||||
|
Libraries.update(instance.data.libraryId, {
|
||||||
|
$pull: {writers: this.id, readers: this.id}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -3,10 +3,12 @@ var childSchema = new SimpleSchema({
|
|||||||
"parent.collection": {type: String},
|
"parent.collection": {type: String},
|
||||||
"parent.id": {type: String, regEx: SimpleSchema.RegEx.Id, index: 1},
|
"parent.id": {type: String, regEx: SimpleSchema.RegEx.Id, index: 1},
|
||||||
"parent.group": {type: String, optional: true},
|
"parent.group": {type: String, optional: true},
|
||||||
|
"removed": {type: Boolean, optional: true, index: 1},
|
||||||
"removedWith": {
|
"removedWith": {
|
||||||
optional: true,
|
optional: true,
|
||||||
type: String,
|
type: String,
|
||||||
regEx: SimpleSchema.RegEx.Id,
|
regEx: SimpleSchema.RegEx.Id,
|
||||||
|
index: 1,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -143,17 +145,17 @@ var checkPermission = function(userId, charId){
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
var cascadeSoftRemove = function(id, removedWithId){
|
var cascadeSoftRemove = function(parentId, removedWithId){
|
||||||
_.each(childCollections, function(treeCollection){
|
_.each(childCollections, function(treeCollection){
|
||||||
treeCollection.update(
|
treeCollection.update(
|
||||||
{"parent.id": id},
|
{"parent.id": parentId},
|
||||||
{$set: {
|
{$set: {
|
||||||
removed: true,
|
removed: true,
|
||||||
removedWith: removedWithId,
|
removedWith: removedWithId,
|
||||||
}},
|
}},
|
||||||
{multi: true}
|
{multi: true}
|
||||||
);
|
);
|
||||||
treeCollection.find({"parent.id": id}).forEach(function(doc){
|
treeCollection.find({"parent.id": parentId, removed: true}).forEach(function(doc){
|
||||||
cascadeSoftRemove(doc._id, removedWithId);
|
cascadeSoftRemove(doc._id, removedWithId);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,14 +1,9 @@
|
|||||||
Meteor.methods({
|
Meteor.methods({
|
||||||
"getUserId": function(username){
|
"getUserId": function(username){
|
||||||
if (!username) return;
|
if (!username) return;
|
||||||
regex = new RegExp("^" + username + "$", "i")
|
if (Meteor.isClient) return;
|
||||||
var user = Meteor.users.findOne(
|
let user = Accounts.findUserByUsername(username) ||
|
||||||
{$or: [
|
Accounts.findUserByEmail(username);
|
||||||
{username: username},
|
|
||||||
{"emails.address": regex},
|
|
||||||
{"services.google.email": regex},
|
|
||||||
]}
|
|
||||||
);
|
|
||||||
return user && user._id;
|
return user && user._id;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
988
app/package-lock.json
generated
988
app/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "rpg-docs",
|
"name": "dicecloud",
|
||||||
"version": "0.10.0",
|
"version": "0.10.0",
|
||||||
"description": "Unofficial Online Realtime D&D 5e App",
|
"description": "Unofficial Online Realtime D&D 5e App",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -11,16 +11,15 @@
|
|||||||
},
|
},
|
||||||
"author": "Stefan Zermatten",
|
"author": "Stefan Zermatten",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.0.0-beta.49",
|
"@babel/runtime": "^7.1.2",
|
||||||
"@polymer/polymer": "^1.2.5-npm-test.2",
|
"@polymer/polymer": "^1.2.5-npm-test.2",
|
||||||
"babel-runtime": "^6.26.0",
|
|
||||||
"bcrypt": "^1.0.3",
|
"bcrypt": "^1.0.3",
|
||||||
"bower": "^1.7.9",
|
"bower": "^1.7.9",
|
||||||
"core-js": "^2.5.7",
|
"core-js": "^2.5.7",
|
||||||
"fibers": "^2.0.2",
|
"fibers": "^2.0.2",
|
||||||
"meteor-node-stubs": "^0.3.3",
|
"meteor-node-stubs": "^0.3.3",
|
||||||
"qrcode": "^1.2.0",
|
"qrcode": "^1.3.0",
|
||||||
"source-map-support": "^0.5.6",
|
"source-map-support": "^0.5.9",
|
||||||
"underscore": "^1.9.1"
|
"underscore": "^1.9.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,29 +15,33 @@ Meteor.startup(() => {
|
|||||||
const now = new Date();
|
const now = new Date();
|
||||||
const thirtyMinutesAgo = new Date(now.getTime() - 30*60000);
|
const thirtyMinutesAgo = new Date(now.getTime() - 30*60000);
|
||||||
_.each(collections, (collection) => {
|
_.each(collections, (collection) => {
|
||||||
numRemoved += collection.remove({
|
collection.remove({
|
||||||
removed: true,
|
removed: true,
|
||||||
removedAt: {$lt: thirtyMinutesAgo} // dates *before* 30 minutes ago
|
removedAt: {$lt: thirtyMinutesAgo} // dates *before* 30 minutes ago
|
||||||
|
}, function(error, result){
|
||||||
|
if (error){
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
return numRemoved;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
SyncedCron.add({
|
SyncedCron.add({
|
||||||
name: "Delete all soft removed items that haven't been restored",
|
name: "deleteSoftRemovedDocs",
|
||||||
schedule: function(parser) {
|
schedule: function(parser) {
|
||||||
return parser.text('every 6 hours');
|
return parser.text('every 2 hours');
|
||||||
},
|
},
|
||||||
job: function() {
|
job: deleteOldSoftRemovedDocs,
|
||||||
deleteOldSoftRemovedDocs();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
SyncedCron.start();
|
||||||
|
|
||||||
// Add a method to manually trigger removal
|
// Add a method to manually trigger removal
|
||||||
Meteor.methods({
|
Meteor.methods({
|
||||||
deleteOldSoftRemovedDocs() {
|
deleteOldSoftRemovedDocs() {
|
||||||
const user = Meteor.users.findOne(this.userId);
|
const user = Meteor.users.findOne(this.userId);
|
||||||
if (user && _.contains(user.roles, "admin")){
|
if (user && _.contains(user.roles, "admin")){
|
||||||
|
this.unblock();
|
||||||
return deleteOldSoftRemovedDocs();
|
return deleteOldSoftRemovedDocs();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -23,3 +23,18 @@ Meteor.publish("standardLibrarySpells", function(level){
|
|||||||
sort: {name: 1},
|
sort: {name: 1},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Meteor.publish("customLibraries", function(){
|
||||||
|
userId = this.userId;
|
||||||
|
return Libraries.find({
|
||||||
|
$or: [
|
||||||
|
{readers: userId},
|
||||||
|
{writers: userId},
|
||||||
|
{owner: userId},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
Meteor.publish("libraryItems", function(libraryId){
|
||||||
|
return LibraryItems.find({library: libraryId});
|
||||||
|
});
|
||||||
|
|||||||
@@ -4,5 +4,6 @@ Meteor.publish("user", function(){
|
|||||||
username: 1,
|
username: 1,
|
||||||
profile: 1,
|
profile: 1,
|
||||||
apiKey: 1,
|
apiKey: 1,
|
||||||
|
librarySubscriptions: 1,
|
||||||
}});
|
}});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user