mirror of
https://github.com/HabitRPG/habitica.git
synced 2026-05-03 19:40:48 -05:00
Merge branch 'develop' into release
This commit is contained in:
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
+28
-4
@@ -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}`);
|
||||
});
|
||||
@@ -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;
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
Generated
+68
-135
@@ -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"
|
||||
}
|
||||
|
||||
+2
-2
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
@@ -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 () => {
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
});
|
||||
|
||||
+3
-13
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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: `<a href="mailto:${TECH_ASSISTANCE_EMAIL}">${TECH_ASSISTANCE_EMAIL}</a>`,
|
||||
},
|
||||
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');
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -126,6 +126,8 @@
|
||||
<style lang="scss" scoped>
|
||||
@import '~client/assets/scss/colors.scss';
|
||||
|
||||
@import url('https://fonts.googleapis.com/css?family=Varela+Round');
|
||||
|
||||
#front {
|
||||
.form-text a {
|
||||
color: #fff !important;
|
||||
@@ -194,6 +196,11 @@
|
||||
.pixel-horizontal-3 {
|
||||
color: #271b3d;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6, button, .strike > span, input {
|
||||
font-family: 'Varela Round', sans-serif;
|
||||
font-weight: normal;
|
||||
}
|
||||
}
|
||||
|
||||
#intro-signup {
|
||||
@@ -256,6 +263,7 @@
|
||||
.strike > span {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
line-height: 1.14;
|
||||
}
|
||||
|
||||
.strike > span:before,
|
||||
@@ -362,13 +370,17 @@
|
||||
}
|
||||
|
||||
strong {
|
||||
font-size: 20px;
|
||||
font-size: 24px;
|
||||
font-family: 'Varela Round', sans-serif;
|
||||
line-height: 1.33;
|
||||
}
|
||||
}
|
||||
|
||||
#use-cases {
|
||||
strong {
|
||||
font-size: 20px;
|
||||
font-size: 24px;
|
||||
font-family: 'Varela Round', sans-serif;
|
||||
line-height: 1.33;
|
||||
}
|
||||
|
||||
img {
|
||||
@@ -445,6 +457,11 @@
|
||||
|
||||
.featured {
|
||||
text-align: center;
|
||||
font-family: 'Varela Round', sans-serif;
|
||||
|
||||
strong {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.svg-icon {
|
||||
vertical-align: bottom;
|
||||
|
||||
@@ -99,7 +99,7 @@
|
||||
span.hint(:popover-title='$t(statInfo.title)', popover-placement='right',
|
||||
:popover='$t(statInfo.popover)', popover-trigger='mouseenter')
|
||||
.stat-title(:class='stat') {{ $t(statInfo.title) }}
|
||||
strong.number {{ statsComputed[stat] | floorWholeNumber }}
|
||||
strong.number {{totalStatPoints(stat) | floorWholeNumber}}
|
||||
.col-12.col-md-6
|
||||
ul.bonus-stats
|
||||
li
|
||||
@@ -113,7 +113,7 @@
|
||||
| {{statsComputed.classBonus[stat]}}
|
||||
li
|
||||
strong {{$t('allocated')}}:
|
||||
| {{user.stats[stat]}}
|
||||
| {{totalAllocatedStats(stat)}}
|
||||
li
|
||||
strong {{$t('buffs')}}:
|
||||
| {{user.stats.buffs[stat]}}
|
||||
@@ -124,7 +124,7 @@
|
||||
h3
|
||||
| {{$t('statPoints')}}
|
||||
.counter.badge(v-if='user.stats.points || userLevel100Plus')
|
||||
| {{user.stats.points}}
|
||||
| {{pointsRemaining}}
|
||||
.col-12.col-md-6
|
||||
.float-right
|
||||
toggle-switch(
|
||||
@@ -137,7 +137,7 @@
|
||||
.box.white.row.col-12
|
||||
.col-9
|
||||
div(:class='stat') {{ $t(stats[stat].title) }}
|
||||
.number {{ user.stats[stat] }}
|
||||
.number {{totalAllocatedStats(stat)}}
|
||||
.points {{$t('pts')}}
|
||||
.col-3
|
||||
div
|
||||
@@ -157,7 +157,7 @@
|
||||
import Content from '../../../common/script/content';
|
||||
import { beastMasterProgress, mountMasterProgress } from '../../../common/script/count';
|
||||
import autoAllocate from '../../../common/script/fns/autoAllocate';
|
||||
import allocate from '../../../common/script/ops/stats/allocate';
|
||||
import allocateBulk from '../../../common/script/ops/stats/allocateBulk';
|
||||
import statsComputed from '../../../common/script/libs/statsComputed';
|
||||
|
||||
import axios from 'axios';
|
||||
@@ -239,14 +239,27 @@
|
||||
return this.user.stats.lvl >= 100;
|
||||
},
|
||||
showStatsSave () {
|
||||
const statsAreBeingUpdated = Object.values(this.statUpdates).find(stat => stat > 0);
|
||||
return Boolean(this.user.stats.points) || statsAreBeingUpdated;
|
||||
return Boolean(this.user.stats.points);
|
||||
},
|
||||
pointsRemaining () {
|
||||
let points = this.user.stats.points;
|
||||
Object.values(this.statUpdates).forEach(value => {
|
||||
points -= value;
|
||||
});
|
||||
return points;
|
||||
},
|
||||
|
||||
},
|
||||
methods: {
|
||||
getGearTitle (key) {
|
||||
return this.flatGear[key].text();
|
||||
},
|
||||
totalAllocatedStats (stat) {
|
||||
return this.user.stats[stat] + this.statUpdates[stat];
|
||||
},
|
||||
totalStatPoints (stat) {
|
||||
return this.statsComputed[stat] + this.statUpdates[stat];
|
||||
},
|
||||
totalCount (objectToCount) {
|
||||
let total = size(objectToCount);
|
||||
return total;
|
||||
@@ -292,14 +305,12 @@
|
||||
return display;
|
||||
},
|
||||
allocate (stat) {
|
||||
allocate(this.user, {query: { stat }});
|
||||
this.statUpdates[stat] += 1;
|
||||
if (this.pointsRemaining === 0) return;
|
||||
this.statUpdates[stat]++;
|
||||
},
|
||||
deallocate (stat) {
|
||||
if (this.user.stats[stat] === 0) return;
|
||||
this.user.stats[stat] -= 1;
|
||||
this.user.stats.points += 1;
|
||||
this.statUpdates[stat] -= 1;
|
||||
if (this.statUpdates[stat] === 0) return;
|
||||
this.statUpdates[stat]--;
|
||||
},
|
||||
async saveAttributes () {
|
||||
this.loading = true;
|
||||
@@ -309,10 +320,7 @@
|
||||
if (this.statUpdates[stat] > 0) statUpdates[stat] = this.statUpdates[stat];
|
||||
});
|
||||
|
||||
await axios.post('/api/v4/user/allocate-bulk', {
|
||||
stats: statUpdates,
|
||||
});
|
||||
|
||||
// reset statUpdates to zero before request to avoid display errors while waiting for server
|
||||
this.statUpdates = {
|
||||
str: 0,
|
||||
int: 0,
|
||||
@@ -320,6 +328,12 @@
|
||||
per: 0,
|
||||
};
|
||||
|
||||
allocateBulk(this.user, { body: { stats: statUpdates } });
|
||||
|
||||
await axios.post('/api/v4/user/allocate-bulk', {
|
||||
stats: statUpdates,
|
||||
});
|
||||
|
||||
this.loading = false;
|
||||
},
|
||||
allocateNow () {
|
||||
|
||||
@@ -188,8 +188,7 @@ shops.getMarketGearCategories = function getMarketGear (user, language) {
|
||||
};
|
||||
|
||||
let specialNonClassGear = filter(content.gear.flat, (gear) => {
|
||||
return user.items.gear.owned[gear.key] === false ||
|
||||
!user.items.gear.owned[gear.key] &&
|
||||
return !user.items.gear.owned[gear.key] &&
|
||||
content.classes.indexOf(gear.klass) === -1 &&
|
||||
content.classes.indexOf(gear.specialClass) === -1 &&
|
||||
(gear.canOwn && gear.canOwn(user));
|
||||
|
||||
@@ -1,18 +1,15 @@
|
||||
import { inboxModel as Inbox } from '../../models/message';
|
||||
import { toArray, orderBy } from 'lodash';
|
||||
|
||||
export async function getUserInbox (user, asArray = true) {
|
||||
const messages = (await Inbox
|
||||
.find({ownerId: user._id})
|
||||
.sort({timestamp: -1})
|
||||
.exec()).map(msg => msg.toJSON());
|
||||
|
||||
const messagesObj = Object.assign({}, user.inbox.messages); // copy, shallow clone
|
||||
|
||||
if (asArray) {
|
||||
messages.push(...toArray(messagesObj));
|
||||
|
||||
return orderBy(messages, ['timestamp'], ['desc']);
|
||||
return messages;
|
||||
} else {
|
||||
const messagesObj = {};
|
||||
messages.forEach(msg => messagesObj[msg._id] = msg);
|
||||
|
||||
return messagesObj;
|
||||
@@ -20,15 +17,9 @@ export async function getUserInbox (user, asArray = true) {
|
||||
}
|
||||
|
||||
export async function deleteMessage (user, messageId) {
|
||||
if (user.inbox.messages[messageId]) { // compatibility
|
||||
delete user.inbox.messages[messageId];
|
||||
user.markModified(`inbox.messages.${messageId}`);
|
||||
await user.save();
|
||||
} else {
|
||||
const message = await Inbox.findOne({_id: messageId, ownerId: user._id }).exec();
|
||||
if (!message) return false;
|
||||
await Inbox.remove({_id: message._id, ownerId: user._id}).exec();
|
||||
}
|
||||
const message = await Inbox.findOne({_id: messageId, ownerId: user._id }).exec();
|
||||
if (!message) return false;
|
||||
await Inbox.remove({_id: message._id, ownerId: user._id}).exec();
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -36,10 +27,6 @@ export async function deleteMessage (user, messageId) {
|
||||
export async function clearPMs (user) {
|
||||
user.inbox.newMessages = 0;
|
||||
|
||||
// compatibility
|
||||
user.inbox.messages = {};
|
||||
user.markModified('inbox.messages');
|
||||
|
||||
await Promise.all([
|
||||
user.save(),
|
||||
Inbox.remove({ownerId: user._id}).exec(),
|
||||
|
||||
@@ -559,11 +559,7 @@ let schema = new Schema({
|
||||
tags: [TagSchema],
|
||||
|
||||
inbox: {
|
||||
// messages are stored in the Inbox collection, this path will be removed
|
||||
// as soon as the migration has run and all the messages have been removed from here
|
||||
messages: {type: Schema.Types.Mixed, default: () => {
|
||||
return {};
|
||||
}},
|
||||
// messages are stored in the Inbox collection
|
||||
newMessages: {type: Number, default: 0},
|
||||
blocks: {type: Array, default: () => []},
|
||||
optOut: {type: Boolean, default: false},
|
||||
|
||||
Reference in New Issue
Block a user