Compare commits
103 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 79c1a5d9c1 | |||
| 8b955e2c5e | |||
| 67c607216f | |||
| 672fd43ad0 | |||
| 69281f80ea | |||
| ec0e5024a7 | |||
| 8e6ce39d64 | |||
| 617832f02d | |||
| 43efe2d6d6 | |||
| 9474a44df3 | |||
| 6a27feb3f9 | |||
| e04d4e8bea | |||
| 2832226539 | |||
| dcbc5da2ba | |||
| 1d44bfe0cd | |||
| 6553118636 | |||
| a238b264e5 | |||
| 4696237b21 | |||
| b7956a82ee | |||
| a5babc493f | |||
| 708bd4a292 | |||
| d9e774dd77 | |||
| 97ef3b1d4b | |||
| 8e3ac280b0 | |||
| 7fedbdde03 | |||
| 6ad808f717 | |||
| c6541399bb | |||
| 81028893b2 | |||
| 943862c0ea | |||
| 43511aacb9 | |||
| ce68e2f855 | |||
| ec06faef32 | |||
| cfac54d44c | |||
| f4b41bd958 | |||
| 7d7d71e95f | |||
| 2b21c2a28e | |||
| dfa8725c55 | |||
| 1d8880c04d | |||
| 07fd3cef4c | |||
| b58443140a | |||
| a0b93d57e4 | |||
| 7cac574c31 | |||
| 86619a2ac9 | |||
| 058c1464d3 | |||
| 70e747c131 | |||
| 8869d3ebf0 | |||
| d82f4f5c91 | |||
| d9a6120cfe | |||
| 5c7ad58ce4 | |||
| 284510d0a3 | |||
| 0791848fa3 | |||
| 466f109edb | |||
| 87fe4aa28e | |||
| baa86bab88 | |||
| 17cbe16773 | |||
| 660f3635aa | |||
| 10a28a9161 | |||
| 2964eff298 | |||
| 7119763c1e | |||
| 12bd10a095 | |||
| 221c95eb14 | |||
| 7815d501fa | |||
| 9dec717e10 | |||
| d23368a826 | |||
| 33913743e9 | |||
| d92b344e91 | |||
| def065d86a | |||
| 5b50761d9f | |||
| 30a52e2bb8 | |||
| b361e3fc82 | |||
| 7419e8a926 | |||
| 0d04509a97 | |||
| 4950ceb814 | |||
| 218d186969 | |||
| 4f6306e748 | |||
| 334478f3d4 | |||
| fd4c50083c | |||
| 435471a48c | |||
| be96e2c3a4 | |||
| d018401f96 | |||
| 63bb9b8e68 | |||
| 682f559762 | |||
| aa39af5ab4 | |||
| d9503ef35a | |||
| 322a826576 | |||
| 362dea6c5c | |||
| f74e908bd5 | |||
| d4c11ff798 | |||
| 24bc3822b6 | |||
| 4fc796b177 | |||
| c099242cb7 | |||
| f7142b6f55 | |||
| c0be28072b | |||
| a218f9ef25 | |||
| 65e881fa5a | |||
| 2c34972629 | |||
| 9bd07034a5 | |||
| 285507d68a | |||
| 0a2e50ce76 | |||
| 49d8c739c0 | |||
| 2c01e6347d | |||
| d050ef4779 | |||
| e4e980a6e3 |
@@ -0,0 +1,82 @@
|
||||
/* eslint-disable no-console */
|
||||
const MIGRATION_NAME = '20201020_pet_color_achievements';
|
||||
import { model as User } from '../../../website/server/models/user';
|
||||
|
||||
const progressCount = 1000;
|
||||
let count = 0;
|
||||
|
||||
async function updateUser (user) {
|
||||
count++;
|
||||
|
||||
const set = {
|
||||
migration: MIGRATION_NAME,
|
||||
};
|
||||
|
||||
if (user && user.items && user.items.pets) {
|
||||
const pets = user.items.pets;
|
||||
if (pets['Wolf-Golden'] > 0
|
||||
&& pets['TigerCub-Skeleton'] > 0
|
||||
&& pets['PandaCub-Skeleton'] > 0
|
||||
&& pets['LionCub-Skeleton'] > 0
|
||||
&& pets['Fox-Skeleton'] > 0
|
||||
&& pets['FlyingPig-Skeleton'] > 0
|
||||
&& pets['Dragon-Skeleton'] > 0
|
||||
&& pets['Cactus-Skeleton'] > 0
|
||||
&& pets['BearCub-Skeleton'] > 0) {
|
||||
set['achievements.boneCollector'] = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (user && user.items && user.items.mounts) {
|
||||
const mounts = user.items.mounts;
|
||||
if (mounts['Wolf-Skeleton']
|
||||
&& mounts['TigerCub-Skeleton']
|
||||
&& mounts['PandaCub-Skeleton']
|
||||
&& mounts['LionCub-Skeleton']
|
||||
&& mounts['Fox-Skeleton']
|
||||
&& mounts['FlyingPig-Skeleton']
|
||||
&& mounts['Dragon-Skeleton']
|
||||
&& mounts['Cactus-Skeleton']
|
||||
&& mounts['BearCub-Skeleton'] ) {
|
||||
set['achievements.skeletonCrew'] = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
|
||||
|
||||
return await User.update({ _id: user._id }, { $set: set }).exec();
|
||||
}
|
||||
|
||||
module.exports = async function processUsers () {
|
||||
let query = {
|
||||
migration: { $ne: MIGRATION_NAME },
|
||||
'auth.timestamps.loggedin': { $gt: new Date('2020-10-01') },
|
||||
};
|
||||
|
||||
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]._id,
|
||||
};
|
||||
}
|
||||
|
||||
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
|
||||
}
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "habitica",
|
||||
"version": "4.161.3",
|
||||
"version": "4.166.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@@ -1191,9 +1191,9 @@
|
||||
}
|
||||
},
|
||||
"@sinonjs/samsam": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-5.1.0.tgz",
|
||||
"integrity": "sha512-42nyaQOVunX5Pm6GRJobmzbS7iLI+fhERITnETXzzwDZh+TtDr/Au3yAvXVjFmZ4wEUaE4Y3NFZfKv0bV0cbtg==",
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-5.2.0.tgz",
|
||||
"integrity": "sha512-CaIcyX5cDsjcW/ab7HposFWzV1kC++4HNsfnEdFJa7cP1QIuILAKV+BgfeqRXhcnSAc76r/Rh/O5C+300BwUIw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@sinonjs/commons": "^1.6.0",
|
||||
@@ -8362,9 +8362,9 @@
|
||||
"integrity": "sha1-h/zPrv/AtozRnVX2cilD+SnqNeo="
|
||||
},
|
||||
"just-extend": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.1.0.tgz",
|
||||
"integrity": "sha512-ApcjaOdVTJ7y4r08xI5wIqpvwS48Q0PBG4DJROcEkH1f8MdAiNFyFxz3xoL0LWAVwjrwPYZdVHHxhRHcx/uGLA==",
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.1.1.tgz",
|
||||
"integrity": "sha512-aWgeGFW67BP3e5181Ep1Fv2v8z//iBJfrvyTnq8wG86vEESwmonn1zPBJ0VfmT9CJq2FIT0VsETtrNFm2a+SHA==",
|
||||
"dev": true
|
||||
},
|
||||
"jwa": {
|
||||
@@ -9346,9 +9346,9 @@
|
||||
"integrity": "sha1-EUyUlnPiqKNenTV4hSeqN7Z52is="
|
||||
},
|
||||
"moment": {
|
||||
"version": "2.29.0",
|
||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.0.tgz",
|
||||
"integrity": "sha512-z6IJ5HXYiuxvFTI6eiQ9dm77uE0gyy1yXNApVHqTcnIKfY9tIwEjlzsZ6u1LQXvVgKeTnv9Xm7NDvJ7lso3MtA=="
|
||||
"version": "2.29.1",
|
||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz",
|
||||
"integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ=="
|
||||
},
|
||||
"moment-recur": {
|
||||
"version": "1.0.7",
|
||||
@@ -9446,13 +9446,13 @@
|
||||
}
|
||||
},
|
||||
"mongoose": {
|
||||
"version": "5.10.3",
|
||||
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.10.3.tgz",
|
||||
"integrity": "sha512-FLemltuzcsCHlFpEZ3bYOiNhJfHful+GoS+3uRgdEWGlY0HKfOjm9xsISM/tql8vRvhjr7qveuRfoBBGO3xNtw==",
|
||||
"version": "5.10.9",
|
||||
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.10.9.tgz",
|
||||
"integrity": "sha512-7dkr1d6Uyk87hELzoc6B7Zo7kkPTx8rKummk51Y0je2V2Ttsw0KFPwTp1G8JIbBta7Wpw8j15PJi0d33Ode2nw==",
|
||||
"requires": {
|
||||
"bson": "^1.1.4",
|
||||
"kareem": "2.3.1",
|
||||
"mongodb": "3.6.1",
|
||||
"mongodb": "3.6.2",
|
||||
"mongoose-legacy-pluralize": "1.0.2",
|
||||
"mpath": "0.7.0",
|
||||
"mquery": "3.2.2",
|
||||
@@ -9473,11 +9473,11 @@
|
||||
}
|
||||
},
|
||||
"mongodb": {
|
||||
"version": "3.6.1",
|
||||
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.6.1.tgz",
|
||||
"integrity": "sha512-uH76Zzr5wPptnjEKJRQnwTsomtFOU/kQEU8a9hKHr2M7y9qVk7Q4Pkv0EQVp88742z9+RwvsdTw6dRjDZCNu1g==",
|
||||
"version": "3.6.2",
|
||||
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.6.2.tgz",
|
||||
"integrity": "sha512-sSZOb04w3HcnrrXC82NEh/YGCmBuRgR+C1hZgmmv4L6dBz4BkRse6Y8/q/neXer9i95fKUBbFi4KgeceXmbsOA==",
|
||||
"requires": {
|
||||
"bl": "^2.2.0",
|
||||
"bl": "^2.2.1",
|
||||
"bson": "^1.1.4",
|
||||
"denque": "^1.4.1",
|
||||
"require_optional": "^1.0.1",
|
||||
@@ -9901,9 +9901,9 @@
|
||||
"integrity": "sha512-0L9FvHG3nfnnmaEQPjT9xhfN4ISk0A8/2j4M37Np4mcDesJjHgEUfgPhdCyZuFI954tjokaIj/A3NdpFNdEh4Q=="
|
||||
},
|
||||
"node-abi": {
|
||||
"version": "2.18.0",
|
||||
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.18.0.tgz",
|
||||
"integrity": "sha512-yi05ZoiuNNEbyT/xXfSySZE+yVnQW6fxPZuFbLyS1s6b5Kw3HzV2PHOM4XR+nsjzkHxByK+2Wg+yCQbe35l8dw==",
|
||||
"version": "2.19.1",
|
||||
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.19.1.tgz",
|
||||
"integrity": "sha512-HbtmIuByq44yhAzK7b9j/FelKlHYISKQn0mtvcBrU5QBkhoCMp5bu8Hv5AI34DcKfOAcJBcOEMwLlwO62FFu9A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"semver": "^5.4.1"
|
||||
@@ -12073,9 +12073,9 @@
|
||||
"integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA=="
|
||||
},
|
||||
"simple-concat": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.0.tgz",
|
||||
"integrity": "sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY=",
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",
|
||||
"integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==",
|
||||
"dev": true
|
||||
},
|
||||
"simple-get": {
|
||||
@@ -12115,15 +12115,15 @@
|
||||
}
|
||||
},
|
||||
"sinon": {
|
||||
"version": "9.0.3",
|
||||
"resolved": "https://registry.npmjs.org/sinon/-/sinon-9.0.3.tgz",
|
||||
"integrity": "sha512-IKo9MIM111+smz9JGwLmw5U1075n1YXeAq8YeSFlndCLhAL5KGn6bLgu7b/4AYHTV/LcEMcRm2wU2YiL55/6Pg==",
|
||||
"version": "9.2.0",
|
||||
"resolved": "https://registry.npmjs.org/sinon/-/sinon-9.2.0.tgz",
|
||||
"integrity": "sha512-eSNXz1XMcGEMHw08NJXSyTHIu6qTCOiN8x9ODACmZpNQpr0aXTBXBnI4xTzQzR+TEpOmLiKowGf9flCuKIzsbw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@sinonjs/commons": "^1.7.2",
|
||||
"@sinonjs/commons": "^1.8.1",
|
||||
"@sinonjs/fake-timers": "^6.0.1",
|
||||
"@sinonjs/formatio": "^5.0.1",
|
||||
"@sinonjs/samsam": "^5.1.0",
|
||||
"@sinonjs/samsam": "^5.2.0",
|
||||
"diff": "^4.0.2",
|
||||
"nise": "^4.0.4",
|
||||
"supports-color": "^7.1.0"
|
||||
@@ -12142,9 +12142,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
|
||||
"integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==",
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"has-flag": "^4.0.0"
|
||||
@@ -12992,12 +12992,12 @@
|
||||
}
|
||||
},
|
||||
"tar-stream": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.1.3.tgz",
|
||||
"integrity": "sha512-Z9yri56Dih8IaK8gncVPx4Wqt86NDmQTSh49XLZgjWpGZL9GK9HKParS2scqHCC4w6X9Gh2jwaU45V47XTKwVA==",
|
||||
"version": "2.1.4",
|
||||
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.1.4.tgz",
|
||||
"integrity": "sha512-o3pS2zlG4gxr67GmFYBLlq+dM8gyRGUOvsrHclSkvtVtQbjV0s/+ZE8OpICbaj8clrX3tjeHngYGP7rweaBnuw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"bl": "^4.0.1",
|
||||
"bl": "^4.0.3",
|
||||
"end-of-stream": "^1.4.1",
|
||||
"fs-constants": "^1.0.0",
|
||||
"inherits": "^2.0.3",
|
||||
@@ -13840,9 +13840,9 @@
|
||||
"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
|
||||
},
|
||||
"uuid": {
|
||||
"version": "8.3.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.0.tgz",
|
||||
"integrity": "sha512-fX6Z5o4m6XsXBdli9g7DtWgAx+osMsRRZFKma1mIUsLCz6vRvv+pz5VNbyu9UEDzpMWulZfvpgb/cmDXVulYFQ=="
|
||||
"version": "8.3.1",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.1.tgz",
|
||||
"integrity": "sha512-FOmRr+FmWEIG8uhZv6C2bTgEVXsHk08kE7mPlrBbEe+c3r9pjceVPgupIfNIhc4yx55H69OXANrUaSuu9eInKg=="
|
||||
},
|
||||
"v8-compile-cache": {
|
||||
"version": "2.1.1",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "habitica",
|
||||
"description": "A habit tracker app which treats your goals like a Role Playing Game.",
|
||||
"version": "4.161.3",
|
||||
"version": "4.166.0",
|
||||
"main": "./website/server/index.js",
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.11.6",
|
||||
@@ -46,9 +46,9 @@
|
||||
"lodash": "^4.17.20",
|
||||
"merge-stream": "^2.0.0",
|
||||
"method-override": "^3.0.0",
|
||||
"moment": "^2.29.0",
|
||||
"moment": "^2.29.1",
|
||||
"moment-recur": "^1.0.7",
|
||||
"mongoose": "^5.10.3",
|
||||
"mongoose": "^5.10.9",
|
||||
"morgan": "^1.10.0",
|
||||
"nconf": "^0.10.0",
|
||||
"node-gcm": "^1.0.3",
|
||||
@@ -70,7 +70,7 @@
|
||||
"superagent": "^6.1.0",
|
||||
"universal-analytics": "^0.4.23",
|
||||
"useragent": "^2.1.9",
|
||||
"uuid": "^8.3.0",
|
||||
"uuid": "^8.3.1",
|
||||
"validator": "^13.1.17",
|
||||
"vinyl-buffer": "^1.0.1",
|
||||
"winston": "^3.3.3",
|
||||
@@ -121,7 +121,7 @@
|
||||
"monk": "^7.3.2",
|
||||
"require-again": "^2.0.0",
|
||||
"run-rs": "^0.6.2",
|
||||
"sinon": "^9.0.3",
|
||||
"sinon": "^9.2.0",
|
||||
"sinon-chai": "^3.5.0",
|
||||
"sinon-stub-promise": "^4.0.0"
|
||||
},
|
||||
|
||||
@@ -248,6 +248,7 @@ describe('payments/index', () => {
|
||||
quantity: 1,
|
||||
gift: true,
|
||||
purchaseValue: 15,
|
||||
firstPurchase: true,
|
||||
headers: {
|
||||
'x-client': 'habitica-web',
|
||||
'user-agent': '',
|
||||
@@ -345,6 +346,7 @@ describe('payments/index', () => {
|
||||
quantity: 1,
|
||||
gift: false,
|
||||
purchaseValue: 15,
|
||||
firstPurchase: true,
|
||||
headers: {
|
||||
'x-client': 'habitica-web',
|
||||
'user-agent': '',
|
||||
@@ -421,10 +423,22 @@ describe('payments/index', () => {
|
||||
});
|
||||
|
||||
context('Mystery Items', () => {
|
||||
it('awards mystery items when within the timeframe for a mystery item', async () => {
|
||||
const mayMysteryItemTimeframe = 1464725113000; // May 31st 2016
|
||||
const fakeClock = sinon.useFakeTimers(mayMysteryItemTimeframe);
|
||||
let clock;
|
||||
const mayMysteryItem = 'armor_mystery_201605';
|
||||
|
||||
beforeEach(() => {
|
||||
const mayMysteryItemTimeframe = new Date(2016, 4, 31); // May 31st 2016
|
||||
clock = sinon.useFakeTimers({
|
||||
now: mayMysteryItemTimeframe,
|
||||
toFake: ['Date'],
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
if (clock) clock.restore();
|
||||
});
|
||||
|
||||
it('awards mystery items when within the timeframe for a mystery item', async () => {
|
||||
data = { paymentMethod: 'PaymentMethod', user, sub: { key: 'basic_3mo' } };
|
||||
|
||||
const oldNotificationsCount = user.notifications.length;
|
||||
@@ -437,14 +451,9 @@ describe('payments/index', () => {
|
||||
expect(user.purchased.plan.mysteryItems).to.include('head_mystery_201605');
|
||||
expect(user.notifications.length).to.equal(oldNotificationsCount + 1);
|
||||
expect(user.notifications[0].type).to.equal('NEW_MYSTERY_ITEMS');
|
||||
|
||||
fakeClock.restore();
|
||||
});
|
||||
|
||||
it('does not award mystery item when user already owns the item', async () => {
|
||||
const mayMysteryItemTimeframe = 1464725113000; // May 31st 2016
|
||||
const fakeClock = sinon.useFakeTimers(mayMysteryItemTimeframe);
|
||||
const mayMysteryItem = 'armor_mystery_201605';
|
||||
user.items.gear.owned[mayMysteryItem] = true;
|
||||
|
||||
data = { paymentMethod: 'PaymentMethod', user, sub: { key: 'basic_3mo' } };
|
||||
@@ -453,14 +462,9 @@ describe('payments/index', () => {
|
||||
|
||||
expect(user.purchased.plan.mysteryItems).to.have.a.lengthOf(1);
|
||||
expect(user.purchased.plan.mysteryItems).to.include('head_mystery_201605');
|
||||
|
||||
fakeClock.restore();
|
||||
});
|
||||
|
||||
it('does not award mystery item when user already has the item in the mystery box', async () => {
|
||||
const mayMysteryItemTimeframe = 1464725113000; // May 31st 2016
|
||||
const fakeClock = sinon.useFakeTimers(mayMysteryItemTimeframe);
|
||||
const mayMysteryItem = 'armor_mystery_201605';
|
||||
user.purchased.plan.mysteryItems = [mayMysteryItem];
|
||||
|
||||
sandbox.spy(user.purchased.plan.mysteryItems, 'push');
|
||||
@@ -470,8 +474,6 @@ describe('payments/index', () => {
|
||||
|
||||
expect(user.purchased.plan.mysteryItems.push).to.be.calledOnce;
|
||||
expect(user.purchased.plan.mysteryItems.push).to.be.calledWith('head_mystery_201605');
|
||||
|
||||
fakeClock.restore();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -293,4 +293,107 @@ describe('cron middleware', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
context('Drop Cap A/B Test', async () => {
|
||||
it('enrolls web users', async () => {
|
||||
user.lastCron = moment(new Date()).subtract({ days: 2 });
|
||||
await user.save();
|
||||
req.headers['x-client'] = 'habitica-web';
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
cronMiddleware(req, res, async err => {
|
||||
if (err) return reject(err);
|
||||
user = await User.findById(user._id).exec();
|
||||
expect(user._ABtests.dropCapNotif).to.be.a.string;
|
||||
|
||||
return resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('does not enroll 50% of users', async () => {
|
||||
sandbox.stub(Math, 'random').returns(0.6);
|
||||
user.lastCron = moment(new Date()).subtract({ days: 2 });
|
||||
await user.save();
|
||||
req.headers['x-client'] = 'habitica-web';
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
cronMiddleware(req, res, async err => {
|
||||
if (err) return reject(err);
|
||||
user = await User.findById(user._id).exec();
|
||||
expect(user._ABtests.dropCapNotif).to.be.equal('drop-cap-notif-not-enrolled');
|
||||
|
||||
return resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('enables the new notification for 25% of users', async () => {
|
||||
sandbox.stub(Math, 'random').returns(0.25);
|
||||
user.lastCron = moment(new Date()).subtract({ days: 2 });
|
||||
await user.save();
|
||||
req.headers['x-client'] = 'habitica-web';
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
cronMiddleware(req, res, async err => {
|
||||
if (err) return reject(err);
|
||||
user = await User.findById(user._id).exec();
|
||||
expect(user._ABtests.dropCapNotif).to.be.equal('drop-cap-notif-enabled');
|
||||
|
||||
return resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('disables the new notification for 25% of users', async () => {
|
||||
sandbox.stub(Math, 'random').returns(0.5);
|
||||
user.lastCron = moment(new Date()).subtract({ days: 2 });
|
||||
await user.save();
|
||||
req.headers['x-client'] = 'habitica-web';
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
cronMiddleware(req, res, async err => {
|
||||
if (err) return reject(err);
|
||||
user = await User.findById(user._id).exec();
|
||||
expect(user._ABtests.dropCapNotif).to.be.equal('drop-cap-notif-disabled');
|
||||
|
||||
return resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('does not affect subscribers', async () => {
|
||||
sandbox.stub(Math, 'random').returns(0.2);
|
||||
user.lastCron = moment(new Date()).subtract({ days: 2 });
|
||||
await user.save();
|
||||
req.headers['x-client'] = 'habitica-web';
|
||||
sandbox.stub(User.prototype, 'isSubscribed').returns(true);
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
cronMiddleware(req, res, async err => {
|
||||
if (err) return reject(err);
|
||||
user = await User.findById(user._id).exec();
|
||||
expect(user._ABtests.dropCapNotif).to.not.exist;
|
||||
|
||||
return resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('does not affect mobile users', async () => {
|
||||
user.lastCron = moment(new Date()).subtract({ days: 2 });
|
||||
await user.save();
|
||||
req.headers['x-client'] = 'habitica-ios';
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
cronMiddleware(req, res, async err => {
|
||||
if (err) return reject(err);
|
||||
user = await User.findById(user._id).exec();
|
||||
expect(user._ABtests.dropCapNotif).to.not.exist;
|
||||
|
||||
return resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
generateNext,
|
||||
} from '../../../helpers/api-unit.helper';
|
||||
import i18n from '../../../../website/common/script/i18n';
|
||||
import { ensureAdmin, ensureSudo } from '../../../../website/server/middlewares/ensureAccessRight';
|
||||
import { ensureAdmin, ensureSudo, ensureNewsPoster } from '../../../../website/server/middlewares/ensureAccessRight';
|
||||
import { NotAuthorized } from '../../../../website/server/libs/errors';
|
||||
import apiError from '../../../../website/server/libs/apiError';
|
||||
|
||||
@@ -40,6 +40,27 @@ describe('ensure access middlewares', () => {
|
||||
});
|
||||
});
|
||||
|
||||
context('ensure newsPoster', () => {
|
||||
it('returns not authorized when user is not a newsPoster', () => {
|
||||
res.locals = { user: { contributor: { newsPoster: false } } };
|
||||
|
||||
ensureNewsPoster(req, res, next);
|
||||
|
||||
const calledWith = next.getCall(0).args;
|
||||
expect(calledWith[0].message).to.equal(apiError('noNewsPosterAccess'));
|
||||
expect(calledWith[0] instanceof NotAuthorized).to.equal(true);
|
||||
});
|
||||
|
||||
it('passes when user is a newsPoster', () => {
|
||||
res.locals = { user: { contributor: { newsPoster: true } } };
|
||||
|
||||
ensureNewsPoster(req, res, next);
|
||||
|
||||
expect(next).to.be.calledOnce;
|
||||
expect(next.args[0]).to.be.empty;
|
||||
});
|
||||
});
|
||||
|
||||
context('ensure sudo', () => {
|
||||
it('returns not authorized when user is not a sudo user', () => {
|
||||
res.locals = { user: { contributor: { sudo: false } } };
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
import { v4 } from 'uuid';
|
||||
import { model as NewsPost, refreshNewsPost } from '../../../../website/server/models/newsPost';
|
||||
import { sleep } from '../../../helpers/api-unit.helper';
|
||||
|
||||
describe('NewsPost Model', () => {
|
||||
const publishDate = Number(new Date());
|
||||
|
||||
// NOTE publishDate is manually increased by +500 for each test
|
||||
// to make sure it's always in the future from the previous one
|
||||
// bevause NewsPost.lastNewsPost() is not reset between tests.
|
||||
// And without a more recent publishDate it wouldn't update
|
||||
|
||||
it('#lastNewsPost', () => {
|
||||
const lastPost = { _id: v4(), publishDate, published: true };
|
||||
NewsPost.updateLastNewsPost(lastPost);
|
||||
expect(NewsPost.lastNewsPost()).to.equal(lastPost);
|
||||
});
|
||||
|
||||
it('#getLastPostFromDatabase', async () => {
|
||||
const expectedId = v4();
|
||||
|
||||
await NewsPost.create([
|
||||
// more recent but not published
|
||||
{
|
||||
_id: v4(),
|
||||
publishDate: new Date(publishDate + 50),
|
||||
author: v4(),
|
||||
published: false,
|
||||
title: 'Title',
|
||||
credits: 'credits',
|
||||
text: 'text',
|
||||
},
|
||||
// expected
|
||||
{
|
||||
_id: expectedId,
|
||||
publishDate,
|
||||
author: v4(),
|
||||
published: true,
|
||||
title: 'Title',
|
||||
credits: 'credits',
|
||||
text: 'text',
|
||||
},
|
||||
// published but less recent
|
||||
{
|
||||
_id: v4(),
|
||||
publishDate: new Date(Number(publishDate) - 50),
|
||||
author: v4(),
|
||||
published: true,
|
||||
title: 'Title',
|
||||
credits: 'credits',
|
||||
text: 'text',
|
||||
},
|
||||
]);
|
||||
|
||||
const fetched = await NewsPost.getLastPostFromDatabase();
|
||||
expect(fetched._id).to.equal(expectedId);
|
||||
});
|
||||
|
||||
context('#updateLastNewsPost', () => {
|
||||
it('updates the post if new one is more recent and published', () => {
|
||||
const previousPost = {
|
||||
_id: v4(),
|
||||
publishDate: new Date(publishDate + 100),
|
||||
published: true,
|
||||
};
|
||||
NewsPost.updateLastNewsPost(previousPost);
|
||||
const newPost = {
|
||||
_id: v4(),
|
||||
publishDate: new Date(publishDate + 150),
|
||||
published: true,
|
||||
};
|
||||
NewsPost.updateLastNewsPost(newPost);
|
||||
expect(NewsPost.lastNewsPost()._id).to.equal(newPost._id);
|
||||
});
|
||||
|
||||
it('does not update the post if new one is from the past', () => {
|
||||
const previousPost = new NewsPost({
|
||||
_id: v4(), publishDate: new Date(publishDate + 200), published: true,
|
||||
});
|
||||
NewsPost.updateLastNewsPost(previousPost);
|
||||
const newPost = new NewsPost({
|
||||
_id: v4(), publishDate: new Date(publishDate + 175), published: true,
|
||||
});
|
||||
NewsPost.updateLastNewsPost(newPost);
|
||||
expect(NewsPost.lastNewsPost()._id).to.equal(previousPost._id);
|
||||
});
|
||||
|
||||
it('does not update the post if new one is not published', () => {
|
||||
const previousPost = new NewsPost({
|
||||
_id: v4(), publishDate: new Date(publishDate + 250), published: true,
|
||||
});
|
||||
NewsPost.updateLastNewsPost(previousPost);
|
||||
const newPost = new NewsPost({
|
||||
_id: v4(), publishDate: new Date(publishDate + 300), published: false,
|
||||
});
|
||||
NewsPost.updateLastNewsPost(newPost);
|
||||
expect(NewsPost.lastNewsPost()._id).to.equal(previousPost._id);
|
||||
});
|
||||
});
|
||||
|
||||
context('refreshes NewsPost', () => {
|
||||
let intervalId;
|
||||
|
||||
beforeEach(async () => {
|
||||
// Delete all existing posts from the database
|
||||
await NewsPost.remove();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
if (intervalId) clearInterval(intervalId);
|
||||
});
|
||||
|
||||
it('refreshes the last post at a specific interval', async () => {
|
||||
await sleep(0.1); // wait 100ms to make sure all previous posts are in the past
|
||||
const previousPost = {
|
||||
_id: v4(), publishDate: new Date(), published: true,
|
||||
};
|
||||
NewsPost.updateLastNewsPost(previousPost);
|
||||
intervalId = refreshNewsPost(50); // refreshes every 50ms
|
||||
|
||||
await sleep(0.1); // wait 100ms to make sure the new post has a more recent publishDate
|
||||
const newPost = await NewsPost.create({
|
||||
_id: v4(),
|
||||
publishDate: new Date(),
|
||||
author: v4(),
|
||||
published: true,
|
||||
title: 'Title',
|
||||
credits: 'credits',
|
||||
text: 'text',
|
||||
});
|
||||
|
||||
expect(NewsPost.lastNewsPost()._id).to.equal(previousPost._id);
|
||||
await sleep(0.15); // wait 150ms
|
||||
|
||||
expect(NewsPost.lastNewsPost()._id).to.equal(newPost._id);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,87 +1,90 @@
|
||||
import moment from 'moment';
|
||||
import { model as User } from '../../../../website/server/models/user';
|
||||
import { model as NewsPost } from '../../../../website/server/models/newsPost';
|
||||
import { model as Group } from '../../../../website/server/models/group';
|
||||
import common from '../../../../website/common';
|
||||
|
||||
describe('User Model', () => {
|
||||
it('keeps user._tmp when calling .toJSON', () => {
|
||||
const user = new User({
|
||||
auth: {
|
||||
local: {
|
||||
username: 'username',
|
||||
lowerCaseUsername: 'username',
|
||||
email: 'email@email.email',
|
||||
salt: 'salt',
|
||||
hashed_password: 'hashed_password', // eslint-disable-line camelcase
|
||||
describe('.toJSON()', () => {
|
||||
it('keeps user._tmp when calling .toJSON', () => {
|
||||
const user = new User({
|
||||
auth: {
|
||||
local: {
|
||||
username: 'username',
|
||||
lowerCaseUsername: 'username',
|
||||
email: 'email@email.email',
|
||||
salt: 'salt',
|
||||
hashed_password: 'hashed_password', // eslint-disable-line camelcase
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
user._tmp = { ok: true };
|
||||
user._nonTmp = { ok: true };
|
||||
|
||||
expect(user._tmp).to.eql({ ok: true });
|
||||
expect(user._nonTmp).to.eql({ ok: true });
|
||||
|
||||
const toObject = user.toObject();
|
||||
const toJSON = user.toJSON();
|
||||
|
||||
expect(toObject).to.not.have.keys('_tmp');
|
||||
expect(toObject).to.not.have.keys('_nonTmp');
|
||||
|
||||
expect(toJSON).to.have.any.key('_tmp');
|
||||
expect(toJSON._tmp).to.eql({ ok: true });
|
||||
expect(toJSON).to.not.have.keys('_nonTmp');
|
||||
});
|
||||
|
||||
user._tmp = { ok: true };
|
||||
user._nonTmp = { ok: true };
|
||||
it('can add computed stats to a JSONified user object', () => {
|
||||
const user = new User();
|
||||
const userToJSON = user.toJSON();
|
||||
|
||||
expect(user._tmp).to.eql({ ok: true });
|
||||
expect(user._nonTmp).to.eql({ ok: true });
|
||||
expect(userToJSON.stats.maxMP).to.not.exist;
|
||||
expect(userToJSON.stats.maxHealth).to.not.exist;
|
||||
expect(userToJSON.stats.toNextLevel).to.not.exist;
|
||||
|
||||
const toObject = user.toObject();
|
||||
const toJSON = user.toJSON();
|
||||
User.addComputedStatsToJSONObj(userToJSON.stats, userToJSON);
|
||||
|
||||
expect(toObject).to.not.have.keys('_tmp');
|
||||
expect(toObject).to.not.have.keys('_nonTmp');
|
||||
expect(userToJSON.stats.maxMP).to.exist;
|
||||
expect(userToJSON.stats.maxHealth).to.equal(common.maxHealth);
|
||||
expect(userToJSON.stats.toNextLevel).to.equal(common.tnl(user.stats.lvl));
|
||||
});
|
||||
|
||||
expect(toJSON).to.have.any.key('_tmp');
|
||||
expect(toJSON._tmp).to.eql({ ok: true });
|
||||
expect(toJSON).to.not.have.keys('_nonTmp');
|
||||
});
|
||||
it('can transform user object without mongoose helpers', async () => {
|
||||
const user = new User();
|
||||
await user.save();
|
||||
const userToJSON = await User.findById(user._id).lean().exec();
|
||||
|
||||
it('can add computed stats to a JSONified user object', () => {
|
||||
const user = new User();
|
||||
const userToJSON = user.toJSON();
|
||||
expect(userToJSON.stats.maxMP).to.not.exist;
|
||||
expect(userToJSON.stats.maxHealth).to.not.exist;
|
||||
expect(userToJSON.stats.toNextLevel).to.not.exist;
|
||||
expect(userToJSON.id).to.not.exist;
|
||||
|
||||
expect(userToJSON.stats.maxMP).to.not.exist;
|
||||
expect(userToJSON.stats.maxHealth).to.not.exist;
|
||||
expect(userToJSON.stats.toNextLevel).to.not.exist;
|
||||
User.transformJSONUser(userToJSON);
|
||||
|
||||
User.addComputedStatsToJSONObj(userToJSON.stats, userToJSON);
|
||||
expect(userToJSON.id).to.equal(userToJSON._id);
|
||||
expect(userToJSON.stats.maxMP).to.not.exist;
|
||||
expect(userToJSON.stats.maxHealth).to.not.exist;
|
||||
expect(userToJSON.stats.toNextLevel).to.not.exist;
|
||||
});
|
||||
|
||||
expect(userToJSON.stats.maxMP).to.exist;
|
||||
expect(userToJSON.stats.maxHealth).to.equal(common.maxHealth);
|
||||
expect(userToJSON.stats.toNextLevel).to.equal(common.tnl(user.stats.lvl));
|
||||
});
|
||||
it('can transform user object without mongoose helpers (including computed stats)', async () => {
|
||||
const user = new User();
|
||||
await user.save();
|
||||
const userToJSON = await User.findById(user._id).lean().exec();
|
||||
|
||||
it('can transform user object without mongoose helpers', async () => {
|
||||
const user = new User();
|
||||
await user.save();
|
||||
const userToJSON = await User.findById(user._id).lean().exec();
|
||||
expect(userToJSON.stats.maxMP).to.not.exist;
|
||||
expect(userToJSON.stats.maxHealth).to.not.exist;
|
||||
expect(userToJSON.stats.toNextLevel).to.not.exist;
|
||||
|
||||
expect(userToJSON.stats.maxMP).to.not.exist;
|
||||
expect(userToJSON.stats.maxHealth).to.not.exist;
|
||||
expect(userToJSON.stats.toNextLevel).to.not.exist;
|
||||
expect(userToJSON.id).to.not.exist;
|
||||
User.transformJSONUser(userToJSON, true);
|
||||
|
||||
User.transformJSONUser(userToJSON);
|
||||
|
||||
expect(userToJSON.id).to.equal(userToJSON._id);
|
||||
expect(userToJSON.stats.maxMP).to.not.exist;
|
||||
expect(userToJSON.stats.maxHealth).to.not.exist;
|
||||
expect(userToJSON.stats.toNextLevel).to.not.exist;
|
||||
});
|
||||
|
||||
it('can transform user object without mongoose helpers (including computed stats)', async () => {
|
||||
const user = new User();
|
||||
await user.save();
|
||||
const userToJSON = await User.findById(user._id).lean().exec();
|
||||
|
||||
expect(userToJSON.stats.maxMP).to.not.exist;
|
||||
expect(userToJSON.stats.maxHealth).to.not.exist;
|
||||
expect(userToJSON.stats.toNextLevel).to.not.exist;
|
||||
|
||||
User.transformJSONUser(userToJSON, true);
|
||||
|
||||
expect(userToJSON.id).to.equal(userToJSON._id);
|
||||
expect(userToJSON.stats.maxMP).to.exist;
|
||||
expect(userToJSON.stats.maxHealth).to.equal(common.maxHealth);
|
||||
expect(userToJSON.stats.toNextLevel).to.equal(common.tnl(user.stats.lvl));
|
||||
expect(userToJSON.id).to.equal(userToJSON._id);
|
||||
expect(userToJSON.stats.maxMP).to.exist;
|
||||
expect(userToJSON.stats.maxHealth).to.equal(common.maxHealth);
|
||||
expect(userToJSON.stats.toNextLevel).to.equal(common.tnl(user.stats.lvl));
|
||||
});
|
||||
});
|
||||
|
||||
context('achievements', () => {
|
||||
@@ -589,6 +592,50 @@ describe('User Model', () => {
|
||||
});
|
||||
|
||||
context('pre-save hook', () => {
|
||||
it('enrolls users that signup through web in the Drop Cap AB test', async () => {
|
||||
let user = new User();
|
||||
user.registeredThrough = 'habitica-web';
|
||||
user = await user.save();
|
||||
expect(user._ABtests.dropCapNotif).to.exist;
|
||||
});
|
||||
|
||||
it('does not enroll users that signup through modal in the Drop Cap AB test', async () => {
|
||||
let user = new User();
|
||||
user.registeredThrough = 'habitica-ios';
|
||||
user = await user.save();
|
||||
expect(user._ABtests.dropCapNotif).to.not.exist;
|
||||
});
|
||||
|
||||
it('marks the last news post as read for new users', async () => {
|
||||
const lastNewsPost = { _id: '1' };
|
||||
sandbox.stub(NewsPost, 'lastNewsPost').returns(lastNewsPost);
|
||||
|
||||
let user = new User();
|
||||
expect(user.isNew).to.equal(true);
|
||||
user = await user.save();
|
||||
|
||||
expect(user.checkNewStuff()).to.equal(false);
|
||||
expect(user.toJSON().flags.newStuff).to.equal(false);
|
||||
expect(user.flags.lastNewStuffRead).to.equal(lastNewsPost._id);
|
||||
});
|
||||
|
||||
it('does not mark the last news post as read for existing users', async () => {
|
||||
const lastNewsPost = { _id: '1' };
|
||||
const lastNewsPostStub = sandbox.stub(NewsPost, 'lastNewsPost');
|
||||
lastNewsPostStub.returns(lastNewsPost);
|
||||
|
||||
let user = new User();
|
||||
user = await user.save();
|
||||
|
||||
expect(user.isNew).to.equal(false);
|
||||
user.profile.name = 'new name';
|
||||
|
||||
lastNewsPostStub.returns({ _id: '2' });
|
||||
user = await user.save();
|
||||
|
||||
expect(user.flags.lastNewStuffRead).to.equal(lastNewsPost._id); // not _id: 2
|
||||
});
|
||||
|
||||
it('does not try to award achievements when achievements or items not selected in query', async () => {
|
||||
let user = new User();
|
||||
user = await user.save(); // necessary for user.isSelected to work correctly
|
||||
@@ -827,4 +874,46 @@ describe('User Model', () => {
|
||||
expect(daysMissed).to.eql(0);
|
||||
});
|
||||
});
|
||||
|
||||
it('isNewsPoster', async () => {
|
||||
const user = new User();
|
||||
await user.save();
|
||||
|
||||
expect(user.isNewsPoster()).to.equal(false);
|
||||
|
||||
user.contributor.newsPoster = true;
|
||||
expect(user.isNewsPoster()).to.equal(true);
|
||||
});
|
||||
|
||||
describe('checkNewStuff', () => {
|
||||
let user;
|
||||
|
||||
beforeEach(() => {
|
||||
user = new User();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
it('no last news post', () => {
|
||||
sandbox.stub(NewsPost, 'lastNewsPost').returns(null);
|
||||
expect(user.checkNewStuff()).to.equal(false);
|
||||
expect(user.toJSON().flags.newStuff).to.equal(false);
|
||||
});
|
||||
|
||||
it('last news post read', () => {
|
||||
sandbox.stub(NewsPost, 'lastNewsPost').returns({ _id: '123' });
|
||||
user.flags.lastNewStuffRead = '123';
|
||||
expect(user.checkNewStuff()).to.equal(false);
|
||||
expect(user.toJSON().flags.newStuff).to.equal(false);
|
||||
});
|
||||
|
||||
it('last news post not read', () => {
|
||||
sandbox.stub(NewsPost, 'lastNewsPost').returns({ _id: '123' });
|
||||
user.flags.lastNewStuffRead = '124';
|
||||
expect(user.checkNewStuff()).to.equal(true);
|
||||
expect(user.toJSON().flags.newStuff).to.equal(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -4,7 +4,6 @@ import {
|
||||
|
||||
describe('GET /news', () => {
|
||||
let api;
|
||||
|
||||
beforeEach(async () => {
|
||||
api = requester();
|
||||
});
|
||||
|
||||
@@ -1,24 +1,27 @@
|
||||
import {
|
||||
generateUser,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
import { model as NewsPost } from '../../../../../website/server/models/newsPost';
|
||||
|
||||
describe('POST /news/tell-me-later', () => {
|
||||
let user;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser({
|
||||
'flags.newStuff': true,
|
||||
NewsPost.updateLastNewsPost({
|
||||
_id: '1234', publishDate: new Date(), title: 'Title', published: true,
|
||||
});
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('marks new stuff as read and adds notification', async () => {
|
||||
expect(user.flags.newStuff).to.equal(true);
|
||||
const initialNotifications = user.notifications.length;
|
||||
|
||||
await user.post('/news/tell-me-later');
|
||||
await user.sync();
|
||||
|
||||
expect(user.flags.newStuff).to.equal(false);
|
||||
expect(user.flags.lastNewStuffRead).to.equal('1234');
|
||||
// fetching the user because newStuff is a computed property
|
||||
expect((await user.get('/user')).flags.newStuff).to.equal(false);
|
||||
expect(user.notifications.length).to.equal(initialNotifications + 1);
|
||||
|
||||
const notification = user.notifications[user.notifications.length - 1];
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
generateUser,
|
||||
translate as t,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
import { model as NewsPost } from '../../../../../website/server/models/newsPost';
|
||||
|
||||
describe('PUT /user', () => {
|
||||
let user;
|
||||
@@ -101,6 +102,24 @@ describe('PUT /user', () => {
|
||||
message: t('displaynameIssueNewline'),
|
||||
});
|
||||
});
|
||||
|
||||
it('can set flags.newStuff to false', async () => {
|
||||
NewsPost.updateLastNewsPost({
|
||||
_id: '1234', publishDate: new Date(), title: 'Title', published: true,
|
||||
});
|
||||
|
||||
await user.update({
|
||||
'flags.lastNewStuffRead': '123',
|
||||
});
|
||||
|
||||
await user.put('/user', {
|
||||
'flags.newStuff': false,
|
||||
});
|
||||
|
||||
await user.sync();
|
||||
|
||||
expect(user.flags.lastNewStuffRead).to.eql('1234');
|
||||
});
|
||||
});
|
||||
|
||||
context('Top Level Protected Operations', () => {
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
import { v4 } from 'uuid';
|
||||
import {
|
||||
generateUser,
|
||||
translate as t,
|
||||
} from '../../../helpers/api-integration/v4';
|
||||
|
||||
describe('DELETE /news/:newsID', () => {
|
||||
let user;
|
||||
const newsPost = {
|
||||
title: 'New Post',
|
||||
publishDate: new Date(),
|
||||
published: true,
|
||||
credits: 'credits',
|
||||
text: 'news body',
|
||||
};
|
||||
beforeEach(async () => {
|
||||
user = await generateUser({
|
||||
'contributor.newsPoster': true,
|
||||
});
|
||||
});
|
||||
|
||||
it('disallows access to non-newsPosters', async () => {
|
||||
const nonAdminUser = await generateUser({ 'contributor.newsPoster': false });
|
||||
await expect(nonAdminUser.del(`/news/${v4()}`)).to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: 'You don\'t have news poster access.',
|
||||
});
|
||||
});
|
||||
|
||||
it('returns an error if the post does not exist', async () => {
|
||||
await expect(user.del(`/news/${v4()}`)).to.eventually.be.rejected.and.eql({
|
||||
code: 404,
|
||||
error: 'NotFound',
|
||||
message: t('newsPostNotFound'),
|
||||
});
|
||||
});
|
||||
|
||||
it('deletes news posts', async () => {
|
||||
const existingPost = await user.post('/news', newsPost);
|
||||
await user.del(`/news/${existingPost._id}`);
|
||||
|
||||
const returnedPosts = await user.get('/news');
|
||||
const deletedPost = returnedPosts.find(returnedPost => returnedPost._id === existingPost._id);
|
||||
|
||||
expect(returnedPosts).is.an('array');
|
||||
expect(deletedPost).to.not.exist;
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,50 @@
|
||||
import {
|
||||
requester, generateUser,
|
||||
} from '../../../helpers/api-integration/v4';
|
||||
|
||||
describe('GET /news', () => {
|
||||
let api;
|
||||
const newsPost = {
|
||||
title: 'New Post',
|
||||
publishDate: new Date(),
|
||||
published: true,
|
||||
credits: 'credits',
|
||||
text: 'news body',
|
||||
};
|
||||
|
||||
before(async () => {
|
||||
api = requester();
|
||||
const user = await generateUser({
|
||||
'contributor.newsPoster': true,
|
||||
});
|
||||
|
||||
await Promise.all([
|
||||
user.post('/news', newsPost),
|
||||
user.post('/news', newsPost),
|
||||
user.post('/news', newsPost),
|
||||
user.post('/news', newsPost),
|
||||
user.post('/news', newsPost),
|
||||
user.post('/news', newsPost),
|
||||
user.post('/news', newsPost),
|
||||
user.post('/news', newsPost),
|
||||
user.post('/news', newsPost),
|
||||
user.post('/news', newsPost),
|
||||
user.post('/news', newsPost),
|
||||
user.post('/news', newsPost),
|
||||
]);
|
||||
});
|
||||
|
||||
it('returns the latest news in json format, does not require authentication, 10 per page', async () => {
|
||||
const res = await api.get('/news');
|
||||
expect(res.length).to.be.equal(10);
|
||||
expect(res[0].title).to.be.not.empty;
|
||||
expect(res[0].text).to.be.not.empty;
|
||||
});
|
||||
|
||||
it('supports pagination', async () => {
|
||||
const res = await api.get('/news?page=1');
|
||||
expect(res.length).to.be.equal(2);
|
||||
expect(res[0].title).to.be.not.empty;
|
||||
expect(res[0].text).to.be.not.empty;
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,36 @@
|
||||
import { v4 } from 'uuid';
|
||||
import {
|
||||
generateUser,
|
||||
translate as t,
|
||||
} from '../../../helpers/api-integration/v4';
|
||||
|
||||
describe('GET /news/:newsID', () => {
|
||||
let user;
|
||||
const newsPost = {
|
||||
title: 'New Post',
|
||||
publishDate: new Date(),
|
||||
published: true,
|
||||
credits: 'credits',
|
||||
text: 'news body',
|
||||
};
|
||||
beforeEach(async () => {
|
||||
user = await generateUser({
|
||||
'contributor.newsPoster': true,
|
||||
});
|
||||
});
|
||||
|
||||
it('returns an error if the post does not exist', async () => {
|
||||
await expect(user.get(`/news/${v4()}`)).to.eventually.be.rejected.and.eql({
|
||||
code: 404,
|
||||
error: 'NotFound',
|
||||
message: t('newsPostNotFound'),
|
||||
});
|
||||
});
|
||||
|
||||
it('fetches an existing post', async () => {
|
||||
const existingPost = await user.post('/news', newsPost);
|
||||
const fetchedPost = await user.get(`/news/${existingPost._id}`);
|
||||
|
||||
expect(fetchedPost._id).to.equal(existingPost._id);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,134 @@
|
||||
import moment from 'moment';
|
||||
import {
|
||||
generateUser,
|
||||
sleep,
|
||||
} from '../../../helpers/api-integration/v4';
|
||||
import { model as NewsPost } from '../../../../website/server/models/newsPost';
|
||||
|
||||
describe('POST /news', () => {
|
||||
let user;
|
||||
const newsPost = {
|
||||
title: 'New Post',
|
||||
publishDate: new Date(),
|
||||
published: true,
|
||||
credits: 'credits',
|
||||
text: 'news body',
|
||||
};
|
||||
beforeEach(async () => {
|
||||
user = await generateUser({
|
||||
'contributor.newsPoster': true,
|
||||
});
|
||||
});
|
||||
|
||||
it('disallows access to non-admins', async () => {
|
||||
const nonAdminUser = await generateUser({ 'contributor.newsPoster': false });
|
||||
await expect(nonAdminUser.post('/news')).to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: 'You don\'t have news poster access.',
|
||||
});
|
||||
});
|
||||
|
||||
it('creates news posts', async () => {
|
||||
const response = await user.post('/news', newsPost);
|
||||
|
||||
expect(response.title).to.equal(newsPost.title);
|
||||
expect(response.credits).to.equal(newsPost.credits);
|
||||
expect(response.text).to.equal(newsPost.text);
|
||||
expect(response._id).to.exist;
|
||||
|
||||
const res = await user.get('/news');
|
||||
expect(res[0]._id).to.equal(response._id);
|
||||
expect(res[0].title).to.equal(newsPost.title);
|
||||
expect(res[0].text).to.equal(newsPost.text);
|
||||
});
|
||||
|
||||
context('calls updateLastNewsPost', () => {
|
||||
beforeEach(async () => {
|
||||
await NewsPost.remove({ });
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
newsPost.publishDate = new Date();
|
||||
newsPost.published = true;
|
||||
});
|
||||
|
||||
it('new post is published and the most recent one', async () => {
|
||||
newsPost.publishDate = new Date();
|
||||
const newPost = await user.post('/news', newsPost);
|
||||
await sleep(0.05);
|
||||
expect(NewsPost.lastNewsPost()._id).to.equal(newPost._id);
|
||||
});
|
||||
|
||||
it('new post is not published', async () => {
|
||||
newsPost.published = false;
|
||||
const newPost = await user.post('/news', newsPost);
|
||||
await sleep(0.05);
|
||||
expect(NewsPost.lastNewsPost()._id).to.not.equal(newPost._id);
|
||||
});
|
||||
|
||||
it('new post is published but in the future', async () => {
|
||||
newsPost.publishDate = moment().add({ days: 1 }).toDate();
|
||||
const newPost = await user.post('/news', newsPost);
|
||||
await sleep(0.05);
|
||||
expect(NewsPost.lastNewsPost()._id).to.not.equal(newPost._id);
|
||||
});
|
||||
|
||||
it('new post is published but not the most recent one', async () => {
|
||||
const oldPost = await user.post('/news', newsPost);
|
||||
newsPost.publishDate = moment().subtract({ days: 1 }).toDate();
|
||||
await user.post('/news', newsPost);
|
||||
await sleep(0.05);
|
||||
expect(NewsPost.lastNewsPost()._id).to.equal(oldPost._id);
|
||||
});
|
||||
});
|
||||
|
||||
it('sets default fields', async () => {
|
||||
const response = await user.post('/news', {
|
||||
title: 'A post',
|
||||
credits: 'Credits',
|
||||
text: 'Text',
|
||||
});
|
||||
|
||||
expect(response.published).to.equal(false);
|
||||
expect(response.publishDate).to.exist;
|
||||
expect(response.author).to.equal(user._id);
|
||||
expect(response.createdAt).to.exist;
|
||||
expect(response.updatedAt).to.exist;
|
||||
});
|
||||
|
||||
context('required fields', () => {
|
||||
it('title', async () => {
|
||||
await expect(user.post('/news', {
|
||||
text: 'Text',
|
||||
credits: 'Credits',
|
||||
})).to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: 'NewsPost validation failed',
|
||||
});
|
||||
});
|
||||
|
||||
it('credits', async () => {
|
||||
await expect(user.post('/news', {
|
||||
text: 'Text',
|
||||
title: 'Title',
|
||||
})).to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: 'NewsPost validation failed',
|
||||
});
|
||||
});
|
||||
|
||||
it('text', async () => {
|
||||
await expect(user.post('/news', {
|
||||
credits: 'credits',
|
||||
title: 'Title',
|
||||
})).to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: 'NewsPost validation failed',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,22 @@
|
||||
import {
|
||||
generateUser,
|
||||
} from '../../../helpers/api-integration/v4';
|
||||
import { model as NewsPost } from '../../../../website/server/models/newsPost';
|
||||
|
||||
describe('POST /news/read', () => {
|
||||
let user;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('marks new stuff as read', async () => {
|
||||
NewsPost.updateLastNewsPost({ _id: '1234', publishDate: new Date(), published: true });
|
||||
await user.post('/news/read');
|
||||
await user.sync();
|
||||
|
||||
expect(user.flags.lastNewStuffRead).to.equal('1234');
|
||||
// fetching the user because newStuff is a computed property
|
||||
expect((await user.get('/user')).flags.newStuff).to.equal(false);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,103 @@
|
||||
import { v4 } from 'uuid';
|
||||
import {
|
||||
generateUser,
|
||||
translate as t,
|
||||
sleep,
|
||||
} from '../../../helpers/api-integration/v4';
|
||||
import { model as NewsPost } from '../../../../website/server/models/newsPost';
|
||||
|
||||
describe('PUT /news/:newsID', () => {
|
||||
let user;
|
||||
const newsPost = {
|
||||
title: 'New Post',
|
||||
publishDate: new Date(),
|
||||
published: true,
|
||||
credits: 'credits',
|
||||
text: 'news body',
|
||||
};
|
||||
beforeEach(async () => {
|
||||
user = await generateUser({
|
||||
'contributor.newsPoster': true,
|
||||
});
|
||||
});
|
||||
|
||||
it('disallows access to non-admins', async () => {
|
||||
const nonAdminUser = await generateUser({ 'contributor.newsPoster': false });
|
||||
await expect(nonAdminUser.put('/news/1234')).to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: 'You don\'t have news poster access.',
|
||||
});
|
||||
});
|
||||
|
||||
it('returns an error if the post does not exist', async () => {
|
||||
await expect(user.put(`/news/${v4()}`)).to.eventually.be.rejected.and.eql({
|
||||
code: 404,
|
||||
error: 'NotFound',
|
||||
message: t('newsPostNotFound'),
|
||||
});
|
||||
});
|
||||
|
||||
it('updates existing news posts', async () => {
|
||||
const existingPost = await user.post('/news', newsPost);
|
||||
const updatedPost = await user.put(`/news/${existingPost._id}`, {
|
||||
title: 'Changed Title',
|
||||
});
|
||||
|
||||
expect(updatedPost.title).to.equal('Changed Title');
|
||||
expect(updatedPost.credits).to.equal(existingPost.credits);
|
||||
expect(updatedPost.text).to.equal(existingPost.text);
|
||||
expect(updatedPost.published).to.equal(existingPost.published);
|
||||
expect(updatedPost._id).to.equal(existingPost._id);
|
||||
});
|
||||
|
||||
context('calls updateLastNewsPost', () => {
|
||||
beforeEach(async () => {
|
||||
await NewsPost.remove({ });
|
||||
});
|
||||
|
||||
it('updates post data', async () => {
|
||||
const existingPost = await user.post('/news', { ...newsPost, publishDate: new Date() });
|
||||
const updatedPost = await user.put(`/news/${existingPost._id}`, {
|
||||
title: 'Changed Title',
|
||||
});
|
||||
await sleep(0.05);
|
||||
|
||||
expect(NewsPost.lastNewsPost().title).to.equal(updatedPost.title);
|
||||
});
|
||||
|
||||
it('updated post is not published', async () => {
|
||||
const oldPost = await user.post('/news', { ...newsPost, publishDate: new Date() });
|
||||
const newUnpublished = await user.post('/news', { ...newsPost, published: false });
|
||||
await user.put(`/news/${newUnpublished._id}`, {
|
||||
title: 'Changed Title',
|
||||
});
|
||||
await sleep(0.05);
|
||||
|
||||
expect(NewsPost.lastNewsPost()._id).to.equal(oldPost._id);
|
||||
});
|
||||
|
||||
it('updated post is published', async () => {
|
||||
await user.post('/news', { ...newsPost, publishDate: new Date() });
|
||||
const newUnpublished = await user.post('/news', { ...newsPost, published: false, publishDate: new Date() });
|
||||
await user.put(`/news/${newUnpublished._id}`, {
|
||||
publishDate: new Date(),
|
||||
published: true,
|
||||
});
|
||||
await sleep(0.05);
|
||||
|
||||
expect(NewsPost.lastNewsPost()._id).to.equal(newUnpublished._id);
|
||||
});
|
||||
|
||||
it('updated post publishDate is in future', async () => {
|
||||
const oldPost = await user.post('/news', { ...newsPost, publishDate: new Date() });
|
||||
const newUnpublished = await user.post('/news', newsPost);
|
||||
await user.put(`/news/${newUnpublished._id}`, {
|
||||
publishDate: Date.now() + 50000,
|
||||
});
|
||||
await sleep(0.05);
|
||||
|
||||
expect(NewsPost.lastNewsPost()._id).to.equal(oldPost._id);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,4 +1,5 @@
|
||||
import randomDrop from '../../../website/common/script/fns/randomDrop';
|
||||
import i18n from '../../../website/common/script/i18n';
|
||||
import {
|
||||
generateUser,
|
||||
generateTodo,
|
||||
@@ -144,5 +145,148 @@ describe('common.fns.randomDrop', () => {
|
||||
expect(acceptableDrops).to.contain(user._tmp.drop.key); // always Desert
|
||||
});
|
||||
});
|
||||
|
||||
context('drop cap notification', () => {
|
||||
let analytics;
|
||||
const req = {};
|
||||
let isSubscribedStub;
|
||||
|
||||
beforeEach(() => {
|
||||
user.addNotification = () => {};
|
||||
sandbox.stub(user, 'addNotification');
|
||||
user.isSubscribed = () => {};
|
||||
isSubscribedStub = sandbox.stub(user, 'isSubscribed');
|
||||
isSubscribedStub.returns(false);
|
||||
analytics = { track () {} };
|
||||
sandbox.stub(analytics, 'track');
|
||||
});
|
||||
|
||||
it('sends a notification if A/B test is enabled when drop cap is reached', () => {
|
||||
user._ABtests.dropCapNotif = 'drop-cap-notif-enabled';
|
||||
predictableRandom.returns(0.1);
|
||||
|
||||
// Max Drop Count is 5
|
||||
expect(user.items.lastDrop.count).to.equal(0);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
expect(user.items.lastDrop.count).to.equal(5);
|
||||
expect(user.addNotification).to.be.calledOnce;
|
||||
expect(user.addNotification).to.be.calledWith('DROP_CAP_REACHED', {
|
||||
message: i18n.t('dropCapReached'),
|
||||
items: 5,
|
||||
});
|
||||
});
|
||||
|
||||
it('does not send a notification if user is enrolled in disabled A/B test group', () => {
|
||||
user._ABtests.dropCapNotif = 'drop-cap-notif-disabled';
|
||||
predictableRandom.returns(0.1);
|
||||
|
||||
// Max Drop Count is 5
|
||||
expect(user.items.lastDrop.count).to.equal(0);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
expect(user.items.lastDrop.count).to.equal(5);
|
||||
expect(user.addNotification).to.not.be.called;
|
||||
});
|
||||
|
||||
it('does not send a notification if user is enrolled in disabled A/B test group', () => {
|
||||
user._ABtests.dropCapNotif = 'drop-cap-notif-not-enrolled';
|
||||
predictableRandom.returns(0.1);
|
||||
|
||||
// Max Drop Count is 5
|
||||
expect(user.items.lastDrop.count).to.equal(0);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
expect(user.items.lastDrop.count).to.equal(5);
|
||||
expect(user.addNotification).to.not.be.called;
|
||||
});
|
||||
|
||||
it('does not send a notification if drop cap is not reached', () => {
|
||||
user._ABtests.dropCapNotif = 'drop-cap-notif-enabled';
|
||||
predictableRandom.returns(0.1);
|
||||
|
||||
// Max Drop Count is 5
|
||||
expect(user.items.lastDrop.count).to.equal(0);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
expect(user.items.lastDrop.count).to.equal(4);
|
||||
expect(user.addNotification).to.not.be.called;
|
||||
});
|
||||
|
||||
it('does not send a notification if user is subscribed', () => {
|
||||
user._ABtests.dropCapNotif = 'drop-cap-notif-enabled';
|
||||
predictableRandom.returns(0.1);
|
||||
isSubscribedStub.returns(true);
|
||||
|
||||
// Max Drop Count is 5
|
||||
expect(user.items.lastDrop.count).to.equal(0);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
expect(user.items.lastDrop.count).to.equal(5);
|
||||
expect(user.addNotification).to.not.be.called;
|
||||
});
|
||||
|
||||
it('tracks drop cap reached event for enrolled users (notification enabled)', () => {
|
||||
user._ABtests.dropCapNotif = 'drop-cap-notif-enabled';
|
||||
predictableRandom.returns(0.1);
|
||||
isSubscribedStub.returns(true);
|
||||
|
||||
// Max Drop Count is 5
|
||||
expect(user.items.lastDrop.count).to.equal(0);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
expect(user.items.lastDrop.count).to.equal(5);
|
||||
expect(analytics.track).to.be.calledWith('drop cap reached');
|
||||
});
|
||||
|
||||
it('tracks drop cap reached event for enrolled users (notification disabled)', () => {
|
||||
user._ABtests.dropCapNotif = 'drop-cap-notif-disabled';
|
||||
predictableRandom.returns(0.1);
|
||||
isSubscribedStub.returns(true);
|
||||
|
||||
// Max Drop Count is 5
|
||||
expect(user.items.lastDrop.count).to.equal(0);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
expect(user.items.lastDrop.count).to.equal(5);
|
||||
expect(analytics.track).to.be.calledWith('drop cap reached');
|
||||
});
|
||||
|
||||
it('does not track drop cap reached event for users not enrolled in A/B test', () => {
|
||||
user._ABtests.dropCapNotif = 'drop-cap-notif-not-enrolled';
|
||||
predictableRandom.returns(0.1);
|
||||
isSubscribedStub.returns(true);
|
||||
|
||||
// Max Drop Count is 5
|
||||
expect(user.items.lastDrop.count).to.equal(0);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
randomDrop(user, { task, predictableRandom }, req, analytics);
|
||||
expect(user.items.lastDrop.count).to.equal(5);
|
||||
expect(analytics.track).to.not.be.calledWith('drop cap reached');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -28,25 +28,30 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"browserslist": {
|
||||
"version": "4.14.2",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.14.2.tgz",
|
||||
"integrity": "sha512-HI4lPveGKUR0x2StIz+2FXfDk9SfVMrxn6PLh1JeGUwcuoDkdKZebWiyLRJ68iIPDpMI4JLVDf7S7XzslgWOhw==",
|
||||
"version": "4.14.5",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.14.5.tgz",
|
||||
"integrity": "sha512-Z+vsCZIvCBvqLoYkBFTwEYH3v5MCQbsAjp50ERycpOjnPmolg1Gjy4+KaWWpm8QOJt9GHkhdqAl14NpCX73CWA==",
|
||||
"requires": {
|
||||
"caniuse-lite": "^1.0.30001125",
|
||||
"electron-to-chromium": "^1.3.564",
|
||||
"escalade": "^3.0.2",
|
||||
"caniuse-lite": "^1.0.30001135",
|
||||
"electron-to-chromium": "^1.3.571",
|
||||
"escalade": "^3.1.0",
|
||||
"node-releases": "^1.1.61"
|
||||
}
|
||||
},
|
||||
"caniuse-lite": {
|
||||
"version": "1.0.30001129",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001129.tgz",
|
||||
"integrity": "sha512-9945fTVKS810DZITpsAbuhQG7Lam0tEfVbZlsBaCFZaszepbryrArS05PWmJSBQ6mta+v9iz0pUIAbW1eBILIg=="
|
||||
"version": "1.0.30001148",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001148.tgz",
|
||||
"integrity": "sha512-E66qcd0KMKZHNJQt9hiLZGE3J4zuTqE1OnU53miEVtylFbwOEmeA5OsRu90noZful+XGSQOni1aT2tiqu/9yYw=="
|
||||
},
|
||||
"electron-to-chromium": {
|
||||
"version": "1.3.567",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.567.tgz",
|
||||
"integrity": "sha512-1aKkw0Hha1Bw9JA5K5PT5eFXC/TXbkJvUfNSNEciPUMgSIsRJZM1hF2GUEAGZpAbgvd8En21EA+Lv820KOhvqA=="
|
||||
"version": "1.3.578",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.578.tgz",
|
||||
"integrity": "sha512-z4gU6dA1CbBJsAErW5swTGAaU2TBzc2mPAonJb00zqW1rOraDo2zfBMDRvaz9cVic+0JEZiYbHWPw/fTaZlG2Q=="
|
||||
},
|
||||
"escalade": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.0.tgz",
|
||||
"integrity": "sha512-mAk+hPSO8fLDkhV7V0dXazH5pDc6MrjBTPyD3VeKzxnVFjH1MIxbCdqGZB9O8+EwWakZs3ZCbDS4IpRt79V1ig=="
|
||||
},
|
||||
"node-releases": {
|
||||
"version": "1.1.61",
|
||||
@@ -303,25 +308,30 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"browserslist": {
|
||||
"version": "4.14.2",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.14.2.tgz",
|
||||
"integrity": "sha512-HI4lPveGKUR0x2StIz+2FXfDk9SfVMrxn6PLh1JeGUwcuoDkdKZebWiyLRJ68iIPDpMI4JLVDf7S7XzslgWOhw==",
|
||||
"version": "4.14.5",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.14.5.tgz",
|
||||
"integrity": "sha512-Z+vsCZIvCBvqLoYkBFTwEYH3v5MCQbsAjp50ERycpOjnPmolg1Gjy4+KaWWpm8QOJt9GHkhdqAl14NpCX73CWA==",
|
||||
"requires": {
|
||||
"caniuse-lite": "^1.0.30001125",
|
||||
"electron-to-chromium": "^1.3.564",
|
||||
"escalade": "^3.0.2",
|
||||
"caniuse-lite": "^1.0.30001135",
|
||||
"electron-to-chromium": "^1.3.571",
|
||||
"escalade": "^3.1.0",
|
||||
"node-releases": "^1.1.61"
|
||||
}
|
||||
},
|
||||
"caniuse-lite": {
|
||||
"version": "1.0.30001129",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001129.tgz",
|
||||
"integrity": "sha512-9945fTVKS810DZITpsAbuhQG7Lam0tEfVbZlsBaCFZaszepbryrArS05PWmJSBQ6mta+v9iz0pUIAbW1eBILIg=="
|
||||
"version": "1.0.30001148",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001148.tgz",
|
||||
"integrity": "sha512-E66qcd0KMKZHNJQt9hiLZGE3J4zuTqE1OnU53miEVtylFbwOEmeA5OsRu90noZful+XGSQOni1aT2tiqu/9yYw=="
|
||||
},
|
||||
"electron-to-chromium": {
|
||||
"version": "1.3.567",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.567.tgz",
|
||||
"integrity": "sha512-1aKkw0Hha1Bw9JA5K5PT5eFXC/TXbkJvUfNSNEciPUMgSIsRJZM1hF2GUEAGZpAbgvd8En21EA+Lv820KOhvqA=="
|
||||
"version": "1.3.578",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.578.tgz",
|
||||
"integrity": "sha512-z4gU6dA1CbBJsAErW5swTGAaU2TBzc2mPAonJb00zqW1rOraDo2zfBMDRvaz9cVic+0JEZiYbHWPw/fTaZlG2Q=="
|
||||
},
|
||||
"escalade": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.0.tgz",
|
||||
"integrity": "sha512-mAk+hPSO8fLDkhV7V0dXazH5pDc6MrjBTPyD3VeKzxnVFjH1MIxbCdqGZB9O8+EwWakZs3ZCbDS4IpRt79V1ig=="
|
||||
},
|
||||
"node-releases": {
|
||||
"version": "1.1.61",
|
||||
@@ -4829,9 +4839,9 @@
|
||||
}
|
||||
},
|
||||
"@types/express-serve-static-core": {
|
||||
"version": "4.17.12",
|
||||
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.12.tgz",
|
||||
"integrity": "sha512-EaEdY+Dty1jEU7U6J4CUWwxL+hyEGMkO5jan5gplfegUgCUsIUWqXxqw47uGjimeT4Qgkz/XUfwoau08+fgvKA==",
|
||||
"version": "4.17.13",
|
||||
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.13.tgz",
|
||||
"integrity": "sha512-RgDi5a4nuzam073lRGKTUIaL3eF2+H7LJvJ8eUnCI0wA6SNjXc44DCmWNiTLs/AZ7QlsFWZiw/gTG3nSQGL0fA==",
|
||||
"requires": {
|
||||
"@types/node": "*",
|
||||
"@types/qs": "*",
|
||||
@@ -4932,9 +4942,9 @@
|
||||
"integrity": "sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw=="
|
||||
},
|
||||
"@types/qs": {
|
||||
"version": "6.9.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.4.tgz",
|
||||
"integrity": "sha512-+wYo+L6ZF6BMoEjtf8zB2esQsqdV6WsjRK/GP9WOgLPrq87PbNWgIxS76dS5uvl/QXtHGakZmwTznIfcPXcKlQ=="
|
||||
"version": "6.9.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.5.tgz",
|
||||
"integrity": "sha512-/JHkVHtx/REVG0VVToGRGH2+23hsYLHdyG+GrvoUGlGAd0ErauXDyvHtRI/7H7mzLm+tBCKA7pfcpkQ1lf58iQ=="
|
||||
},
|
||||
"@types/range-parser": {
|
||||
"version": "1.2.3",
|
||||
@@ -5097,9 +5107,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"camelcase": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.0.0.tgz",
|
||||
"integrity": "sha512-8KMDF1Vz2gzOq54ONPJS65IvTUaB1cHJ2DMM7MbPmLZljDH1qpzzLsWdiN9pHh6qvkRVDTi/07+eNGch/oLU4w=="
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.1.0.tgz",
|
||||
"integrity": "sha512-WCMml9ivU60+8rEJgELlFp1gxFcEGxwYleE3bziHEDeqsqAWGHdimB7beBFGjLzVNgPGyDsfgXLQEYMpmIFnVQ=="
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -5124,9 +5134,9 @@
|
||||
}
|
||||
},
|
||||
"@vue/babel-preset-app": {
|
||||
"version": "4.5.6",
|
||||
"resolved": "https://registry.npmjs.org/@vue/babel-preset-app/-/babel-preset-app-4.5.6.tgz",
|
||||
"integrity": "sha512-Eps83UNiBJeqlbpR9afYnhvjVLElVtA4fDLNuVUr1r3RbepoxWuq+mUTr3TBArPQebnAaDcrZaNHBWTLRbfo3A==",
|
||||
"version": "4.5.7",
|
||||
"resolved": "https://registry.npmjs.org/@vue/babel-preset-app/-/babel-preset-app-4.5.7.tgz",
|
||||
"integrity": "sha512-A9ujqmvR9wb8nWiMnEYZW/8QfGZbqxC/etzbKIDrUdsqJ27jx106leMHJc8nmAn58RqGd6iww6uZ3Sx7aYiG3A==",
|
||||
"requires": {
|
||||
"@babel/core": "^7.11.0",
|
||||
"@babel/helper-compilation-targets": "^7.9.6",
|
||||
@@ -5880,20 +5890,20 @@
|
||||
}
|
||||
},
|
||||
"browserslist": {
|
||||
"version": "4.14.2",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.14.2.tgz",
|
||||
"integrity": "sha512-HI4lPveGKUR0x2StIz+2FXfDk9SfVMrxn6PLh1JeGUwcuoDkdKZebWiyLRJ68iIPDpMI4JLVDf7S7XzslgWOhw==",
|
||||
"version": "4.14.5",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.14.5.tgz",
|
||||
"integrity": "sha512-Z+vsCZIvCBvqLoYkBFTwEYH3v5MCQbsAjp50ERycpOjnPmolg1Gjy4+KaWWpm8QOJt9GHkhdqAl14NpCX73CWA==",
|
||||
"requires": {
|
||||
"caniuse-lite": "^1.0.30001125",
|
||||
"electron-to-chromium": "^1.3.564",
|
||||
"escalade": "^3.0.2",
|
||||
"caniuse-lite": "^1.0.30001135",
|
||||
"electron-to-chromium": "^1.3.571",
|
||||
"escalade": "^3.1.0",
|
||||
"node-releases": "^1.1.61"
|
||||
}
|
||||
},
|
||||
"caniuse-lite": {
|
||||
"version": "1.0.30001129",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001129.tgz",
|
||||
"integrity": "sha512-9945fTVKS810DZITpsAbuhQG7Lam0tEfVbZlsBaCFZaszepbryrArS05PWmJSBQ6mta+v9iz0pUIAbW1eBILIg=="
|
||||
"version": "1.0.30001148",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001148.tgz",
|
||||
"integrity": "sha512-E66qcd0KMKZHNJQt9hiLZGE3J4zuTqE1OnU53miEVtylFbwOEmeA5OsRu90noZful+XGSQOni1aT2tiqu/9yYw=="
|
||||
},
|
||||
"core-js-compat": {
|
||||
"version": "3.6.5",
|
||||
@@ -5912,9 +5922,14 @@
|
||||
}
|
||||
},
|
||||
"electron-to-chromium": {
|
||||
"version": "1.3.567",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.567.tgz",
|
||||
"integrity": "sha512-1aKkw0Hha1Bw9JA5K5PT5eFXC/TXbkJvUfNSNEciPUMgSIsRJZM1hF2GUEAGZpAbgvd8En21EA+Lv820KOhvqA=="
|
||||
"version": "1.3.578",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.578.tgz",
|
||||
"integrity": "sha512-z4gU6dA1CbBJsAErW5swTGAaU2TBzc2mPAonJb00zqW1rOraDo2zfBMDRvaz9cVic+0JEZiYbHWPw/fTaZlG2Q=="
|
||||
},
|
||||
"escalade": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.0.tgz",
|
||||
"integrity": "sha512-mAk+hPSO8fLDkhV7V0dXazH5pDc6MrjBTPyD3VeKzxnVFjH1MIxbCdqGZB9O8+EwWakZs3ZCbDS4IpRt79V1ig=="
|
||||
},
|
||||
"node-releases": {
|
||||
"version": "1.1.61",
|
||||
@@ -5935,9 +5950,9 @@
|
||||
"integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew=="
|
||||
},
|
||||
"regexpu-core": {
|
||||
"version": "4.7.0",
|
||||
"resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.0.tgz",
|
||||
"integrity": "sha512-TQ4KXRnIn6tz6tjnrXEkD/sshygKH/j5KzK86X8MkeHyZ8qst/LZ89j3X4/8HEIfHANTFIP/AbXakeRhWIl5YQ==",
|
||||
"version": "4.7.1",
|
||||
"resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.1.tgz",
|
||||
"integrity": "sha512-ywH2VUraA44DZQuRKzARmw6S66mr48pQVva4LBeRhcOltJ6hExvWly5ZjFLYo67xbIxb6W1q4bAGtgfEl20zfQ==",
|
||||
"requires": {
|
||||
"regenerate": "^1.4.0",
|
||||
"regenerate-unicode-properties": "^8.2.0",
|
||||
@@ -6034,30 +6049,69 @@
|
||||
}
|
||||
},
|
||||
"@vue/cli-overlay": {
|
||||
"version": "4.5.6",
|
||||
"resolved": "https://registry.npmjs.org/@vue/cli-overlay/-/cli-overlay-4.5.6.tgz",
|
||||
"integrity": "sha512-8kFIdiErtGRlvKWJV0AcF6SXakQDxeuqqcMhWt3qIJxRH6aD33RTC37Q3KWuMsYryBZpEY3tNWGhS1d4spQu0g=="
|
||||
"version": "4.5.7",
|
||||
"resolved": "https://registry.npmjs.org/@vue/cli-overlay/-/cli-overlay-4.5.7.tgz",
|
||||
"integrity": "sha512-45BbVPR2dTa27QGaFap7eNYbJSzuIhGff1R5L50tWlpw/lf8fIyOuXSdSNQGZCVe+Y3NbcD2DK7mZryxOXWGmw=="
|
||||
},
|
||||
"@vue/cli-plugin-babel": {
|
||||
"version": "4.5.6",
|
||||
"resolved": "https://registry.npmjs.org/@vue/cli-plugin-babel/-/cli-plugin-babel-4.5.6.tgz",
|
||||
"integrity": "sha512-jkeXIpvxg2Og+6igsck6qBMFwFN5poqbgDL7JEQP94DPRMAGt+AOoEz6Ultwvykd9lRDD/xLmzZ2MTeXvrpq4A==",
|
||||
"version": "4.5.7",
|
||||
"resolved": "https://registry.npmjs.org/@vue/cli-plugin-babel/-/cli-plugin-babel-4.5.7.tgz",
|
||||
"integrity": "sha512-cqtHoXWHxtMj8qyN0A2TvFRuEQsqtDlYeKaOT1XDwbfHZwWXlD4BBsqXZBnqQkQI0hijMOA0QOnqA63/x0lpMg==",
|
||||
"requires": {
|
||||
"@babel/core": "^7.11.0",
|
||||
"@vue/babel-preset-app": "^4.5.6",
|
||||
"@vue/cli-shared-utils": "^4.5.6",
|
||||
"@vue/babel-preset-app": "^4.5.7",
|
||||
"@vue/cli-shared-utils": "^4.5.7",
|
||||
"babel-loader": "^8.1.0",
|
||||
"cache-loader": "^4.1.0",
|
||||
"thread-loader": "^2.1.3",
|
||||
"webpack": "^4.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vue/cli-shared-utils": {
|
||||
"version": "4.5.7",
|
||||
"resolved": "https://registry.npmjs.org/@vue/cli-shared-utils/-/cli-shared-utils-4.5.7.tgz",
|
||||
"integrity": "sha512-oicFfx9PvgupxN/LW0s2ktdn1U6bBu8J4lPcW2xj6TtTWUkkxwzis4Tm+XOvgvZnu44+d7216y0Y4TX90q645w==",
|
||||
"requires": {
|
||||
"@hapi/joi": "^15.0.1",
|
||||
"chalk": "^2.4.2",
|
||||
"execa": "^1.0.0",
|
||||
"launch-editor": "^2.2.1",
|
||||
"lru-cache": "^5.1.1",
|
||||
"node-ipc": "^9.1.1",
|
||||
"open": "^6.3.0",
|
||||
"ora": "^3.4.0",
|
||||
"read-pkg": "^5.1.1",
|
||||
"request": "^2.88.2",
|
||||
"semver": "^6.1.0",
|
||||
"strip-ansi": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"ansi-regex": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
|
||||
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg=="
|
||||
},
|
||||
"semver": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
|
||||
"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
|
||||
"requires": {
|
||||
"ansi-regex": "^5.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@vue/cli-plugin-eslint": {
|
||||
"version": "4.5.6",
|
||||
"resolved": "https://registry.npmjs.org/@vue/cli-plugin-eslint/-/cli-plugin-eslint-4.5.6.tgz",
|
||||
"integrity": "sha512-maG3dy64pGVT9mMQq7KvP6kbBK6TeVgcj1aa1QwzT5yrw65E2So8bKMrEMEjy53b88bgR9jZ7gshOks00jrYsg==",
|
||||
"version": "4.5.7",
|
||||
"resolved": "https://registry.npmjs.org/@vue/cli-plugin-eslint/-/cli-plugin-eslint-4.5.7.tgz",
|
||||
"integrity": "sha512-6fWob1xh2W0uif2++YhNiBWITDBsAEktdgnLRgIgM/UqUg9oFpz9tqs0i85PQwjUDIn/erMT2ID3hnOncYTxxQ==",
|
||||
"requires": {
|
||||
"@vue/cli-shared-utils": "^4.5.6",
|
||||
"@vue/cli-shared-utils": "^4.5.7",
|
||||
"eslint-loader": "^2.2.1",
|
||||
"globby": "^9.2.0",
|
||||
"inquirer": "^7.1.0",
|
||||
@@ -6065,29 +6119,38 @@
|
||||
"yorkie": "^2.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vue/cli-shared-utils": {
|
||||
"version": "4.5.7",
|
||||
"resolved": "https://registry.npmjs.org/@vue/cli-shared-utils/-/cli-shared-utils-4.5.7.tgz",
|
||||
"integrity": "sha512-oicFfx9PvgupxN/LW0s2ktdn1U6bBu8J4lPcW2xj6TtTWUkkxwzis4Tm+XOvgvZnu44+d7216y0Y4TX90q645w==",
|
||||
"requires": {
|
||||
"@hapi/joi": "^15.0.1",
|
||||
"chalk": "^2.4.2",
|
||||
"execa": "^1.0.0",
|
||||
"launch-editor": "^2.2.1",
|
||||
"lru-cache": "^5.1.1",
|
||||
"node-ipc": "^9.1.1",
|
||||
"open": "^6.3.0",
|
||||
"ora": "^3.4.0",
|
||||
"read-pkg": "^5.1.1",
|
||||
"request": "^2.88.2",
|
||||
"semver": "^6.1.0",
|
||||
"strip-ansi": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"ansi-regex": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
|
||||
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg=="
|
||||
},
|
||||
"ansi-styles": {
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
|
||||
"integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"requires": {
|
||||
"@types/color-name": "^1.1.1",
|
||||
"color-convert": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"chalk": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
|
||||
"integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
|
||||
"requires": {
|
||||
"ansi-styles": "^4.1.0",
|
||||
"supports-color": "^7.1.0"
|
||||
}
|
||||
},
|
||||
"cli-cursor": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
|
||||
@@ -6142,6 +6205,17 @@
|
||||
"string-width": "^4.1.0",
|
||||
"strip-ansi": "^6.0.0",
|
||||
"through": "^2.3.6"
|
||||
},
|
||||
"dependencies": {
|
||||
"chalk": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
|
||||
"integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
|
||||
"requires": {
|
||||
"ansi-styles": "^4.1.0",
|
||||
"supports-color": "^7.1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"is-fullwidth-code-point": {
|
||||
@@ -6184,6 +6258,11 @@
|
||||
"tslib": "^1.9.0"
|
||||
}
|
||||
},
|
||||
"semver": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
|
||||
},
|
||||
"string-width": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz",
|
||||
@@ -6213,34 +6292,112 @@
|
||||
}
|
||||
},
|
||||
"@vue/cli-plugin-router": {
|
||||
"version": "4.5.6",
|
||||
"resolved": "https://registry.npmjs.org/@vue/cli-plugin-router/-/cli-plugin-router-4.5.6.tgz",
|
||||
"integrity": "sha512-QEqOGglg0JEKddZPuyiSnAzAVK7IzLrdTPCUegigzGSbUXDW4gQiltY3/2nij2q538YvdIM7JXtW1sUfy4MgHQ==",
|
||||
"version": "4.5.7",
|
||||
"resolved": "https://registry.npmjs.org/@vue/cli-plugin-router/-/cli-plugin-router-4.5.7.tgz",
|
||||
"integrity": "sha512-wzKz8+qOXNqVglcw90lYHbu5UJQo8QoyNXHAiM0RIX4r3W8KqiHrvu7MZFCOVKM3ojRFbDofumorypN2yieSXA==",
|
||||
"requires": {
|
||||
"@vue/cli-shared-utils": "^4.5.6"
|
||||
"@vue/cli-shared-utils": "^4.5.7"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vue/cli-shared-utils": {
|
||||
"version": "4.5.7",
|
||||
"resolved": "https://registry.npmjs.org/@vue/cli-shared-utils/-/cli-shared-utils-4.5.7.tgz",
|
||||
"integrity": "sha512-oicFfx9PvgupxN/LW0s2ktdn1U6bBu8J4lPcW2xj6TtTWUkkxwzis4Tm+XOvgvZnu44+d7216y0Y4TX90q645w==",
|
||||
"requires": {
|
||||
"@hapi/joi": "^15.0.1",
|
||||
"chalk": "^2.4.2",
|
||||
"execa": "^1.0.0",
|
||||
"launch-editor": "^2.2.1",
|
||||
"lru-cache": "^5.1.1",
|
||||
"node-ipc": "^9.1.1",
|
||||
"open": "^6.3.0",
|
||||
"ora": "^3.4.0",
|
||||
"read-pkg": "^5.1.1",
|
||||
"request": "^2.88.2",
|
||||
"semver": "^6.1.0",
|
||||
"strip-ansi": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"ansi-regex": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
|
||||
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg=="
|
||||
},
|
||||
"semver": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
|
||||
"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
|
||||
"requires": {
|
||||
"ansi-regex": "^5.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@vue/cli-plugin-unit-mocha": {
|
||||
"version": "4.5.6",
|
||||
"resolved": "https://registry.npmjs.org/@vue/cli-plugin-unit-mocha/-/cli-plugin-unit-mocha-4.5.6.tgz",
|
||||
"integrity": "sha512-qaMJgGfjgPA1hVVE6u8ry+hAtvU+fPQAKv5c4nYysLnvxnlLiAgEtK4dIRaH9GZQbA0/HVgRE5C0O7lAOxWapg==",
|
||||
"version": "4.5.7",
|
||||
"resolved": "https://registry.npmjs.org/@vue/cli-plugin-unit-mocha/-/cli-plugin-unit-mocha-4.5.7.tgz",
|
||||
"integrity": "sha512-OIH3ZdU1NJOHZmtbtoeAomgF7Rq8iLt76zOusY2OJsbkin/BAknxZ6rgmtylzgtvFgYoOXio8zznpzbTzBTFkA==",
|
||||
"requires": {
|
||||
"@vue/cli-shared-utils": "^4.5.6",
|
||||
"@vue/cli-shared-utils": "^4.5.7",
|
||||
"jsdom": "^15.2.1",
|
||||
"jsdom-global": "^3.0.2",
|
||||
"mocha": "^6.2.2",
|
||||
"mochapack": "^1.1.15"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vue/cli-shared-utils": {
|
||||
"version": "4.5.7",
|
||||
"resolved": "https://registry.npmjs.org/@vue/cli-shared-utils/-/cli-shared-utils-4.5.7.tgz",
|
||||
"integrity": "sha512-oicFfx9PvgupxN/LW0s2ktdn1U6bBu8J4lPcW2xj6TtTWUkkxwzis4Tm+XOvgvZnu44+d7216y0Y4TX90q645w==",
|
||||
"requires": {
|
||||
"@hapi/joi": "^15.0.1",
|
||||
"chalk": "^2.4.2",
|
||||
"execa": "^1.0.0",
|
||||
"launch-editor": "^2.2.1",
|
||||
"lru-cache": "^5.1.1",
|
||||
"node-ipc": "^9.1.1",
|
||||
"open": "^6.3.0",
|
||||
"ora": "^3.4.0",
|
||||
"read-pkg": "^5.1.1",
|
||||
"request": "^2.88.2",
|
||||
"semver": "^6.1.0",
|
||||
"strip-ansi": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"ansi-regex": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
|
||||
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg=="
|
||||
},
|
||||
"semver": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
|
||||
"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
|
||||
"requires": {
|
||||
"ansi-regex": "^5.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@vue/cli-plugin-vuex": {
|
||||
"version": "4.5.6",
|
||||
"resolved": "https://registry.npmjs.org/@vue/cli-plugin-vuex/-/cli-plugin-vuex-4.5.6.tgz",
|
||||
"integrity": "sha512-cWxj0jIhhupU+oFl0mc1St3ig9iF5F01XKwAhKEbvvuHR97zHxLd29My/vvcRwojZMy4aY320oJ+0ljoCIbueQ=="
|
||||
"version": "4.5.7",
|
||||
"resolved": "https://registry.npmjs.org/@vue/cli-plugin-vuex/-/cli-plugin-vuex-4.5.7.tgz",
|
||||
"integrity": "sha512-bHH2JSAd/S9fABtZdr3xVSgbIPm3PGcan56adMt0hGlm6HG/QxDNuPLppMleuBLr9uHoHX5x7sQmbtZvzIYjxw=="
|
||||
},
|
||||
"@vue/cli-service": {
|
||||
"version": "4.5.6",
|
||||
"resolved": "https://registry.npmjs.org/@vue/cli-service/-/cli-service-4.5.6.tgz",
|
||||
"integrity": "sha512-wl0rhjHSpy2Mc2zNU6sfhaUVNNaRzgXNfZMIpTZMO3wJalPMLuvGC3KLMaXcpvuI01zeQBmkEocAdhzay4lQ0w==",
|
||||
"version": "4.5.7",
|
||||
"resolved": "https://registry.npmjs.org/@vue/cli-service/-/cli-service-4.5.7.tgz",
|
||||
"integrity": "sha512-iT5wb5JbF/kbJCY7HR8qabWEiaMvZP4/KPezsnEp/6vNGAF0Akx0FGvCuU9sm7uf6w0UKzIJ38I6JJBtkOMvJA==",
|
||||
"requires": {
|
||||
"@intervolga/optimize-cssnano-plugin": "^1.0.5",
|
||||
"@soda/friendly-errors-webpack-plugin": "^1.7.1",
|
||||
@@ -6248,10 +6405,10 @@
|
||||
"@types/minimist": "^1.2.0",
|
||||
"@types/webpack": "^4.0.0",
|
||||
"@types/webpack-dev-server": "^3.11.0",
|
||||
"@vue/cli-overlay": "^4.5.6",
|
||||
"@vue/cli-plugin-router": "^4.5.6",
|
||||
"@vue/cli-plugin-vuex": "^4.5.6",
|
||||
"@vue/cli-shared-utils": "^4.5.6",
|
||||
"@vue/cli-overlay": "^4.5.7",
|
||||
"@vue/cli-plugin-router": "^4.5.7",
|
||||
"@vue/cli-plugin-vuex": "^4.5.7",
|
||||
"@vue/cli-shared-utils": "^4.5.7",
|
||||
"@vue/component-compiler-utils": "^3.1.2",
|
||||
"@vue/preload-webpack-plugin": "^1.1.0",
|
||||
"@vue/web-component-wrapper": "^1.2.0",
|
||||
@@ -6306,17 +6463,36 @@
|
||||
"integrity": "sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw=="
|
||||
},
|
||||
"@vue/cli-plugin-router": {
|
||||
"version": "4.5.6",
|
||||
"resolved": "https://registry.npmjs.org/@vue/cli-plugin-router/-/cli-plugin-router-4.5.6.tgz",
|
||||
"integrity": "sha512-QEqOGglg0JEKddZPuyiSnAzAVK7IzLrdTPCUegigzGSbUXDW4gQiltY3/2nij2q538YvdIM7JXtW1sUfy4MgHQ==",
|
||||
"version": "4.5.7",
|
||||
"resolved": "https://registry.npmjs.org/@vue/cli-plugin-router/-/cli-plugin-router-4.5.7.tgz",
|
||||
"integrity": "sha512-wzKz8+qOXNqVglcw90lYHbu5UJQo8QoyNXHAiM0RIX4r3W8KqiHrvu7MZFCOVKM3ojRFbDofumorypN2yieSXA==",
|
||||
"requires": {
|
||||
"@vue/cli-shared-utils": "^4.5.6"
|
||||
"@vue/cli-shared-utils": "^4.5.7"
|
||||
}
|
||||
},
|
||||
"@vue/cli-shared-utils": {
|
||||
"version": "4.5.7",
|
||||
"resolved": "https://registry.npmjs.org/@vue/cli-shared-utils/-/cli-shared-utils-4.5.7.tgz",
|
||||
"integrity": "sha512-oicFfx9PvgupxN/LW0s2ktdn1U6bBu8J4lPcW2xj6TtTWUkkxwzis4Tm+XOvgvZnu44+d7216y0Y4TX90q645w==",
|
||||
"requires": {
|
||||
"@hapi/joi": "^15.0.1",
|
||||
"chalk": "^2.4.2",
|
||||
"execa": "^1.0.0",
|
||||
"launch-editor": "^2.2.1",
|
||||
"lru-cache": "^5.1.1",
|
||||
"node-ipc": "^9.1.1",
|
||||
"open": "^6.3.0",
|
||||
"ora": "^3.4.0",
|
||||
"read-pkg": "^5.1.1",
|
||||
"request": "^2.88.2",
|
||||
"semver": "^6.1.0",
|
||||
"strip-ansi": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"acorn": {
|
||||
"version": "7.4.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.0.tgz",
|
||||
"integrity": "sha512-+G7P8jJmCHr+S+cLfQxygbWhXy+8YTVGzAkpEbcLo2mLoL7tij/VG41QSHACSf5QgYRhMZYHuNc6drJaO0Da+w=="
|
||||
"version": "7.4.1",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
|
||||
"integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A=="
|
||||
},
|
||||
"acorn-walk": {
|
||||
"version": "7.2.0",
|
||||
@@ -6324,9 +6500,9 @@
|
||||
"integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA=="
|
||||
},
|
||||
"ajv": {
|
||||
"version": "6.12.5",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.5.tgz",
|
||||
"integrity": "sha512-lRF8RORchjpKG50/WFf8xmg7sgCLFiYNNnqdKflk63whMQcWR5ngGjiSXkL9bjxy6B2npOK2HSMN49jEBMSkag==",
|
||||
"version": "6.12.6",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||
"requires": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
@@ -6345,11 +6521,10 @@
|
||||
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg=="
|
||||
},
|
||||
"ansi-styles": {
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
|
||||
"integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"requires": {
|
||||
"@types/color-name": "^1.1.1",
|
||||
"color-convert": "^2.0.1"
|
||||
}
|
||||
},
|
||||
@@ -6368,20 +6543,20 @@
|
||||
}
|
||||
},
|
||||
"browserslist": {
|
||||
"version": "4.14.2",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.14.2.tgz",
|
||||
"integrity": "sha512-HI4lPveGKUR0x2StIz+2FXfDk9SfVMrxn6PLh1JeGUwcuoDkdKZebWiyLRJ68iIPDpMI4JLVDf7S7XzslgWOhw==",
|
||||
"version": "4.14.5",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.14.5.tgz",
|
||||
"integrity": "sha512-Z+vsCZIvCBvqLoYkBFTwEYH3v5MCQbsAjp50ERycpOjnPmolg1Gjy4+KaWWpm8QOJt9GHkhdqAl14NpCX73CWA==",
|
||||
"requires": {
|
||||
"caniuse-lite": "^1.0.30001125",
|
||||
"electron-to-chromium": "^1.3.564",
|
||||
"escalade": "^3.0.2",
|
||||
"caniuse-lite": "^1.0.30001135",
|
||||
"electron-to-chromium": "^1.3.571",
|
||||
"escalade": "^3.1.0",
|
||||
"node-releases": "^1.1.61"
|
||||
}
|
||||
},
|
||||
"caniuse-lite": {
|
||||
"version": "1.0.30001129",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001129.tgz",
|
||||
"integrity": "sha512-9945fTVKS810DZITpsAbuhQG7Lam0tEfVbZlsBaCFZaszepbryrArS05PWmJSBQ6mta+v9iz0pUIAbW1eBILIg=="
|
||||
"version": "1.0.30001148",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001148.tgz",
|
||||
"integrity": "sha512-E66qcd0KMKZHNJQt9hiLZGE3J4zuTqE1OnU53miEVtylFbwOEmeA5OsRu90noZful+XGSQOni1aT2tiqu/9yYw=="
|
||||
},
|
||||
"cliui": {
|
||||
"version": "6.0.0",
|
||||
@@ -6427,9 +6602,9 @@
|
||||
}
|
||||
},
|
||||
"electron-to-chromium": {
|
||||
"version": "1.3.567",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.567.tgz",
|
||||
"integrity": "sha512-1aKkw0Hha1Bw9JA5K5PT5eFXC/TXbkJvUfNSNEciPUMgSIsRJZM1hF2GUEAGZpAbgvd8En21EA+Lv820KOhvqA=="
|
||||
"version": "1.3.578",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.578.tgz",
|
||||
"integrity": "sha512-z4gU6dA1CbBJsAErW5swTGAaU2TBzc2mPAonJb00zqW1rOraDo2zfBMDRvaz9cVic+0JEZiYbHWPw/fTaZlG2Q=="
|
||||
},
|
||||
"emoji-regex": {
|
||||
"version": "8.0.0",
|
||||
@@ -6441,6 +6616,11 @@
|
||||
"resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz",
|
||||
"integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q=="
|
||||
},
|
||||
"escalade": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.0.tgz",
|
||||
"integrity": "sha512-mAk+hPSO8fLDkhV7V0dXazH5pDc6MrjBTPyD3VeKzxnVFjH1MIxbCdqGZB9O8+EwWakZs3ZCbDS4IpRt79V1ig=="
|
||||
},
|
||||
"fast-deep-equal": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||
@@ -6470,9 +6650,9 @@
|
||||
}
|
||||
},
|
||||
"postcss": {
|
||||
"version": "7.0.32",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.32.tgz",
|
||||
"integrity": "sha512-03eXong5NLnNCD05xscnGKGDZ98CyzoqPSMjOe6SuoQY7Z2hIj0Ld1g/O/UQRuOle2aRtiIRDg9tDcTGAkLfKw==",
|
||||
"version": "7.0.35",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz",
|
||||
"integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==",
|
||||
"requires": {
|
||||
"chalk": "^2.4.2",
|
||||
"source-map": "^0.6.1",
|
||||
@@ -7759,9 +7939,9 @@
|
||||
"integrity": "sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw=="
|
||||
},
|
||||
"ajv": {
|
||||
"version": "6.12.5",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.5.tgz",
|
||||
"integrity": "sha512-lRF8RORchjpKG50/WFf8xmg7sgCLFiYNNnqdKflk63whMQcWR5ngGjiSXkL9bjxy6B2npOK2HSMN49jEBMSkag==",
|
||||
"version": "6.12.6",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||
"requires": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
@@ -8967,11 +9147,10 @@
|
||||
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg=="
|
||||
},
|
||||
"ansi-styles": {
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
|
||||
"integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"requires": {
|
||||
"@types/color-name": "^1.1.1",
|
||||
"color-convert": "^2.0.1"
|
||||
}
|
||||
},
|
||||
@@ -9248,12 +9427,12 @@
|
||||
}
|
||||
},
|
||||
"color": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/color/-/color-3.1.2.tgz",
|
||||
"integrity": "sha512-vXTJhHebByxZn3lDvDJYw4lR5+uB3vuoHsuYA5AKuxRVn5wzzIfQKGLBmgdVRHKTJYeK5rvJcHnrd0Li49CFpg==",
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/color/-/color-3.1.3.tgz",
|
||||
"integrity": "sha512-xgXAcTHa2HeFCGLE9Xs/R82hujGtu9Jd9x4NW3T34+OMs7VoPsjwzRczKHvTAHeJwWFwX5j15+MgAppE8ztObQ==",
|
||||
"requires": {
|
||||
"color-convert": "^1.9.1",
|
||||
"color-string": "^1.5.2"
|
||||
"color-string": "^1.5.4"
|
||||
}
|
||||
},
|
||||
"color-convert": {
|
||||
@@ -9270,9 +9449,9 @@
|
||||
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
|
||||
},
|
||||
"color-string": {
|
||||
"version": "1.5.3",
|
||||
"resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz",
|
||||
"integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==",
|
||||
"version": "1.5.4",
|
||||
"resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.4.tgz",
|
||||
"integrity": "sha512-57yF5yt8Xa3czSEW1jfQDE79Idk0+AkN/4KWad6tbdxUmAs3MvjxlWSWD4deYytcRfoZ9nhKyFl1kj5tBvidbw==",
|
||||
"requires": {
|
||||
"color-name": "^1.0.0",
|
||||
"simple-swizzle": "^0.2.2"
|
||||
@@ -9331,9 +9510,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"mime-db": {
|
||||
"version": "1.44.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz",
|
||||
"integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg=="
|
||||
"version": "1.45.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.45.0.tgz",
|
||||
"integrity": "sha512-CkqLUxUk15hofLoLyljJSrukZi8mAtgd+yE5uO4tqRZsdsAJKv0O+rFMhVDRJgozy+yG6md5KwuXhD4ocIoP+w=="
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -12897,9 +13076,9 @@
|
||||
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="
|
||||
},
|
||||
"hellojs": {
|
||||
"version": "1.18.4",
|
||||
"resolved": "https://registry.npmjs.org/hellojs/-/hellojs-1.18.4.tgz",
|
||||
"integrity": "sha512-h9s7cYE5TlojfgPtdMDXWAyYInQuBCC21jL9TwJGY3NN6wYOw4B83NrEC4p+R015kndQAqZamWe/VolOaQrLeQ=="
|
||||
"version": "1.18.6",
|
||||
"resolved": "https://registry.npmjs.org/hellojs/-/hellojs-1.18.6.tgz",
|
||||
"integrity": "sha512-xt2zI4w9e6pxNP/WHrHME5Shaex2HuUIni7ZZ1EeqvTTDUd3FOaWdmDKUSRmQgw3LFv06bUi3mVWwGuM5pOtrw=="
|
||||
},
|
||||
"hex-color-regex": {
|
||||
"version": "1.1.0",
|
||||
@@ -13907,6 +14086,11 @@
|
||||
"resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.1.tgz",
|
||||
"integrity": "sha512-T/S49scO8plUiAOA2DBTBG3JHpn1yiw0kRp6dgiZ0v2/6twi5eiB0rHtHFH9ZIrvlWc6+4O+m4zg5+Z833aXgw=="
|
||||
},
|
||||
"is-negative-zero": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.0.tgz",
|
||||
"integrity": "sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE="
|
||||
},
|
||||
"is-number": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
|
||||
@@ -14180,9 +14364,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"acorn": {
|
||||
"version": "7.4.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.0.tgz",
|
||||
"integrity": "sha512-+G7P8jJmCHr+S+cLfQxygbWhXy+8YTVGzAkpEbcLo2mLoL7tij/VG41QSHACSf5QgYRhMZYHuNc6drJaO0Da+w=="
|
||||
"version": "7.4.1",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
|
||||
"integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A=="
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -15246,9 +15430,9 @@
|
||||
}
|
||||
},
|
||||
"moment": {
|
||||
"version": "2.29.0",
|
||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.0.tgz",
|
||||
"integrity": "sha512-z6IJ5HXYiuxvFTI6eiQ9dm77uE0gyy1yXNApVHqTcnIKfY9tIwEjlzsZ6u1LQXvVgKeTnv9Xm7NDvJ7lso3MtA=="
|
||||
"version": "2.29.1",
|
||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz",
|
||||
"integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ=="
|
||||
},
|
||||
"move-concurrently": {
|
||||
"version": "1.0.1",
|
||||
@@ -15472,9 +15656,9 @@
|
||||
"integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA=="
|
||||
},
|
||||
"node-forge": {
|
||||
"version": "0.9.0",
|
||||
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.9.0.tgz",
|
||||
"integrity": "sha512-7ASaDa3pD+lJ3WvXFsxekJQelBKRpne+GOVbLbtHYdd7pFspyeuJHnWfLplGf3SwKGbfs/aYl5V/JCIaHVUKKQ=="
|
||||
"version": "0.10.0",
|
||||
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz",
|
||||
"integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA=="
|
||||
},
|
||||
"node-ipc": {
|
||||
"version": "9.1.1",
|
||||
@@ -15660,28 +15844,29 @@
|
||||
"integrity": "sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ=="
|
||||
},
|
||||
"object-is": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.2.tgz",
|
||||
"integrity": "sha512-5lHCz+0uufF6wZ7CRFWJN3hp8Jqblpgve06U5CMQ3f//6iDjPr2PEo9MWCjEssDsa+UZEL4PkFpr+BMop6aKzQ==",
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.3.tgz",
|
||||
"integrity": "sha512-teyqLvFWzLkq5B9ki8FVWA902UER2qkxmdA4nLf+wjOLAWgxzCWZNCxpDq9MvE8MmhWNr+I8w3BN49Vx36Y6Xg==",
|
||||
"requires": {
|
||||
"define-properties": "^1.1.3",
|
||||
"es-abstract": "^1.17.5"
|
||||
"es-abstract": "^1.18.0-next.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"es-abstract": {
|
||||
"version": "1.17.6",
|
||||
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz",
|
||||
"integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==",
|
||||
"version": "1.18.0-next.1",
|
||||
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz",
|
||||
"integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==",
|
||||
"requires": {
|
||||
"es-to-primitive": "^1.2.1",
|
||||
"function-bind": "^1.1.1",
|
||||
"has": "^1.0.3",
|
||||
"has-symbols": "^1.0.1",
|
||||
"is-callable": "^1.2.0",
|
||||
"is-regex": "^1.1.0",
|
||||
"object-inspect": "^1.7.0",
|
||||
"is-callable": "^1.2.2",
|
||||
"is-negative-zero": "^2.0.0",
|
||||
"is-regex": "^1.1.1",
|
||||
"object-inspect": "^1.8.0",
|
||||
"object-keys": "^1.1.1",
|
||||
"object.assign": "^4.1.0",
|
||||
"object.assign": "^4.1.1",
|
||||
"string.prototype.trimend": "^1.0.1",
|
||||
"string.prototype.trimstart": "^1.0.1"
|
||||
}
|
||||
@@ -15702,9 +15887,9 @@
|
||||
"integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg=="
|
||||
},
|
||||
"is-callable": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.1.tgz",
|
||||
"integrity": "sha512-wliAfSzx6V+6WfMOmus1xy0XvSgf/dlStkvTfq7F0g4bOIW0PSUbnyse3NhDwdyYS1ozfUtAAySqTws3z9Eqgg=="
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz",
|
||||
"integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA=="
|
||||
},
|
||||
"is-regex": {
|
||||
"version": "1.1.1",
|
||||
@@ -15718,6 +15903,17 @@
|
||||
"version": "1.8.0",
|
||||
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz",
|
||||
"integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA=="
|
||||
},
|
||||
"object.assign": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.1.tgz",
|
||||
"integrity": "sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA==",
|
||||
"requires": {
|
||||
"define-properties": "^1.1.3",
|
||||
"es-abstract": "^1.18.0-next.0",
|
||||
"has-symbols": "^1.0.1",
|
||||
"object-keys": "^1.1.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -16398,9 +16594,9 @@
|
||||
}
|
||||
},
|
||||
"postcss-calc": {
|
||||
"version": "7.0.4",
|
||||
"resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-7.0.4.tgz",
|
||||
"integrity": "sha512-0I79VRAd1UTkaHzY9w83P39YGO/M3bG7/tNLrHGEunBolfoGM0hSjrGvjoeaj0JE/zIw5GsI2KZ0UwDJqv5hjw==",
|
||||
"version": "7.0.5",
|
||||
"resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-7.0.5.tgz",
|
||||
"integrity": "sha512-1tKHutbGtLtEZF6PT4JSihCHfIVldU72mZ8SdZHIYriIZ9fh9k9aWSppaT8rHsyI3dX+KSR+W+Ix9BMY3AODrg==",
|
||||
"requires": {
|
||||
"postcss": "^7.0.27",
|
||||
"postcss-selector-parser": "^6.0.2",
|
||||
@@ -16408,9 +16604,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"postcss": {
|
||||
"version": "7.0.32",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.32.tgz",
|
||||
"integrity": "sha512-03eXong5NLnNCD05xscnGKGDZ98CyzoqPSMjOe6SuoQY7Z2hIj0Ld1g/O/UQRuOle2aRtiIRDg9tDcTGAkLfKw==",
|
||||
"version": "7.0.35",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz",
|
||||
"integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==",
|
||||
"requires": {
|
||||
"chalk": "^2.4.2",
|
||||
"source-map": "^0.6.1",
|
||||
@@ -16941,13 +17137,14 @@
|
||||
}
|
||||
},
|
||||
"postcss-selector-parser": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz",
|
||||
"integrity": "sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg==",
|
||||
"version": "6.0.4",
|
||||
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.4.tgz",
|
||||
"integrity": "sha512-gjMeXBempyInaBqpp8gODmwZ52WaYsVOsfr4L4lDQ7n3ncD6mEyySiDtgzCT+NYC0mmeOLvtsF8iaEf0YT6dBw==",
|
||||
"requires": {
|
||||
"cssesc": "^3.0.0",
|
||||
"indexes-of": "^1.0.1",
|
||||
"uniq": "^1.0.1"
|
||||
"uniq": "^1.0.1",
|
||||
"util-deprecate": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"postcss-svgo": {
|
||||
@@ -18394,9 +18591,9 @@
|
||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
||||
},
|
||||
"sass": {
|
||||
"version": "1.26.11",
|
||||
"resolved": "https://registry.npmjs.org/sass/-/sass-1.26.11.tgz",
|
||||
"integrity": "sha512-W1l/+vjGjIamsJ6OnTe0K37U2DBO/dgsv2Z4c89XQ8ZOO6l/VwkqwLSqoYzJeJs6CLuGSTRWc91GbQFL3lvrvw==",
|
||||
"version": "1.27.0",
|
||||
"resolved": "https://registry.npmjs.org/sass/-/sass-1.27.0.tgz",
|
||||
"integrity": "sha512-0gcrER56OkzotK/GGwgg4fPrKuiFlPNitO7eUJ18Bs+/NBlofJfMxmxqpqJxjae9vu0Wq8TZzrSyxZal00WDig==",
|
||||
"requires": {
|
||||
"chokidar": ">=2.0.0 <4.0.0"
|
||||
}
|
||||
@@ -18478,11 +18675,11 @@
|
||||
"integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo="
|
||||
},
|
||||
"selfsigned": {
|
||||
"version": "1.10.7",
|
||||
"resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.7.tgz",
|
||||
"integrity": "sha512-8M3wBCzeWIJnQfl43IKwOmC4H/RAp50S8DF60znzjW5GVqTcSe2vWclt7hmYVPkKPlHWOu5EaWOMZ2Y6W8ZXTA==",
|
||||
"version": "1.10.8",
|
||||
"resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.8.tgz",
|
||||
"integrity": "sha512-2P4PtieJeEwVgTU9QEcwIRDQ/mXJLX8/+I3ur+Pg16nS8oNbrGxEso9NyYWy8NAmXiNl4dlAp5MwoNeCWzON4w==",
|
||||
"requires": {
|
||||
"node-forge": "0.9.0"
|
||||
"node-forge": "^0.10.0"
|
||||
}
|
||||
},
|
||||
"semver": {
|
||||
@@ -20855,9 +21052,9 @@
|
||||
"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
|
||||
},
|
||||
"uuid": {
|
||||
"version": "8.3.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.0.tgz",
|
||||
"integrity": "sha512-fX6Z5o4m6XsXBdli9g7DtWgAx+osMsRRZFKma1mIUsLCz6vRvv+pz5VNbyu9UEDzpMWulZfvpgb/cmDXVulYFQ=="
|
||||
"version": "8.3.1",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.1.tgz",
|
||||
"integrity": "sha512-FOmRr+FmWEIG8uhZv6C2bTgEVXsHk08kE7mPlrBbEe+c3r9pjceVPgupIfNIhc4yx55H69OXANrUaSuu9eInKg=="
|
||||
},
|
||||
"v8-compile-cache": {
|
||||
"version": "2.1.0",
|
||||
@@ -20981,9 +21178,9 @@
|
||||
}
|
||||
},
|
||||
"vue-router": {
|
||||
"version": "3.4.5",
|
||||
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.4.5.tgz",
|
||||
"integrity": "sha512-ioRY5QyDpXM9TDjOX6hX79gtaMXSVDDzSlbIlyAmbHNteIL81WIVB2e+jbzV23vzxtoV0krdS2XHm+GxFg+Nxg=="
|
||||
"version": "3.4.6",
|
||||
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.4.6.tgz",
|
||||
"integrity": "sha512-kaXnB3pfFxhAJl/Mp+XG1HJMyFqrL/xPqV7oXlpXn4AwMmm6VNgf0nllW8ksflmZANfI4kdo0bVn/FYSsAolPQ=="
|
||||
},
|
||||
"vue-style-loader": {
|
||||
"version": "4.1.2",
|
||||
@@ -21269,9 +21466,9 @@
|
||||
}
|
||||
},
|
||||
"webpack-bundle-analyzer": {
|
||||
"version": "3.8.0",
|
||||
"resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-3.8.0.tgz",
|
||||
"integrity": "sha512-PODQhAYVEourCcOuU+NiYI7WdR8QyELZGgPvB1y2tjbUpbmcQOt5Q7jEK+ttd5se0KSBKD9SXHCEozS++Wllmw==",
|
||||
"version": "3.9.0",
|
||||
"resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-3.9.0.tgz",
|
||||
"integrity": "sha512-Ob8amZfCm3rMB1ScjQVlbYYUEJyEjdEtQ92jqiFUYt5VkEeO2v5UMbv49P/gnmCZm3A6yaFQzCBvpZqN4MUsdA==",
|
||||
"requires": {
|
||||
"acorn": "^7.1.1",
|
||||
"acorn-walk": "^7.1.1",
|
||||
@@ -21282,16 +21479,16 @@
|
||||
"express": "^4.16.3",
|
||||
"filesize": "^3.6.1",
|
||||
"gzip-size": "^5.0.0",
|
||||
"lodash": "^4.17.15",
|
||||
"lodash": "^4.17.19",
|
||||
"mkdirp": "^0.5.1",
|
||||
"opener": "^1.5.1",
|
||||
"ws": "^6.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"acorn": {
|
||||
"version": "7.4.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.0.tgz",
|
||||
"integrity": "sha512-+G7P8jJmCHr+S+cLfQxygbWhXy+8YTVGzAkpEbcLo2mLoL7tij/VG41QSHACSf5QgYRhMZYHuNc6drJaO0Da+w=="
|
||||
"version": "7.4.1",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
|
||||
"integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A=="
|
||||
},
|
||||
"acorn-walk": {
|
||||
"version": "7.2.0",
|
||||
|
||||
@@ -18,11 +18,11 @@
|
||||
"@storybook/addon-links": "^5.3.19",
|
||||
"@storybook/addon-notes": "^5.3.21",
|
||||
"@storybook/vue": "^5.3.19",
|
||||
"@vue/cli-plugin-babel": "^4.5.6",
|
||||
"@vue/cli-plugin-eslint": "^4.5.6",
|
||||
"@vue/cli-plugin-router": "^4.5.6",
|
||||
"@vue/cli-plugin-unit-mocha": "^4.5.6",
|
||||
"@vue/cli-service": "^4.5.6",
|
||||
"@vue/cli-plugin-babel": "^4.5.7",
|
||||
"@vue/cli-plugin-eslint": "^4.5.7",
|
||||
"@vue/cli-plugin-router": "^4.5.7",
|
||||
"@vue/cli-plugin-unit-mocha": "^4.5.7",
|
||||
"@vue/cli-service": "^4.5.7",
|
||||
"@vue/test-utils": "1.0.0-beta.29",
|
||||
"amplitude-js": "^7.2.2",
|
||||
"axios": "^0.19.2",
|
||||
@@ -37,26 +37,26 @@
|
||||
"eslint-plugin-mocha": "^5.3.0",
|
||||
"eslint-plugin-vue": "^6.2.2",
|
||||
"habitica-markdown": "^3.0.0",
|
||||
"hellojs": "^1.18.4",
|
||||
"hellojs": "^1.18.6",
|
||||
"inspectpack": "^4.5.2",
|
||||
"intro.js": "^2.9.3",
|
||||
"jquery": "^3.5.1",
|
||||
"lodash": "^4.17.20",
|
||||
"moment": "^2.29.0",
|
||||
"moment": "^2.29.1",
|
||||
"nconf": "^0.10.0",
|
||||
"sass": "^1.26.11",
|
||||
"sass": "^1.27.0",
|
||||
"sass-loader": "^8.0.2",
|
||||
"smartbanner.js": "^1.16.0",
|
||||
"svg-inline-loader": "^0.8.2",
|
||||
"svg-url-loader": "^6.0.0",
|
||||
"svgo": "^1.3.2",
|
||||
"svgo-loader": "^2.2.1",
|
||||
"uuid": "^8.3.0",
|
||||
"uuid": "^8.3.1",
|
||||
"validator": "^13.1.17",
|
||||
"vue": "^2.6.12",
|
||||
"vue-cli-plugin-storybook": "^0.6.1",
|
||||
"vue-mugen-scroll": "^0.2.6",
|
||||
"vue-router": "^3.4.5",
|
||||
"vue-router": "^3.4.6",
|
||||
"vue-template-compiler": "^2.6.12",
|
||||
"vuedraggable": "^2.24.1",
|
||||
"vuejs-datepicker": "git://github.com/habitrpg/vuejs-datepicker.git#153d339e4dbebb73733658aeda1d5b7fcc55b0a0",
|
||||
|
||||
@@ -1,48 +1,66 @@
|
||||
.promo_armoire_backgrounds_202009 {
|
||||
.promo_armoire_backgrounds_202010 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: 0px -452px;
|
||||
background-position: 0px -639px;
|
||||
width: 423px;
|
||||
height: 147px;
|
||||
}
|
||||
.promo_fall_customizations {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: 0px -244px;
|
||||
background-position: -532px 0px;
|
||||
width: 336px;
|
||||
height: 207px;
|
||||
}
|
||||
.promo_fall_festival_2019 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: -532px 0px;
|
||||
background-position: 0px -449px;
|
||||
width: 360px;
|
||||
height: 189px;
|
||||
}
|
||||
.promo_fall_festival_2020 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: -532px -190px;
|
||||
background-position: -361px -449px;
|
||||
width: 360px;
|
||||
height: 174px;
|
||||
}
|
||||
.promo_mystery_202009 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: 0px -600px;
|
||||
background-position: -869px -296px;
|
||||
width: 282px;
|
||||
height: 147px;
|
||||
}
|
||||
.promo_mystery_202010 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: -283px -600px;
|
||||
background-position: -869px -444px;
|
||||
width: 282px;
|
||||
height: 147px;
|
||||
}
|
||||
.promo_sandy_sidekicks_bundle {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: -869px -148px;
|
||||
width: 420px;
|
||||
height: 147px;
|
||||
}
|
||||
.promo_skeleton_achievements {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: 0px -244px;
|
||||
width: 366px;
|
||||
height: 204px;
|
||||
}
|
||||
.promo_spooky_sparkles {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: -424px -639px;
|
||||
width: 423px;
|
||||
height: 147px;
|
||||
}
|
||||
.promo_take_this {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: -893px -170px;
|
||||
background-position: -1152px -296px;
|
||||
width: 96px;
|
||||
height: 69px;
|
||||
}
|
||||
.promo_vampire_potions {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: -424px -452px;
|
||||
background-position: -869px 0px;
|
||||
width: 423px;
|
||||
height: 147px;
|
||||
}
|
||||
@@ -54,13 +72,13 @@
|
||||
}
|
||||
.scene_squall {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: -893px 0px;
|
||||
background-position: -532px -208px;
|
||||
width: 141px;
|
||||
height: 169px;
|
||||
}
|
||||
.scene_strength {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: -337px -244px;
|
||||
background-position: -869px -592px;
|
||||
width: 192px;
|
||||
height: 129px;
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 91 KiB After Width: | Height: | Size: 120 KiB |
|
Before Width: | Height: | Size: 472 KiB After Width: | Height: | Size: 466 KiB |
|
Before Width: | Height: | Size: 565 KiB After Width: | Height: | Size: 555 KiB |
|
Before Width: | Height: | Size: 125 KiB After Width: | Height: | Size: 127 KiB |
|
Before Width: | Height: | Size: 122 KiB After Width: | Height: | Size: 120 KiB |
|
Before Width: | Height: | Size: 111 KiB After Width: | Height: | Size: 112 KiB |
|
Before Width: | Height: | Size: 357 KiB After Width: | Height: | Size: 345 KiB |
|
Before Width: | Height: | Size: 346 KiB After Width: | Height: | Size: 344 KiB |
|
Before Width: | Height: | Size: 154 KiB After Width: | Height: | Size: 162 KiB |
|
Before Width: | Height: | Size: 166 KiB After Width: | Height: | Size: 167 KiB |
|
Before Width: | Height: | Size: 146 KiB After Width: | Height: | Size: 149 KiB |
|
Before Width: | Height: | Size: 139 KiB After Width: | Height: | Size: 139 KiB |
|
Before Width: | Height: | Size: 150 KiB After Width: | Height: | Size: 153 KiB |
|
Before Width: | Height: | Size: 311 KiB After Width: | Height: | Size: 330 KiB |
|
Before Width: | Height: | Size: 174 KiB After Width: | Height: | Size: 173 KiB |
|
Before Width: | Height: | Size: 150 KiB After Width: | Height: | Size: 146 KiB |
|
Before Width: | Height: | Size: 151 KiB After Width: | Height: | Size: 154 KiB |
|
Before Width: | Height: | Size: 158 KiB After Width: | Height: | Size: 158 KiB |
|
Before Width: | Height: | Size: 147 KiB After Width: | Height: | Size: 149 KiB |
|
Before Width: | Height: | Size: 181 KiB After Width: | Height: | Size: 168 KiB |
|
Before Width: | Height: | Size: 172 KiB After Width: | Height: | Size: 182 KiB |
|
Before Width: | Height: | Size: 160 KiB After Width: | Height: | Size: 162 KiB |
|
Before Width: | Height: | Size: 151 KiB After Width: | Height: | Size: 159 KiB |
|
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 61 KiB |
|
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 73 KiB |
|
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 134 KiB After Width: | Height: | Size: 134 KiB |
|
Before Width: | Height: | Size: 123 KiB After Width: | Height: | Size: 124 KiB |
|
Before Width: | Height: | Size: 119 KiB After Width: | Height: | Size: 119 KiB |
|
Before Width: | Height: | Size: 153 KiB After Width: | Height: | Size: 152 KiB |
|
After Width: | Height: | Size: 6.1 KiB |
|
After Width: | Height: | Size: 7.3 KiB |
|
After Width: | Height: | Size: 12 KiB |
@@ -44,7 +44,7 @@
|
||||
|
||||
font-size: 14px;
|
||||
line-height: 1.71;
|
||||
color: $gray-50;
|
||||
color: $gray-50 !important;
|
||||
cursor: pointer;
|
||||
|
||||
&:focus {
|
||||
@@ -54,16 +54,16 @@
|
||||
|
||||
|
||||
&:active, &:hover, &:focus, &.active {
|
||||
background-color: rgba($purple-600, 0.32);
|
||||
color: $purple-200;
|
||||
background-color: rgba($purple-600, 0.32) !important;
|
||||
color: $purple-200 !important;
|
||||
}
|
||||
|
||||
&.dropdown-inactive {
|
||||
cursor: default;
|
||||
|
||||
&:active, &:hover, &.active {
|
||||
background-color: inherit;
|
||||
color: inherit;
|
||||
background-color: inherit !important;
|
||||
color: inherit !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,15 @@
|
||||
padding-left: 0px !important;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
border-bottom-right-radius: 8px;
|
||||
border-bottom-left-radius: 8px;
|
||||
}
|
||||
|
||||
.modal-dialog {
|
||||
margin: 3rem auto 3rem;
|
||||
width: auto;
|
||||
|
||||
@@ -342,7 +342,7 @@
|
||||
|
||||
&-negative {
|
||||
&-enabled {
|
||||
padding-top: 13px;
|
||||
padding-top: 12px;
|
||||
}
|
||||
&-disabled {
|
||||
padding-top: 12px;
|
||||
@@ -351,7 +351,7 @@
|
||||
|
||||
&-positive {
|
||||
&-enabled {
|
||||
padding-top: 9px;
|
||||
padding-top: 8px;
|
||||
}
|
||||
&-disabled {
|
||||
padding-top: 8px;
|
||||
|
||||
@@ -0,0 +1,250 @@
|
||||
<template>
|
||||
<b-modal
|
||||
id="drop-cap-reached"
|
||||
size="md"
|
||||
:hide-header="true"
|
||||
:hide-footer="hasSubscription"
|
||||
>
|
||||
<div class="text-center">
|
||||
<div
|
||||
class="modal-close"
|
||||
@click="close()"
|
||||
>
|
||||
<div
|
||||
v-once
|
||||
class="svg-icon"
|
||||
v-html="icons.close"
|
||||
></div>
|
||||
</div>
|
||||
<h1
|
||||
v-once
|
||||
class="header purple"
|
||||
>
|
||||
{{ $t('dropCapReached') }}
|
||||
</h1>
|
||||
<div class="max-items-wrapper d-flex align-items-center justify-content-center">
|
||||
<div
|
||||
class="svg-icon sparkles sparkles-rotate"
|
||||
v-html="icons.sparkles"
|
||||
></div>
|
||||
<div class="max-items-module d-flex align-items-center justify-content-center flex-column">
|
||||
<h1 class="max-items">
|
||||
{{ maxItems }}
|
||||
</h1>
|
||||
<span
|
||||
v-once
|
||||
class="items-text"
|
||||
>{{ $t('items') }}</span>
|
||||
</div>
|
||||
<div
|
||||
class="svg-icon sparkles"
|
||||
v-html="icons.sparkles"
|
||||
></div>
|
||||
</div>
|
||||
<p
|
||||
v-once
|
||||
class="mb-4"
|
||||
>
|
||||
{{ $t('dropCapExplanation') }}
|
||||
</p>
|
||||
<a
|
||||
v-once
|
||||
class="standard-link d-block mb-3"
|
||||
@click="toWiki()"
|
||||
>
|
||||
{{ $t('dropCapLearnMore') }}
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
slot="modal-footer"
|
||||
class="footer"
|
||||
>
|
||||
<span
|
||||
v-once
|
||||
class="purple d-block font-weight-bold mb-3 mt-3"
|
||||
>
|
||||
{{ $t('lookingForMoreItems') }}
|
||||
</span>
|
||||
<img
|
||||
class="swords mb-3"
|
||||
srcset="
|
||||
~@/assets/images/swords.png,
|
||||
~@/assets/images/swords@2x.png 2x,
|
||||
~@/assets/images/swords@3x.png 3x"
|
||||
src="~@/assets/images/swords.png"
|
||||
>
|
||||
<p
|
||||
v-once
|
||||
class="subs-benefits mb-3"
|
||||
>
|
||||
{{ $t('dropCapSubs') }}
|
||||
</p>
|
||||
<button
|
||||
v-once
|
||||
class="btn btn-primary"
|
||||
@click="toLearnMore()"
|
||||
>
|
||||
{{ $t('learnMore') }}
|
||||
</button>
|
||||
</div>
|
||||
</b-modal>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@import '~@/assets/scss/colors.scss';
|
||||
|
||||
#drop-cap-reached {
|
||||
.modal-body {
|
||||
padding: 0 1.5rem;
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
background: $gray-700;
|
||||
border-top: none;
|
||||
padding: 0 1.5rem 2rem 1.5rem;
|
||||
}
|
||||
|
||||
.modal-dialog {
|
||||
width: 20.625rem;
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.71;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '~@/assets/scss/colors.scss';
|
||||
|
||||
.modal-close {
|
||||
position: absolute;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
padding: 4px;
|
||||
right: 16px;
|
||||
top: 16px;
|
||||
cursor: pointer;
|
||||
.svg-icon {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.subs-benefits {
|
||||
font-size: 0.75rem;
|
||||
line-height: 1.33;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.purple {
|
||||
color: $purple-300;
|
||||
}
|
||||
|
||||
.header {
|
||||
font-size: 1.25rem;
|
||||
line-height: 1.4;
|
||||
text-align: center;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.sparkles {
|
||||
width: 2.5rem;
|
||||
height: 4rem;
|
||||
|
||||
&.sparkles-rotate {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
|
||||
.max-items-wrapper {
|
||||
margin: 17px auto;
|
||||
}
|
||||
|
||||
.max-items-module {
|
||||
background: white;
|
||||
border-radius: 92px;
|
||||
border: 8px solid $purple-400;
|
||||
width: 92px;
|
||||
height: 92px;
|
||||
margin-left: 17px;
|
||||
margin-right: 17px;
|
||||
|
||||
.items-text {
|
||||
font-size: 0.75rem;
|
||||
line-height: 1.33;
|
||||
color: $gray-100;
|
||||
}
|
||||
}
|
||||
|
||||
.max-items {
|
||||
font-size: 2rem;
|
||||
line-height: 1.25;
|
||||
color: $purple-300;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.swords {
|
||||
width: 7rem;
|
||||
height: 3rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import closeIcon from '@/assets/svg/close.svg';
|
||||
import sparkles from '@/assets/svg/star-group.svg';
|
||||
import * as Analytics from '@/libs/analytics';
|
||||
import { mapState } from '@/libs/store';
|
||||
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
icons: Object.freeze({
|
||||
close: closeIcon,
|
||||
sparkles,
|
||||
}),
|
||||
maxItems: null,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState({ user: 'user.data' }),
|
||||
hasSubscription () {
|
||||
return Boolean(this.user.purchased.plan.customerId);
|
||||
},
|
||||
},
|
||||
mounted () {
|
||||
this.$root.$on('habitica:drop-cap-reached', notification => {
|
||||
this.maxItems = notification.data.items;
|
||||
this.$root.$emit('bv::show::modal', 'drop-cap-reached');
|
||||
});
|
||||
},
|
||||
beforeDestroy () {
|
||||
this.$root.$off('habitica:drop-cap-reached');
|
||||
},
|
||||
methods: {
|
||||
close () {
|
||||
this.$root.$emit('bv::hide::modal', 'drop-cap-reached');
|
||||
},
|
||||
toWiki () {
|
||||
window.open('https://habitica.fandom.com/wiki/Drops', '_blank');
|
||||
|
||||
Analytics.track({
|
||||
hitType: 'event',
|
||||
eventCategory: 'drop-cap-reached',
|
||||
eventAction: 'click',
|
||||
eventLabel: 'Drop Cap Reached > Modal > Wiki',
|
||||
});
|
||||
},
|
||||
toLearnMore () {
|
||||
Analytics.track({
|
||||
hitType: 'event',
|
||||
eventCategory: 'drop-cap-reached',
|
||||
eventAction: 'click',
|
||||
eventLabel: 'Drop Cap Reached > Modal > Subscriptions',
|
||||
});
|
||||
|
||||
this.close();
|
||||
this.$router.push('/user/settings/subscription');
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -1,81 +0,0 @@
|
||||
<template>
|
||||
<b-modal
|
||||
id="new-stuff"
|
||||
size="lg"
|
||||
:hide-header="true"
|
||||
:hide-footer="true"
|
||||
no-close-on-esc="no-close-on-esc"
|
||||
no-close-on-backdrop="no-close-on-backdrop"
|
||||
>
|
||||
<div class="modal-body">
|
||||
<div
|
||||
class="static-view"
|
||||
v-html="html"
|
||||
></div>
|
||||
</div>
|
||||
<div class="modal-footer d-flex align-items-center pb-0">
|
||||
<a
|
||||
href="http://habitica.fandom.com/wiki/Whats_New"
|
||||
target="_blank"
|
||||
class="mr-auto"
|
||||
>{{ this.$t('newsArchive') }}</a>
|
||||
<button
|
||||
class="btn btn-secondary ml-auto"
|
||||
@click="tellMeLater()"
|
||||
>
|
||||
{{ this.$t('tellMeLater') }}
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-primary"
|
||||
@click="dismissAlert();"
|
||||
>
|
||||
{{ this.$t('dismissAlert') }}
|
||||
</button>
|
||||
</div>
|
||||
</b-modal>
|
||||
</template>
|
||||
|
||||
<style lang='scss'>
|
||||
@import '~@/assets/scss/static.scss';
|
||||
#new-stuff {
|
||||
.modal-body .modal-body {
|
||||
padding-top: 0rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import axios from 'axios';
|
||||
import { mapState } from '@/libs/store';
|
||||
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
html: '',
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState({ user: 'user.data' }),
|
||||
},
|
||||
async mounted () {
|
||||
this.$root.$on('bv::show::modal', async modalId => {
|
||||
if (modalId !== 'new-stuff') return;
|
||||
const response = await axios.get('/api/v4/news');
|
||||
this.html = response.data.html;
|
||||
});
|
||||
},
|
||||
beforeDestroy () {
|
||||
this.$root.$off('bv::show::modal');
|
||||
},
|
||||
methods: {
|
||||
tellMeLater () {
|
||||
this.$store.dispatch('user:newStuffLater');
|
||||
this.$root.$emit('bv::hide::modal', 'new-stuff');
|
||||
},
|
||||
dismissAlert () {
|
||||
this.$store.dispatch('user:set', { 'flags.newStuff': false });
|
||||
this.$root.$emit('bv::hide::modal', 'new-stuff');
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -22,12 +22,15 @@
|
||||
export default {
|
||||
props: {
|
||||
categories: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
owner: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
member: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -25,43 +25,41 @@
|
||||
<div class="col-12 col-md-6">
|
||||
<div class="row icon-row">
|
||||
<div
|
||||
class="col-4 offset-4"
|
||||
:class="{ 'offset-8': isParty }"
|
||||
class="item-with-icon"
|
||||
tabindex="0"
|
||||
role="button"
|
||||
@keyup.enter="showMemberModal()"
|
||||
@click="showMemberModal()"
|
||||
>
|
||||
<div
|
||||
class="item-with-icon"
|
||||
@click="showMemberModal()"
|
||||
v-if="group.memberCount > 1000"
|
||||
class="svg-icon shield"
|
||||
v-html="icons.goldGuildBadgeIcon"
|
||||
></div>
|
||||
<div
|
||||
v-if="group.memberCount > 100 && group.memberCount < 999"
|
||||
class="svg-icon shield"
|
||||
v-html="icons.silverGuildBadgeIcon"
|
||||
></div>
|
||||
<div
|
||||
v-if="group.memberCount < 100"
|
||||
class="svg-icon shield"
|
||||
v-html="icons.bronzeGuildBadgeIcon"
|
||||
></div>
|
||||
<span class="number">{{ group.memberCount | abbrNum }}</span>
|
||||
<div
|
||||
v-once
|
||||
class="member-list label"
|
||||
>
|
||||
<div
|
||||
v-if="group.memberCount > 1000"
|
||||
class="svg-icon shield"
|
||||
v-html="icons.goldGuildBadgeIcon"
|
||||
></div>
|
||||
<div
|
||||
v-if="group.memberCount > 100 && group.memberCount < 999"
|
||||
class="svg-icon shield"
|
||||
v-html="icons.silverGuildBadgeIcon"
|
||||
></div>
|
||||
<div
|
||||
v-if="group.memberCount < 100"
|
||||
class="svg-icon shield"
|
||||
v-html="icons.bronzeGuildBadgeIcon"
|
||||
></div>
|
||||
<span class="number">{{ group.memberCount | abbrNum }}</span>
|
||||
<div
|
||||
v-once
|
||||
class="member-list label"
|
||||
>
|
||||
{{ $t('memberList') }}
|
||||
</div>
|
||||
{{ $t('memberList') }}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="!isParty"
|
||||
class="col-4"
|
||||
>
|
||||
<div v-if="!isParty">
|
||||
<div
|
||||
class="item-with-icon"
|
||||
tabindex="0"
|
||||
role="button"
|
||||
@keyup.enter="showGroupGems()"
|
||||
@click="showGroupGems()"
|
||||
>
|
||||
<div
|
||||
@@ -225,9 +223,13 @@
|
||||
box-shadow: 0 2px 2px 0 rgba(26, 24, 29, 0.16), 0 1px 4px 0 rgba(26, 24, 29, 0.12);
|
||||
padding: 1em;
|
||||
text-align: center;
|
||||
min-width: 80px;
|
||||
max-width: 120px;
|
||||
min-width: 120px;
|
||||
height: 76px;
|
||||
margin-right: 1rem;
|
||||
|
||||
&:last-of-type {
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
.svg-icon.shield, .svg-icon.gem {
|
||||
width: 28px;
|
||||
@@ -312,6 +314,7 @@
|
||||
|
||||
.icon-row {
|
||||
margin-top: 1em;
|
||||
justify-content: flex-end;
|
||||
|
||||
.number {
|
||||
font-size: 22px;
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
@import '~@/assets/scss/colors.scss';
|
||||
|
||||
.message-count {
|
||||
background-color: $blue-50;
|
||||
background-color: $red-50;
|
||||
border-radius: 50%;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
@@ -31,7 +31,6 @@
|
||||
right: 0.3em;
|
||||
top: -0.8em;
|
||||
padding: 0.2em;
|
||||
background-color: $red-50;
|
||||
}
|
||||
|
||||
.message-count.top-count-gray {
|
||||
|
||||
@@ -118,11 +118,11 @@
|
||||
}
|
||||
|
||||
.notification-remove {
|
||||
position: absolute;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
padding: 4px;
|
||||
right: 24px;
|
||||
position: relative;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
right: 0px;
|
||||
top: 10.5px;
|
||||
|
||||
.svg-icon {
|
||||
width: 10px;
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
<template>
|
||||
<base-notification
|
||||
:can-remove="canRemove"
|
||||
:has-icon="false"
|
||||
:notification="notification"
|
||||
:read-after-click="true"
|
||||
@click="action"
|
||||
>
|
||||
<div
|
||||
slot="content"
|
||||
class="notification-bold-blue"
|
||||
>
|
||||
{{ $t('dropCapReached') }}
|
||||
</div>
|
||||
</base-notification>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import BaseNotification from './base';
|
||||
import * as Analytics from '@/libs/analytics';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
BaseNotification,
|
||||
},
|
||||
props: {
|
||||
notification: { type: Object, required: true },
|
||||
canRemove: { type: Boolean, required: true },
|
||||
},
|
||||
methods: {
|
||||
action () {
|
||||
this.$root.$emit('habitica:drop-cap-reached', this.notification);
|
||||
|
||||
Analytics.track({
|
||||
hitType: 'event',
|
||||
eventCategory: 'drop-cap-reached',
|
||||
eventAction: 'click',
|
||||
eventLabel: 'Drop Cap Reached > Notification Click',
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -149,6 +149,7 @@ import ACHIEVEMENT_MIND_OVER_MATTER from './notifications/mindOverMatter';
|
||||
import ONBOARDING_COMPLETE from './notifications/onboardingComplete';
|
||||
import GIFT_ONE_GET_ONE from './notifications/g1g1';
|
||||
import OnboardingGuide from './onboardingGuide';
|
||||
import DROP_CAP_REACHED from './notifications/dropCapReached';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -178,6 +179,7 @@ export default {
|
||||
OnboardingGuide,
|
||||
ONBOARDING_COMPLETE,
|
||||
GIFT_ONE_GET_ONE,
|
||||
DROP_CAP_REACHED,
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
@@ -203,7 +205,7 @@ export default {
|
||||
'GROUP_TASK_CLAIMED', 'NEW_MYSTERY_ITEMS', 'CARD_RECEIVED',
|
||||
'NEW_INBOX_MESSAGE', 'NEW_CHAT_MESSAGE', 'UNALLOCATED_STATS_POINTS',
|
||||
'ACHIEVEMENT_JUST_ADD_WATER', 'ACHIEVEMENT_LOST_MASTERCLASSER', 'ACHIEVEMENT_MIND_OVER_MATTER',
|
||||
'VERIFY_USERNAME', 'ONBOARDING_COMPLETE',
|
||||
'VERIFY_USERNAME', 'ONBOARDING_COMPLETE', 'DROP_CAP_REACHED',
|
||||
],
|
||||
};
|
||||
},
|
||||
|
||||
@@ -23,13 +23,6 @@
|
||||
slot="dropdown-content"
|
||||
class="user-dropdown"
|
||||
>
|
||||
<a
|
||||
class="topbar-dropdown-item dropdown-item edit-avatar dropdown-separated"
|
||||
@click="showAvatar('body', 'size')"
|
||||
>
|
||||
<h3>{{ user.profile.name }}</h3>
|
||||
<span class="small-text">{{ $t('editAvatar') }}</span>
|
||||
</a>
|
||||
<a
|
||||
class="topbar-dropdown-item nav-link dropdown-item
|
||||
dropdown-separated d-flex justify-content-between align-items-center"
|
||||
@@ -43,20 +36,24 @@
|
||||
</a>
|
||||
<a
|
||||
class="topbar-dropdown-item dropdown-item"
|
||||
@click="showAvatar('body', 'size')"
|
||||
>{{ $t('editAvatar') }}</a>
|
||||
<a
|
||||
class="topbar-dropdown-item dropdown-item dropdown-separated"
|
||||
@click="showAvatar('backgrounds', '2020')"
|
||||
>{{ $t('backgrounds') }}</a>
|
||||
<a
|
||||
class="topbar-dropdown-item dropdown-item"
|
||||
@click="showProfile('profile')"
|
||||
>{{ $t('profile') }}</a>
|
||||
<a
|
||||
class="topbar-dropdown-item dropdown-item"
|
||||
@click="showProfile('stats')"
|
||||
>{{ $t('stats') }}</a>
|
||||
<a
|
||||
class="topbar-dropdown-item dropdown-item"
|
||||
class="topbar-dropdown-item dropdown-item dropdown-separated"
|
||||
@click="showProfile('achievements')"
|
||||
>{{ $t('achievements') }}</a>
|
||||
<a
|
||||
class="topbar-dropdown-item dropdown-item dropdown-separated"
|
||||
@click="showProfile('profile')"
|
||||
>{{ $t('profile') }}</a>
|
||||
<router-link
|
||||
class="topbar-dropdown-item dropdown-item"
|
||||
:to="{name: 'site'}"
|
||||
@@ -75,19 +72,36 @@
|
||||
>{{ $t('logout') }}</a>
|
||||
<li
|
||||
v-if="!user.purchased.plan.customerId"
|
||||
@click="showBuyGemsModal()"
|
||||
class="topbar-dropdown-item dropdown-item dropdown-separated
|
||||
d-flex flex-column justify-content-center align-items-center dropdown-inactive subs-info"
|
||||
>
|
||||
<div class="topbar-dropdown-item dropdown-item text-center">
|
||||
<h3 class="purple">
|
||||
{{ $t('needMoreGems') }}
|
||||
</h3>
|
||||
<span class="small-text">{{ $t('needMoreGemsInfo') }}</span>
|
||||
</div>
|
||||
<div class="learn-background py-2 text-center">
|
||||
<button class="btn btn-primary btn-lg learn-button">
|
||||
{{ $t('learnMore') }}
|
||||
</button>
|
||||
</div>
|
||||
<span
|
||||
v-once
|
||||
class="purple d-block font-weight-bold mb-3"
|
||||
>
|
||||
{{ $t('lookingForMoreItems') }}
|
||||
</span>
|
||||
<img
|
||||
class="swords mb-3"
|
||||
srcset="
|
||||
~@/assets/images/swords.png,
|
||||
~@/assets/images/swords@2x.png 2x,
|
||||
~@/assets/images/swords@3x.png 3x"
|
||||
src="~@/assets/images/swords.png"
|
||||
>
|
||||
<p
|
||||
v-once
|
||||
class="subs-benefits mb-3"
|
||||
>
|
||||
{{ $t('dropCapSubs') }}
|
||||
</p>
|
||||
<button
|
||||
v-once
|
||||
class="btn btn-primary mb-4"
|
||||
@click="toLearnMore()"
|
||||
>
|
||||
{{ $t('learnMore') }}
|
||||
</button>
|
||||
</li>
|
||||
</div>
|
||||
</menu-dropdown>
|
||||
@@ -96,39 +110,30 @@
|
||||
<style lang='scss' scoped>
|
||||
@import '~@/assets/scss/colors.scss';
|
||||
|
||||
.edit-avatar {
|
||||
h3 {
|
||||
color: $gray-10;
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
padding-top: 16px;
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
|
||||
.user-dropdown {
|
||||
width: 14.75em;
|
||||
}
|
||||
|
||||
.learn-background {
|
||||
background: url('~@/assets/images/gem-rain.png') bottom left no-repeat,
|
||||
url('~@/assets/images/gold-rain.png') bottom right no-repeat;
|
||||
}
|
||||
|
||||
.learn-button {
|
||||
margin: 0.75em 0.75em 0.75em 1em;
|
||||
}
|
||||
|
||||
.purple {
|
||||
color: $purple-200;
|
||||
color: $purple-300;
|
||||
}
|
||||
|
||||
.small-text {
|
||||
color: $gray-200;
|
||||
.subs-info {
|
||||
padding-top: 1.438rem;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.subs-benefits {
|
||||
font-size: 0.75rem;
|
||||
line-height: 1.33;
|
||||
font-style: normal;
|
||||
display: block;
|
||||
white-space: normal;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.swords {
|
||||
width: 7rem;
|
||||
height: 3rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -172,15 +177,15 @@ export default {
|
||||
showProfile (startingPage) {
|
||||
this.$router.push({ name: startingPage });
|
||||
},
|
||||
showBuyGemsModal () {
|
||||
toLearnMore () {
|
||||
Analytics.track({
|
||||
hitType: 'event',
|
||||
eventCategory: 'button',
|
||||
eventAction: 'click',
|
||||
eventLabel: 'Gems > User Dropdown',
|
||||
eventLabel: 'User Dropdown > Subscriptions',
|
||||
});
|
||||
|
||||
this.$root.$emit('bv::show::modal', 'buy-gems', { alreadyTracked: true });
|
||||
this.$router.push({ name: 'subscription' });
|
||||
},
|
||||
logout () {
|
||||
this.$store.dispatch('auth:logout');
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
<div class="class-badge d-flex justify-content-center">
|
||||
<div
|
||||
class="align-self-center svg-icon"
|
||||
:aria-label="$t(memberClass)"
|
||||
v-html="icons[memberClass]"
|
||||
></div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
<template>
|
||||
<b-modal
|
||||
id="new-stuff"
|
||||
size="lg"
|
||||
:hide-header="true"
|
||||
:hide-footer="true"
|
||||
no-close-on-esc
|
||||
no-close-on-backdrop
|
||||
@shown="onShow()"
|
||||
>
|
||||
<div class="modal-body">
|
||||
<news-content ref="newsContent" />
|
||||
</div>
|
||||
|
||||
<div class="modal-footer d-flex align-items-center pb-0">
|
||||
<a
|
||||
href="http://habitica.fandom.com/wiki/Whats_New"
|
||||
target="_blank"
|
||||
class="mr-auto"
|
||||
>{{ $t('newsArchive') }}</a>
|
||||
<button
|
||||
class="btn btn-secondary ml-auto"
|
||||
@click="tellMeLater()"
|
||||
>
|
||||
{{ $t('tellMeLater') }}
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-primary"
|
||||
@click="dismissAlert()"
|
||||
>
|
||||
{{ $t('dismissAlert') }}
|
||||
</button>
|
||||
</div>
|
||||
</b-modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import newsContent from './newsContent';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
newsContent,
|
||||
},
|
||||
methods: {
|
||||
async onShow () {
|
||||
this.$refs.newsContent.getPosts();
|
||||
},
|
||||
tellMeLater () {
|
||||
this.$store.dispatch('news:remindMeLater');
|
||||
this.$root.$emit('bv::hide::modal', 'new-stuff');
|
||||
},
|
||||
dismissAlert () {
|
||||
this.$store.dispatch('news:markAsRead');
|
||||
this.$root.$emit('bv::hide::modal', 'new-stuff');
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,107 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="bailey-header d-flex align-items-center mb-3">
|
||||
<div class="npc_bailey mr-3"></div>
|
||||
<h1 v-once>
|
||||
{{ $t('newStuff') }}
|
||||
</h1>
|
||||
</div>
|
||||
<div
|
||||
v-for="(post, index) in posts"
|
||||
:key="post._id"
|
||||
class="static-view bailey"
|
||||
:class="{'bailey-last': index == (posts.length - 1)}"
|
||||
>
|
||||
<small
|
||||
v-if="!post.published"
|
||||
class="draft"
|
||||
>DRAFT</small>
|
||||
<h2 class="title">
|
||||
{{ getPostDate(post) }} - {{ post.title.toUpperCase() }}
|
||||
</h2>
|
||||
|
||||
<hr>
|
||||
|
||||
<div v-html="renderMarkdown(post.text)"></div>
|
||||
<small>by {{ post.credits }}</small>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang='scss'>
|
||||
@import '~@/assets/scss/static.scss';
|
||||
</style>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
@import '~@/assets/scss/colors.scss';
|
||||
|
||||
h1 {
|
||||
color: $purple-200;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.bailey {
|
||||
margin-bottom: 1rem;
|
||||
|
||||
&.bailey-last {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.title {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.draft {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
color: $purple-200;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import moment from 'moment';
|
||||
import habiticaMarkdown from 'habitica-markdown';
|
||||
import { mapState } from '@/libs/store';
|
||||
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
posts: [],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState({ user: 'user.data' }),
|
||||
},
|
||||
methods: {
|
||||
async getPosts () {
|
||||
const postsFromServer = await this.$store.dispatch('news:fetch');
|
||||
|
||||
// Show the last published post + any draft for the authorized users
|
||||
this.posts = [];
|
||||
|
||||
const lastPublishedPost = postsFromServer
|
||||
.find(p => p.published && moment().isAfter(p.publishDate));
|
||||
|
||||
if (lastPublishedPost) this.posts.push(lastPublishedPost);
|
||||
|
||||
// If the user is authorized, show any draft
|
||||
if (this.user && this.user.contributor.newsPoster) {
|
||||
this.posts.unshift(
|
||||
...postsFromServer
|
||||
.filter(p => !p.published || moment().isBefore(p.publishDate)),
|
||||
);
|
||||
}
|
||||
},
|
||||
renderMarkdown (text) {
|
||||
return habiticaMarkdown.unsafeHTMLRender(text);
|
||||
},
|
||||
getPostDate (post) {
|
||||
const format = this.user ? this.user.preferences.dateFormat.toUpperCase() : 'MM/DD/yyyy';
|
||||
return moment(post.publishDate).format(format);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -35,6 +35,7 @@
|
||||
<mind-over-matter />
|
||||
<onboarding-complete />
|
||||
<first-drops />
|
||||
<drop-cap-reached-modal />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -118,7 +119,7 @@ import notifications from '@/mixins/notifications';
|
||||
import guide from '@/mixins/guide';
|
||||
|
||||
import yesterdailyModal from './tasks/yesterdailyModal';
|
||||
import newStuff from './achievements/newStuff';
|
||||
import newStuff from './news/modal';
|
||||
import death from './achievements/death';
|
||||
import lowHealth from './achievements/lowHealth';
|
||||
import levelUp from './achievements/levelUp';
|
||||
@@ -145,6 +146,7 @@ import loginIncentives from './achievements/login-incentives';
|
||||
import onboardingComplete from './achievements/onboardingComplete';
|
||||
import verifyUsername from './settings/verifyUsername';
|
||||
import firstDrops from './achievements/firstDrops';
|
||||
import DropCapReachedModal from '@/components/achievements/dropCapReached';
|
||||
|
||||
const NOTIFICATIONS = {
|
||||
CHALLENGE_JOINED_ACHIEVEMENT: {
|
||||
@@ -186,7 +188,7 @@ const NOTIFICATIONS = {
|
||||
label: $t => `${$t('achievement')}: ${$t('achievementBackToBasics')}`,
|
||||
modalId: 'generic-achievement',
|
||||
data: {
|
||||
achievement: 'backToBasics', // defined manually until the server sends all the necessary data
|
||||
achievement: 'backToBasics',
|
||||
},
|
||||
},
|
||||
ACHIEVEMENT_DUST_DEVIL: {
|
||||
@@ -194,7 +196,7 @@ const NOTIFICATIONS = {
|
||||
label: $t => `${$t('achievement')}: ${$t('achievementDustDevil')}`,
|
||||
modalId: 'generic-achievement',
|
||||
data: {
|
||||
achievement: 'dustDevil', // defined manually until the server sends all the necessary data
|
||||
achievement: 'dustDevil',
|
||||
},
|
||||
},
|
||||
ACHIEVEMENT_ARID_AUTHORITY: {
|
||||
@@ -202,7 +204,7 @@ const NOTIFICATIONS = {
|
||||
label: $t => `${$t('achievement')}: ${$t('achievementAridAuthority')}`,
|
||||
modalId: 'generic-achievement',
|
||||
data: {
|
||||
achievement: 'aridAuthority', // defined manually until the server sends all the necessary data
|
||||
achievement: 'aridAuthority',
|
||||
},
|
||||
},
|
||||
ACHIEVEMENT_PARTY_UP: {
|
||||
@@ -212,7 +214,7 @@ const NOTIFICATIONS = {
|
||||
data: {
|
||||
message: $t => $t('achievement'),
|
||||
modalText: $t => $t('achievementPartyUp'),
|
||||
achievement: 'partyUp', // defined manually until the server sends all the necessary data
|
||||
achievement: 'partyUp',
|
||||
},
|
||||
},
|
||||
ACHIEVEMENT_PARTY_ON: {
|
||||
@@ -222,7 +224,7 @@ const NOTIFICATIONS = {
|
||||
data: {
|
||||
message: $t => $t('achievement'),
|
||||
modalText: $t => $t('achievementPartyOn'),
|
||||
achievement: 'partyOn', // defined manually until the server sends all the necessary data
|
||||
achievement: 'partyOn',
|
||||
},
|
||||
},
|
||||
ACHIEVEMENT_BEAST_MASTER: {
|
||||
@@ -232,7 +234,7 @@ const NOTIFICATIONS = {
|
||||
data: {
|
||||
message: $t => $t('achievement'),
|
||||
modalText: $t => $t('beastAchievement'),
|
||||
achievement: 'beastMaster', // defined manually until the server sends all the necessary data
|
||||
achievement: 'beastMaster',
|
||||
},
|
||||
},
|
||||
ACHIEVEMENT_MOUNT_MASTER: {
|
||||
@@ -242,7 +244,7 @@ const NOTIFICATIONS = {
|
||||
data: {
|
||||
message: $t => $t('achievement'),
|
||||
modalText: $t => $t('mountAchievement'),
|
||||
achievement: 'mountMaster', // defined manually until the server sends all the necessary data
|
||||
achievement: 'mountMaster',
|
||||
},
|
||||
},
|
||||
ACHIEVEMENT_TRIAD_BINGO: {
|
||||
@@ -252,7 +254,7 @@ const NOTIFICATIONS = {
|
||||
data: {
|
||||
message: $t => $t('achievement'),
|
||||
modalText: $t => $t('triadBingoAchievement'),
|
||||
achievement: 'triadBingo', // defined manually until the server sends all the necessary data
|
||||
achievement: 'triadBingo',
|
||||
},
|
||||
},
|
||||
ACHIEVEMENT_MONSTER_MAGUS: {
|
||||
@@ -260,7 +262,7 @@ const NOTIFICATIONS = {
|
||||
label: $t => `${$t('achievement')}: ${$t('achievementMonsterMagus')}`,
|
||||
modalId: 'generic-achievement',
|
||||
data: {
|
||||
achievement: 'monsterMagus', // defined manually until the server sends all the necessary data
|
||||
achievement: 'monsterMagus',
|
||||
},
|
||||
},
|
||||
ACHIEVEMENT_UNDEAD_UNDERTAKER: {
|
||||
@@ -268,7 +270,7 @@ const NOTIFICATIONS = {
|
||||
label: $t => `${$t('achievement')}: ${$t('achievementUndeadUndertaker')}`,
|
||||
modalId: 'generic-achievement',
|
||||
data: {
|
||||
achievement: 'undeadUndertaker', // defined manually until the server sends all the necessary data
|
||||
achievement: 'undeadUndertaker',
|
||||
},
|
||||
},
|
||||
ACHIEVEMENT: { // data filled in handleUserNotifications
|
||||
@@ -285,7 +287,7 @@ const NOTIFICATIONS = {
|
||||
label: $t => `${$t('achievement')}: ${$t('achievementPrimedForPainting')}`,
|
||||
modalId: 'generic-achievement',
|
||||
data: {
|
||||
achievement: 'primedForPainting', // defined manually until the server sends all the necessary data
|
||||
achievement: 'primedForPainting',
|
||||
},
|
||||
},
|
||||
ACHIEVEMENT_PEARLY_PRO: {
|
||||
@@ -293,7 +295,7 @@ const NOTIFICATIONS = {
|
||||
label: $t => `${$t('achievement')}: ${$t('achievementPearlyPro')}`,
|
||||
modalId: 'generic-achievement',
|
||||
data: {
|
||||
achievement: 'pearlyPro', // defined manually until the server sends all the necessary data
|
||||
achievement: 'pearlyPro',
|
||||
},
|
||||
},
|
||||
ACHIEVEMENT_TICKLED_PINK: {
|
||||
@@ -301,7 +303,7 @@ const NOTIFICATIONS = {
|
||||
label: $t => `${$t('achievement')}: ${$t('achievementTickledPink')}`,
|
||||
modalId: 'generic-achievement',
|
||||
data: {
|
||||
achievement: 'tickledPink', // defined manually until the server sends all the necessary data
|
||||
achievement: 'tickledPink',
|
||||
},
|
||||
},
|
||||
ACHIEVEMENT_ROSY_OUTLOOK: {
|
||||
@@ -309,7 +311,7 @@ const NOTIFICATIONS = {
|
||||
label: $t => `${$t('achievement')}: ${$t('achievementRosyOutlook')}`,
|
||||
modalId: 'generic-achievement',
|
||||
data: {
|
||||
achievement: 'rosyOutlook', // defined manually until the server sends all the necessary data
|
||||
achievement: 'rosyOutlook',
|
||||
},
|
||||
},
|
||||
ACHIEVEMENT_BUG_BONANZA: {
|
||||
@@ -317,7 +319,7 @@ const NOTIFICATIONS = {
|
||||
label: $t => `${$t('achievement')}: ${$t('achievementBugBonanza')}`,
|
||||
modalId: 'generic-achievement',
|
||||
data: {
|
||||
achievement: 'bugBonanza', // defined manually until the server sends all the necessary data
|
||||
achievement: 'bugBonanza',
|
||||
},
|
||||
},
|
||||
ACHIEVEMENT_BARE_NECESSITIES: {
|
||||
@@ -325,7 +327,7 @@ const NOTIFICATIONS = {
|
||||
label: $t => `${$t('achievement')}: ${$t('achievementBareNecessities')}`,
|
||||
modalId: 'generic-achievement',
|
||||
data: {
|
||||
achievement: 'bareNecessities', // defined manually until the server sends all the necessary data
|
||||
achievement: 'bareNecessities',
|
||||
},
|
||||
},
|
||||
ACHIEVEMENT_FRESHWATER_FRIENDS: {
|
||||
@@ -333,7 +335,7 @@ const NOTIFICATIONS = {
|
||||
label: $t => `${$t('achievement')}: ${$t('achievementFreshwaterFriends')}`,
|
||||
modalId: 'generic-achievement',
|
||||
data: {
|
||||
achievement: 'freshwaterFriends', // defined manually until the server sends all the necessary data
|
||||
achievement: 'freshwaterFriends',
|
||||
},
|
||||
},
|
||||
ACHIEVEMENT_GOOD_AS_GOLD: {
|
||||
@@ -341,7 +343,7 @@ const NOTIFICATIONS = {
|
||||
label: $t => `${$t('achievement')}: ${$t('achievementGoodAsGold')}`,
|
||||
modalId: 'generic-achievement',
|
||||
data: {
|
||||
achievement: 'goodAsGold', // defined manually until the server sends all the necessary data
|
||||
achievement: 'goodAsGold',
|
||||
},
|
||||
},
|
||||
ACHIEVEMENT_ALL_THAT_GLITTERS: {
|
||||
@@ -349,7 +351,23 @@ const NOTIFICATIONS = {
|
||||
label: $t => `${$t('achievement')}: ${$t('achievementAllThatGlitters')}`,
|
||||
modalId: 'generic-achievement',
|
||||
data: {
|
||||
achievement: 'allThatGlitters', // defined manually until the server sends all the necessary data
|
||||
achievement: 'allThatGlitters',
|
||||
},
|
||||
},
|
||||
ACHIEVEMENT_BONE_COLLECTOR: {
|
||||
achievement: true,
|
||||
label: $t => `${$t('achievement')}: ${$t('achievementBoneCollector')}`,
|
||||
modalId: 'generic-achievement',
|
||||
data: {
|
||||
achievement: 'boneCollector',
|
||||
},
|
||||
},
|
||||
ACHIEVEMENT_SKELETON_CREW: {
|
||||
achievement: true,
|
||||
label: $t => `${$t('achievement')}: ${$t('achievementSkeletonCrew')}`,
|
||||
modalId: 'generic-achievement',
|
||||
data: {
|
||||
achievement: 'skeletonCrew',
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -384,6 +402,7 @@ export default {
|
||||
justAddWater,
|
||||
onboardingComplete,
|
||||
firstDrops,
|
||||
DropCapReachedModal,
|
||||
},
|
||||
mixins: [notifications, guide],
|
||||
data () {
|
||||
@@ -412,6 +431,7 @@ export default {
|
||||
'ACHIEVEMENT_PEARLY_PRO', 'ACHIEVEMENT_TICKLED_PINK', 'ACHIEVEMENT_ROSY_OUTLOOK', 'ACHIEVEMENT',
|
||||
'ONBOARDING_COMPLETE', 'FIRST_DROPS', 'ACHIEVEMENT_BUG_BONANZA', 'ACHIEVEMENT_BARE_NECESSITIES',
|
||||
'ACHIEVEMENT_FRESHWATER_FRIENDS', 'ACHIEVEMENT_GOOD_AS_GOLD', 'ACHIEVEMENT_ALL_THAT_GLITTERS',
|
||||
'ACHIEVEMENT_BONE_COLLECTOR', 'ACHIEVEMENT_SKELETON_CREW',
|
||||
].forEach(type => {
|
||||
handledNotifications[type] = true;
|
||||
});
|
||||
@@ -828,6 +848,8 @@ export default {
|
||||
case 'ACHIEVEMENT_FRESHWATER_FRIENDS':
|
||||
case 'ACHIEVEMENT_GOOD_AS_GOLD':
|
||||
case 'ACHIEVEMENT_ALL_THAT_GLITTERS':
|
||||
case 'ACHIEVEMENT_BONE_COLLECTOR':
|
||||
case 'ACHIEVEMENT_SKELETON_CREW':
|
||||
case 'GENERIC_ACHIEVEMENT':
|
||||
this.showNotificationWithModal(notification);
|
||||
break;
|
||||
|
||||
@@ -7,9 +7,9 @@
|
||||
<span
|
||||
v-if="withPin"
|
||||
class="badge-dialog"
|
||||
tabindex="0"
|
||||
@click.prevent.stop="togglePinned()"
|
||||
@keypress.enter.prevent.stop="togglePinned()"
|
||||
tabindex="0"
|
||||
>
|
||||
<pin-badge
|
||||
:pinned="isPinned"
|
||||
@@ -19,9 +19,9 @@
|
||||
<span
|
||||
class="svg-icon icon-12 close-icon"
|
||||
aria-hidden="true"
|
||||
tabindex="0"
|
||||
@click="hideDialog()"
|
||||
@keypress.enter="hideDialog()"
|
||||
tabindex="0"
|
||||
v-html="icons.close"
|
||||
></span>
|
||||
</div>
|
||||
@@ -154,8 +154,8 @@
|
||||
!enoughCurrency(getPriceClass(), item.value * selectedAmountToBuy)"
|
||||
:class="{'notEnough': !preventHealthPotion ||
|
||||
!enoughCurrency(getPriceClass(), item.value * selectedAmountToBuy)}"
|
||||
@click="buyItem()"
|
||||
tabindex="0"
|
||||
@click="buyItem()"
|
||||
>
|
||||
{{ $t('buyNow') }}
|
||||
</button>
|
||||
|
||||