diff --git a/app/Model/Users/Users.js b/app/Model/Users/Users.js
index a54d3f7d..a818962e 100644
--- a/app/Model/Users/Users.js
+++ b/app/Model/Users/Users.js
@@ -70,42 +70,41 @@ Schemas.User = new SimpleSchema({
Meteor.users.attachSchema(Schemas.User);
-Meteor.users.allow({
- update: function(userId, doc, fields, modifier) {
- if (
- doc._id === userId &&
- _.contains(fields, "username") &&
- _.contains(fields, "profile") &&
- fields.length === 2 &&
- _.keys(modifier).length === 1 &&
- modifier.$set &&
- modifier.$set["profile.username"] &&
- modifier.$set.username &&
- _.keys(modifier.$set).length === 2
- ){
- var expectedUsername = modifier.$set["profile.username"];
- expectedUsername = expectedUsername.toLowerCase().replace(/\s+/gm, "");
- if (modifier.$set.username !== expectedUsername){
- return false;
- }
- var foundUser = Meteor.call("getUserId", expectedUsername);
- return !foundUser || foundUser === userId;
- }
- }
-});
-
-if (Meteor.isServer) Meteor.methods({
- generateMyApiKey() {
+Meteor.users.gnerateApiKey = new ValidatedMethod({
+ name: "Users.methods.generateApiKey",
+ validate: null,
+ run(){
+ if(Meteor.isClient) return;
var user = Meteor.users.findOne(this.userId);
if (!user) return;
if (user && user.apiKey) return;
var apiKey = Random.id(30);
Meteor.users.update(this.userId, {$set: {apiKey}});
},
- sendVerificationEmail(address) {
- var user = Meteor.users.findOne(this.userId);
- if (!user) return;
- if (!_.some(user.emails, email => email.address === address)) return;
- Accounts.sendVerificationEmail(this.userId, address);
- },
+});
+
+Meteor.users.sendVerificationEmail = new ValidatedMethod({
+ name: "Users.methods.sendVerificationEmail",
+ validate: new SimpleSchema({
+ userId:{
+ type: String,
+ optional: true,
+ },
+ address: {
+ type: String,
+ },
+ }).validator(),
+ run(userId, address){
+ userId = this.userId || userId;
+ let user = Meteor.users.findOne();
+ if (!user) {
+ throw new Meteor.Error("User not found",
+ "Can't send a validation email to a user that does not exist");
+ }
+ if (!_.some(user.emails, email => email.address === address)) {
+ throw new Meteor.Error("Email address not found",
+ "The specified email address wasn't found on this user account");
+ }
+ Accounts.sendVerificationEmail(this.userId, address);
+ }
});
diff --git a/app/imports/ui/components/DialogBase.vue b/app/imports/ui/components/DialogBase.vue
deleted file mode 100644
index de48808a..00000000
--- a/app/imports/ui/components/DialogBase.vue
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-
-
-
-
-
-
-
diff --git a/app/imports/ui/components/DialogStack.vue b/app/imports/ui/components/DialogStack.vue
deleted file mode 100644
index e416aa6d..00000000
--- a/app/imports/ui/components/DialogStack.vue
+++ /dev/null
@@ -1,18 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/imports/ui/dialogStack/DialogBase.vue b/app/imports/ui/dialogStack/DialogBase.vue
new file mode 100644
index 00000000..e7b3b142
--- /dev/null
+++ b/app/imports/ui/dialogStack/DialogBase.vue
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/imports/ui/dialogStack/DialogStack.vue b/app/imports/ui/dialogStack/DialogStack.vue
new file mode 100644
index 00000000..effc4049
--- /dev/null
+++ b/app/imports/ui/dialogStack/DialogStack.vue
@@ -0,0 +1,98 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/imports/ui/dialogStack/TestDialog.vue b/app/imports/ui/dialogStack/TestDialog.vue
new file mode 100644
index 00000000..798deb55
--- /dev/null
+++ b/app/imports/ui/dialogStack/TestDialog.vue
@@ -0,0 +1,28 @@
+
+
+
+ Test Dialog
+
+
+ Open Dialog
+
+
+
+
+
diff --git a/app/imports/ui/dialogStack/dialogStackStore.js b/app/imports/ui/dialogStack/dialogStackStore.js
new file mode 100644
index 00000000..a2cad8ce
--- /dev/null
+++ b/app/imports/ui/dialogStack/dialogStackStore.js
@@ -0,0 +1,87 @@
+import store from "/imports/ui/vuexStore.js";
+
+const offset = 16;
+const duration = 400;
+let dialogStack = {};
+dialogStack.dialogs = [];
+
+const dialogStackStore = {
+ state: {
+ dialogs: [],
+ },
+ mutations: {
+ pushDialogStack(state, {component, data, element, returnElement, callback}){
+ // Generate a new _id so that Vue knows how to shuffle the array
+ const _id = Random.id();
+ state.dialogs.push({
+ _id,
+ component,
+ data,
+ element,
+ returnElement,
+ callback,
+ });
+ updateHistory();
+ },
+ popDialogStackMutation (state, result){
+ const dialog = state.dialogs.pop();
+ updateHistory();
+ if (!dialog) return;
+ dialog.callback && dialog.callback(result);
+ },
+ },
+ actions: {
+ popDialogStack(context, result){
+ if (history && history.state && history.state.openDialogs){
+ history.back();
+ } else {
+ context.commit("popDialogStackMutation", result)
+ }
+ }
+ }
+};
+
+export default dialogStackStore;
+
+const updateHistory = function(){
+ // history should looks like: [{openDialogs: 0}, {openDialogs: n}] where
+ // n is the number of open dialogs
+
+ // If we can't access the history object, give up
+ if (!history) return;
+ // Make sure that there is a state tracking open dialogs
+ // replace the state without bashing it in the process
+ if (!history.state || !_.isFinite(history.state.openDialogs)){
+ let newState = _.clone(history.state) || {};
+ newState.openDialogs = 0;
+ history.replaceState(newState, "");
+ }
+
+ const numDialogs = dialogStackStore.state.dialogs.length;
+ const stateDialogs = history.state.openDialogs;
+
+ // If the number of dialogs and state dialogs are equal, we don't need to do
+ // anything
+ if (numDialogs === stateDialogs) return;
+
+ if (stateDialogs > 0){
+ // On a dialog count
+ if (numDialogs === 0){
+ // but shouldn't be
+ history.back();
+ } else {
+ // but should replace with correct count
+ let newState = _.clone(history.state) || {};
+ newState.openDialogs = dialogStackStore.state.dialogs.length;
+ history.replaceState(newState, "");
+ }
+ } else if (numDialogs > 0 && stateDialogs === 0){
+ // On the zero state, push a dialog count
+ history.pushState({openDialogs: numDialogs}, "");
+ } else {
+ console.warn(
+ "History could not be updated correctly, unexpected case",
+ {stateDialogs, numDialogs},
+ )
+ }
+};
diff --git a/app/imports/ui/dialogStack/dialogStackWindowEvents.js b/app/imports/ui/dialogStack/dialogStackWindowEvents.js
new file mode 100644
index 00000000..ec728c26
--- /dev/null
+++ b/app/imports/ui/dialogStack/dialogStackWindowEvents.js
@@ -0,0 +1,11 @@
+import store from "/imports/ui/vuexStore.js";
+
+if (window){
+ window.onpopstate = function(event){
+ let state = event.state;
+ let numDialogs = store.state.dialogStack.dialogs.length;
+ if (_.isFinite(state.openDialogs) && numDialogs > state.openDialogs){
+ store.commit("popDialogStackMutation");
+ }
+ };
+}
diff --git a/app/imports/ui/layouts/AppLayout.vue b/app/imports/ui/layouts/AppLayout.vue
index 00709f08..6cf917fb 100644
--- a/app/imports/ui/layouts/AppLayout.vue
+++ b/app/imports/ui/layouts/AppLayout.vue
@@ -4,11 +4,13 @@
+
diff --git a/app/imports/ui/pages/Account.vue b/app/imports/ui/pages/Account.vue
index 614302a4..b054f876 100644
--- a/app/imports/ui/pages/Account.vue
+++ b/app/imports/ui/pages/Account.vue
@@ -140,6 +140,8 @@
data(){ return {
showApiKey: false,
signOutBusy: false,
+ apiKeyGenerationError: null,
+ emailVerificationError: null,
}},
methods: {
signOut(){
@@ -148,11 +150,15 @@
});
},
generateKey(){
- Meteor.call("generateMyApiKey");
+ Meteor.users.gnerateApiKey.call(error => {
+ if(error) this.apiKeyGenerationError = error.reason;
+ });
this.showApiKey = true;
},
verifyEmail(address){
- Meteor.call("sendVerificationEmail", address);
+ Meteor.users.sendVerificationEmail.call({address}, error => {
+ if(error) this.emailVerificationError = error.reason;
+ });
},
},
components: {
diff --git a/app/imports/ui/pages/NotFound.vue b/app/imports/ui/pages/NotFound.vue
index e69de29b..5added14 100644
--- a/app/imports/ui/pages/NotFound.vue
+++ b/app/imports/ui/pages/NotFound.vue
@@ -0,0 +1,12 @@
+
+
+
+ Not Found
+
+
+
+ No page was found for this address
+
+
+
+
diff --git a/app/imports/ui/pages/Register.vue b/app/imports/ui/pages/Register.vue
index 77a1a00e..53f23582 100644
--- a/app/imports/ui/pages/Register.vue
+++ b/app/imports/ui/pages/Register.vue
@@ -67,6 +67,7 @@