diff --git a/config.json.example b/config.json.example
index 57464ef94a..81cd66622b 100644
--- a/config.json.example
+++ b/config.json.example
@@ -39,6 +39,7 @@
"NEW_RELIC_API_KEY":"NEW_RELIC_API_KEY",
"GA_ID": "GA_ID",
"AMPLITUDE_KEY": "AMPLITUDE_KEY",
+ "AMPLITUDE_SECRET": "AMPLITUDE_SECRET",
"AMAZON_PAYMENTS": {
"SELLER_ID": "SELLER_ID",
"CLIENT_ID": "CLIENT_ID",
diff --git a/database_reports/20181001_backtoschool_challenge.js b/database_reports/20181001_backtoschool_challenge.js
new file mode 100644
index 0000000000..ec22d1de8d
--- /dev/null
+++ b/database_reports/20181001_backtoschool_challenge.js
@@ -0,0 +1,48 @@
+import monk from 'monk';
+import nconf from 'nconf';
+
+/*
+ * Output data on users who completed all the To-Do tasks in the 2018 Back-to-School Challenge.
+ * User ID,Profile Name
+ */
+const CONNECTION_STRING = nconf.get('MIGRATION_CONNECT_STRING');
+const CHALLENGE_ID = '0acb1d56-1660-41a4-af80-9259f080b62b';
+
+let dbUsers = monk(CONNECTION_STRING).get('users', { castIds: false });
+let dbTasks = monk(CONNECTION_STRING).get('tasks', { castIds: false });
+
+function usersReport() {
+ console.info('User ID,Profile Name');
+ let userCount = 0;
+
+ dbUsers.find(
+ {challenges: CHALLENGE_ID},
+ {fields:
+ {_id: 1, 'profile.name': 1}
+ },
+ ).each((user, {close, pause, resume}) => {
+ pause();
+ userCount++;
+ let completedTodos = 0;
+ return dbTasks.find(
+ {
+ userId: user._id,
+ 'challenge.id': CHALLENGE_ID,
+ type: 'todo',
+ },
+ {fields: {completed: 1}}
+ ).each((task) => {
+ if (task.completed) completedTodos++;
+ }).then(() => {
+ if (completedTodos >= 7) {
+ console.info(`${user._id},${user.profile.name}`);
+ }
+ resume();
+ });
+ }).then(() => {
+ console.info(`${userCount} users reviewed`);
+ return process.exit(0);
+ });
+}
+
+module.exports = usersReport;
diff --git a/migrations/20180811_inboxOutsideUser.js b/migrations/archive/2018/20180811_inboxOutsideUser.js
similarity index 79%
rename from migrations/20180811_inboxOutsideUser.js
rename to migrations/archive/2018/20180811_inboxOutsideUser.js
index 29a1061827..a58df2a4cf 100644
--- a/migrations/20180811_inboxOutsideUser.js
+++ b/migrations/archive/2018/20180811_inboxOutsideUser.js
@@ -8,6 +8,7 @@ const authorUuid = 'ed4c688c-6652-4a92-9d03-a5a79844174a'; // ... own data is do
const monk = require('monk');
const nconf = require('nconf');
+const uuid = require('uuid').v4;
const Inbox = require('../website/server/models/message').inboxModel;
const connectionString = nconf.get('MIGRATION_CONNECT_STRING'); // FOR TEST DATABASE
@@ -84,16 +85,39 @@ function updateUser (user) {
return newMsg.toJSON();
});
- return dbInboxes.insert(newInboxMessages)
- .then(() => {
+ const promises = newInboxMessages.map(newMsg => {
+ return (async function fn () {
+ const existing = await dbInboxes.find({_id: newMsg._id});
+
+ if (existing.length > 0) {
+ if (
+ existing[0].ownerId === newMsg.ownerId &&
+ existing[0].text === newMsg.text &&
+ existing[0].uuid === newMsg.uuid &&
+ existing[0].sent === newMsg.sent
+ ) {
+ return null;
+ }
+
+ newMsg.id = newMsg._id = uuid();
+ }
+
+ return newMsg;
+ })();
+ });
+
+ return Promise.all(promises)
+ .then((filteredNewMsg) => {
+ filteredNewMsg = filteredNewMsg.filter(m => Boolean(m && m.id && m._id && m.id == m._id));
+ return dbInboxes.insert(filteredNewMsg);
+ }).then(() => {
return dbUsers.update({_id: user._id}, {
$set: {
migration: migrationName,
'inbox.messages': {},
},
});
- })
- .catch((err) => {
+ }).catch((err) => {
console.log(err);
return exiting(1, `ERROR! ${ err}`);
});
diff --git a/migrations/migration-runner.js b/migrations/migration-runner.js
index cc8d33e9b8..e9b62d5c2e 100644
--- a/migrations/migration-runner.js
+++ b/migrations/migration-runner.js
@@ -18,4 +18,11 @@ setUpServer();
// Replace this with your migration
const processUsers = require('./users/takeThis.js');
-processUsers();
+processUsers()
+ .then(function success () {
+ process.exitCode = 0;
+ })
+ .catch(function failure (err) {
+ console.log(err);
+ process.exitCode = 1;
+ });
diff --git a/migrations/users/20181002_username_email.js b/migrations/users/20181002_username_email.js
new file mode 100644
index 0000000000..908d83682e
--- /dev/null
+++ b/migrations/users/20181002_username_email.js
@@ -0,0 +1,107 @@
+const MIGRATION_NAME = '20181003_username_email.js';
+let authorName = 'Sabe'; // in case script author needs to know when their ...
+let authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; // ... own data is done
+
+/*
+ * Send emails to eligible users announcing upcoming username changes
+ */
+
+import monk from 'monk';
+import nconf from 'nconf';
+import { sendTxn } from '../../website/server/libs/email';
+const CONNECTION_STRING = nconf.get('MIGRATION_CONNECT_STRING');
+let dbUsers = monk(CONNECTION_STRING).get('users', { castIds: false });
+
+function processUsers (lastId) {
+ // specify a query to limit the affected users (empty for all users):
+ let query = {
+ migration: {$ne: MIGRATION_NAME},
+ 'auth.timestamps.loggedin': {$gt: new Date('2018-04-01')},
+ };
+
+ if (lastId) {
+ query._id = {
+ $gt: lastId,
+ };
+ }
+
+ dbUsers.find(query, {
+ sort: {_id: 1},
+ limit: 100,
+ fields: [
+ '_id',
+ 'auth',
+ 'preferences',
+ 'profile',
+ ], // specify fields we are interested in to limit retrieved data (empty if we're not reading data):
+ })
+ .then(updateUsers)
+ .catch((err) => {
+ console.log(err);
+ return exiting(1, `ERROR! ${ err}`);
+ });
+}
+
+let progressCount = 1000;
+let count = 0;
+
+function updateUsers (users) {
+ if (!users || users.length === 0) {
+ console.warn('All appropriate users found and modified.');
+ displayData();
+ return;
+ }
+
+ let userPromises = users.map(updateUser);
+ let lastUser = users[users.length - 1];
+
+ return Promise.all(userPromises)
+ .then(() => delay(7000))
+ .then(() => {
+ processUsers(lastUser._id);
+ });
+}
+
+function updateUser (user) {
+ count++;
+
+ dbUsers.update({_id: user._id}, {$set: {migration: MIGRATION_NAME}});
+
+ sendTxn(
+ user,
+ 'username-change',
+ [{name: 'UNSUB_EMAIL_TYPE_URL', content: '/user/settings/notifications?unsubFrom=importantAnnouncements'},
+ {name: 'LOGIN_NAME', content: user.auth.local.username}]
+ );
+
+ if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
+ if (user._id === authorUuid) console.warn(`${authorName} processed`);
+}
+
+function displayData () {
+ console.warn(`\n${count} users processed\n`);
+ return exiting(0);
+}
+
+function delay (t, v) {
+ return new Promise(function batchPause (resolve) {
+ setTimeout(resolve.bind(null, v), t);
+ });
+}
+
+function exiting (code, msg) {
+ code = code || 0; // 0 = success
+ if (code && !msg) {
+ msg = 'ERROR!';
+ }
+ if (msg) {
+ if (code) {
+ console.error(msg);
+ } else {
+ console.log(msg);
+ }
+ }
+ process.exit(code);
+}
+
+module.exports = processUsers;
diff --git a/package-lock.json b/package-lock.json
index 6975801500..182503785b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -277,6 +277,7 @@
"version": "3.0.1",
"resolved": "http://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz",
"integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=",
+ "dev": true,
"requires": {
"acorn": "^3.0.4"
},
@@ -284,7 +285,8 @@
"acorn": {
"version": "3.3.0",
"resolved": "http://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz",
- "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo="
+ "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=",
+ "dev": true
}
}
},
@@ -322,7 +324,8 @@
"ajv-keywords": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-2.1.1.tgz",
- "integrity": "sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I="
+ "integrity": "sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I=",
+ "dev": true
},
"align-text": {
"version": "0.1.4",
@@ -1075,7 +1078,8 @@
"arrify": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz",
- "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0="
+ "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=",
+ "dev": true
},
"asap": {
"version": "2.0.6",
@@ -1682,9 +1686,9 @@
}
},
"aws-sdk": {
- "version": "2.320.0",
- "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.320.0.tgz",
- "integrity": "sha512-qJBjZ0sIIy6AzBe0RkK5HDl3Kl1uAz01R4Nqy+RyflB//XWz+dPN8CFYtzrQNyfpLtPe/4uPrmxC4NwGW1MXBQ==",
+ "version": "2.329.0",
+ "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.329.0.tgz",
+ "integrity": "sha512-5yti9jyL6QL8CP5LGKX3uyrQcLAvLk6kyMZ749+F68IsutFKi0DaYxjxqx7lqWfzCSH3Z5REJ+XeywJLQ2tqfQ==",
"requires": {
"buffer": "4.9.1",
"events": "1.1.1",
@@ -5175,6 +5179,7 @@
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz",
"integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=",
+ "dev": true,
"requires": {
"callsites": "^0.2.0"
}
@@ -5188,7 +5193,8 @@
"callsites": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz",
- "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo="
+ "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=",
+ "dev": true
},
"camel-case": {
"version": "3.0.0",
@@ -5707,7 +5713,8 @@
"circular-json": {
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz",
- "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A=="
+ "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==",
+ "dev": true
},
"clap": {
"version": "1.2.3",
@@ -8104,7 +8111,8 @@
"deep-is": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
- "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ="
+ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=",
+ "dev": true
},
"default-compare": {
"version": "1.0.0",
@@ -8246,6 +8254,7 @@
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz",
"integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=",
+ "dev": true,
"requires": {
"globby": "^5.0.0",
"is-path-cwd": "^1.0.0",
@@ -8260,6 +8269,7 @@
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz",
"integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=",
+ "dev": true,
"requires": {
"array-union": "^1.0.1",
"arrify": "^1.0.0",
@@ -8272,7 +8282,8 @@
"pify": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
- "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw="
+ "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+ "dev": true
}
}
},
@@ -8380,6 +8391,7 @@
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
"integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
+ "dev": true,
"requires": {
"esutils": "^2.0.2"
}
@@ -9733,6 +9745,7 @@
"version": "3.5.4",
"resolved": "https://registry.npmjs.org/espree/-/espree-3.5.4.tgz",
"integrity": "sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A==",
+ "dev": true,
"requires": {
"acorn": "^5.5.0",
"acorn-jsx": "^3.0.0"
@@ -9747,6 +9760,7 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz",
"integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==",
+ "dev": true,
"requires": {
"estraverse": "^4.0.0"
}
@@ -10568,7 +10582,8 @@
"fast-levenshtein": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
- "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc="
+ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
+ "dev": true
},
"fastparse": {
"version": "1.1.1",
@@ -10624,6 +10639,7 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz",
"integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=",
+ "dev": true,
"requires": {
"flat-cache": "^1.2.1",
"object-assign": "^4.0.1"
@@ -10952,6 +10968,7 @@
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.0.tgz",
"integrity": "sha1-0wMLMrOBVPTjt+nHCfSQ9++XxIE=",
+ "dev": true,
"requires": {
"circular-json": "^0.3.1",
"del": "^2.0.2",
@@ -11637,7 +11654,8 @@
"functional-red-black-tree": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
- "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc="
+ "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=",
+ "dev": true
},
"gauge": {
"version": "2.7.4",
@@ -13298,7 +13316,8 @@
"ignore": {
"version": "3.3.10",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz",
- "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug=="
+ "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==",
+ "dev": true
},
"ignore-by-default": {
"version": "1.0.1",
@@ -13393,22 +13412,16 @@
"integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o="
},
"in-app-purchase": {
- "version": "1.10.1",
- "resolved": "https://registry.npmjs.org/in-app-purchase/-/in-app-purchase-1.10.1.tgz",
- "integrity": "sha512-pGWJ6YltJxVjYsk6ZISFROcm2E0FjGLF7FDoPaED1sedENqr560+1oVga2MbxgbxMnv8ufbSORUzQRYhx+ea6A==",
+ "version": "1.10.2",
+ "resolved": "https://registry.npmjs.org/in-app-purchase/-/in-app-purchase-1.10.2.tgz",
+ "integrity": "sha512-NMyNJ0xaHB9F6fAgJ4VROzZRwFyX+iNYbniUCbd2OadDZdosUbPWRbFaP4QzyMRzGPa5sVLGm91LqIqk37UnDQ==",
"requires": {
- "eslint": "4.18.2",
"jwt-simple": "^0.5.1",
"request": "2.87.0",
"xml-crypto": "0.10.1",
"xmldom": "0.1.19"
},
"dependencies": {
- "ansi-regex": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
- "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg="
- },
"assert-plus": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
@@ -13424,73 +13437,6 @@
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
"integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="
},
- "cross-spawn": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
- "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
- "requires": {
- "lru-cache": "^4.0.1",
- "shebang-command": "^1.2.0",
- "which": "^1.2.9"
- }
- },
- "debug": {
- "version": "3.2.5",
- "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.5.tgz",
- "integrity": "sha512-D61LaDQPQkxJ5AUM2mbSJRbPkNs/TmdmOeLAi1hgDkpDfIfetSrjmWhccwtuResSwMbACjx/xXQofvM9CE/aeg==",
- "requires": {
- "ms": "^2.1.1"
- }
- },
- "eslint": {
- "version": "4.18.2",
- "resolved": "http://registry.npmjs.org/eslint/-/eslint-4.18.2.tgz",
- "integrity": "sha512-qy4i3wODqKMYfz9LUI8N2qYDkHkoieTbiHpMrYUI/WbjhXJQr7lI4VngixTgaG+yHX+NBCv7nW4hA0ShbvaNKw==",
- "requires": {
- "ajv": "^5.3.0",
- "babel-code-frame": "^6.22.0",
- "chalk": "^2.1.0",
- "concat-stream": "^1.6.0",
- "cross-spawn": "^5.1.0",
- "debug": "^3.1.0",
- "doctrine": "^2.1.0",
- "eslint-scope": "^3.7.1",
- "eslint-visitor-keys": "^1.0.0",
- "espree": "^3.5.2",
- "esquery": "^1.0.0",
- "esutils": "^2.0.2",
- "file-entry-cache": "^2.0.0",
- "functional-red-black-tree": "^1.0.1",
- "glob": "^7.1.2",
- "globals": "^11.0.1",
- "ignore": "^3.3.3",
- "imurmurhash": "^0.1.4",
- "inquirer": "^3.0.6",
- "is-resolvable": "^1.0.0",
- "js-yaml": "^3.9.1",
- "json-stable-stringify-without-jsonify": "^1.0.1",
- "levn": "^0.3.0",
- "lodash": "^4.17.4",
- "minimatch": "^3.0.2",
- "mkdirp": "^0.5.1",
- "natural-compare": "^1.4.0",
- "optionator": "^0.8.2",
- "path-is-inside": "^1.0.2",
- "pluralize": "^7.0.0",
- "progress": "^2.0.0",
- "require-uncached": "^1.0.3",
- "semver": "^5.3.0",
- "strip-ansi": "^4.0.0",
- "strip-json-comments": "~2.0.1",
- "table": "4.0.2",
- "text-table": "~0.2.0"
- }
- },
- "esprima": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
- "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="
- },
"form-data": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz",
@@ -13511,11 +13457,6 @@
}
}
},
- "globals": {
- "version": "11.7.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-11.7.0.tgz",
- "integrity": "sha512-K8BNSPySfeShBQXsahYB/AbbWruVOTyVpgoIDnl8odPpeSfP2J5QO2oLFFdl2j7GfDCtZj2bMKar2T49itTPCg=="
- },
"har-validator": {
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz",
@@ -13535,20 +13476,6 @@
"sshpk": "^1.7.0"
}
},
- "js-yaml": {
- "version": "3.12.0",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz",
- "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==",
- "requires": {
- "argparse": "^1.0.7",
- "esprima": "^4.0.0"
- }
- },
- "ms": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
- "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
- },
"qs": {
"version": "6.5.2",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
@@ -13581,19 +13508,6 @@
"uuid": "^3.1.0"
}
},
- "semver": {
- "version": "5.5.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.1.tgz",
- "integrity": "sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw=="
- },
- "strip-ansi": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
- "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
- "requires": {
- "ansi-regex": "^3.0.0"
- }
- },
"tunnel-agent": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
@@ -14293,7 +14207,8 @@
"is-resolvable": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz",
- "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg=="
+ "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==",
+ "dev": true
},
"is-retry-allowed": {
"version": "1.1.0",
@@ -14724,7 +14639,8 @@
"json-stable-stringify-without-jsonify": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
- "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE="
+ "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=",
+ "dev": true
},
"json-stringify-safe": {
"version": "5.0.1",
@@ -14840,9 +14756,9 @@
}
},
"jwt-simple": {
- "version": "0.5.3",
- "resolved": "https://registry.npmjs.org/jwt-simple/-/jwt-simple-0.5.3.tgz",
- "integrity": "sha512-mGXp4PTJalEBh5WGe/Ow0P6g+Q4tM/9qAysX7YxCdB/xahDoBKrASZKf7hXT/qTREhOTPJvi4WWpabT2WXh1BQ=="
+ "version": "0.5.5",
+ "resolved": "https://registry.npmjs.org/jwt-simple/-/jwt-simple-0.5.5.tgz",
+ "integrity": "sha512-KEyanRIDHooo8KuBxY3CC019NbwHtxdsxzRJUfaGqcxMrvBPBkosN+RUxx1nZFI6yrErq3KTW8HI/qrNIxHe0g=="
},
"kareem": {
"version": "2.1.0",
@@ -15439,6 +15355,7 @@
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
"integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=",
+ "dev": true,
"requires": {
"prelude-ls": "~1.1.2",
"type-check": "~0.3.2"
@@ -17613,7 +17530,8 @@
"natural-compare": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
- "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc="
+ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
+ "dev": true
},
"nconf": {
"version": "0.10.0",
@@ -18918,6 +18836,7 @@
"version": "0.8.2",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz",
"integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=",
+ "dev": true,
"requires": {
"deep-is": "~0.1.3",
"fast-levenshtein": "~2.0.4",
@@ -18930,7 +18849,8 @@
"wordwrap": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
- "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus="
+ "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=",
+ "dev": true
}
}
},
@@ -20078,7 +19998,8 @@
"pluralize": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz",
- "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow=="
+ "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==",
+ "dev": true
},
"pngjs": {
"version": "3.3.3",
@@ -21752,7 +21673,8 @@
"prelude-ls": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
- "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ="
+ "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=",
+ "dev": true
},
"prepend-http": {
"version": "1.0.4",
@@ -21833,7 +21755,8 @@
"progress": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/progress/-/progress-2.0.0.tgz",
- "integrity": "sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8="
+ "integrity": "sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8=",
+ "dev": true
},
"project-name": {
"version": "0.2.6",
@@ -23159,6 +23082,7 @@
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz",
"integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=",
+ "dev": true,
"requires": {
"caller-path": "^0.1.0",
"resolve-from": "^1.0.0"
@@ -23278,7 +23202,8 @@
"resolve-from": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz",
- "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY="
+ "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=",
+ "dev": true
},
"resolve-glob": {
"version": "1.0.0",
@@ -23845,6 +23770,7 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz",
"integrity": "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==",
+ "dev": true,
"requires": {
"is-fullwidth-code-point": "^2.0.0"
},
@@ -23852,7 +23778,8 @@
"is-fullwidth-code-point": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
- "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="
+ "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+ "dev": true
}
}
},
@@ -25052,6 +24979,7 @@
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/table/-/table-4.0.2.tgz",
"integrity": "sha512-UUkEAPdSGxtRpiV9ozJ5cMTtYiqz7Ni1OGqLXRCynrvzdtR1p+cfOWe2RJLwvUG8hNanaSRjecIqwOjqeatDsA==",
+ "dev": true,
"requires": {
"ajv": "^5.2.3",
"ajv-keywords": "^2.1.0",
@@ -25064,17 +24992,20 @@
"ansi-regex": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
- "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg="
+ "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
+ "dev": true
},
"is-fullwidth-code-point": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
- "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="
+ "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+ "dev": true
},
"string-width": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
"integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
+ "dev": true,
"requires": {
"is-fullwidth-code-point": "^2.0.0",
"strip-ansi": "^4.0.0"
@@ -25084,6 +25015,7 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
"integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+ "dev": true,
"requires": {
"ansi-regex": "^3.0.0"
}
@@ -25862,6 +25794,7 @@
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
"integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=",
+ "dev": true,
"requires": {
"prelude-ls": "~1.1.2"
}
diff --git a/package.json b/package.json
index e513f268d9..a5a1ae0108 100644
--- a/package.json
+++ b/package.json
@@ -11,7 +11,7 @@
"apidoc": "^0.17.5",
"apn": "^2.2.0",
"autoprefixer": "^8.5.0",
- "aws-sdk": "^2.239.1",
+ "aws-sdk": "^2.329.0",
"axios": "^0.18.0",
"axios-progress-bar": "^1.2.0",
"babel-core": "^6.26.3",
@@ -53,7 +53,7 @@
"hellojs": "^1.15.1",
"html-webpack-plugin": "^3.2.0",
"image-size": "^0.6.2",
- "in-app-purchase": "^1.9.4",
+ "in-app-purchase": "^1.10.2",
"intro.js": "^2.9.3",
"jquery": ">=3.0.0",
"js2xmlparser": "^3.0.0",
diff --git a/scripts/gdpr-delete-users.js b/scripts/gdpr-delete-users.js
new file mode 100644
index 0000000000..d5942dace4
--- /dev/null
+++ b/scripts/gdpr-delete-users.js
@@ -0,0 +1,82 @@
+/* eslint-disable no-console */
+import axios from 'axios';
+import { model as User } from '../website/server/models/user';
+import nconf from 'nconf';
+
+const AMPLITUDE_KEY = nconf.get('AMPLITUDE_KEY');
+const AMPLITUDE_SECRET = nconf.get('AMPLITUDE_SECRET');
+const BASE_URL = nconf.get('BASE_URL');
+
+async function _deleteAmplitudeData (userId, email) {
+ const response = await axios.post(
+ 'https://amplitude.com/api/2/deletions/users',
+ {
+ user_ids: userId, // eslint-disable-line camelcase
+ requester: email,
+ },
+ {
+ auth: {
+ username: AMPLITUDE_KEY,
+ password: AMPLITUDE_SECRET,
+ },
+ }
+ );
+
+ console.log(`${response.status} ${response.statusText}`);
+}
+
+async function _deleteHabiticaData (user) {
+ await User.update(
+ {_id: user._id},
+ {$set: {
+ 'auth.local.passwordHashMethod': 'bcrypt',
+ 'auth.local.hashed_password': '$2a$10$QDnNh1j1yMPnTXDEOV38xOePEWFd4X8DSYwAM8XTmqmacG5X0DKjW',
+ }}
+ );
+ const response = await axios.delete(
+ `${BASE_URL}/api/v3/user`,
+ {
+ data: {
+ password: 'test',
+ },
+ headers: {
+ 'x-api-user': user._id,
+ 'x-api-key': user.apiToken,
+ },
+ }
+ );
+
+ console.log(`${response.status} ${response.statusText}`);
+ if (response.status === 200) console.log(`${user._id} removed. Last login: ${user.auth.timestamps.loggedin}`);
+}
+
+async function _processEmailAddress (email) {
+ const emailRegex = new RegExp(`^${email}`, 'i');
+ const users = await User.find({
+ $or: [
+ {'auth.local.email': emailRegex},
+ {'auth.facebook.emails.value': emailRegex},
+ {'auth.google.emails.value': emailRegex},
+ ]},
+ {
+ _id: 1,
+ apiToken: 1,
+ auth: 1,
+ }).exec();
+
+ if (users.length < 1) {
+ console.log(`No users found with email address ${email}`);
+ } else {
+ for (const user of users) {
+ await _deleteAmplitudeData(user._id, email); // eslint-disable-line no-await-in-loop
+ await _deleteHabiticaData(user); // eslint-disable-line no-await-in-loop
+ }
+ }
+}
+
+function deleteUserData (emails) {
+ const emailPromises = emails.map(_processEmailAddress);
+ return Promise.all(emailPromises);
+}
+
+module.exports = deleteUserData;
diff --git a/test/api/v3/integration/user/DELETE-user_messages.test.js b/test/api/v3/integration/user/DELETE-user_messages.test.js
index 3b5686c03b..079550b136 100644
--- a/test/api/v3/integration/user/DELETE-user_messages.test.js
+++ b/test/api/v3/integration/user/DELETE-user_messages.test.js
@@ -20,8 +20,8 @@ describe('DELETE user message', () => {
messagesId = Object.keys(userRes.inbox.messages);
expect(messagesId.length).to.eql(2);
- expect(userRes.inbox.messages[messagesId[0]].text).to.eql('first');
- expect(userRes.inbox.messages[messagesId[1]].text).to.eql('second');
+ expect(userRes.inbox.messages[messagesId[0]].text).to.eql('second');
+ expect(userRes.inbox.messages[messagesId[1]].text).to.eql('first');
});
it('one message', async () => {
@@ -31,7 +31,7 @@ describe('DELETE user message', () => {
let userRes = await user.get('/user');
expect(Object.keys(userRes.inbox.messages).length).to.eql(1);
- expect(userRes.inbox.messages[messagesId[0]].text).to.eql('second');
+ expect(userRes.inbox.messages[messagesId[0]].text).to.eql('first');
});
it('clear all', async () => {
diff --git a/test/api/v4/user/GET-user.test.js b/test/api/v4/user/GET-user.test.js
index bf6cd2bb50..4b1bad4054 100644
--- a/test/api/v4/user/GET-user.test.js
+++ b/test/api/v4/user/GET-user.test.js
@@ -53,6 +53,6 @@ describe('GET /user', () => {
let returnedUser = await user.get('/user');
expect(returnedUser._id).to.equal(user._id);
- expect(returnedUser.inbox.messages).to.be.empty;
+ expect(returnedUser.inbox.messages).to.be.undefined;
});
});
diff --git a/website/client/app.vue b/website/client/app.vue
index 9f1680eeff..6583241956 100644
--- a/website/client/app.vue
+++ b/website/client/app.vue
@@ -329,23 +329,13 @@ export default {
if (notificationNotFoundMessage.indexOf(errorMessage) !== -1) snackbarTimeout = true;
let errorsToShow = [];
- let usernameCheck = false;
- let emailCheck = false;
- let passwordCheck = false;
// show only the first error for each param
+ let paramErrorsFound = {};
if (errorData.errors) {
for (let e of errorData.errors) {
- if (!usernameCheck && e.param === 'username') {
+ if (!paramErrorsFound[e.param]) {
errorsToShow.push(e.message);
- usernameCheck = true;
- }
- if (!emailCheck && e.param === 'email') {
- errorsToShow.push(e.message);
- emailCheck = true;
- }
- if (!passwordCheck && e.param === 'password') {
- errorsToShow.push(e.message);
- passwordCheck = true;
+ paramErrorsFound[e.param] = true;
}
}
} else {
diff --git a/website/client/components/challenges/myChallenges.vue b/website/client/components/challenges/myChallenges.vue
index a512c355f4..e13f3c7c15 100644
--- a/website/client/components/challenges/myChallenges.vue
+++ b/website/client/components/challenges/myChallenges.vue
@@ -7,6 +7,7 @@
.row.header-row
.col-md-8.text-left
h1(v-once) {{$t('myChallenges')}}
+ h2(v-if='loading && challenges.length === 0') {{ $t('loading') }}
.col-md-4
// @TODO: implement sorting span.dropdown-label {{ $t('sortBy') }}
b-dropdown(:text="$t('sort')", right=true)
@@ -16,7 +17,7 @@
span(v-once) {{$t('createChallenge')}}
.row
- .no-challenges.text-center.col-md-6.offset-3(v-if='filteredChallenges.length === 0')
+ .no-challenges.text-center.col-md-6.offset-3(v-if='!loading && challenges.length === 0')
.svg-icon(v-html="icons.challengeIcon")
h2(v-once) {{$t('noChallengeTitle')}}
p(v-once) {{$t('challengeDescription1')}}
@@ -48,14 +49,15 @@
}
.no-challenges {
- color: $gray-300;
+ color: $gray-200;
margin-top: 10em;
h2 {
- color: $gray-300;
+ color: $gray-200;
}
.svg-icon {
+ color: #C3C0C7;
width: 88.7px;
margin: 1em auto;
}
@@ -84,6 +86,7 @@ export default {
challengeIcon,
positiveIcon,
}),
+ loading: false,
challenges: [],
sort: 'none',
sortOptions: [
@@ -113,7 +116,7 @@ export default {
};
},
mounted () {
- this.loadchallanges();
+ this.loadChallenges();
},
computed: {
filteredChallenges () {
@@ -138,10 +141,12 @@ export default {
this.$store.state.challengeOptions.workingChallenge = {};
this.$root.$emit('bv::show::modal', 'challenge-modal');
},
- async loadchallanges () {
+ async loadChallenges () {
+ this.loading = true;
this.challenges = await this.$store.dispatch('challenges:getUserChallenges', {
member: true,
});
+ this.loading = false;
},
challengeCreated (challenge) {
this.challenges.push(challenge);
diff --git a/website/client/components/chat/autoComplete.vue b/website/client/components/chat/autoComplete.vue
index dca7480ff3..8b5cc3cf43 100644
--- a/website/client/components/chat/autoComplete.vue
+++ b/website/client/components/chat/autoComplete.vue
@@ -14,7 +14,7 @@ div.autocomplete-selection(v-if='searchResults.length > 0', :style='autocomplete
import groupBy from 'lodash/groupBy';
export default {
- props: ['selections', 'text', 'coords', 'chat'],
+ props: ['selections', 'text', 'coords', 'chat', 'textbox'],
data () {
return {
currentSearch: '',
@@ -25,9 +25,15 @@ export default {
},
computed: {
autocompleteStyle () {
+ function heightToUse (textBox, topCoords) {
+ let textBoxHeight = textBox['user-entry'].clientHeight;
+ return topCoords < textBoxHeight ? topCoords + 30 : textBoxHeight + 10;
+ }
return {
- top: `${this.coords.TOP + 30}px`,
+ top: `${heightToUse(this.textbox, this.coords.TOP)}px`,
left: `${this.coords.LEFT + 30}px`,
+ marginLeft: '-28px',
+ marginTop: '28px',
position: 'absolute',
minWidth: '100px',
minHeight: '100px',
@@ -42,6 +48,7 @@ export default {
return option.toLowerCase().indexOf(currentSearch.toLowerCase()) !== -1;
});
},
+
},
mounted () {
this.grabUserNames();
diff --git a/website/client/components/groups/chat.vue b/website/client/components/groups/chat.vue
index 5babe873f9..9c89526fbf 100644
--- a/website/client/components/groups/chat.vue
+++ b/website/client/components/groups/chat.vue
@@ -6,6 +6,7 @@
.row
textarea(:placeholder='placeholder',
v-model='newMessage',
+ ref='user-entry',
:class='{"user-entry": newMessage}',
@keydown='updateCarretPosition',
@keyup.ctrl.enter='sendMessageShortcut()',
@@ -16,6 +17,7 @@
autocomplete(
:text='newMessage',
v-on:select="selectedAutocomplete",
+ :textbox='textbox',
:coords='coords',
:chat='group.chat')
@@ -62,6 +64,7 @@
TOP: 0,
LEFT: 0,
},
+ textbox: this.$refs,
};
},
computed: {
@@ -187,7 +190,7 @@
position: relative;
textarea {
- height: 150px;
+ min-height: 150px;
width: 100%;
background-color: $white;
border: solid 1px $gray-400;
diff --git a/website/client/components/groups/myGuilds.vue b/website/client/components/groups/myGuilds.vue
index a75aaf71b4..7e3ae9973f 100644
--- a/website/client/components/groups/myGuilds.vue
+++ b/website/client/components/groups/myGuilds.vue
@@ -2,18 +2,11 @@
.row
sidebar(v-on:search="updateSearch", v-on:filter="updateFilters")
- .no-guilds.standard-page(v-if='filteredGuilds.length === 0')
- .no-guilds-wrapper
- .svg-icon(v-html='icons.greyBadge')
- h2 {{$t('noGuildsTitle')}}
- p {{$t('noGuildsParagraph1')}}
- p {{$t('noGuildsParagraph2')}}
- span(v-if='loading') {{ $t('loading') }}
-
- .standard-page(v-if='filteredGuilds.length > 0')
+ .standard-page
.row
- .col-md-8
- h1.page-header.float-left(v-once) {{ $t('myGuilds') }}
+ .col-md-8.text-left
+ h1.page-header(v-once) {{ $t('myGuilds') }}
+ h2(v-if='loading && guilds.length === 0') {{ $t('loading') }}
.col-4
button.btn.btn-secondary.create-group-button.float-right(@click='createGroup()')
.svg-icon.positive-icon(v-html="icons.positiveIcon")
@@ -22,6 +15,14 @@
span.dropdown-label {{ $t('sortBy') }}
b-dropdown(:text="$t('sort')", right=true)
b-dropdown-item(v-for='sortOption in sortOptions', :key="sortOption.value", @click='sort(sortOption.value)') {{sortOption.text}}
+
+ .row
+ .no-guilds.text-center.col-md-6.offset-md-3(v-if='!loading && guilds.length === 0')
+ .svg-icon(v-html='icons.greyBadge')
+ h2(v-once) {{$t('noGuildsTitle')}}
+ p(v-once) {{$t('noGuildsParagraph1')}}
+ p(v-once) {{$t('noGuildsParagraph2')}}
+
.row
.col-md-12
public-guild-item(v-for="guild in filteredGuilds", :key='guild._id', :guild="guild", :display-gem-bank='true')
@@ -41,29 +42,16 @@
}
.no-guilds {
- text-align: center;
color: $gray-200;
- margin-top: 15em;
+ margin-top: 10em;
- p {
- font-size: 14px;
- line-height: 1.43;
+ h2 {
+ color: $gray-200;
}
- .no-guilds-wrapper {
- width: 400px;
- margin: 0 auto;
-
- .svg-icon {
- width: 60px;
- margin: 0 auto;
- }
- }
- }
-
- @media only screen and (max-width: 768px) {
- .no-guilds-wrapper {
- width: 100% !important;
+ .svg-icon {
+ width: 88.7px;
+ margin: 1em auto;
}
}
diff --git a/website/client/components/header/index.vue b/website/client/components/header/index.vue
index 9c28765893..dffdef7046 100644
--- a/website/client/components/header/index.vue
+++ b/website/client/components/header/index.vue
@@ -154,11 +154,13 @@ export default {
this.$root.$emit('bv::show::modal', 'create-party-modal');
}
},
- showPartyMembers () {
+ async showPartyMembers () {
+ const party = await this.$store.dispatch('party:getParty');
+
this.$root.$emit('habitica:show-member-modal', {
- groupId: this.user.party._id,
+ groupId: party.data._id,
viewingMembers: this.partyMembers,
- group: this.user.party,
+ group: party.data,
});
},
setPartyMembersWidth ($event) {
diff --git a/website/client/components/payments/sendGemsModal.vue b/website/client/components/payments/sendGemsModal.vue
index 98d32993c9..791ebe0ae5 100644
--- a/website/client/components/payments/sendGemsModal.vue
+++ b/website/client/components/payments/sendGemsModal.vue
@@ -41,7 +41,11 @@ b-modal#send-gems(:title="title", :hide-footer="true", size='lg', @hide='onHide(
//include ../formatting-help
.modal-footer
- button.btn.btn-primary(v-if='fromBal', @click='sendGift()') {{ $t("send") }}
+ button.btn.btn-primary(
+ v-if="fromBal",
+ @click="sendGift()",
+ :disabled="sendingInProgress"
+ ) {{ $t("send") }}
template(v-else)
button.btn.btn-primary(@click='showStripe({gift, uuid: userReceivingGems._id})') {{ $t('card') }}
button.btn.btn-warning(@click='openPaypalGift({gift: gift, giftedTo: userReceivingGems._id})') PayPal
@@ -103,6 +107,7 @@ export default {
assistanceEmailObject: {
hrefTechAssistanceEmail: `${TECH_ASSISTANCE_EMAIL}`,
},
+ sendingInProgress: false,
};
},
computed: {
@@ -130,6 +135,7 @@ export default {
methods: {
// @TODO move to payments mixin or action (problem is that we need notifications)
async sendGift () {
+ this.sendingInProgress = true;
await this.$store.dispatch('members:transferGems', {
message: this.gift.message,
toUserId: this.userReceivingGems._id,
@@ -139,7 +145,9 @@ export default {
this.close();
},
onHide () {
+ this.gift.gems.amount = 0;
this.gift.message = '';
+ this.sendingInProgress = false;
},
close () {
this.$root.$emit('bv::hide::modal', 'send-gems');
diff --git a/website/client/components/static/header.vue b/website/client/components/static/header.vue
index 667172b646..9d17a17fb0 100644
--- a/website/client/components/static/header.vue
+++ b/website/client/components/static/header.vue
@@ -42,6 +42,11 @@
}
}
+ .home-header, .home-header .btn {
+ font-family: 'Varela Round', sans-serif;
+ font-weight: normal;
+ }
+
.btn-primary.pull-right {
height: 2.5em;
margin: auto 0px auto auto;
@@ -66,9 +71,8 @@
.nav-item {
.nav-link {
- font-size: 16px;
+ font-size: 16px !important;
color: $white;
- font-weight: bold;
line-height: 1.5;
padding: 16px 24px;
transition: none;
diff --git a/website/client/components/static/home.vue b/website/client/components/static/home.vue
index 57c7ec06aa..fa1e604317 100644
--- a/website/client/components/static/home.vue
+++ b/website/client/components/static/home.vue
@@ -126,6 +126,8 @@