Compare commits
100 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 492ec66f49 | |||
| 3d3abf0829 | |||
| 747cb9d2ca | |||
| 25015ae8e0 | |||
| f501e23835 | |||
| 20a2c0fbfd | |||
| 2693d49870 | |||
| 169ae8e011 | |||
| 2007f7fc89 | |||
| f5b1804602 | |||
| 225b3e77a1 | |||
| da9e07d71a | |||
| 726fd843de | |||
| a50c5aa28d | |||
| 47b77edca7 | |||
| 730aefa2fa | |||
| 7d7654bb04 | |||
| f7e9b467a5 | |||
| 86fce69928 | |||
| 1c45d170dd | |||
| be107f02d3 | |||
| c4b16f86db | |||
| cc7d065445 | |||
| e4edab2b9d | |||
| fb1ea935e6 | |||
| 9795597c79 | |||
| c36f01773f | |||
| a4eabc05e7 | |||
| ae26cc3244 | |||
| 9fe28c6d51 | |||
| 34983f1221 | |||
| 8f5a0cfe79 | |||
| a00a8cced8 | |||
| 1aceccc6c4 | |||
| 10c4baba4a | |||
| 56cdc94860 | |||
| 1346b71b81 | |||
| 023173fada | |||
| b5182ed7d4 | |||
| 315604073b | |||
| 3a5e93f82e | |||
| a45b04b1cf | |||
| a49f95e1a5 | |||
| 0ac12cb6a5 | |||
| cd90a281c2 | |||
| e223311aac | |||
| bdb3cf25c1 | |||
| 0bb69665f8 | |||
| 7a52386daf | |||
| 7e256c2d96 | |||
| 5ea1211571 | |||
| 73a91c4f3c | |||
| 185e695e31 | |||
| 6fdce9f51c | |||
| f905da7d57 | |||
| 33a69452e8 | |||
| a049b56a63 | |||
| 1e85ec9007 | |||
| 1d69f44e7e | |||
| 53a15f9fa0 | |||
| aa1fcb4956 | |||
| 97a1d0f474 | |||
| 9a13594cca | |||
| 6c36ebc952 | |||
| b6371971a2 | |||
| 11b1bf01cc | |||
| 393aef1b04 | |||
| 3466e62aa5 | |||
| afd82784b2 | |||
| ad20d451b3 | |||
| 29046cc2d5 | |||
| e8a19c32fe | |||
| 37fc8850a8 | |||
| 302fa9b4b9 | |||
| b2102657af | |||
| 0b16ec775e | |||
| 9edac6d56e | |||
| a5bfec0c95 | |||
| 891bb2e2ae | |||
| 297cce06fc | |||
| c5587fcbdb | |||
| 6314a53136 | |||
| 9379325731 | |||
| 59c4b32d69 | |||
| b108b047cd | |||
| 3c394e7448 | |||
| e8eb014647 | |||
| da8346ea50 | |||
| 0731dcd453 | |||
| 960985dee5 | |||
| eb991ae5eb | |||
| e86b23a98a | |||
| aedabd2266 | |||
| 60a12e8090 | |||
| 33e9e7b647 | |||
| 90f732f545 | |||
| 3ce74a3fd1 | |||
| 907dd406f3 | |||
| 42687335af | |||
| 3797d29835 |
@@ -0,0 +1,118 @@
|
||||
/* eslint-disable no-console */
|
||||
const MIGRATION_NAME = '20191231_nye';
|
||||
import { model as User } from '../../../website/server/models/user';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
|
||||
const progressCount = 1000;
|
||||
let count = 0;
|
||||
|
||||
async function updateUser (user) {
|
||||
count++;
|
||||
|
||||
const set = {'flags.newStuff': true};
|
||||
let push;
|
||||
|
||||
set.migration = MIGRATION_NAME;
|
||||
|
||||
if (typeof user.items.gear.owned.head_special_nye2018 !== 'undefined') {
|
||||
set['items.gear.owned.head_special_nye2019'] = false;
|
||||
push = [
|
||||
{
|
||||
type: 'marketGear',
|
||||
path: 'gear.flat.head_special_nye2019',
|
||||
_id: uuid(),
|
||||
},
|
||||
];
|
||||
} else if (typeof user.items.gear.owned.head_special_nye2017 !== 'undefined') {
|
||||
set['items.gear.owned.head_special_nye2018'] = false;
|
||||
push = [
|
||||
{
|
||||
type: 'marketGear',
|
||||
path: 'gear.flat.head_special_nye2018',
|
||||
_id: uuid(),
|
||||
},
|
||||
];
|
||||
} else if (typeof user.items.gear.owned.head_special_nye2016 !== 'undefined') {
|
||||
set['items.gear.owned.head_special_nye2017'] = false;
|
||||
push = [
|
||||
{
|
||||
type: 'marketGear',
|
||||
path: 'gear.flat.head_special_nye2017',
|
||||
_id: uuid(),
|
||||
},
|
||||
];
|
||||
} else if (typeof user.items.gear.owned.head_special_nye2015 !== 'undefined') {
|
||||
set['items.gear.owned.head_special_nye2016'] = false;
|
||||
push = [
|
||||
{
|
||||
type: 'marketGear',
|
||||
path: 'gear.flat.head_special_nye2016',
|
||||
_id: uuid(),
|
||||
},
|
||||
];
|
||||
} else if (typeof user.items.gear.owned.head_special_nye2014 !== 'undefined') {
|
||||
set['items.gear.owned.head_special_nye2015'] = false;
|
||||
push = [
|
||||
{
|
||||
type: 'marketGear',
|
||||
path: 'gear.flat.head_special_nye2015',
|
||||
_id: uuid(),
|
||||
},
|
||||
];
|
||||
} else if (typeof user.items.gear.owned.head_special_nye !== 'undefined') {
|
||||
set['items.gear.owned.head_special_nye2014'] = false;
|
||||
push = [
|
||||
{
|
||||
type: 'marketGear',
|
||||
path: 'gear.flat.head_special_nye2014',
|
||||
_id: uuid(),
|
||||
},
|
||||
];
|
||||
} else {
|
||||
set['items.gear.owned.head_special_nye'] = false;
|
||||
push = [
|
||||
{
|
||||
type: 'marketGear',
|
||||
path: 'gear.flat.head_special_nye',
|
||||
_id: uuid(),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
|
||||
|
||||
return await User.update({_id: user._id}, {$set: set, $push: {pinnedItems: {$each: push}}}).exec();
|
||||
}
|
||||
|
||||
export default async function processUsers () {
|
||||
let query = {
|
||||
migration: {$ne: MIGRATION_NAME},
|
||||
};
|
||||
|
||||
const fields = {
|
||||
_id: 1,
|
||||
items: 1,
|
||||
};
|
||||
|
||||
while (true) { // eslint-disable-line no-constant-condition
|
||||
const users = await User // eslint-disable-line no-await-in-loop
|
||||
.find(query)
|
||||
.limit(250)
|
||||
.sort({_id: 1})
|
||||
.select(fields)
|
||||
.lean()
|
||||
.exec();
|
||||
|
||||
if (users.length === 0) {
|
||||
console.warn('All appropriate users found and modified.');
|
||||
console.warn(`\n${count} users processed\n`);
|
||||
break;
|
||||
} else {
|
||||
query._id = {
|
||||
$gt: users[users.length - 1],
|
||||
};
|
||||
}
|
||||
|
||||
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
|
||||
}
|
||||
};
|
||||
@@ -10,7 +10,7 @@ async function syncChallengeToMembers (challenges) {
|
||||
|
||||
const promises = [];
|
||||
users.forEach(user => {
|
||||
promises.push(challenge.syncToUser(user));
|
||||
promises.push(challenge.syncTasksToUser(user));
|
||||
promises.push(challenge.save());
|
||||
promises.push(user.save());
|
||||
});
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Award Onboarding Achievements for existing users
|
||||
*/
|
||||
/* eslint-disable no-console */
|
||||
|
||||
import { model as User } from '../../website/server/models/user';
|
||||
|
||||
const MIGRATION_NAME = '20191218_onboarding_achievements';
|
||||
|
||||
const progressCount = 1000;
|
||||
let count = 0;
|
||||
|
||||
async function updateUser (user) {
|
||||
count += 1;
|
||||
|
||||
const set = {};
|
||||
|
||||
set.migration = MIGRATION_NAME;
|
||||
|
||||
const hasPet = Object.keys(user.items.pets).find(petKey => {
|
||||
const pet = user.items.pets[petKey];
|
||||
|
||||
if (pet >= 5) return true;
|
||||
return false;
|
||||
});
|
||||
|
||||
if (hasPet) {
|
||||
set['achievements.hatchedPet'] = true;
|
||||
}
|
||||
|
||||
const hasFedPet = Object.keys(user.items.pets).find(petKey => {
|
||||
const pet = user.items.pets[petKey];
|
||||
|
||||
if (pet > 5) return true;
|
||||
return false;
|
||||
});
|
||||
|
||||
if (hasFedPet) {
|
||||
set['achievements.fedPet'] = true;
|
||||
}
|
||||
|
||||
const hasGear = Object.keys(user.items.gear.owned).find(gearKey => {
|
||||
const gear = user.items.gear.owned[gearKey];
|
||||
|
||||
if (gear === true && gearKey.indexOf('_special_') === -1) return true;
|
||||
return false;
|
||||
});
|
||||
|
||||
if (hasGear) {
|
||||
set['achievements.purchasedEquipment'] = true;
|
||||
}
|
||||
|
||||
const hasTask = Object.keys(user.tasksOrder).find(tasksOrderType => {
|
||||
const order = user.tasksOrder[tasksOrderType];
|
||||
|
||||
if (order && order.length > 0) return true;
|
||||
return false;
|
||||
});
|
||||
|
||||
if (hasTask) {
|
||||
set['achievements.createdTask'] = true;
|
||||
}
|
||||
|
||||
const hasExperience = user.stats && user.stats.exp && user.stats.exp > 0;
|
||||
if (hasTask && hasExperience) {
|
||||
set['achievements.completedTask'] = true;
|
||||
}
|
||||
|
||||
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
|
||||
return User.update({ _id: user._id }, { $set: set }).exec();
|
||||
}
|
||||
|
||||
module.exports = async function processUsers () { // eslint-disable-line import/no-commonjs
|
||||
const query = {
|
||||
migration: { $ne: MIGRATION_NAME },
|
||||
};
|
||||
|
||||
const fields = {
|
||||
_id: 1,
|
||||
stats: 1,
|
||||
items: 1,
|
||||
achievements: 1,
|
||||
tasksOrder: 1,
|
||||
};
|
||||
|
||||
while (true) { // eslint-disable-line no-constant-condition
|
||||
const users = await User // eslint-disable-line no-await-in-loop
|
||||
.find(query)
|
||||
.limit(100)
|
||||
.sort({ _id: 1 })
|
||||
.select(fields)
|
||||
.lean()
|
||||
.exec();
|
||||
|
||||
if (users.length === 0) {
|
||||
console.warn('All appropriate users found and modified.');
|
||||
console.warn(`\n${count} users processed\n`);
|
||||
break;
|
||||
} else {
|
||||
query._id = {
|
||||
$gt: users[users.length - 1],
|
||||
};
|
||||
}
|
||||
|
||||
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
|
||||
}
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "habitica",
|
||||
"version": "4.126.0",
|
||||
"version": "4.128.1",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@@ -13,14 +13,14 @@
|
||||
}
|
||||
},
|
||||
"@babel/core": {
|
||||
"version": "7.7.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.7.4.tgz",
|
||||
"integrity": "sha512-+bYbx56j4nYBmpsWtnPUsKW3NdnYxbqyfrP2w9wILBuHzdfIKz9prieZK0DFPyIzkjYVUe4QkusGL07r5pXznQ==",
|
||||
"version": "7.7.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.7.5.tgz",
|
||||
"integrity": "sha512-M42+ScN4+1S9iB6f+TL7QBpoQETxbclx+KNoKJABghnKYE+fMzSGqst0BZJc8CpI625bwPwYgUyRvxZ+0mZzpw==",
|
||||
"requires": {
|
||||
"@babel/code-frame": "^7.5.5",
|
||||
"@babel/generator": "^7.7.4",
|
||||
"@babel/helpers": "^7.7.4",
|
||||
"@babel/parser": "^7.7.4",
|
||||
"@babel/parser": "^7.7.5",
|
||||
"@babel/template": "^7.7.4",
|
||||
"@babel/traverse": "^7.7.4",
|
||||
"@babel/types": "^7.7.4",
|
||||
@@ -33,6 +33,11 @@
|
||||
"source-map": "^0.5.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/parser": {
|
||||
"version": "7.7.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.7.5.tgz",
|
||||
"integrity": "sha512-KNlOe9+/nk4i29g0VXgl8PEXIRms5xKLJeuZ6UptN0fHv+jDiriG+y94X6qAgWTR0h3KaoM1wK5G5h7MHFRSig=="
|
||||
},
|
||||
"convert-source-map": {
|
||||
"version": "1.7.0",
|
||||
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz",
|
||||
@@ -152,9 +157,9 @@
|
||||
}
|
||||
},
|
||||
"@babel/helper-module-transforms": {
|
||||
"version": "7.7.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.7.4.tgz",
|
||||
"integrity": "sha512-ehGBu4mXrhs0FxAqN8tWkzF8GSIGAiEumu4ONZ/hD9M88uHcD+Yu2ttKfOCgwzoesJOJrtQh7trI5YPbRtMmnA==",
|
||||
"version": "7.7.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.7.5.tgz",
|
||||
"integrity": "sha512-A7pSxyJf1gN5qXVcidwLWydjftUN878VkalhXX5iQDuGyiGK3sOrrKKHF4/A4fwHtnsotv/NipwAeLzY4KQPvw==",
|
||||
"requires": {
|
||||
"@babel/helper-module-imports": "^7.7.4",
|
||||
"@babel/helper-simple-access": "^7.7.4",
|
||||
@@ -490,21 +495,21 @@
|
||||
}
|
||||
},
|
||||
"@babel/plugin-transform-modules-amd": {
|
||||
"version": "7.7.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.7.4.tgz",
|
||||
"integrity": "sha512-/542/5LNA18YDtg1F+QHvvUSlxdvjZoD/aldQwkq+E3WCkbEjNSN9zdrOXaSlfg3IfGi22ijzecklF/A7kVZFQ==",
|
||||
"version": "7.7.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.7.5.tgz",
|
||||
"integrity": "sha512-CT57FG4A2ZUNU1v+HdvDSDrjNWBrtCmSH6YbbgN3Lrf0Di/q/lWRxZrE72p3+HCCz9UjfZOEBdphgC0nzOS6DQ==",
|
||||
"requires": {
|
||||
"@babel/helper-module-transforms": "^7.7.4",
|
||||
"@babel/helper-module-transforms": "^7.7.5",
|
||||
"@babel/helper-plugin-utils": "^7.0.0",
|
||||
"babel-plugin-dynamic-import-node": "^2.3.0"
|
||||
}
|
||||
},
|
||||
"@babel/plugin-transform-modules-commonjs": {
|
||||
"version": "7.7.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.7.4.tgz",
|
||||
"integrity": "sha512-k8iVS7Jhc367IcNF53KCwIXtKAH7czev866ThsTgy8CwlXjnKZna2VHwChglzLleYrcHz1eQEIJlGRQxB53nqA==",
|
||||
"version": "7.7.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.7.5.tgz",
|
||||
"integrity": "sha512-9Cq4zTFExwFhQI6MT1aFxgqhIsMWQWDVwOgLzl7PTWJHsNaqFvklAU+Oz6AQLAS0dJKTwZSOCo20INwktxpi3Q==",
|
||||
"requires": {
|
||||
"@babel/helper-module-transforms": "^7.7.4",
|
||||
"@babel/helper-module-transforms": "^7.7.5",
|
||||
"@babel/helper-plugin-utils": "^7.0.0",
|
||||
"@babel/helper-simple-access": "^7.7.4",
|
||||
"babel-plugin-dynamic-import-node": "^2.3.0"
|
||||
@@ -573,9 +578,9 @@
|
||||
}
|
||||
},
|
||||
"@babel/plugin-transform-regenerator": {
|
||||
"version": "7.7.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.7.4.tgz",
|
||||
"integrity": "sha512-e7MWl5UJvmPEwFJTwkBlPmqixCtr9yAASBqff4ggXTNicZiwbF8Eefzm6NVgfiBp7JdAGItecnctKTgH44q2Jw==",
|
||||
"version": "7.7.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.7.5.tgz",
|
||||
"integrity": "sha512-/8I8tPvX2FkuEyWbjRCt4qTAgZK0DVy8QRguhA524UH48RfGJy94On2ri+dCuwOpcerPRl9O4ebQkRcVzIaGBw==",
|
||||
"requires": {
|
||||
"regenerator-transform": "^0.14.0"
|
||||
}
|
||||
@@ -640,9 +645,9 @@
|
||||
}
|
||||
},
|
||||
"@babel/preset-env": {
|
||||
"version": "7.7.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.7.4.tgz",
|
||||
"integrity": "sha512-Dg+ciGJjwvC1NIe/DGblMbcGq1HOtKbw8RLl4nIjlfcILKEOkWT/vRqPpumswABEBVudii6dnVwrBtzD7ibm4g==",
|
||||
"version": "7.7.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.7.6.tgz",
|
||||
"integrity": "sha512-k5hO17iF/Q7tR9Jv8PdNBZWYW6RofxhnxKjBMc0nG4JTaWvOTiPoO/RLFwAKcA4FpmuBFm6jkoqaRJLGi0zdaQ==",
|
||||
"requires": {
|
||||
"@babel/helper-module-imports": "^7.7.4",
|
||||
"@babel/helper-plugin-utils": "^7.0.0",
|
||||
@@ -672,8 +677,8 @@
|
||||
"@babel/plugin-transform-function-name": "^7.7.4",
|
||||
"@babel/plugin-transform-literals": "^7.7.4",
|
||||
"@babel/plugin-transform-member-expression-literals": "^7.7.4",
|
||||
"@babel/plugin-transform-modules-amd": "^7.7.4",
|
||||
"@babel/plugin-transform-modules-commonjs": "^7.7.4",
|
||||
"@babel/plugin-transform-modules-amd": "^7.7.5",
|
||||
"@babel/plugin-transform-modules-commonjs": "^7.7.5",
|
||||
"@babel/plugin-transform-modules-systemjs": "^7.7.4",
|
||||
"@babel/plugin-transform-modules-umd": "^7.7.4",
|
||||
"@babel/plugin-transform-named-capturing-groups-regex": "^7.7.4",
|
||||
@@ -681,7 +686,7 @@
|
||||
"@babel/plugin-transform-object-super": "^7.7.4",
|
||||
"@babel/plugin-transform-parameters": "^7.7.4",
|
||||
"@babel/plugin-transform-property-literals": "^7.7.4",
|
||||
"@babel/plugin-transform-regenerator": "^7.7.4",
|
||||
"@babel/plugin-transform-regenerator": "^7.7.5",
|
||||
"@babel/plugin-transform-reserved-words": "^7.7.4",
|
||||
"@babel/plugin-transform-shorthand-properties": "^7.7.4",
|
||||
"@babel/plugin-transform-spread": "^7.7.4",
|
||||
@@ -691,7 +696,7 @@
|
||||
"@babel/plugin-transform-unicode-regex": "^7.7.4",
|
||||
"@babel/types": "^7.7.4",
|
||||
"browserslist": "^4.6.0",
|
||||
"core-js-compat": "^3.1.1",
|
||||
"core-js-compat": "^3.4.7",
|
||||
"invariant": "^2.2.2",
|
||||
"js-levenshtein": "^1.1.3",
|
||||
"semver": "^5.5.0"
|
||||
@@ -746,9 +751,9 @@
|
||||
}
|
||||
},
|
||||
"@google-cloud/common": {
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-2.2.3.tgz",
|
||||
"integrity": "sha512-lvw54mGKn8VqVIy2NzAk0l5fntBFX4UwQhHk6HaqkyCQ7WBl5oz4XhzKMtMilozF/3ObPcDogqwuyEWyZ6rnQQ==",
|
||||
"version": "2.2.5",
|
||||
"resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-2.2.5.tgz",
|
||||
"integrity": "sha512-Iw6LJj7V8XEVFDORCHC/I+YVmK98KmZSa8z10O4h2Wpkzlk/Sjj8Ruz4QJHawj7GwuWjQQ47O2Z4JECXf1S3ag==",
|
||||
"requires": {
|
||||
"@google-cloud/projectify": "^1.0.0",
|
||||
"@google-cloud/promisify": "^1.0.0",
|
||||
@@ -762,22 +767,22 @@
|
||||
}
|
||||
},
|
||||
"@google-cloud/projectify": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-1.0.2.tgz",
|
||||
"integrity": "sha512-WnkGxvk4U1kAJpoS/Ehk+3MZXVW+XHHhwc/QyD6G8Za4xml3Fv+NRn/bYffl1TxSg+gE0N0mj9Shgc7e8+fl8A=="
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-1.0.4.tgz",
|
||||
"integrity": "sha512-ZdzQUN02eRsmTKfBj9FDL0KNDIFNjBn/d6tHQmA/+FImH5DO6ZV8E7FzxMgAUiVAUq41RFAkb25p1oHOZ8psfg=="
|
||||
},
|
||||
"@google-cloud/promisify": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-1.0.3.tgz",
|
||||
"integrity": "sha512-Rufgfl3TnkIil3CjsH33Q6093zeoVqyqCdvtvgHuCqRJxCZYfaVPIyr8JViMeLTD4Ja630pRKKZVSjKggoVbNg=="
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-1.0.4.tgz",
|
||||
"integrity": "sha512-VccZDcOql77obTnFh0TbNED/6ZbbmHDf8UMNnzO1d5g9V0Htfm4k5cllY8P1tJsRKC3zWYGRLaViiupcgVjBoQ=="
|
||||
},
|
||||
"@google-cloud/trace-agent": {
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@google-cloud/trace-agent/-/trace-agent-4.2.3.tgz",
|
||||
"integrity": "sha512-BezSjercoxF7CAjklEwlJ9w7sS7lrGcMKCiyfdGq9cl7ZlsrYU2FnO3x8aDXQicnfaoHEeBFvAcR4ADhoGZamQ==",
|
||||
"version": "4.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@google-cloud/trace-agent/-/trace-agent-4.2.4.tgz",
|
||||
"integrity": "sha512-XCiEQ2D241RvYmMyeX/Q4My6NGAnSNjZZ/ff82MTGLc12iBxvYD3zMU/Yx6XtcByHIaSFqmCexK1hZzkALHjyQ==",
|
||||
"requires": {
|
||||
"@google-cloud/common": "^2.0.0",
|
||||
"@opencensus/propagation-stackdriver": "0.0.18",
|
||||
"@opencensus/propagation-stackdriver": "0.0.19",
|
||||
"builtin-modules": "^3.0.0",
|
||||
"console-log-level": "^1.4.0",
|
||||
"continuation-local-storage": "^3.2.1",
|
||||
@@ -824,9 +829,9 @@
|
||||
}
|
||||
},
|
||||
"@opencensus/core": {
|
||||
"version": "0.0.18",
|
||||
"resolved": "https://registry.npmjs.org/@opencensus/core/-/core-0.0.18.tgz",
|
||||
"integrity": "sha512-PgRQXLyb3bLi8Z6pQct9erYFRdnYAZNQXAEVPf6Xq6IMkZaH20wiOTNNPxEckjI31mq5utgstAbwOn4gJiPjBQ==",
|
||||
"version": "0.0.19",
|
||||
"resolved": "https://registry.npmjs.org/@opencensus/core/-/core-0.0.19.tgz",
|
||||
"integrity": "sha512-Y5QXa7vggMU0+jveLcworfX9jNnztix7x1NraAV0uGkTp4y46HrFl0DnNcnNxUDvBu/cYeWRwlmhiWlr9+adOQ==",
|
||||
"requires": {
|
||||
"continuation-local-storage": "^3.2.1",
|
||||
"log-driver": "^1.2.7",
|
||||
@@ -843,11 +848,11 @@
|
||||
}
|
||||
},
|
||||
"@opencensus/propagation-stackdriver": {
|
||||
"version": "0.0.18",
|
||||
"resolved": "https://registry.npmjs.org/@opencensus/propagation-stackdriver/-/propagation-stackdriver-0.0.18.tgz",
|
||||
"integrity": "sha512-BLwfszIGAfqN2mqGf/atfEu84cWeoLM/YuXGfXDO1iDN2k5GXz4QFyhS8sz5l63HtsYuQqFuV+Ze7ZM0NvJp2A==",
|
||||
"version": "0.0.19",
|
||||
"resolved": "https://registry.npmjs.org/@opencensus/propagation-stackdriver/-/propagation-stackdriver-0.0.19.tgz",
|
||||
"integrity": "sha512-TTL9KIOkvTpd+DT2gj3R3JP7XqOAf69ab/wzxIwpBlFqfRiIFBkOALyC/Gy4pKooAe5DemDhXZuRtIa0PgfoZQ==",
|
||||
"requires": {
|
||||
"@opencensus/core": "^0.0.18",
|
||||
"@opencensus/core": "^0.0.19",
|
||||
"hex2dec": "^1.0.1",
|
||||
"uuid": "^3.2.1"
|
||||
}
|
||||
@@ -1707,25 +1712,44 @@
|
||||
"integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg=="
|
||||
},
|
||||
"aws-sdk": {
|
||||
"version": "2.578.0",
|
||||
"resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.578.0.tgz",
|
||||
"integrity": "sha512-QOot7ha8J+w+AQf1UNzpGpbcZtCaK/mqjenG177ybm2nvm00a4PKa5dz/kF/bYi2qMx9yJmiQ17kn32Q5ar8Kg==",
|
||||
"version": "2.590.0",
|
||||
"resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.590.0.tgz",
|
||||
"integrity": "sha512-cdH3B/IuEuds84zRvi52WEW0UXOjsyl9hEkdbS2Uo8P7pWepaKTOZ3BKIUsy/lXen0nHcRWDCHeUkBwNW1Q2bg==",
|
||||
"requires": {
|
||||
"buffer": "^4.9.1",
|
||||
"events": "^1.1.1",
|
||||
"ieee754": "^1.1.13",
|
||||
"jmespath": "^0.15.0",
|
||||
"querystring": "^0.2.0",
|
||||
"sax": "^1.2.1",
|
||||
"url": "^0.10.3",
|
||||
"uuid": "^3.3.2",
|
||||
"xml2js": "^0.4.19"
|
||||
"buffer": "4.9.1",
|
||||
"events": "1.1.1",
|
||||
"ieee754": "1.1.13",
|
||||
"jmespath": "0.15.0",
|
||||
"querystring": "0.2.0",
|
||||
"sax": "1.2.1",
|
||||
"url": "0.10.3",
|
||||
"uuid": "3.3.2",
|
||||
"xml2js": "0.4.19"
|
||||
},
|
||||
"dependencies": {
|
||||
"sax": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
|
||||
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz",
|
||||
"integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o="
|
||||
},
|
||||
"uuid": {
|
||||
"version": "3.3.2",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
|
||||
"integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA=="
|
||||
},
|
||||
"xml2js": {
|
||||
"version": "0.4.19",
|
||||
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz",
|
||||
"integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==",
|
||||
"requires": {
|
||||
"sax": ">=0.6.0",
|
||||
"xmlbuilder": "~9.0.1"
|
||||
}
|
||||
},
|
||||
"xmlbuilder": {
|
||||
"version": "9.0.7",
|
||||
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz",
|
||||
"integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0="
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -2488,13 +2512,13 @@
|
||||
"dev": true
|
||||
},
|
||||
"browserslist": {
|
||||
"version": "4.7.3",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.7.3.tgz",
|
||||
"integrity": "sha512-jWvmhqYpx+9EZm/FxcZSbUZyDEvDTLDi3nSAKbzEkyWvtI0mNSmUosey+5awDW1RUlrgXbQb5A6qY1xQH9U6MQ==",
|
||||
"version": "4.8.2",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.8.2.tgz",
|
||||
"integrity": "sha512-+M4oeaTplPm/f1pXDw84YohEv7B1i/2Aisei8s4s6k3QsoSHa7i5sz8u/cGQkkatCPxMASKxPualR4wwYgVboA==",
|
||||
"requires": {
|
||||
"caniuse-lite": "^1.0.30001010",
|
||||
"electron-to-chromium": "^1.3.306",
|
||||
"node-releases": "^1.1.40"
|
||||
"caniuse-lite": "^1.0.30001015",
|
||||
"electron-to-chromium": "^1.3.322",
|
||||
"node-releases": "^1.1.42"
|
||||
}
|
||||
},
|
||||
"bson": {
|
||||
@@ -2503,9 +2527,9 @@
|
||||
"integrity": "sha512-jCGVYLoYMHDkOsbwJZBCqwMHyH4c+wzgI9hG7Z6SZJRXWr+x58pdIbm2i9a/jFGCkRJqRUr8eoI7lDWa0hTkxg=="
|
||||
},
|
||||
"buffer": {
|
||||
"version": "4.9.2",
|
||||
"resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz",
|
||||
"integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==",
|
||||
"version": "4.9.1",
|
||||
"resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz",
|
||||
"integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=",
|
||||
"requires": {
|
||||
"base64-js": "^1.0.2",
|
||||
"ieee754": "^1.1.4",
|
||||
@@ -2641,9 +2665,9 @@
|
||||
"integrity": "sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs="
|
||||
},
|
||||
"caniuse-lite": {
|
||||
"version": "1.0.30001012",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001012.tgz",
|
||||
"integrity": "sha512-7RR4Uh04t9K1uYRWzOJmzplgEOAXbfK72oVNokCdMzA67trrhPzy93ahKk1AWHiA0c58tD2P+NHqxrA8FZ+Trg=="
|
||||
"version": "1.0.30001015",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001015.tgz",
|
||||
"integrity": "sha512-/xL2AbW/XWHNu1gnIrO8UitBGoFthcsDgU9VLK1/dpsoxbaD5LscHozKze05R6WLsBvLhqv78dAPozMFQBYLbQ=="
|
||||
},
|
||||
"capture-stack-trace": {
|
||||
"version": "1.0.1",
|
||||
@@ -3319,11 +3343,11 @@
|
||||
"dev": true
|
||||
},
|
||||
"core-js-compat": {
|
||||
"version": "3.4.2",
|
||||
"resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.4.2.tgz",
|
||||
"integrity": "sha512-W0Aj+LM3EAxxjD0Kp2o4be8UlnxIZHNupBv2znqrheR4aY2nOn91794k/xoSp+SxqqriiZpTsSwBtZr60cbkwQ==",
|
||||
"version": "3.4.8",
|
||||
"resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.4.8.tgz",
|
||||
"integrity": "sha512-l3WTmnXHV2Sfu5VuD7EHE2w7y+K68+kULKt5RJg8ZJk3YhHF1qLD4O8v8AmNq+8vbOwnPFFDvds25/AoEvMqlQ==",
|
||||
"requires": {
|
||||
"browserslist": "^4.7.3",
|
||||
"browserslist": "^4.8.2",
|
||||
"semver": "^6.3.0"
|
||||
},
|
||||
"dependencies": {
|
||||
@@ -3432,9 +3456,9 @@
|
||||
}
|
||||
},
|
||||
"csv-stringify": {
|
||||
"version": "5.3.3",
|
||||
"resolved": "https://registry.npmjs.org/csv-stringify/-/csv-stringify-5.3.3.tgz",
|
||||
"integrity": "sha512-q8Qj+/lN74LRmG7Mg0LauE5WcnJOD5MEGe1gI57IYJCB61KWuEbAFHm1uIPDkI26aqElyBB57SlE2GGwq2EY5A=="
|
||||
"version": "5.3.4",
|
||||
"resolved": "https://registry.npmjs.org/csv-stringify/-/csv-stringify-5.3.4.tgz",
|
||||
"integrity": "sha512-w3sjZh/b5xvN1NeWPbMBnvW+Q4D+cCoAk/2J0C/DqJKV3dHqseQGzP/BsdpqbIBl5UTFQxHgHkSUu5aiMFT62g=="
|
||||
},
|
||||
"currently-unhandled": {
|
||||
"version": "0.4.1",
|
||||
@@ -4088,9 +4112,9 @@
|
||||
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
|
||||
},
|
||||
"electron-to-chromium": {
|
||||
"version": "1.3.314",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.314.tgz",
|
||||
"integrity": "sha512-IKDR/xCxKFhPts7h+VaSXS02Z1mznP3fli1BbXWXeN89i2gCzKraU8qLpEid8YzKcmZdZD3Mly3cn5/lY9xsBQ=="
|
||||
"version": "1.3.322",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.322.tgz",
|
||||
"integrity": "sha512-Tc8JQEfGQ1MzfSzI/bTlSr7btJv/FFO7Yh6tanqVmIWOuNCu6/D1MilIEgLtmWqIrsv+o4IjpLAhgMBr/ncNAA=="
|
||||
},
|
||||
"emitter-listener": {
|
||||
"version": "1.1.2",
|
||||
@@ -4247,9 +4271,9 @@
|
||||
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
|
||||
},
|
||||
"eslint": {
|
||||
"version": "6.7.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-6.7.1.tgz",
|
||||
"integrity": "sha512-UWzBS79pNcsDSxgxbdjkmzn/B6BhsXMfUaOHnNwyE8nD+Q6pyT96ow2MccVayUTV4yMid4qLhMiQaywctRkBLA==",
|
||||
"version": "6.7.2",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-6.7.2.tgz",
|
||||
"integrity": "sha512-qMlSWJaCSxDFr8fBPvJM9kJwbazrhNcBU3+DszDW1OlEwKBBRWsJc7NJFelvwQpanHCR14cOLD41x8Eqvo3Nng==",
|
||||
"requires": {
|
||||
"@babel/code-frame": "^7.0.0",
|
||||
"ajv": "^6.10.0",
|
||||
@@ -6026,9 +6050,9 @@
|
||||
}
|
||||
},
|
||||
"gaxios": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/gaxios/-/gaxios-2.1.0.tgz",
|
||||
"integrity": "sha512-Gtpb5sdQmb82sgVkT2GnS2n+Kx4dlFwbeMYcDlD395aEvsLCSQXJJcHt7oJ2LrGxDEAeiOkK79Zv2A8Pzt6CFg==",
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/gaxios/-/gaxios-2.2.0.tgz",
|
||||
"integrity": "sha512-54Y7s3yvtEO9CZ0yBVQHI5fzS7TzkjlnuLdDEkeyL1SNYMv877VofvA56E/C3dvj3rS7GFiyMWl833Qrr+nrkg==",
|
||||
"requires": {
|
||||
"abort-controller": "^3.0.0",
|
||||
"extend": "^3.0.2",
|
||||
@@ -6344,9 +6368,9 @@
|
||||
}
|
||||
},
|
||||
"google-auth-library": {
|
||||
"version": "5.5.1",
|
||||
"resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-5.5.1.tgz",
|
||||
"integrity": "sha512-zCtjQccWS/EHYyFdXRbfeSGM/gW+d7uMAcVnvXRnjBXON5ijo6s0nsObP0ifqileIDSbZjTlLtgo+UoN8IFJcg==",
|
||||
"version": "5.6.1",
|
||||
"resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-5.6.1.tgz",
|
||||
"integrity": "sha512-qPbg/KxZao9DxPcvP9QmKFjHJGdqsJb4PTcbPl8QcHtbzukXhSKNAt+ErfizAn80MuzReBPihsrMQhgfGqirdw==",
|
||||
"requires": {
|
||||
"arrify": "^2.0.0",
|
||||
"base64-js": "^1.3.0",
|
||||
@@ -6402,9 +6426,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"gtoken": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/gtoken/-/gtoken-4.1.2.tgz",
|
||||
"integrity": "sha512-/YvFlrrRjiSJf6a4Kmr4+E+HFbYdEntRPD/W+ZGLYDziHiWjX4S6ld8TNCyPMtUG3J3hPkZkpHbva40JA56spw==",
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/gtoken/-/gtoken-4.1.3.tgz",
|
||||
"integrity": "sha512-ofW+FiXjswyKdkjMcDbe6E4K7cDDdE82dGDhZIc++kUECqaE7MSErf6arJPAjcnYn1qxE1/Ti06qQuqgVusovQ==",
|
||||
"requires": {
|
||||
"gaxios": "^2.1.0",
|
||||
"google-p12-pem": "^2.0.0",
|
||||
@@ -6563,28 +6587,14 @@
|
||||
}
|
||||
},
|
||||
"habitica-markdown": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/habitica-markdown/-/habitica-markdown-1.3.0.tgz",
|
||||
"integrity": "sha1-DN8rTqjwNjPXBmBRyI0W6YOTeSE=",
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/habitica-markdown/-/habitica-markdown-1.3.2.tgz",
|
||||
"integrity": "sha512-IyiS583DfqE+KvW4NQAB4K2HjJZ1oF50L0EDz7KaixyK7C41s47wsbN81QtNMB8LnRqbMHFDesD2xEzdicjFXw==",
|
||||
"requires": {
|
||||
"habitica-markdown-emoji": "1.2.4",
|
||||
"markdown-it": "8.0.0",
|
||||
"markdown-it": "8.4.2",
|
||||
"markdown-it-link-attributes": "1.0.0",
|
||||
"markdown-it-linkify-images": "1.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"markdown-it": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-8.0.0.tgz",
|
||||
"integrity": "sha1-5mJVSXoOQJ6Bb7xngHl19PJvb4I=",
|
||||
"requires": {
|
||||
"argparse": "^1.0.7",
|
||||
"entities": "~1.1.1",
|
||||
"linkify-it": "^2.0.0",
|
||||
"mdurl": "^1.0.1",
|
||||
"uc.micro": "^1.0.3"
|
||||
}
|
||||
}
|
||||
"markdown-it-linkify-images": "^1.1.1"
|
||||
}
|
||||
},
|
||||
"habitica-markdown-emoji": {
|
||||
@@ -8522,9 +8532,12 @@
|
||||
"integrity": "sha1-jaHKFynw+hbGVhWwsQdbecg3Gi4="
|
||||
},
|
||||
"markdown-it-linkify-images": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/markdown-it-linkify-images/-/markdown-it-linkify-images-1.0.0.tgz",
|
||||
"integrity": "sha1-gTTsj0gM4pxD44Ck4narBnyuRoY="
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/markdown-it-linkify-images/-/markdown-it-linkify-images-1.1.1.tgz",
|
||||
"integrity": "sha512-1IEmAaAjIgAwY+tZI0sxDXdy9QKHutj5cN0lH2JBiSZt+2NYKrWRJj0cloQW3OFIfP2MLFA1E+6OLJhXPiLgNw==",
|
||||
"requires": {
|
||||
"markdown-it": "^8.4.2"
|
||||
}
|
||||
},
|
||||
"matchdep": {
|
||||
"version": "2.0.0",
|
||||
@@ -8946,13 +8959,13 @@
|
||||
}
|
||||
},
|
||||
"mongoose": {
|
||||
"version": "5.7.12",
|
||||
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.7.12.tgz",
|
||||
"integrity": "sha512-TqRiJPUeHN1u7Xs/zQmiMHcrzEb1/uKjK3uGzZhyqw25RKQiMV2vSBeBTMt5HXoYlUuDEWXE75FMjpK9X9kD0w==",
|
||||
"version": "5.8.1",
|
||||
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.8.1.tgz",
|
||||
"integrity": "sha512-8Cffl52cMK2iBlpLipoRKW/RdrhkxvVzXsy+xVsfbKHQBCWkFiS0T0jU4smYzomTMP4gW0sReJoRA7Gu/7VVgQ==",
|
||||
"requires": {
|
||||
"bson": "~1.1.1",
|
||||
"kareem": "2.3.1",
|
||||
"mongodb": "3.3.4",
|
||||
"mongodb": "3.3.5",
|
||||
"mongoose-legacy-pluralize": "1.0.2",
|
||||
"mpath": "0.6.0",
|
||||
"mquery": "3.2.2",
|
||||
@@ -8964,9 +8977,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"mongodb": {
|
||||
"version": "3.3.4",
|
||||
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.3.4.tgz",
|
||||
"integrity": "sha512-6fmHu3FJTpeZxacJcfjUGIP3BSteG0l2cxLkSrf1nnnS1OrlnVGiP9P/wAC4aB6dM6H4vQ2io8YDjkuPkje7AA==",
|
||||
"version": "3.3.5",
|
||||
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.3.5.tgz",
|
||||
"integrity": "sha512-6NAv5gTFdwRyVfCz+O+KDszvjpyxmZw+VlmqmqKR2GmpkeKrKFRv/ZslgTtZba2dc9JYixIf99T5Gih7TIWv7Q==",
|
||||
"requires": {
|
||||
"bson": "^1.1.1",
|
||||
"require_optional": "^1.0.1",
|
||||
@@ -9463,9 +9476,9 @@
|
||||
}
|
||||
},
|
||||
"node-releases": {
|
||||
"version": "1.1.41",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.41.tgz",
|
||||
"integrity": "sha512-+IctMa7wIs8Cfsa8iYzeaLTFwv5Y4r5jZud+4AnfymzeEXKBCavFX0KBgzVaPVqf0ywa6PrO8/b+bPqdwjGBSg==",
|
||||
"version": "1.1.42",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.42.tgz",
|
||||
"integrity": "sha512-OQ/ESmUqGawI2PRX+XIRao44qWYBBfN54ImQYdWVTQqUckuejOg76ysSqDBK8NG3zwySRVnX36JwDQ6x+9GxzA==",
|
||||
"requires": {
|
||||
"semver": "^6.3.0"
|
||||
},
|
||||
@@ -10131,9 +10144,9 @@
|
||||
"integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ="
|
||||
},
|
||||
"passport": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/passport/-/passport-0.4.0.tgz",
|
||||
"integrity": "sha1-xQlWkTR71a07XhgCOMORTRbwWBE=",
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/passport/-/passport-0.4.1.tgz",
|
||||
"integrity": "sha512-IxXgZZs8d7uFSt3eqNjM9NQ3g3uQCW5avD8mRNoXV99Yig50vjuaez6dQK2qC0kVWPRTujxY0dWgGfT09adjYg==",
|
||||
"requires": {
|
||||
"passport-strategy": "1.x.x",
|
||||
"pause": "0.0.1"
|
||||
@@ -10881,6 +10894,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"remove-markdown": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/remove-markdown/-/remove-markdown-0.3.0.tgz",
|
||||
"integrity": "sha1-XktmdJOpNXlyjz1S7MHbnKUF3Jg="
|
||||
},
|
||||
"remove-trailing-separator": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
|
||||
@@ -10965,9 +10983,9 @@
|
||||
"integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I="
|
||||
},
|
||||
"require-in-the-middle": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-5.0.0.tgz",
|
||||
"integrity": "sha512-92TmKFoE6qS0AbZvHPekkgon45OY5bbHl4+cCHm0qBx8+ktV9gEAQoYtuB1ArJnTbqb0020bn5Co9QzjsGkyJg==",
|
||||
"version": "5.0.2",
|
||||
"resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-5.0.2.tgz",
|
||||
"integrity": "sha512-l2r6F9i6t5xp4OE9cw/daB/ooQKHZOOW1AYPADhEvk/Tj/THJDS8gePp76Zyuht6Cj57a0KL+eHK5Dyv7wZnKA==",
|
||||
"requires": {
|
||||
"debug": "^4.1.1",
|
||||
"module-details-from-path": "^1.0.3",
|
||||
@@ -11988,9 +12006,9 @@
|
||||
"integrity": "sha1-IrD6OkE4WzO+PzMVUbu4N/oM164="
|
||||
},
|
||||
"stripe": {
|
||||
"version": "7.13.1",
|
||||
"resolved": "https://registry.npmjs.org/stripe/-/stripe-7.13.1.tgz",
|
||||
"integrity": "sha512-aF9F/M10MulZumSrwoDULBOZKqNUvyYA22Bg6u98VJEH/2Dck60W1FcT793WhsHHa/+z6TPR/8Ev7GNq1+SYOQ==",
|
||||
"version": "7.14.0",
|
||||
"resolved": "https://registry.npmjs.org/stripe/-/stripe-7.14.0.tgz",
|
||||
"integrity": "sha512-2YSSuJ18jxue0xhW2gZIoz4asC8rITvsNxzh/KuYVKK3dELK6/4M8yoGfSjFcic0D1FtJBB0j6l8Ty26a2L08w==",
|
||||
"requires": {
|
||||
"qs": "^6.6.0"
|
||||
},
|
||||
@@ -12008,30 +12026,30 @@
|
||||
"integrity": "sha1-6NK6H6nJBXAwPAMLaQD31fiavls="
|
||||
},
|
||||
"superagent": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/superagent/-/superagent-5.1.1.tgz",
|
||||
"integrity": "sha512-bpTO/3yQsHPH5w6f7qPCWGTuhEV2w93fwFGpYODnUc5tPa3rmbHUCmwC7iuEFBQQJsyhiW1WVc/ISpfAEv6ojQ==",
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/superagent/-/superagent-5.1.2.tgz",
|
||||
"integrity": "sha512-VwPCbi9H02qDtTbdY+e3+cK5XR0YHsJy9hmeCOXLQ8ezjq8+S1Bs4MdNRmpmf2QjDBetD7drG7/nEta7E3E6Sg==",
|
||||
"requires": {
|
||||
"component-emitter": "^1.3.0",
|
||||
"cookiejar": "^2.1.2",
|
||||
"debug": "^4.1.1",
|
||||
"fast-safe-stringify": "^2.0.6",
|
||||
"form-data": "^2.3.3",
|
||||
"fast-safe-stringify": "^2.0.7",
|
||||
"form-data": "^3.0.0",
|
||||
"formidable": "^1.2.1",
|
||||
"methods": "^1.1.2",
|
||||
"mime": "^2.4.4",
|
||||
"qs": "^6.7.0",
|
||||
"qs": "^6.9.1",
|
||||
"readable-stream": "^3.4.0",
|
||||
"semver": "^6.1.1"
|
||||
"semver": "^6.3.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"form-data": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz",
|
||||
"integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==",
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.0.tgz",
|
||||
"integrity": "sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg==",
|
||||
"requires": {
|
||||
"asynckit": "^0.4.0",
|
||||
"combined-stream": "^1.0.6",
|
||||
"combined-stream": "^1.0.8",
|
||||
"mime-types": "^2.1.12"
|
||||
}
|
||||
},
|
||||
@@ -12179,9 +12197,9 @@
|
||||
}
|
||||
},
|
||||
"teeny-request": {
|
||||
"version": "5.3.1",
|
||||
"resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-5.3.1.tgz",
|
||||
"integrity": "sha512-hnUeun3xryzv92FbrnprltcdeDfSVaGFBlFPRvKJ2fO/ioQx9N0aSUbbXSfTO+ArRXine1gSWdWFWcgfrggWXw==",
|
||||
"version": "5.3.2",
|
||||
"resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-5.3.2.tgz",
|
||||
"integrity": "sha512-5cse2cocD8Drq0uYN7NPcQ/6qbrxH6Kp0TlNLP+Y5f0twEkGtIg0ti8HoYF2LCC2TO5IkK/I9/Xs38/rDvs5UA==",
|
||||
"requires": {
|
||||
"http-proxy-agent": "^2.1.0",
|
||||
"https-proxy-agent": "^3.0.0",
|
||||
|
||||
@@ -1,29 +1,29 @@
|
||||
{
|
||||
"name": "habitica",
|
||||
"description": "A habit tracker app which treats your goals like a Role Playing Game.",
|
||||
"version": "4.126.0",
|
||||
"version": "4.128.1",
|
||||
"main": "./website/server/index.js",
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.7.4",
|
||||
"@babel/preset-env": "^7.7.4",
|
||||
"@babel/core": "^7.7.5",
|
||||
"@babel/preset-env": "^7.7.6",
|
||||
"@babel/register": "^7.7.4",
|
||||
"@google-cloud/trace-agent": "^4.2.3",
|
||||
"@google-cloud/trace-agent": "^4.2.4",
|
||||
"@slack/client": "^3.8.1",
|
||||
"accepts": "^1.3.5",
|
||||
"amazon-payments": "^0.2.7",
|
||||
"amplitude": "^3.5.0",
|
||||
"apidoc": "^0.17.5",
|
||||
"apn": "^2.2.0",
|
||||
"aws-sdk": "^2.578.0",
|
||||
"aws-sdk": "^2.590.0",
|
||||
"bcrypt": "^3.0.7",
|
||||
"body-parser": "^1.18.3",
|
||||
"compression": "^1.7.4",
|
||||
"cookie-session": "^1.3.3",
|
||||
"coupon-code": "^0.4.5",
|
||||
"csv-stringify": "^5.1.0",
|
||||
"csv-stringify": "^5.3.4",
|
||||
"cwait": "^1.1.1",
|
||||
"domain-middleware": "~0.1.0",
|
||||
"eslint": "^6.7.1",
|
||||
"eslint": "^6.7.2",
|
||||
"eslint-config-habitrpg": "^6.2.0",
|
||||
"eslint-plugin-mocha": "^5.0.0",
|
||||
"express": "^4.16.3",
|
||||
@@ -36,7 +36,7 @@
|
||||
"gulp-imagemin": "^6.2.0",
|
||||
"gulp-nodemon": "^2.4.1",
|
||||
"gulp.spritesmith": "^6.9.0",
|
||||
"habitica-markdown": "^1.3.0",
|
||||
"habitica-markdown": "^1.3.2",
|
||||
"helmet": "^3.21.2",
|
||||
"image-size": "^0.8.3",
|
||||
"in-app-purchase": "^1.11.3",
|
||||
@@ -46,12 +46,12 @@
|
||||
"method-override": "^3.0.0",
|
||||
"moment": "^2.24.0",
|
||||
"moment-recur": "^1.0.7",
|
||||
"mongoose": "^5.7.12",
|
||||
"mongoose": "^5.8.1",
|
||||
"morgan": "^1.7.0",
|
||||
"nconf": "^0.10.0",
|
||||
"node-gcm": "^1.0.2",
|
||||
"pageres": "^5.1.0",
|
||||
"passport": "^0.4.0",
|
||||
"passport": "^0.4.1",
|
||||
"passport-facebook": "^3.0.0",
|
||||
"passport-google-oauth2": "^0.2.0",
|
||||
"passport-google-oauth20": "1.0.0",
|
||||
@@ -59,10 +59,11 @@
|
||||
"paypal-rest-sdk": "^1.8.1",
|
||||
"ps-tree": "^1.0.0",
|
||||
"regenerator-runtime": "^0.13.3",
|
||||
"remove-markdown": "^0.3.0",
|
||||
"rimraf": "^3.0.0",
|
||||
"short-uuid": "^3.0.0",
|
||||
"stripe": "^7.13.1",
|
||||
"superagent": "^5.1.1",
|
||||
"stripe": "^7.14.0",
|
||||
"superagent": "^5.1.2",
|
||||
"universal-analytics": "^0.4.17",
|
||||
"useragent": "^2.1.9",
|
||||
"uuid": "^3.3.3",
|
||||
|
||||
@@ -11,6 +11,7 @@ import { model as User } from '../../../../../../website/server/models/user';
|
||||
import { model as Group } from '../../../../../../website/server/models/group';
|
||||
import {
|
||||
generateGroup,
|
||||
sleep,
|
||||
} from '../../../../../helpers/api-unit.helper';
|
||||
|
||||
describe('Purchasing a group plan for group', () => {
|
||||
@@ -307,6 +308,7 @@ describe('Purchasing a group plan for group', () => {
|
||||
data.groupId = group._id;
|
||||
|
||||
await api.createSubscription(data);
|
||||
await sleep(0.5);
|
||||
|
||||
expect(sender.sendTxn).to.have.callCount(4);
|
||||
expect(sender.sendTxn.args[0][0]._id).to.equal(TECH_ASSISTANCE_EMAIL);
|
||||
@@ -340,6 +342,7 @@ describe('Purchasing a group plan for group', () => {
|
||||
data.groupId = group._id;
|
||||
|
||||
await api.createSubscription(data);
|
||||
await sleep(0.5);
|
||||
|
||||
expect(sender.sendTxn).to.have.callCount(4);
|
||||
expect(sender.sendTxn.args[0][0]._id).to.equal(TECH_ASSISTANCE_EMAIL);
|
||||
|
||||
@@ -211,7 +211,7 @@ describe('payments/index', () => {
|
||||
await api.createSubscription(data);
|
||||
const msg = '`Hello recipient, sender has sent you 3 months of subscription!`';
|
||||
|
||||
expect(user.sendMessage).to.be.calledOnce;
|
||||
expect(user.sendMessage).to.be.calledTwice;
|
||||
expect(user.sendMessage).to.be.calledWith(
|
||||
recipient,
|
||||
{ receiverMsg: msg, senderMsg: msg, save: false },
|
||||
@@ -252,6 +252,77 @@ describe('payments/index', () => {
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
context('Winter 2019-20 Gift-1-Get-1 Promotion', async () => {
|
||||
it('creates a gift subscription for purchaser and recipient if none exist', async () => {
|
||||
await api.createSubscription(data);
|
||||
|
||||
expect(user.items.pets['Jackalope-RoyalPurple']).to.eql(5);
|
||||
expect(user.purchased.plan.customerId).to.eql('Gift');
|
||||
expect(user.purchased.plan.dateTerminated).to.exist;
|
||||
expect(user.purchased.plan.dateUpdated).to.exist;
|
||||
expect(user.purchased.plan.dateCreated).to.exist;
|
||||
|
||||
expect(recipient.items.pets['Jackalope-RoyalPurple']).to.eql(5);
|
||||
expect(recipient.purchased.plan.customerId).to.eql('Gift');
|
||||
expect(recipient.purchased.plan.dateTerminated).to.exist;
|
||||
expect(recipient.purchased.plan.dateUpdated).to.exist;
|
||||
expect(recipient.purchased.plan.dateCreated).to.exist;
|
||||
});
|
||||
|
||||
it('adds extraMonths to existing subscription for purchaser and creates a gift subscription for recipient without sub', async () => {
|
||||
user.purchased.plan = plan;
|
||||
|
||||
expect(user.purchased.plan.extraMonths).to.eql(0);
|
||||
|
||||
await api.createSubscription(data);
|
||||
|
||||
expect(user.purchased.plan.extraMonths).to.eql(3);
|
||||
|
||||
expect(recipient.items.pets['Jackalope-RoyalPurple']).to.eql(5);
|
||||
expect(recipient.purchased.plan.customerId).to.eql('Gift');
|
||||
expect(recipient.purchased.plan.dateTerminated).to.exist;
|
||||
expect(recipient.purchased.plan.dateUpdated).to.exist;
|
||||
expect(recipient.purchased.plan.dateCreated).to.exist;
|
||||
});
|
||||
|
||||
it('adds extraMonths to existing subscription for recipient and creates a gift subscription for purchaser without sub', async () => {
|
||||
recipient.purchased.plan = plan;
|
||||
|
||||
expect(recipient.purchased.plan.extraMonths).to.eql(0);
|
||||
|
||||
await api.createSubscription(data);
|
||||
|
||||
expect(recipient.purchased.plan.extraMonths).to.eql(3);
|
||||
|
||||
expect(user.items.pets['Jackalope-RoyalPurple']).to.eql(5);
|
||||
expect(user.purchased.plan.customerId).to.eql('Gift');
|
||||
expect(user.purchased.plan.dateTerminated).to.exist;
|
||||
expect(user.purchased.plan.dateUpdated).to.exist;
|
||||
expect(user.purchased.plan.dateCreated).to.exist;
|
||||
});
|
||||
|
||||
it('adds extraMonths to existing subscriptions for purchaser and recipient', async () => {
|
||||
user.purchased.plan = plan;
|
||||
recipient.purchased.plan = plan;
|
||||
|
||||
expect(user.purchased.plan.extraMonths).to.eql(0);
|
||||
expect(recipient.purchased.plan.extraMonths).to.eql(0);
|
||||
|
||||
await api.createSubscription(data);
|
||||
|
||||
expect(user.purchased.plan.extraMonths).to.eql(3);
|
||||
expect(recipient.purchased.plan.extraMonths).to.eql(3);
|
||||
});
|
||||
|
||||
it('sends a private message about the promotion', async () => {
|
||||
await api.createSubscription(data);
|
||||
const msg = '`Hello sender, you received 3 months of subscription as part of our holiday gift-giving promotion!`';
|
||||
|
||||
expect(user.sendMessage).to.be.calledTwice;
|
||||
expect(user.sendMessage).to.be.calledWith(user, { senderMsg: msg });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
context('Purchasing a subscription for self', () => {
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
moveTask,
|
||||
} from '../../../../website/server/libs/taskManager';
|
||||
import i18n from '../../../../website/common/script/i18n';
|
||||
import shared from '../../../../website/common/script';
|
||||
import {
|
||||
generateUser,
|
||||
generateGroup,
|
||||
@@ -58,6 +59,51 @@ describe('taskManager', () => {
|
||||
expect(newTask.createdAt).to.exist;
|
||||
});
|
||||
|
||||
describe('onboarding', () => {
|
||||
beforeEach(() => {
|
||||
user.addAchievement = sinon.spy();
|
||||
sinon.stub(shared.onboarding, 'checkOnboardingStatus');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
shared.onboarding.checkOnboardingStatus.restore();
|
||||
});
|
||||
|
||||
it('adds the onboarding achievement to the user and checks the onboarding status', async () => {
|
||||
req.body = testHabit;
|
||||
res.t = i18n.t;
|
||||
user.flags.welcomed = true;
|
||||
|
||||
await createTasks(req, res, { user });
|
||||
|
||||
expect(user.addAchievement).to.be.calledOnce;
|
||||
expect(user.addAchievement).to.be.calledWith('createdTask');
|
||||
|
||||
expect(shared.onboarding.checkOnboardingStatus).to.be.calledOnce;
|
||||
expect(shared.onboarding.checkOnboardingStatus).to.be.calledWith(user);
|
||||
});
|
||||
|
||||
it('does not add the onboarding achievement to the user if flags.welcomed is false', async () => {
|
||||
req.body = testHabit;
|
||||
res.t = i18n.t;
|
||||
user.flags.welcomed = false;
|
||||
|
||||
await createTasks(req, res, { user });
|
||||
|
||||
expect(user.addAchievement).to.not.be.called;
|
||||
});
|
||||
|
||||
it('does not add the onboarding achievement to the user if it\'s already been awarded', async () => {
|
||||
req.body = testHabit;
|
||||
res.t = i18n.t;
|
||||
user.achievements.createdTask = true;
|
||||
|
||||
await createTasks(req, res, { user });
|
||||
|
||||
expect(user.addAchievement).to.not.be.called;
|
||||
});
|
||||
});
|
||||
|
||||
it('gets user tasks', async () => {
|
||||
req.body = testHabit;
|
||||
res.t = i18n.t;
|
||||
|
||||
@@ -90,7 +90,37 @@ describe('Challenge Model', () => {
|
||||
expect(syncedTask.tags[0]).to.eql(challenge._id);
|
||||
});
|
||||
|
||||
it('syncs a challenge to a user', async () => {
|
||||
it('adds a challenge to a user', async () => {
|
||||
const newMember = new User({
|
||||
guilds: [guild._id],
|
||||
});
|
||||
await newMember.save();
|
||||
|
||||
const addedSuccessfully = await challenge.addToUser(newMember);
|
||||
|
||||
const updatedNewMember = await User.findById(newMember._id);
|
||||
|
||||
expect(addedSuccessfully).to.eql(true);
|
||||
expect(updatedNewMember.challenges).to.contain(challenge._id);
|
||||
});
|
||||
|
||||
it('does not add a challenge to a user that already in the challenge', async () => {
|
||||
const newMember = new User({
|
||||
guilds: [guild._id],
|
||||
challenges: [challenge._id],
|
||||
});
|
||||
await newMember.save();
|
||||
|
||||
const addedSuccessfully = await challenge.addToUser(newMember);
|
||||
|
||||
const updatedNewMember = await User.findById(newMember._id);
|
||||
|
||||
expect(addedSuccessfully).to.eql(false);
|
||||
expect(updatedNewMember.challenges).to.contain(challenge._id);
|
||||
expect(updatedNewMember.challenges.length).to.eql(1);
|
||||
});
|
||||
|
||||
it('syncs challenge tasks to a user', async () => {
|
||||
await challenge.addTasks([task]);
|
||||
|
||||
const newMember = new User({
|
||||
@@ -98,7 +128,7 @@ describe('Challenge Model', () => {
|
||||
});
|
||||
await newMember.save();
|
||||
|
||||
await challenge.syncToUser(newMember);
|
||||
await challenge.syncTasksToUser(newMember);
|
||||
|
||||
const updatedNewMember = await User.findById(newMember._id);
|
||||
const updatedNewMemberTasks = await Tasks.Task.find({ _id: { $in: updatedNewMember.tasksOrder[`${taskType}s`] } });
|
||||
@@ -110,14 +140,13 @@ describe('Challenge Model', () => {
|
||||
),
|
||||
);
|
||||
|
||||
expect(updatedNewMember.challenges).to.contain(challenge._id);
|
||||
expect(updatedNewMember.tags[7].id).to.equal(challenge._id);
|
||||
expect(updatedNewMember.tags[7].name).to.equal(challenge.shortName);
|
||||
expect(syncedTask).to.exist;
|
||||
expect(syncedTask.attribute).to.eql('str');
|
||||
});
|
||||
|
||||
it('syncs a challenge to a user with the existing task', async () => {
|
||||
it('syncs challenge tasks to a user with the existing task', async () => {
|
||||
await challenge.addTasks([task]);
|
||||
|
||||
let updatedLeader = await User.findOne({ _id: leader._id });
|
||||
@@ -134,7 +163,7 @@ describe('Challenge Model', () => {
|
||||
task.text = newTitle;
|
||||
task.attribute = 'int';
|
||||
await task.save();
|
||||
await challenge.syncToUser(leader);
|
||||
await challenge.syncTasksToUser(leader);
|
||||
|
||||
updatedLeader = await User.findOne({ _id: leader._id });
|
||||
updatedLeadersTasks = await Tasks.Task.find({ _id: { $in: updatedLeader.tasksOrder[`${taskType}s`] } });
|
||||
|
||||
@@ -1317,7 +1317,7 @@ describe('Group Model', () => {
|
||||
|
||||
it('formats message', () => {
|
||||
const chatMessage = party.sendChat({
|
||||
message: 'a new message',
|
||||
message: 'a _new_ message with *markdown*',
|
||||
user: {
|
||||
_id: 'user-id',
|
||||
profile: { name: 'user name' },
|
||||
@@ -1336,7 +1336,8 @@ describe('Group Model', () => {
|
||||
|
||||
const chat = chatMessage;
|
||||
|
||||
expect(chat.text).to.eql('a new message');
|
||||
expect(chat.text).to.eql('a _new_ message with *markdown*');
|
||||
expect(chat.unformattedText).to.eql('a new message with markdown');
|
||||
expect(validator.isUUID(chat.id)).to.eql(true);
|
||||
expect(chat.timestamp).to.be.a('date');
|
||||
expect(chat.likes).to.eql({});
|
||||
@@ -1878,6 +1879,8 @@ describe('Group Model', () => {
|
||||
await questLeader.save();
|
||||
await party.finishQuest(quest);
|
||||
|
||||
await sleep(0.5);
|
||||
|
||||
const [
|
||||
updatedLeader,
|
||||
updatedParticipatingMember,
|
||||
|
||||
@@ -84,6 +84,103 @@ describe('User Model', () => {
|
||||
expect(userToJSON.stats.toNextLevel).to.equal(common.tnl(user.stats.lvl));
|
||||
});
|
||||
|
||||
context('achievements', () => {
|
||||
it('can add an achievement', () => {
|
||||
const user = new User();
|
||||
const originalUserToJSON = user.toJSON({ minimize: false });
|
||||
expect(originalUserToJSON.achievements.createdTask).to.not.eql(true);
|
||||
const notificationsN = originalUserToJSON.notifications.length;
|
||||
|
||||
user.addAchievement('createdTask');
|
||||
|
||||
const userToJSON = user.toJSON();
|
||||
expect(user.notifications.length).to.equal(notificationsN + 1);
|
||||
expect(userToJSON.notifications[0]).to.have.all.keys(['data', 'id', 'type', 'seen']);
|
||||
expect(userToJSON.notifications[0].type).to.equal('ACHIEVEMENT');
|
||||
expect(userToJSON.notifications[0].data).to.eql({
|
||||
achievement: 'createdTask',
|
||||
});
|
||||
expect(userToJSON.notifications[0].seen).to.eql(false);
|
||||
|
||||
expect(userToJSON.achievements.createdTask).to.eql(true);
|
||||
});
|
||||
|
||||
it('throws an error if the achievement is not valid', () => {
|
||||
const user = new User();
|
||||
expect(() => user.addAchievement('notAnAchievement')).to.throw;
|
||||
});
|
||||
|
||||
context('static push method', () => {
|
||||
it('throws an error if the achievement is not valid', async () => {
|
||||
const user = new User();
|
||||
await user.save();
|
||||
|
||||
await expect(User.addAchievementUpdate({ _id: user._id }, 'notAnAchievement'))
|
||||
.to.eventually.be.rejected;
|
||||
|
||||
expect(() => user.addAchievement('notAnAchievement')).to.throw;
|
||||
});
|
||||
|
||||
it('adds an achievement for a single member via static method', async () => {
|
||||
let user = new User();
|
||||
await user.save();
|
||||
|
||||
const originalUserToJSON = user.toJSON({ minimize: false });
|
||||
expect(originalUserToJSON.achievements.createdTask).to.not.eql(true);
|
||||
const notificationsN = originalUserToJSON.notifications.length;
|
||||
|
||||
await User.addAchievementUpdate({ _id: user._id }, 'createdTask');
|
||||
|
||||
user = await User.findOne({ _id: user._id }).exec();
|
||||
|
||||
const userToJSON = user.toJSON();
|
||||
expect(user.notifications.length).to.equal(notificationsN + 1);
|
||||
expect(userToJSON.notifications[0]).to.have.all.keys(['data', 'id', 'type', 'seen']);
|
||||
expect(userToJSON.notifications[0].type).to.equal('ACHIEVEMENT');
|
||||
expect(userToJSON.notifications[0].data).to.eql({
|
||||
achievement: 'createdTask',
|
||||
});
|
||||
expect(userToJSON.notifications[0].seen).to.eql(false);
|
||||
|
||||
expect(userToJSON.achievements.createdTask).to.eql(true);
|
||||
});
|
||||
|
||||
it('adds an achievement for all given users via static method', async () => {
|
||||
let user = new User();
|
||||
const otherUser = new User();
|
||||
await Promise.all([user.save(), otherUser.save()]);
|
||||
|
||||
await User.addAchievementUpdate({ _id: { $in: [user._id, otherUser._id] } }, 'createdTask');
|
||||
|
||||
user = await User.findOne({ _id: user._id }).exec();
|
||||
|
||||
let userToJSON = user.toJSON();
|
||||
expect(user.notifications.length).to.equal(1);
|
||||
expect(userToJSON.notifications[0]).to.have.all.keys(['data', 'id', 'type', 'seen']);
|
||||
expect(userToJSON.notifications[0].type).to.equal('ACHIEVEMENT');
|
||||
expect(userToJSON.notifications[0].data).to.eql({
|
||||
achievement: 'createdTask',
|
||||
});
|
||||
expect(userToJSON.notifications[0].seen).to.eql(false);
|
||||
|
||||
expect(userToJSON.achievements.createdTask).to.eql(true);
|
||||
|
||||
user = await User.findOne({ _id: otherUser._id }).exec();
|
||||
|
||||
userToJSON = user.toJSON();
|
||||
expect(user.notifications.length).to.equal(1);
|
||||
expect(userToJSON.notifications[0]).to.have.all.keys(['data', 'id', 'type', 'seen']);
|
||||
expect(userToJSON.notifications[0].type).to.equal('ACHIEVEMENT');
|
||||
expect(userToJSON.notifications[0].data).to.eql({
|
||||
achievement: 'createdTask',
|
||||
});
|
||||
expect(userToJSON.notifications[0].seen).to.eql(false);
|
||||
|
||||
expect(userToJSON.achievements.createdTask).to.eql(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
context('notifications', () => {
|
||||
it('can add notifications without data', () => {
|
||||
const user = new User();
|
||||
|
||||
@@ -6,7 +6,8 @@ import {
|
||||
|
||||
describe('POST /members/send-private-message', () => {
|
||||
let userToSendMessage;
|
||||
const messageToSend = 'Test Private Message';
|
||||
const messageToSend = 'Test *Private* Message';
|
||||
const unformattedMessage = 'Test Private Message';
|
||||
|
||||
beforeEach(async () => {
|
||||
userToSendMessage = await generateUser();
|
||||
@@ -110,7 +111,9 @@ describe('POST /members/send-private-message', () => {
|
||||
|
||||
const sendersMessageInReceiversInbox = _.find(
|
||||
updatedReceiver.inbox.messages,
|
||||
message => message.uuid === userToSendMessage._id && message.text === messageToSend,
|
||||
message => message.uuid === userToSendMessage._id
|
||||
&& message.text === messageToSend
|
||||
&& message.unformattedText === unformattedMessage,
|
||||
);
|
||||
|
||||
const sendersMessageInSendersInbox = _.find(
|
||||
|
||||
@@ -15,7 +15,7 @@ describe('GET /user/toggle-pinned-item', () => {
|
||||
.to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: t('cannotUnpinArmoirPotion'),
|
||||
message: t('cannotUnpinItem'),
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -189,6 +189,28 @@ describe('POST /user/auth/reset-password-set-new-one', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('renders the error page if the password is too short', async () => {
|
||||
const user = await generateUser();
|
||||
|
||||
const code = encrypt(JSON.stringify({
|
||||
userId: user._id,
|
||||
expiresAt: moment().add({ days: 1 }),
|
||||
}));
|
||||
await user.update({
|
||||
'auth.local.passwordResetCode': code,
|
||||
});
|
||||
|
||||
await expect(api.post(`${endpoint}`, {
|
||||
newPassword: 'short',
|
||||
confirmPassword: 'short',
|
||||
code,
|
||||
})).to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: t('invalidReqParams'),
|
||||
});
|
||||
});
|
||||
|
||||
it('renders the success page and save the user', async () => {
|
||||
const user = await generateUser();
|
||||
|
||||
|
||||
@@ -326,6 +326,24 @@ describe('POST /user/auth/local/register', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('requires minimum length for the password', async () => {
|
||||
const username = generateRandomUserName();
|
||||
const email = `${username}@example.com`;
|
||||
const password = '1234567';
|
||||
const confirmPassword = '1234567';
|
||||
|
||||
await expect(api.post('/user/auth/local/register', {
|
||||
username,
|
||||
email,
|
||||
password,
|
||||
confirmPassword,
|
||||
})).to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: t('invalidReqParams'),
|
||||
});
|
||||
});
|
||||
|
||||
it('requires a username', async () => {
|
||||
const email = `${generateRandomUserName()}@example.com`;
|
||||
const password = 'password';
|
||||
|
||||
@@ -82,6 +82,20 @@ describe('PUT /user/auth/update-password', async () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('returns an error when newPassword is too short', async () => {
|
||||
const body = {
|
||||
password,
|
||||
newPassword: '1234567',
|
||||
confirmPassword: '1234567',
|
||||
};
|
||||
|
||||
await expect(user.put(ENDPOINT, body)).to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: t('invalidReqParams'),
|
||||
});
|
||||
});
|
||||
|
||||
it('returns an error when confirmPassword is missing', async () => {
|
||||
const body = {
|
||||
password,
|
||||
|
||||
@@ -239,6 +239,47 @@ describe('achievements', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('unearned onboarding achievements', () => {
|
||||
const user = generateUser();
|
||||
const onboardingAchievs = shared
|
||||
.achievements.getAchievementsForProfile(user).onboarding.achievements;
|
||||
|
||||
it('created task achievement exists with no count', () => {
|
||||
const { createdTask } = onboardingAchievs;
|
||||
|
||||
expect(createdTask).to.exist;
|
||||
expect(createdTask.optionalCount).to.be.undefined;
|
||||
});
|
||||
|
||||
it('completed task achievement exists with no count', () => {
|
||||
const { completedTask } = onboardingAchievs;
|
||||
|
||||
expect(completedTask).to.exist;
|
||||
expect(completedTask.optionalCount).to.be.undefined;
|
||||
});
|
||||
|
||||
it('hatched pet achievement exists with no count', () => {
|
||||
const { hatchedPet } = onboardingAchievs;
|
||||
|
||||
expect(hatchedPet).to.exist;
|
||||
expect(hatchedPet.optionalCount).to.be.undefined;
|
||||
});
|
||||
|
||||
it('fed pet achievement exists with no count', () => {
|
||||
const { fedPet } = onboardingAchievs;
|
||||
|
||||
expect(fedPet).to.exist;
|
||||
expect(fedPet.optionalCount).to.be.undefined;
|
||||
});
|
||||
|
||||
it('purchased equipment achievement exists with no count', () => {
|
||||
const { purchasedEquipment } = onboardingAchievs;
|
||||
|
||||
expect(purchasedEquipment).to.exist;
|
||||
expect(purchasedEquipment.optionalCount).to.be.undefined;
|
||||
});
|
||||
});
|
||||
|
||||
describe('earned seasonal achievements', () => {
|
||||
const user = generateUser();
|
||||
const quests = ['dilatory', 'stressbeast', 'burnout', 'bewilder'];
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
|
||||
import {
|
||||
generateUser,
|
||||
} from '../../helpers/common.helper';
|
||||
|
||||
import getDebuffPotionItems from '../../../website/common/script/libs/getDebuffPotionItems';
|
||||
import { TRANSFORMATION_DEBUFFS_LIST } from '../../../website/common/script/constants';
|
||||
|
||||
describe('getDebuffPotionItems', () => {
|
||||
let user;
|
||||
|
||||
beforeEach(() => {
|
||||
user = generateUser();
|
||||
});
|
||||
|
||||
for (const key of Object.keys(TRANSFORMATION_DEBUFFS_LIST)) {
|
||||
const debuff = TRANSFORMATION_DEBUFFS_LIST[key];
|
||||
// Here we itterate whole object to dynamicaly create test suites as
|
||||
// it described in dock of mocha
|
||||
// https://mochajs.org/#dynamically-generating-tests
|
||||
// That's why we have eslint-disable here
|
||||
// eslint-disable-next-line no-loop-func
|
||||
it(`Should return the ${debuff} on ${key} buff`, () => {
|
||||
user.stats.buffs[key] = true;
|
||||
|
||||
const result = getDebuffPotionItems(user);
|
||||
|
||||
expect(result).to.be.an('array').that.deep
|
||||
.includes({ path: `spells.special.${debuff}`, type: 'debuffPotion' });
|
||||
});
|
||||
}
|
||||
|
||||
it('Should return all debuff potions for all buffs', () => {
|
||||
user.stats.buffs.seafoam = true;
|
||||
user.stats.buffs.spookySparkles = true;
|
||||
user.stats.buffs.snowball = true;
|
||||
user.stats.buffs.shinySeed = true;
|
||||
|
||||
|
||||
const result = getDebuffPotionItems(user);
|
||||
|
||||
expect(result).to.be.an('array').that.deep.include.members([
|
||||
{ path: 'spells.special.sand', type: 'debuffPotion' },
|
||||
{ path: 'spells.special.petalFreePotion', type: 'debuffPotion' },
|
||||
{ path: 'spells.special.salt', type: 'debuffPotion' },
|
||||
{ path: 'spells.special.opaquePotion', type: 'debuffPotion' },
|
||||
]);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,100 @@
|
||||
import moment from 'moment';
|
||||
import {
|
||||
hasActiveOnboarding,
|
||||
hasCompletedOnboarding,
|
||||
onOnboardingComplete,
|
||||
checkOnboardingStatus,
|
||||
} from '../../../website/common/script/libs/onboarding';
|
||||
import { generateUser } from '../../helpers/common.helper';
|
||||
|
||||
describe('onboarding', () => {
|
||||
let user;
|
||||
|
||||
beforeEach(() => {
|
||||
user = generateUser();
|
||||
user.addNotification = sinon.spy();
|
||||
// Make sure the onboarding is active
|
||||
user.auth.timestamps.created = moment('2019-12-20').toDate();
|
||||
});
|
||||
|
||||
describe('hasActiveOnboarding', () => {
|
||||
// The value of BEGIN DATE is available in common/script/libs/onboarding
|
||||
|
||||
it('returns true if the account is created after BEGIN_DATE', () => {
|
||||
expect(hasActiveOnboarding(user)).to.eql(true);
|
||||
});
|
||||
|
||||
it('returns false if the account is created before BEGIN_DATE', () => {
|
||||
user.auth.timestamps.created = moment('2019-12-01').toDate();
|
||||
expect(hasActiveOnboarding(user)).to.eql(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('hasCompletedOnboarding', () => {
|
||||
it('returns false if no achievement has been awarded', () => {
|
||||
const result = hasCompletedOnboarding(user);
|
||||
expect(result).to.eql(false);
|
||||
});
|
||||
|
||||
it('returns false if not all achievements have been awarded', () => {
|
||||
user.achievements.completedTask = true;
|
||||
const result = hasCompletedOnboarding(user);
|
||||
expect(result).to.eql(false);
|
||||
});
|
||||
|
||||
it('returns true if all achievements have been awarded', () => {
|
||||
user.achievements.createdTask = true;
|
||||
user.achievements.completedTask = true;
|
||||
user.achievements.hatchedPet = true;
|
||||
user.achievements.fedPet = true;
|
||||
user.achievements.purchasedEquipment = true;
|
||||
|
||||
const result = hasCompletedOnboarding(user);
|
||||
expect(result).to.eql(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('onOnboardingComplete', () => {
|
||||
it('awards prizes', () => {
|
||||
const { gp } = user.stats;
|
||||
onOnboardingComplete(user);
|
||||
expect(user.stats.gp).to.eql(gp + 100);
|
||||
});
|
||||
});
|
||||
|
||||
describe('checkOnboardingStatus', () => {
|
||||
it('does nothing if onboarding is not active', () => {
|
||||
const { gp } = user.stats;
|
||||
user.auth.timestamps.created = moment('2019-12-01').toDate();
|
||||
|
||||
checkOnboardingStatus(user);
|
||||
expect(user.addNotification).to.not.be.called;
|
||||
|
||||
expect(user.stats.gp).to.eql(gp);
|
||||
});
|
||||
|
||||
it('does nothing if onboarding is not complete', () => {
|
||||
const { gp } = user.stats;
|
||||
|
||||
checkOnboardingStatus(user);
|
||||
expect(user.addNotification).to.not.be.called;
|
||||
|
||||
expect(user.stats.gp).to.eql(gp);
|
||||
});
|
||||
|
||||
it('awards prize and add notification when onboarding is complete', () => {
|
||||
user.achievements.createdTask = true;
|
||||
user.achievements.completedTask = true;
|
||||
user.achievements.hatchedPet = true;
|
||||
user.achievements.fedPet = true;
|
||||
user.achievements.purchasedEquipment = true;
|
||||
const { gp } = user.stats;
|
||||
|
||||
checkOnboardingStatus(user);
|
||||
expect(user.addNotification).to.be.calledOnce;
|
||||
expect(user.addNotification).to.be.calledWith('ONBOARDING_COMPLETE');
|
||||
|
||||
expect(user.stats.gp).to.eql(gp + 100);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,53 @@
|
||||
|
||||
import {
|
||||
generateUser,
|
||||
} from '../../helpers/common.helper';
|
||||
|
||||
import setDebuffPotionItems from '../../../website/common/script/libs/setDebuffPotionItems';
|
||||
|
||||
describe('setDebuffPotionItems', () => {
|
||||
let user;
|
||||
|
||||
beforeEach(() => {
|
||||
user = generateUser();
|
||||
});
|
||||
|
||||
it('Should push the debuff item to pinned items of user', () => {
|
||||
user.stats.buffs.spookySparkles = true;
|
||||
const previousPinnedItemsLength = user.pinnedItems.length;
|
||||
|
||||
const result = setDebuffPotionItems(user);
|
||||
|
||||
expect(result.pinnedItems.length).to.be.greaterThan(previousPinnedItemsLength);
|
||||
});
|
||||
|
||||
it('Shouldn\'t create duplicate of already added debuff potion', () => {
|
||||
user.stats.buffs.spookySparkles = true;
|
||||
|
||||
const firstSetResult = [...setDebuffPotionItems(user).pinnedItems];
|
||||
const secondSetResult = [...setDebuffPotionItems(user).pinnedItems];
|
||||
|
||||
|
||||
expect(firstSetResult).to.be.deep.equal(secondSetResult);
|
||||
});
|
||||
|
||||
it('Should remove all debuff items from pinnedItems of the user if user have no buffs', () => {
|
||||
user.stats.buffs.seafoam = true;
|
||||
user.stats.buffs.spookySparkles = true;
|
||||
user.stats.buffs.snowball = true;
|
||||
user.stats.buffs.shinySeed = true;
|
||||
|
||||
const firstSetResult = [...setDebuffPotionItems(user).pinnedItems];
|
||||
|
||||
expect(firstSetResult).to.have.lengthOf(4);
|
||||
|
||||
user.stats.buffs.seafoam = false;
|
||||
user.stats.buffs.spookySparkles = false;
|
||||
user.stats.buffs.snowball = false;
|
||||
user.stats.buffs.shinySeed = false;
|
||||
|
||||
const secondSetResult = [...setDebuffPotionItems(user).pinnedItems];
|
||||
|
||||
expect(secondSetResult).to.have.lengthOf(0);
|
||||
});
|
||||
});
|
||||
@@ -41,7 +41,10 @@ describe('shared.ops.buyMarketGear', () => {
|
||||
},
|
||||
});
|
||||
|
||||
user.addAchievement = sinon.spy();
|
||||
|
||||
sinon.stub(shared, 'randomVal');
|
||||
sinon.stub(shared.onboarding, 'checkOnboardingStatus');
|
||||
sinon.stub(shared.fns, 'predictableRandom');
|
||||
sinon.stub(analytics, 'track');
|
||||
});
|
||||
@@ -49,6 +52,7 @@ describe('shared.ops.buyMarketGear', () => {
|
||||
afterEach(() => {
|
||||
shared.randomVal.restore();
|
||||
shared.fns.predictableRandom.restore();
|
||||
shared.onboarding.checkOnboardingStatus.restore();
|
||||
analytics.track.restore();
|
||||
});
|
||||
|
||||
@@ -86,6 +90,27 @@ describe('shared.ops.buyMarketGear', () => {
|
||||
expect(analytics.track).to.be.calledOnce;
|
||||
});
|
||||
|
||||
it('adds the onboarding achievement to the user and checks the onboarding status', () => {
|
||||
user.stats.gp = 31;
|
||||
|
||||
buyGear(user, { params: { key: 'armor_warrior_1' } }, analytics);
|
||||
|
||||
expect(user.addAchievement).to.be.calledOnce;
|
||||
expect(user.addAchievement).to.be.calledWith('purchasedEquipment');
|
||||
|
||||
expect(shared.onboarding.checkOnboardingStatus).to.be.calledOnce;
|
||||
expect(shared.onboarding.checkOnboardingStatus).to.be.calledWith(user);
|
||||
});
|
||||
|
||||
it('does not add the onboarding achievement to the user if it\'s already been awarded', () => {
|
||||
user.stats.gp = 31;
|
||||
user.achievements.purchasedEquipment = true;
|
||||
|
||||
buyGear(user, { params: { key: 'armor_warrior_1' } }, analytics);
|
||||
|
||||
expect(user.addAchievement).to.not.be.called;
|
||||
});
|
||||
|
||||
it('deducts gold from user', () => {
|
||||
user.stats.gp = 31;
|
||||
|
||||
|
||||
@@ -10,12 +10,19 @@ import {
|
||||
generateUser,
|
||||
} from '../../helpers/common.helper';
|
||||
import errorMessage from '../../../website/common/script/libs/errorMessage';
|
||||
import shared from '../../../website/common/script';
|
||||
|
||||
describe('shared.ops.feed', () => {
|
||||
let user;
|
||||
|
||||
beforeEach(() => {
|
||||
user = generateUser();
|
||||
user.addAchievement = sinon.spy();
|
||||
sinon.stub(shared.onboarding, 'checkOnboardingStatus');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
shared.onboarding.checkOnboardingStatus.restore();
|
||||
});
|
||||
|
||||
context('failure conditions', () => {
|
||||
@@ -223,5 +230,28 @@ describe('shared.ops.feed', () => {
|
||||
expect(user.items.mounts['Wolf-Base']).to.equal(true);
|
||||
expect(user.items.currentPet).to.equal('');
|
||||
});
|
||||
|
||||
it('adds the onboarding achievement to the user and checks the onboarding status', () => {
|
||||
user.items.pets['Wolf-Base'] = 5;
|
||||
user.items.food.Meat = 2;
|
||||
|
||||
feed(user, { params: { pet: 'Wolf-Base', food: 'Meat' } });
|
||||
|
||||
expect(user.addAchievement).to.be.calledOnce;
|
||||
expect(user.addAchievement).to.be.calledWith('fedPet');
|
||||
|
||||
expect(shared.onboarding.checkOnboardingStatus).to.be.calledOnce;
|
||||
expect(shared.onboarding.checkOnboardingStatus).to.be.calledWith(user);
|
||||
});
|
||||
|
||||
it('does not add the onboarding achievement to the user if it\'s already been awarded', () => {
|
||||
user.items.pets['Wolf-Base'] = 5;
|
||||
user.items.food.Meat = 2;
|
||||
user.achievements.fedPet = true;
|
||||
|
||||
feed(user, { params: { pet: 'Wolf-Base', food: 'Meat' } });
|
||||
|
||||
expect(user.addAchievement).to.not.be.called;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -9,12 +9,19 @@ import {
|
||||
generateUser,
|
||||
} from '../../helpers/common.helper';
|
||||
import errorMessage from '../../../website/common/script/libs/errorMessage';
|
||||
import shared from '../../../website/common/script';
|
||||
|
||||
describe('shared.ops.hatch', () => {
|
||||
let user;
|
||||
|
||||
beforeEach(() => {
|
||||
user = generateUser();
|
||||
user.addAchievement = sinon.spy();
|
||||
sinon.stub(shared.onboarding, 'checkOnboardingStatus');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
shared.onboarding.checkOnboardingStatus.restore();
|
||||
});
|
||||
|
||||
context('Pet Hatching', () => {
|
||||
@@ -195,6 +202,30 @@ describe('shared.ops.hatch', () => {
|
||||
hatch(user, { params: { egg: 'Wolf', hatchingPotion: 'Spooky' } });
|
||||
expect(user.achievements.dustDevil).to.eql(true);
|
||||
});
|
||||
|
||||
it('adds the onboarding achievement to the user and checks the onboarding status', () => {
|
||||
user.items.eggs = { Wolf: 1 };
|
||||
user.items.hatchingPotions = { Base: 1 };
|
||||
user.items.pets = {};
|
||||
hatch(user, { params: { egg: 'Wolf', hatchingPotion: 'Base' } });
|
||||
|
||||
expect(user.addAchievement).to.be.calledOnce;
|
||||
expect(user.addAchievement).to.be.calledWith('hatchedPet');
|
||||
|
||||
expect(shared.onboarding.checkOnboardingStatus).to.be.calledOnce;
|
||||
expect(shared.onboarding.checkOnboardingStatus).to.be.calledWith(user);
|
||||
});
|
||||
|
||||
it('does not add the onboarding achievement to the user if it\'s already been awarded', () => {
|
||||
user.items.eggs = { Wolf: 1 };
|
||||
user.items.hatchingPotions = { Base: 1 };
|
||||
user.items.pets = {};
|
||||
user.achievements.hatchedPet = true;
|
||||
|
||||
hatch(user, { params: { egg: 'Wolf', hatchingPotion: 'Base' } });
|
||||
|
||||
expect(user.addAchievement).to.not.be.called;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
NotAuthorized,
|
||||
} from '../../../website/common/script/libs/errors';
|
||||
import crit from '../../../website/common/script/fns/crit';
|
||||
import shared from '../../../website/common/script';
|
||||
|
||||
const EPSILON = 0.0001; // negligible distance between datapoints
|
||||
|
||||
@@ -340,5 +341,49 @@ describe('shared.ops.scoreTask', () => {
|
||||
expectClosePoints(ref.beforeUser, ref.afterUser, freshTodo, todo);
|
||||
});
|
||||
});
|
||||
|
||||
context('onboarding', () => {
|
||||
beforeEach(() => {
|
||||
ref.afterUser.addAchievement = sinon.spy();
|
||||
sinon.stub(shared.onboarding, 'checkOnboardingStatus');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
shared.onboarding.checkOnboardingStatus.restore();
|
||||
});
|
||||
|
||||
it('adds the achievement to the user and checks the onboarding status', () => {
|
||||
scoreTask({ user: ref.afterUser, task: todo, direction: 'up' });
|
||||
expect(ref.afterUser.addAchievement).to.be.calledOnce;
|
||||
expect(ref.afterUser.addAchievement).to.be.calledWith('completedTask');
|
||||
|
||||
expect(shared.onboarding.checkOnboardingStatus).to.be.calledOnce;
|
||||
expect(shared.onboarding.checkOnboardingStatus).to.be.calledWith(ref.afterUser);
|
||||
});
|
||||
|
||||
it('does not add the onboarding achievement to the user if it\'s already been awarded', () => {
|
||||
ref.afterUser.achievements.completedTask = true;
|
||||
scoreTask({ user: ref.afterUser, task: todo, direction: 'up' });
|
||||
|
||||
expect(ref.afterUser.addAchievement).to.not.be.called;
|
||||
});
|
||||
|
||||
it('does not add the onboarding achievement to the user if it\'s scored down', () => {
|
||||
scoreTask({ user: ref.afterUser, task: todo, direction: 'down' });
|
||||
|
||||
expect(ref.afterUser.addAchievement).to.not.be.called;
|
||||
});
|
||||
|
||||
it('does not add the onboarding achievement to the user if cron is running', () => {
|
||||
scoreTask({
|
||||
user: ref.afterUser,
|
||||
task: todo,
|
||||
direction: 'up',
|
||||
cron: true,
|
||||
});
|
||||
|
||||
expect(ref.afterUser.addAchievement).to.not.be.called;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -7,6 +7,7 @@ module.exports = {
|
||||
extends: [
|
||||
'habitrpg/lib/vue',
|
||||
],
|
||||
ignorePatterns: ['dist/', 'node_modules/'],
|
||||
rules: {
|
||||
'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
|
||||
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# new_client
|
||||
# Habitica Client
|
||||
|
||||
## Project setup
|
||||
```
|
||||
@@ -27,3 +27,31 @@ npm run lint
|
||||
|
||||
### Customize configuration
|
||||
See [Configuration Reference](https://cli.vuejs.org/config/).
|
||||
|
||||
## Storybook
|
||||
|
||||
Storybook is mainly used while working on UI-Components to see changes faster instead of using the website.
|
||||
|
||||
### Start Storybook
|
||||
|
||||
```
|
||||
npm run storybook:serve
|
||||
```
|
||||
|
||||
This will start the storybook process, every `*.stories.js`-File is searched and added to the storybook overview.
|
||||
|
||||
### Storybook Worklow
|
||||
|
||||
Usually when you working on `component-name.vue` you also create a `component-name.stories.js` file.
|
||||
|
||||
Example of the stories structure - [Storybook Docs][StorybookDocsExample] - [CountBadge][CountBadgeExample]
|
||||
|
||||
[StorybookDocsExample]: https://storybook.js.org/docs/guides/guide-vue/#step-4-write-your-stories
|
||||
[CountBadgeExample]: src/components/ui/countBadge.stories.js
|
||||
|
||||
Each function or example of this component will be put after `storiesOf('Your Component', module)`,
|
||||
in a separate `.add('function of component', ...`
|
||||
|
||||
### Storybook Build
|
||||
|
||||
After each client build, storybook build is also triggered and will be available in `dist/storybook`
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
import '@storybook/addon-actions/register';
|
||||
import '@storybook/addon-knobs/register';
|
||||
import '@storybook/addon-links/register';
|
||||
import '@storybook/addon-notes/register';
|
||||
@@ -0,0 +1,11 @@
|
||||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
import { configure } from '@storybook/vue';
|
||||
import '../../src/assets/scss/index.scss';
|
||||
|
||||
const req = require.context('../../src', true, /.stories.js$/);
|
||||
|
||||
function loadStories () {
|
||||
req.keys().forEach(filename => req(filename));
|
||||
}
|
||||
|
||||
configure(loadStories, module);
|
||||
@@ -5,31 +5,38 @@
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve",
|
||||
"build": "vue-cli-service build",
|
||||
"test:unit": "vue-cli-service test:unit --require ./tests/unit/helpers.js",
|
||||
"lint": "vue-cli-service lint .",
|
||||
"lint-no-fix": "vue-cli-service lint --no-fix .",
|
||||
"postinstall": "node ./scripts/npm-postinstall.js"
|
||||
"postinstall": "node ./scripts/npm-postinstall.js",
|
||||
"storybook:build": "vue-cli-service storybook:build -c config/storybook -o dist/storybook",
|
||||
"storybook:serve": "vue-cli-service storybook:serve -p 6006 -c config/storybook",
|
||||
"test:unit": "vue-cli-service test:unit --require ./tests/unit/helpers.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vue/cli-plugin-babel": "^4.0.5",
|
||||
"@vue/cli-plugin-eslint": "^4.0.5",
|
||||
"@vue/cli-plugin-router": "^4.0.5",
|
||||
"@vue/cli-plugin-unit-mocha": "^4.0.5",
|
||||
"@vue/cli-service": "^4.0.5",
|
||||
"@vue/cli-plugin-babel": "^4.1.1",
|
||||
"@vue/cli-plugin-eslint": "^4.1.1",
|
||||
"@vue/cli-plugin-router": "^4.1.1",
|
||||
"@vue/cli-plugin-unit-mocha": "^4.1.1",
|
||||
"@vue/cli-service": "^4.1.1",
|
||||
"@storybook/addon-actions": "^5.0.0",
|
||||
"@storybook/addon-knobs": "^5.0.0",
|
||||
"@storybook/addon-links": "^5.0.0",
|
||||
"@storybook/addon-notes": "^5.0.0",
|
||||
"@storybook/vue": "^5.2.5",
|
||||
"@vue/test-utils": "1.0.0-beta.29",
|
||||
"amplitude-js": "^5.7.0",
|
||||
"amplitude-js": "^5.8.0",
|
||||
"axios": "^0.19.0",
|
||||
"axios-progress-bar": "^1.2.0",
|
||||
"babel-eslint": "^10.0.1",
|
||||
"bootstrap": "^4.3.1",
|
||||
"bootstrap": "^4.4.1",
|
||||
"bootstrap-vue": "^2.1.0",
|
||||
"chai": "^4.1.2",
|
||||
"core-js": "^3.4.2",
|
||||
"eslint": "^6.7.1",
|
||||
"core-js": "^3.5.0",
|
||||
"eslint": "^6.7.2",
|
||||
"eslint-config-habitrpg": "^6.2.0",
|
||||
"eslint-plugin-mocha": "^5.3.0",
|
||||
"eslint-plugin-vue": "^6.0.1",
|
||||
"habitica-markdown": "^1.3.0",
|
||||
"habitica-markdown": "^1.3.2",
|
||||
"hellojs": "^1.18.1",
|
||||
"inspectpack": "^4.2.2",
|
||||
"intro.js": "^2.9.3",
|
||||
@@ -46,10 +53,11 @@
|
||||
"svgo-loader": "^2.2.1",
|
||||
"uuid": "^3.3.3",
|
||||
"validator": "^11.1.0",
|
||||
"vue": "^2.6.10",
|
||||
"vue": "^2.6.11",
|
||||
"vue-cli-plugin-storybook": "^0.6.1",
|
||||
"vue-mugen-scroll": "^0.2.6",
|
||||
"vue-router": "^3.0.6",
|
||||
"vue-template-compiler": "^2.6.10",
|
||||
"vue-template-compiler": "^2.6.11",
|
||||
"vuedraggable": "^2.23.1",
|
||||
"vuejs-datepicker": "git://github.com/habitrpg/vuejs-datepicker.git#5d237615463a84a23dd6f3f77c6ab577d68593ec",
|
||||
"webpack": "^4.41.2"
|
||||
|
||||
@@ -6,4 +6,8 @@ if (process.env.NODE_ENV === 'production') {
|
||||
execSync('npm run build', {
|
||||
stdio: 'inherit',
|
||||
});
|
||||
|
||||
execSync('npm run storybook:build', {
|
||||
stdio: 'inherit',
|
||||
});
|
||||
}
|
||||
|
||||
@@ -64,6 +64,30 @@
|
||||
></span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="g1g1-banner d-flex justify-content-center align-items-center"
|
||||
v-if="!giftingHidden">
|
||||
<div
|
||||
class="svg-icon svg-gifts left-gift"
|
||||
v-html="icons.gifts">
|
||||
</div>
|
||||
<router-link class="g1g1-link" to="/user/settings/subscription">
|
||||
{{ $t('g1g1Announcement') }}
|
||||
</router-link>
|
||||
<div
|
||||
class="svg-icon svg-gifts right-gift"
|
||||
v-html="icons.gifts">
|
||||
</div>
|
||||
<div
|
||||
class="closepadding"
|
||||
@click="hideGiftingBanner()">
|
||||
<span
|
||||
class="svg-icon inline-icon icon-10"
|
||||
aria-hidden="true"
|
||||
v-html="icons.close">
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<notifications-display />
|
||||
<app-menu />
|
||||
<div class="container-fluid">
|
||||
@@ -135,10 +159,43 @@
|
||||
cursor: crosshair;
|
||||
}
|
||||
|
||||
.closepadding {
|
||||
margin: 11px 24px;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
right: 0;
|
||||
top: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.container-fluid {
|
||||
flex: 1 0 auto;
|
||||
}
|
||||
|
||||
.g1g1-banner {
|
||||
width: 100%;
|
||||
min-height: 2.5rem;
|
||||
background-color: $teal-50;
|
||||
}
|
||||
|
||||
.g1g1-link {
|
||||
color: $white;
|
||||
}
|
||||
|
||||
.left-gift {
|
||||
margin: auto 1rem auto auto;
|
||||
}
|
||||
|
||||
.right-gift {
|
||||
margin: auto auto auto 1rem;
|
||||
filter: flipH;
|
||||
transform: scaleX(-1);
|
||||
}
|
||||
|
||||
.svg-gifts {
|
||||
width: 4.6rem;
|
||||
}
|
||||
|
||||
.notification {
|
||||
border-radius: 1000px;
|
||||
background-color: $green-10;
|
||||
@@ -165,15 +222,6 @@
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.closepadding {
|
||||
margin: 11px 24px;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
right: 0;
|
||||
top: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 768px) {
|
||||
.content {
|
||||
font-size: 12px;
|
||||
@@ -239,8 +287,14 @@ import subCancelModalConfirm from '@/components/payments/cancelModalConfirm';
|
||||
import subCanceledModal from '@/components/payments/canceledModal';
|
||||
|
||||
import spellsMixin from '@/mixins/spells';
|
||||
import { CONSTANTS, getLocalSetting, removeLocalSetting } from '@/libs/userlocalManager';
|
||||
import {
|
||||
CONSTANTS,
|
||||
getLocalSetting,
|
||||
removeLocalSetting,
|
||||
setLocalSetting,
|
||||
} from '@/libs/userlocalManager';
|
||||
|
||||
import gifts from '@/assets/svg/gifts.svg';
|
||||
import svgClose from '@/assets/svg/close.svg';
|
||||
import bannedAccountModal from '@/components/bannedAccountModal';
|
||||
|
||||
@@ -267,6 +321,7 @@ export default {
|
||||
return {
|
||||
icons: Object.freeze({
|
||||
close: svgClose,
|
||||
gifts,
|
||||
}),
|
||||
selectedItemToBuy: null,
|
||||
selectedSpellToBuy: null,
|
||||
@@ -277,6 +332,7 @@ export default {
|
||||
loading: true,
|
||||
currentTipNumber: 0,
|
||||
bannerHidden: false,
|
||||
giftingHidden: getLocalSetting(CONSTANTS.keyConstants.GIFTING_BANNER_DISPLAY) === 'dismissed2019',
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@@ -670,6 +726,10 @@ export default {
|
||||
hideBanner () {
|
||||
this.bannerHidden = true;
|
||||
},
|
||||
hideGiftingBanner () {
|
||||
setLocalSetting(CONSTANTS.keyConstants.GIFTING_BANNER_DISPLAY, 'dismissed2019');
|
||||
this.giftingHidden = true;
|
||||
},
|
||||
resumeDamage () {
|
||||
this.$store.dispatch('user:sleep');
|
||||
},
|
||||
|
||||
@@ -1,72 +1,78 @@
|
||||
.promo_achievement_white {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: -928px -465px;
|
||||
background-position: -862px -233px;
|
||||
width: 204px;
|
||||
height: 102px;
|
||||
}
|
||||
.promo_armoire_backgrounds_201912 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: -928px 0px;
|
||||
background-position: -424px -475px;
|
||||
width: 423px;
|
||||
height: 147px;
|
||||
}
|
||||
.promo_costume_achievement {
|
||||
.promo_g1g1_2019 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: -1175px -296px;
|
||||
width: 144px;
|
||||
height: 156px;
|
||||
}
|
||||
.promo_delightful_dinos {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: 0px -752px;
|
||||
width: 423px;
|
||||
height: 147px;
|
||||
}
|
||||
.promo_ember_thunderstorm_potions {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: -424px -752px;
|
||||
width: 423px;
|
||||
height: 147px;
|
||||
}
|
||||
.promo_harvest_feast {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: -928px -296px;
|
||||
width: 246px;
|
||||
height: 168px;
|
||||
background-position: -469px -327px;
|
||||
width: 357px;
|
||||
height: 144px;
|
||||
}
|
||||
.promo_mystery_201912 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: -928px -148px;
|
||||
background-position: -424px -623px;
|
||||
width: 282px;
|
||||
height: 147px;
|
||||
}
|
||||
.promo_snowballs {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: -441px 0px;
|
||||
width: 420px;
|
||||
height: 174px;
|
||||
}
|
||||
.promo_take_this {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: -1211px -148px;
|
||||
background-position: -862px -336px;
|
||||
width: 96px;
|
||||
height: 69px;
|
||||
}
|
||||
.scene_habitica_map {
|
||||
.promo_winter_potions_2020 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: 0px -475px;
|
||||
width: 423px;
|
||||
height: 147px;
|
||||
}
|
||||
.promo_winter_quests_bundle {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: 0px -623px;
|
||||
width: 423px;
|
||||
height: 147px;
|
||||
}
|
||||
.promo_winter_wonderland_2019 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: -441px -175px;
|
||||
width: 402px;
|
||||
height: 147px;
|
||||
}
|
||||
.promo_winter_wonderland_2020 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: 0px -327px;
|
||||
width: 468px;
|
||||
height: 147px;
|
||||
}
|
||||
.scene_studying_hard {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: -862px 0px;
|
||||
width: 220px;
|
||||
height: 232px;
|
||||
}
|
||||
.scene_tavern {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: 0px 0px;
|
||||
width: 450px;
|
||||
height: 450px;
|
||||
width: 440px;
|
||||
height: 326px;
|
||||
}
|
||||
.scene_office {
|
||||
.scene_todos {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: -421px -451px;
|
||||
width: 360px;
|
||||
height: 240px;
|
||||
}
|
||||
.scene_seaserpent {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: -451px 0px;
|
||||
width: 476px;
|
||||
height: 364px;
|
||||
}
|
||||
.scene_yarn_boss {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: 0px -451px;
|
||||
width: 420px;
|
||||
height: 300px;
|
||||
background-position: 0px -771px;
|
||||
width: 240px;
|
||||
height: 195px;
|
||||
}
|
||||
|
||||
|
After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 93 KiB After Width: | Height: | Size: 104 KiB |
|
Before Width: | Height: | Size: 477 KiB After Width: | Height: | Size: 477 KiB |
|
Before Width: | Height: | Size: 685 KiB After Width: | Height: | Size: 688 KiB |
|
Before Width: | Height: | Size: 115 KiB After Width: | Height: | Size: 115 KiB |
|
Before Width: | Height: | Size: 111 KiB After Width: | Height: | Size: 113 KiB |
|
Before Width: | Height: | Size: 363 KiB After Width: | Height: | Size: 336 KiB |
|
Before Width: | Height: | Size: 322 KiB After Width: | Height: | Size: 338 KiB |
|
Before Width: | Height: | Size: 139 KiB After Width: | Height: | Size: 144 KiB |
|
Before Width: | Height: | Size: 157 KiB After Width: | Height: | Size: 157 KiB |
|
Before Width: | Height: | Size: 140 KiB After Width: | Height: | Size: 137 KiB |
|
Before Width: | Height: | Size: 130 KiB After Width: | Height: | Size: 139 KiB |
|
Before Width: | Height: | Size: 174 KiB After Width: | Height: | Size: 173 KiB |
|
Before Width: | Height: | Size: 140 KiB After Width: | Height: | Size: 134 KiB |
|
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 74 KiB |
|
Before Width: | Height: | Size: 152 KiB After Width: | Height: | Size: 153 KiB |
|
Before Width: | Height: | Size: 150 KiB After Width: | Height: | Size: 146 KiB |
|
Before Width: | Height: | Size: 148 KiB After Width: | Height: | Size: 153 KiB |
|
Before Width: | Height: | Size: 178 KiB After Width: | Height: | Size: 160 KiB |
|
Before Width: | Height: | Size: 162 KiB After Width: | Height: | Size: 179 KiB |
|
Before Width: | Height: | Size: 162 KiB After Width: | Height: | Size: 160 KiB |
|
Before Width: | Height: | Size: 97 KiB After Width: | Height: | Size: 152 KiB |
|
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 60 KiB |
|
Before Width: | Height: | Size: 67 KiB After Width: | Height: | Size: 68 KiB |
|
Before Width: | Height: | Size: 96 KiB After Width: | Height: | Size: 90 KiB |
|
Before Width: | Height: | Size: 121 KiB After Width: | Height: | Size: 122 KiB |
|
Before Width: | Height: | Size: 120 KiB After Width: | Height: | Size: 119 KiB |
|
Before Width: | Height: | Size: 142 KiB After Width: | Height: | Size: 142 KiB |
|
Before Width: | Height: | Size: 126 KiB After Width: | Height: | Size: 129 KiB |
@@ -20,7 +20,7 @@
|
||||
@include btn-focus-hover-shadow();
|
||||
}
|
||||
|
||||
&:hover:not(.btn-flat):not(.disabled) {
|
||||
&:hover:not(.btn-flat):not(.disabled):not(:disabled) {
|
||||
@include btn-focus-hover-shadow();
|
||||
border-color: transparent;
|
||||
}
|
||||
@@ -49,7 +49,7 @@
|
||||
background: $purple-200;
|
||||
}
|
||||
|
||||
&:hover:not(:disabled), &:active, &.active, &:focus {
|
||||
&:hover:not(:disabled):not(.disabled), &:active:not(:disabled):not(.disabled), &.active:not(:disabled):not(.disabled), &:focus:not(:disabled):not(.disabled) {
|
||||
background: #5d3b9c !important;
|
||||
color: $white;
|
||||
}
|
||||
@@ -59,7 +59,7 @@
|
||||
color: $gray-50;
|
||||
background: $white !important;
|
||||
|
||||
&:hover:not(:disabled):not(.disabled), &:active, &.active, &:focus {
|
||||
&:hover:not(:disabled):not(.disabled), &:active:not(:disabled):not(.disabled), &.active:not(:disabled):not(.disabled), &:focus:not(:disabled):not(.disabled) {
|
||||
color: $purple-200 !important;
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@
|
||||
background: $green-100;
|
||||
}
|
||||
|
||||
&:hover:not(:disabled), &:active, &.active {
|
||||
&:hover:not(:disabled):not(.disabled), &:active:not(:disabled):not(.disabled), &.active:not(:disabled):not(.disabled) {
|
||||
background: $green-50;
|
||||
}
|
||||
}
|
||||
@@ -96,8 +96,12 @@
|
||||
background: $blue-50;
|
||||
}
|
||||
|
||||
&:hover:not(:disabled), &:active, &.active {
|
||||
background: $blue-100;
|
||||
&:hover:not(:disabled):not(.disabled) {
|
||||
background-color: $blue-100;
|
||||
}
|
||||
|
||||
&:active:not(:disabled):not(.disabled), &.active:not(:disabled):not(.disabled) {
|
||||
background: $blue-50;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,7 +112,11 @@
|
||||
background: $red-50;
|
||||
}
|
||||
|
||||
&:hover:not(:disabled), &:active, &.active {
|
||||
&:hover:not(:disabled):not(.disabled) {
|
||||
background: $red-100;
|
||||
}
|
||||
|
||||
&:active:not(:disabled):not(.disabled), &.active:not(:disabled):not(.disabled) {
|
||||
background: $red-100;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,6 +65,7 @@ input, textarea, input.form-control, textarea.form-control {
|
||||
padding-right: 40px;
|
||||
background-image: url(~@/assets/svg/for-css/alert.svg);
|
||||
background-size: 16px 16px;
|
||||
border-color: $red-100 !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -276,3 +277,19 @@ $bg-disabled-control: #34303a;
|
||||
.toggle-switch-container.no-margin {
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Disable default style Firefox for invalid elements.
|
||||
// Selectors taken from view-source:resource://gre-resources/forms.css on Firefox
|
||||
:not(output):-moz-ui-invalid {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
:not(output):-moz-ui-invalid:-moz-focusring {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
output:-moz-ui-invalid {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
@@ -50,3 +50,18 @@
|
||||
width: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.close-icon {
|
||||
position: absolute;
|
||||
top: 24px;
|
||||
right: 24px;
|
||||
cursor: pointer;
|
||||
|
||||
& svg path {
|
||||
stroke: $gray-200;
|
||||
}
|
||||
|
||||
&:hover svg path {
|
||||
stroke: $gray-100;
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@
|
||||
.tier3 {
|
||||
color: #d70e14;
|
||||
|
||||
:hover, :active, :focus {
|
||||
&:hover, &:active, &:focus {
|
||||
color: #d70e14;
|
||||
}
|
||||
}
|
||||
@@ -25,7 +25,7 @@
|
||||
.tier4 {
|
||||
color: #c24d00;
|
||||
|
||||
&:hover, &:active, :focus {
|
||||
&:hover, &:active, &:focus {
|
||||
color: #c24d00;
|
||||
}
|
||||
}
|
||||
@@ -33,7 +33,7 @@
|
||||
.tier5 {
|
||||
color: #9e650f;
|
||||
|
||||
:hover, :active, :focus {
|
||||
&:hover, &:active, &:focus {
|
||||
color: #9e650f;
|
||||
}
|
||||
}
|
||||
@@ -41,7 +41,7 @@
|
||||
.tier6 {
|
||||
color: #2b8363;
|
||||
|
||||
:hover, :active, &:focus {
|
||||
&:hover, &:active, &:focus {
|
||||
color: #2b8363;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
// possible values are: normal, fall, habitoween, thanksgiving, winter, nye, birthday, valentines, spring, summer
|
||||
// more to be added on future seasons
|
||||
|
||||
$npc_market_flavor: 'normal';
|
||||
$npc_quests_flavor: 'normal';
|
||||
$npc_seasonal_flavor: 'normal';
|
||||
$npc_timetravelers_flavor: 'normal';
|
||||
$npc_tavern_flavor: 'normal';
|
||||
$npc_market_flavor: 'nye';
|
||||
$npc_quests_flavor: 'nye';
|
||||
$npc_seasonal_flavor: 'nye';
|
||||
$npc_timetravelers_flavor: 'winter';
|
||||
$npc_tavern_flavor: 'nye';
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="52" height="62" viewBox="0 0 52 62">
|
||||
<g fill="none" fill-rule="evenodd">
|
||||
<path stroke="#FFA624" stroke-width="2" d="M18.286 6.252c.186-1.717-.286-3.6-1.658-4.473-1.373-.872-2.831-.21-2.994 1.027-.163 1.237.714 1.988 3.413 4.686.766.766 1.054.477 1.24-1.24z"/>
|
||||
<path stroke="#FFBE5D" stroke-width="2" d="M21.882 8.328c1.395-1.018 3.262-1.552 4.704-.8 1.441.753 1.597 2.347.607 3.107-.99.76-2.079.375-5.764-.613-1.047-.28-.941-.675.453-1.694z"/>
|
||||
<path fill="#EE9109" d="M20.698 6.227c-2.754-1.59-2.924 2.514-1.82 3.152 1.106.637 4.574-1.562 1.82-3.152z"/>
|
||||
<path fill="#F8F9F9" d="M19.167 8.88l10.18 5.878-2.939 5.09-10.18-5.878z"/>
|
||||
<path fill="#DDF3F3" d="M8.986 3.003l10.18 5.877-2.938 5.09-10.18-5.877z"/>
|
||||
<path fill="#FFBE5D" d="M15.773 6.921l6.787 3.918-2.939 5.09-6.787-3.918z"/>
|
||||
<path fill="#FFA624" d="M15.773 6.921l3.394 1.96-2.94 5.09-3.393-1.96z"/>
|
||||
<path fill="#FFA624" d="M19.167 8.88l3.393 1.96-.98 1.696-3.393-1.96z"/>
|
||||
<path fill="#EE9109" d="M15.773 6.921l3.394 1.96-.98 1.696-3.393-1.96z"/>
|
||||
<path fill="#F8F9F9" d="M16.228 13.97l8.483 4.898-6.857 11.877-8.483-4.898z"/>
|
||||
<path fill="#DDF3F3" d="M7.744 9.072l8.484 4.898L9.37 25.847.887 20.95z"/>
|
||||
<path fill="#FFA624" d="M14.531 12.99l1.697.98L9.37 25.847l-1.697-.98z"/>
|
||||
<path fill="#FFBE5D" d="M16.228 13.97l1.696.98-6.857 11.876-1.696-.98z"/>
|
||||
<path fill="#EE9109" d="M8.654 23.171l1.696.98-.98 1.696-1.696-.98zM14.531 12.99l1.697.98-.98 1.697-1.697-.98z"/>
|
||||
<path fill="#C1E9E9" d="M7.744 9.072l6.787 3.919-.98 1.696-6.786-3.918z"/>
|
||||
<path fill="#C1E9E9" d="M7.027 6.396l6.787 3.918-.98 1.697-6.786-3.918zM1.867 19.253l6.787 3.918-.98 1.697-6.787-3.919z"/>
|
||||
<path fill="#DDF3F3" d="M17.924 14.95l6.787 3.918-.98 1.697-6.786-3.918z"/>
|
||||
<path fill="#DDF3F3" d="M20.6 14.233l6.788 3.918-.98 1.697-6.787-3.918zM12.047 25.13l6.787 3.919-.98 1.696-6.787-3.918z"/>
|
||||
<path fill="#FFA624" d="M10.35 24.15l1.697.98-.98 1.697-1.696-.98zM16.228 13.97l1.696.98-.98 1.697-1.696-.98z"/>
|
||||
<path fill="#8EEDF6" d="M32.753 23.621l-.564 2.362 2.228-.966 2.361.564-.965-2.228.564-2.361-2.228.965-2.361-.564zM17.733 45.172l-.564 2.362 2.228-.966 2.361.564-.966-2.228.565-2.361-2.228.965-2.362-.564zM42.733 15.02l.769 2.958 1.905-2.39 2.96-.769-2.391-1.905-.769-2.96-1.906 2.391-2.958.769z"/>
|
||||
<g>
|
||||
<path fill="#F8F9F9" d="M37.688 45.676l9.462-2.536 3.55 13.247-9.463 2.536z"/>
|
||||
<path fill="#DDF3F3" d="M28.225 48.211l9.463-2.535 3.55 13.247-9.463 2.535z"/>
|
||||
<path fill="#FFA624" d="M35.795 46.183l1.893-.507 3.55 13.247-1.893.507z"/>
|
||||
<path fill="#FFBE5D" d="M37.688 45.676l1.892-.507 3.55 13.247-1.893.507z"/>
|
||||
<path fill="#EE9109" d="M38.838 57.537l1.892-.507.507 1.893-1.892.507zM35.795 46.183l1.893-.507.507 1.892-1.893.507z"/>
|
||||
<path fill="#C1E9E9" d="M28.225 48.211l7.57-2.028.507 1.892-7.57 2.028zM31.268 59.566l7.57-2.029.507 1.893-7.57 2.028z"/>
|
||||
<path fill="#DDF3F3" d="M39.58 45.169l7.57-2.029.507 1.893-7.57 2.028zM42.622 56.523l7.57-2.028.507 1.892-7.57 2.029z"/>
|
||||
<path fill="#FFA624" d="M40.73 57.03l1.892-.507.507 1.893-1.892.507zM37.688 45.676l1.892-.507.507 1.892-1.892.507z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path stroke="#FFA624" stroke-width="2" d="M37.861 35.357c-.471-1.661-1.614-3.232-3.214-3.526-1.599-.294-2.703.866-2.39 2.074.311 1.208 1.407 1.575 4.92 3.066.996.423 1.156.047.684-1.614z"/>
|
||||
<path stroke="#FFBE5D" stroke-width="2" d="M41.973 35.935c.911-1.467 2.443-2.662 4.061-2.504 1.619.158 2.36 1.578 1.727 2.653-.633 1.075-1.787 1.126-5.574 1.591-1.075.132-1.125-.273-.214-1.74z"/>
|
||||
<path fill="#EE9109" d="M40.088 34.43c-3.15-.442-1.77 3.426-.506 3.604 1.263.177 3.655-3.161.506-3.604z"/>
|
||||
<path fill="#F8F9F9" d="M39.662 37.464l11.64 1.636-.818 5.82-11.64-1.636z"/>
|
||||
<path fill="#DDF3F3" d="M28.02 35.828l11.642 1.636-.818 5.82-11.641-1.636z"/>
|
||||
<path fill="#FFBE5D" d="M35.781 36.919l7.76 1.09-.817 5.82-7.76-1.09z"/>
|
||||
<path fill="#FFA624" d="M35.781 36.919l3.88.545-.817 5.82-3.88-.545zM39.662 37.464l3.88.545-.273 1.94-3.88-.545z"/>
|
||||
<path fill="#EE9109" d="M35.781 36.919l3.88.545-.272 1.94-3.88-.545z"/>
|
||||
<path fill="#C1E9E9" d="M27.476 39.708l7.76 1.09-.273 1.941-7.76-1.09z"/>
|
||||
<path fill="#DDF3F3" d="M42.997 41.89l7.76 1.09-.273 1.94-7.76-1.09z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.5 KiB |
@@ -0,0 +1,48 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="150" height="48" viewBox="0 0 150 48">
|
||||
<defs>
|
||||
<filter id="a" width="225%" height="225%" x="-62.5%" y="-62.5%" filterUnits="objectBoundingBox">
|
||||
<feOffset dy="2" in="SourceAlpha" result="shadowOffsetOuter1"/>
|
||||
<feGaussianBlur in="shadowOffsetOuter1" result="shadowBlurOuter1" stdDeviation="4"/>
|
||||
<feColorMatrix in="shadowBlurOuter1" result="shadowMatrixOuter1" values="0 0 0 0 0.101960784 0 0 0 0 0.0941176471 0 0 0 0 0.11372549 0 0 0 0.32 0"/>
|
||||
<feMerge>
|
||||
<feMergeNode in="shadowMatrixOuter1"/>
|
||||
<feMergeNode in="SourceGraphic"/>
|
||||
</feMerge>
|
||||
</filter>
|
||||
</defs>
|
||||
<g fill="none" fill-rule="evenodd">
|
||||
<g fill="#FFA624">
|
||||
<path d="M125.45 35.414l3.346-2.967-4.382-.896-2.967-3.347-.896 4.382-3.347 2.967 4.382.896 2.967 3.347z" opacity=".75"/>
|
||||
<path d="M136.43 30.407l3.325-.442-2.348-2.395-.442-3.325-2.395 2.348-3.325.442 2.348 2.395.442 3.325z" opacity=".5"/>
|
||||
<path d="M146.901 35.41l2.067.854-.557-2.165.853-2.067-2.165.557-2.067-.853.557 2.165-.853 2.067z" opacity=".25"/>
|
||||
<path d="M138.353 39.874l1.488-.093-.967-1.134-.093-1.488-1.134.967-1.488.093.967 1.134.093 1.488z" opacity=".35"/>
|
||||
</g>
|
||||
<g fill="#FFA624">
|
||||
<path d="M24.55 35.414l-3.346-2.967 4.382-.896 2.967-3.347.896 4.382 3.347 2.967-4.382.896-2.967 3.347z" opacity=".75"/>
|
||||
<path d="M13.57 30.407l-3.325-.442 2.348-2.395.442-3.325 2.395 2.348 3.325.442-2.348 2.395-.442 3.325z" opacity=".5"/>
|
||||
<path d="M3.099 35.41l-2.067.854.557-2.165-.853-2.067 2.165.557 2.067-.853-.557 2.165.853 2.067z" opacity=".25"/>
|
||||
<path d="M11.647 39.874l-1.488-.093.967-1.134.093-1.488 1.134.967 1.488.093-.967 1.134-.093 1.488z" opacity=".35"/>
|
||||
</g>
|
||||
<g opacity=".5" transform="translate(83 22)">
|
||||
<circle cx="12" cy="12" r="12" fill="#FFA623"/>
|
||||
<path fill="#FFF" d="M6.3 17.7c-3.1-3.1-3.1-8.2 0-11.3 3.1-3.1 8.2-3.1 11.3 0" opacity=".5"/>
|
||||
<path fill="#FFF" d="M17.7 6.3c3.1 3.1 3.1 8.2 0 11.3-3.1 3.1-8.2 3.1-11.3 0" opacity=".25"/>
|
||||
<path fill="#BF7D1A" d="M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10 10-4.5 10-10S17.5 2 12 2zm0 18c-4.4 0-8-3.6-8-8s3.6-8 8-8 8 3.6 8 8-3.6 8-8 8z" opacity=".5"/>
|
||||
<path fill="#BF7D1A" d="M13 9v2h-2V9H9v6h2v-2h2v2h2V9z" opacity=".75"/>
|
||||
</g>
|
||||
<g opacity=".5" transform="translate(43 22)">
|
||||
<circle cx="12" cy="12" r="12" fill="#FFA623"/>
|
||||
<path fill="#FFF" d="M6.3 17.7c-3.1-3.1-3.1-8.2 0-11.3 3.1-3.1 8.2-3.1 11.3 0" opacity=".5"/>
|
||||
<path fill="#FFF" d="M17.7 6.3c3.1 3.1 3.1 8.2 0 11.3-3.1 3.1-8.2 3.1-11.3 0" opacity=".25"/>
|
||||
<path fill="#BF7D1A" d="M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10 10-4.5 10-10S17.5 2 12 2zm0 18c-4.4 0-8-3.6-8-8s3.6-8 8-8 8 3.6 8 8-3.6 8-8 8z" opacity=".5"/>
|
||||
<path fill="#BF7D1A" d="M13 9v2h-2V9H9v6h2v-2h2v2h2V9z" opacity=".75"/>
|
||||
</g>
|
||||
<g transform="translate(59 6)">
|
||||
<circle cx="16" cy="16" r="16" fill="#FFA623"/>
|
||||
<path fill="#FFF" d="M8.4 23.6c-4.133-4.133-4.133-10.933 0-15.067 4.133-4.133 10.933-4.133 15.067 0" opacity=".5"/>
|
||||
<path fill="#FFF" d="M23.6 8.4c4.133 4.133 4.133 10.933 0 15.067-4.133 4.133-10.933 4.133-15.067 0" opacity=".25"/>
|
||||
<path fill="#BF7D1A" d="M16 2.667c-7.333 0-13.333 6-13.333 13.333s6 13.333 13.333 13.333 13.333-6 13.333-13.333S23.333 2.667 16 2.667zm0 24c-5.867 0-10.667-4.8-10.667-10.667S10.133 5.333 16 5.333 26.667 10.133 26.667 16 21.867 26.667 16 26.667z" opacity=".5"/>
|
||||
<path fill="#BF7D1A" d="M17.333 12v2.667h-2.666V12H12v8h2.667v-2.667h2.666V20H20v-8z" opacity=".75"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.7 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="11" viewBox="0 0 12 11">
|
||||
<path fill="#FFF" fill-rule="evenodd" d="M6 8.76l-3.527 2.094.902-4.001L.294 4.146l4.084-.379L6 0l1.622 3.767 4.084.379-3.081 2.707.902 4.001z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 241 B |
@@ -1,104 +1,16 @@
|
||||
<template>
|
||||
<div
|
||||
class="modal-footer"
|
||||
style="margin-top:0"
|
||||
ng-init="loadWidgets()"
|
||||
>
|
||||
<div class="container-fluid share-buttons">
|
||||
<div class="row">
|
||||
<div class="col-12 text-center">
|
||||
<a
|
||||
class="twitter-share-button share-button"
|
||||
:href="twitterLink"
|
||||
target="_blank"
|
||||
>
|
||||
<div
|
||||
class="social-icon twitter svg-icon"
|
||||
v-html="icons.twitter"
|
||||
></div>
|
||||
{{ $t('tweet') }}
|
||||
</a>
|
||||
<a
|
||||
class="fb-share-button share-button"
|
||||
:href="facebookLink"
|
||||
target="_blank"
|
||||
>
|
||||
<div
|
||||
class="social-icon facebook svg-icon"
|
||||
v-html="icons.facebook"
|
||||
></div>
|
||||
{{ $t('share') }}
|
||||
</a>
|
||||
<!-- @TODO: Still want this?
|
||||
.col-4a.tumblr-share-button(:data-href='socialLevelLink', data-notes='none')-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- TODO disabled for now, see https://github.com/HabitRPG/habitica/issues/11283 -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.share-buttons {
|
||||
margin-top: 1em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.share-button {
|
||||
display: inline-block;
|
||||
width: 77px;
|
||||
padding: .5em;
|
||||
border-radius: 2px;
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.fb-share-button {
|
||||
background-color: #2995cd;
|
||||
}
|
||||
|
||||
.twitter-share-button {
|
||||
margin-right: .5em;
|
||||
background-color: #3bcad7;
|
||||
}
|
||||
|
||||
.social-icon {
|
||||
width: 16px;
|
||||
display: inline-block;
|
||||
vertical-align: bottom;
|
||||
margin-right: .5em;
|
||||
}
|
||||
|
||||
.social-icon.facebook svg {
|
||||
width: 7.5px;
|
||||
margin-bottom: .2em;
|
||||
}
|
||||
|
||||
.social-icon.twitter {
|
||||
margin-bottom: .2em;
|
||||
}
|
||||
<style lang="scss" scoped>
|
||||
.modal-footer {
|
||||
border-top: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
// @TODO:
|
||||
import twitter from '@/assets/svg/twitter.svg';
|
||||
import facebook from '@/assets/svg/facebook.svg';
|
||||
|
||||
const BASE_URL = 'https://habitica.com';
|
||||
|
||||
export default {
|
||||
data () {
|
||||
const tweet = this.$t('achievementShare');
|
||||
|
||||
return {
|
||||
icons: Object.freeze({
|
||||
twitter,
|
||||
facebook,
|
||||
}),
|
||||
tweet,
|
||||
achievementLink: `${BASE_URL}`,
|
||||
twitterLink: `https://twitter.com/intent/tweet?text=${tweet}&via=habitica&url=${BASE_URL}&count=none`,
|
||||
facebookLink: `https://www.facebook.com/sharer/sharer.php?text=${tweet}&u=${BASE_URL}`,
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -225,30 +225,30 @@ export default {
|
||||
classGear (heroClass) {
|
||||
if (heroClass === 'rogue') {
|
||||
return {
|
||||
armor: 'armor_rogue_5',
|
||||
head: 'head_rogue_5',
|
||||
shield: 'shield_rogue_6',
|
||||
weapon: 'weapon_rogue_6',
|
||||
armor: 'armor_special_winter2020Rogue',
|
||||
head: 'head_special_winter2020Rogue',
|
||||
shield: 'shield_special_winter2020Rogue',
|
||||
weapon: 'weapon_special_winter2020Rogue',
|
||||
};
|
||||
} if (heroClass === 'wizard') {
|
||||
return {
|
||||
armor: 'armor_wizard_5',
|
||||
head: 'head_wizard_5',
|
||||
weapon: 'weapon_wizard_6',
|
||||
armor: 'armor_special_winter2020Mage',
|
||||
head: 'head_special_winter2020Mage',
|
||||
weapon: 'weapon_special_winter2020Mage',
|
||||
};
|
||||
} if (heroClass === 'healer') {
|
||||
return {
|
||||
armor: 'armor_healer_5',
|
||||
head: 'head_healer_5',
|
||||
shield: 'shield_healer_5',
|
||||
weapon: 'weapon_healer_6',
|
||||
armor: 'armor_special_winter2020Healer',
|
||||
head: 'head_special_winter2020Healer',
|
||||
shield: 'shield_special_winter2020Healer',
|
||||
weapon: 'weapon_special_winter2020Healer',
|
||||
};
|
||||
}
|
||||
return {
|
||||
armor: 'armor_warrior_5',
|
||||
head: 'head_warrior_5',
|
||||
shield: 'shield_warrior_5',
|
||||
weapon: 'weapon_warrior_6',
|
||||
armor: 'armor_special_winter2020Warrior',
|
||||
head: 'head_special_winter2020Warrior',
|
||||
shield: 'shield_special_winter2020Warrior',
|
||||
weapon: 'weapon_special_winter2020Warrior',
|
||||
};
|
||||
},
|
||||
selectionBox (selectedClass, heroClass) {
|
||||
|
||||
@@ -3,14 +3,32 @@
|
||||
id="generic-achievement"
|
||||
:title="data.message"
|
||||
size="md"
|
||||
:hide-footer="true"
|
||||
:hide-header="true"
|
||||
>
|
||||
<div class="modal-body">
|
||||
<div class="col-12">
|
||||
<achievement-avatar class="avatar" />
|
||||
<span
|
||||
class="close-icon svg-icon inline icon-10"
|
||||
@click="close()"
|
||||
v-html="icons.close"
|
||||
></span>
|
||||
<div class="content">
|
||||
<div
|
||||
v-once
|
||||
class="dialog-header title"
|
||||
>
|
||||
{{ $t('earnedAchievement') }}
|
||||
</div>
|
||||
<div class="col-6 offset-3 text-center">
|
||||
<p v-html="data.modalText"></p>
|
||||
<div class="inner-content">
|
||||
<div class="achievement-background d-flex align-items-center">
|
||||
<div
|
||||
class="icon"
|
||||
:class="achievementClass"
|
||||
></div>
|
||||
</div>
|
||||
<h4
|
||||
class="title"
|
||||
v-html="$t(achievement.titleKey)"
|
||||
>
|
||||
</h4>
|
||||
<button
|
||||
class="btn btn-primary"
|
||||
@click="close()"
|
||||
@@ -19,33 +37,86 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<achievement-footer />
|
||||
<div
|
||||
slot="modal-footer"
|
||||
class="clearfix"
|
||||
></div>
|
||||
</b-modal>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.avatar {
|
||||
width: 140px;
|
||||
<style lang="scss">
|
||||
@import '~@/assets/scss/modal.scss';
|
||||
|
||||
#generic-achievement {
|
||||
@include centeredModal();
|
||||
|
||||
.modal-dialog {
|
||||
width: 330px;
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
padding-top: 0px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '~@/assets/scss/colors.scss';
|
||||
|
||||
.content {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.inner-content {
|
||||
margin: 24px auto auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.achievement-background {
|
||||
width: 112px;
|
||||
height: 112px;
|
||||
border-radius: 4px;
|
||||
background-color: $gray-700;
|
||||
}
|
||||
|
||||
.dialog-header {
|
||||
margin-top: 16px !important;
|
||||
color: $purple-200 !important;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin-bottom: 24px !important;
|
||||
}
|
||||
|
||||
.icon {
|
||||
margin: 0 auto;
|
||||
margin-bottom: 1.5em;
|
||||
margin-top: 1.5em;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import achievementFooter from './achievementFooter';
|
||||
import achievementAvatar from './achievementAvatar';
|
||||
|
||||
import { mapState } from '@/libs/store';
|
||||
import achievements from '@/../../common/script/content/achievements';
|
||||
import svgClose from '@/assets/svg/close.svg';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
achievementFooter,
|
||||
achievementAvatar,
|
||||
},
|
||||
props: ['data'],
|
||||
data () {
|
||||
return {
|
||||
icons: Object.freeze({
|
||||
close: svgClose,
|
||||
}),
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState({ user: 'user.data' }),
|
||||
achievement () {
|
||||
return achievements[this.data.achievement];
|
||||
},
|
||||
achievementClass () {
|
||||
return `${this.achievement.icon}2x`;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
close () {
|
||||
|
||||