mirror of
https://github.com/HabitRPG/habitica.git
synced 2026-05-12 11:39:44 -05:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 84708ca368 |
@@ -2,9 +2,6 @@ name: Test
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
version: "3"
|
||||
services:
|
||||
client:
|
||||
build:
|
||||
@@ -8,6 +9,7 @@ services:
|
||||
- server
|
||||
environment:
|
||||
- BASE_URL=http://server:3000
|
||||
image: habitica
|
||||
networks:
|
||||
- habitica
|
||||
ports:
|
||||
@@ -25,6 +27,7 @@ services:
|
||||
- mongo
|
||||
environment:
|
||||
- NODE_DB_URI=mongodb://mongo/habitrpg
|
||||
image: habitica
|
||||
networks:
|
||||
- habitica
|
||||
ports:
|
||||
|
||||
@@ -1,119 +0,0 @@
|
||||
/* eslint-disable no-console */
|
||||
const MIGRATION_NAME = '20221031_pet_set_group_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-Skeleton']
|
||||
&& pets['TigerCub-Skeleton']
|
||||
&& pets['PandaCub-Skeleton']
|
||||
&& pets['LionCub-Skeleton']
|
||||
&& pets['Fox-Skeleton']
|
||||
&& pets['FlyingPig-Skeleton']
|
||||
&& pets['Dragon-Skeleton']
|
||||
&& pets['Cactus-Skeleton']
|
||||
&& pets['BearCub-Skeleton']
|
||||
&& pets['Gryphon-Skeleton']
|
||||
&& pets['Hedgehog-Skeleton']
|
||||
&& pets['Deer-Skeleton']
|
||||
&& pets['Egg-Skeleton']
|
||||
&& pets['Rat-Skeleton']
|
||||
&& pets['Octopus-Skeleton']
|
||||
&& pets['Seahorse-Skeleton']
|
||||
&& pets['Parrot-Skeleton']
|
||||
&& pets['Rooster-Skeleton']
|
||||
&& pets['Spider-Skeleton']
|
||||
&& pets['Owl-Skeleton']
|
||||
&& pets['Penguin-Skeleton']
|
||||
&& pets['TRex-Skeleton']
|
||||
&& pets['Rock-Skeleton']
|
||||
&& pets['Bunny-Skeleton']
|
||||
&& pets['Slime-Skeleton']
|
||||
&& pets['Sheep-Skeleton']
|
||||
&& pets['Cuttlefish-Skeleton']
|
||||
&& pets['Whale-Skeleton']
|
||||
&& pets['Cheetah-Skeleton']
|
||||
&& pets['Horse-Skeleton']
|
||||
&& pets['Frog-Skeleton']
|
||||
&& pets['Snake-Skeleton']
|
||||
&& pets['Unicorn-Skeleton']
|
||||
&& pets['Sabretooth-Skeleton']
|
||||
&& pets['Monkey-Skeleton']
|
||||
&& pets['Snail-Skeleton']
|
||||
&& pets['Falcon-Skeleton']
|
||||
&& pets['Treeling-Skeleton']
|
||||
&& pets['Axolotl-Skeleton']
|
||||
&& pets['Turtle-Skeleton']
|
||||
&& pets['Armadillo-Skeleton']
|
||||
&& pets['Cow-Skeleton']
|
||||
&& pets['Beetle-Skeleton']
|
||||
&& pets['Ferret-Skeleton']
|
||||
&& pets['Sloth-Skeleton']
|
||||
&& pets['Triceratops-Skeleton']
|
||||
&& pets['GuineaPig-Skeleton']
|
||||
&& pets['Peacock-Skeleton']
|
||||
&& pets['Butterfly-Skeleton']
|
||||
&& pets['Nudibranch-Skeleton']
|
||||
&& pets['Hippo-Skeleton']
|
||||
&& pets['Yarn-Skeleton']
|
||||
&& pets['Pterodactyl-Skeleton']
|
||||
&& pets['Badger-Skeleton']
|
||||
&& pets['Squirrel-Skeleton']
|
||||
&& pets['SeaSerpent-Skeleton']
|
||||
&& pets['Kangaroo-Skeleton']
|
||||
&& pets['Alligator-Skeleton']
|
||||
&& pets['Velociraptor-Skeleton']
|
||||
&& pets['Dolphin-Skeleton']
|
||||
&& pets['Robot-Skeleton']) {
|
||||
set['achievements.boneToPick'] = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
|
||||
|
||||
return await User.update({ _id: user._id }, { $set: set }).exec();
|
||||
}
|
||||
|
||||
export default async function processUsers () {
|
||||
let query = {
|
||||
// migration: { $ne: MIGRATION_NAME },
|
||||
'auth.timestamps.loggedin': { $gt: new Date('2022-01-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
|
||||
}
|
||||
};
|
||||
Generated
+330
-596
File diff suppressed because it is too large
Load Diff
+15
-15
@@ -1,13 +1,13 @@
|
||||
{
|
||||
"name": "habitica",
|
||||
"description": "A habit tracker app which treats your goals like a Role Playing Game.",
|
||||
"version": "4.248.1",
|
||||
"version": "4.248.0",
|
||||
"main": "./website/server/index.js",
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.19.3",
|
||||
"@babel/core": "^7.18.13",
|
||||
"@babel/preset-env": "^7.19.1",
|
||||
"@babel/register": "^7.18.9",
|
||||
"@google-cloud/trace-agent": "^7.1.2",
|
||||
"@google-cloud/trace-agent": "^5.1.6",
|
||||
"@parse/node-apn": "^5.1.3",
|
||||
"@slack/webhook": "^6.1.0",
|
||||
"accepts": "^1.3.8",
|
||||
@@ -15,8 +15,8 @@
|
||||
"amplitude": "^6.0.0",
|
||||
"apidoc": "^0.53.0",
|
||||
"apple-auth": "^1.0.7",
|
||||
"bcrypt": "^5.1.0",
|
||||
"body-parser": "^1.20.1",
|
||||
"bcrypt": "^5.0.1",
|
||||
"body-parser": "^1.20.0",
|
||||
"bootstrap": "^4.6.0",
|
||||
"compression": "^1.7.4",
|
||||
"cookie-session": "^2.0.0",
|
||||
@@ -27,7 +27,7 @@
|
||||
"eslint": "^6.8.0",
|
||||
"eslint-config-habitrpg": "^6.2.0",
|
||||
"eslint-plugin-mocha": "^5.0.0",
|
||||
"express": "^4.18.2",
|
||||
"express": "^4.18.1",
|
||||
"express-basic-auth": "^1.2.1",
|
||||
"express-validator": "^5.2.0",
|
||||
"glob": "^8.0.3",
|
||||
@@ -41,9 +41,9 @@
|
||||
"helmet": "^4.6.0",
|
||||
"image-size": "^1.0.2",
|
||||
"in-app-purchase": "^1.11.3",
|
||||
"js2xmlparser": "^5.0.0",
|
||||
"js2xmlparser": "^4.0.2",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"jwks-rsa": "^2.1.5",
|
||||
"jwks-rsa": "^2.1.4",
|
||||
"lodash": "^4.17.21",
|
||||
"merge-stream": "^2.0.0",
|
||||
"method-override": "^3.0.0",
|
||||
@@ -54,27 +54,27 @@
|
||||
"nconf": "^0.12.0",
|
||||
"node-gcm": "^1.0.5",
|
||||
"on-headers": "^1.0.2",
|
||||
"passport": "^0.6.0",
|
||||
"passport": "^0.5.0",
|
||||
"passport-facebook": "^3.0.0",
|
||||
"passport-google-oauth2": "^0.2.0",
|
||||
"passport-google-oauth20": "2.0.0",
|
||||
"paypal-rest-sdk": "^1.8.1",
|
||||
"pp-ipn": "^1.1.0",
|
||||
"ps-tree": "^1.0.0",
|
||||
"rate-limiter-flexible": "^2.3.11",
|
||||
"rate-limiter-flexible": "^2.3.10",
|
||||
"redis": "^3.1.2",
|
||||
"regenerator-runtime": "^0.13.9",
|
||||
"remove-markdown": "^0.5.0",
|
||||
"rimraf": "^3.0.2",
|
||||
"short-uuid": "^4.2.0",
|
||||
"stripe": "^10.13.0",
|
||||
"superagent": "^8.0.2",
|
||||
"stripe": "^8.222.0",
|
||||
"superagent": "^7.1.6",
|
||||
"universal-analytics": "^0.5.3",
|
||||
"useragent": "^2.1.9",
|
||||
"uuid": "^8.3.2",
|
||||
"validator": "^13.7.0",
|
||||
"vinyl-buffer": "^1.0.1",
|
||||
"winston": "^3.8.2",
|
||||
"winston": "^3.8.1",
|
||||
"winston-loggly-bulk": "^3.2.1",
|
||||
"xml2js": "^0.4.23"
|
||||
},
|
||||
@@ -114,7 +114,7 @@
|
||||
"chai": "^4.3.6",
|
||||
"chai-as-promised": "^7.1.1",
|
||||
"chai-moment": "^0.1.0",
|
||||
"chalk": "^5.1.0",
|
||||
"chalk": "^4.1.2",
|
||||
"cross-spawn": "^7.0.3",
|
||||
"expect.js": "^0.3.1",
|
||||
"istanbul": "^1.1.0-alpha.1",
|
||||
@@ -122,7 +122,7 @@
|
||||
"monk": "^7.3.4",
|
||||
"require-again": "^2.0.0",
|
||||
"run-rs": "^0.7.7",
|
||||
"sinon": "^14.0.1",
|
||||
"sinon": "^13.0.2",
|
||||
"sinon-chai": "^3.7.0",
|
||||
"sinon-stub-promise": "^4.0.0"
|
||||
},
|
||||
|
||||
@@ -218,7 +218,6 @@ describe('Apple Payments', () => {
|
||||
headers = {};
|
||||
receipt = `{"token": "${token}"}`;
|
||||
nextPaymentProcessing = moment.utc().add({ days: 2 });
|
||||
user = new User();
|
||||
|
||||
iapSetupStub = sinon.stub(iap, 'setup')
|
||||
.resolves();
|
||||
@@ -299,7 +298,6 @@ describe('Apple Payments', () => {
|
||||
expirationDate: moment.utc().add({ day: 1 }).toDate(),
|
||||
productId: option.sku,
|
||||
transactionId: token,
|
||||
originalTransactionId: token,
|
||||
}]);
|
||||
sub = common.content.subscriptionBlocks[option.subKey];
|
||||
|
||||
@@ -323,111 +321,11 @@ describe('Apple Payments', () => {
|
||||
nextPaymentProcessing,
|
||||
});
|
||||
});
|
||||
if (option !== subOptions[3]) {
|
||||
const newOption = subOptions[3];
|
||||
it(`upgrades a subscription from ${option.sku} to ${newOption.sku}`, async () => {
|
||||
const oldSub = common.content.subscriptionBlocks[option.subKey];
|
||||
user.profile.name = 'sender';
|
||||
user.purchased.plan.paymentMethod = applePayments.constants.PAYMENT_METHOD_APPLE;
|
||||
user.purchased.plan.customerId = token;
|
||||
user.purchased.plan.planId = option.subKey;
|
||||
user.purchased.plan.additionalData = receipt;
|
||||
iap.getPurchaseData.restore();
|
||||
iapGetPurchaseDataStub = sinon.stub(iap, 'getPurchaseData')
|
||||
.returns([{
|
||||
expirationDate: moment.utc().add({ day: 2 }).toDate(),
|
||||
productId: newOption.sku,
|
||||
transactionId: `${token}new`,
|
||||
originalTransactionId: token,
|
||||
}]);
|
||||
sub = common.content.subscriptionBlocks[newOption.subKey];
|
||||
|
||||
await applePayments.subscribe(newOption.sku,
|
||||
user,
|
||||
receipt,
|
||||
headers,
|
||||
nextPaymentProcessing);
|
||||
|
||||
expect(iapSetupStub).to.be.calledOnce;
|
||||
expect(iapValidateStub).to.be.calledOnce;
|
||||
expect(iapValidateStub).to.be.calledWith(iap.APPLE, receipt);
|
||||
expect(iapIsValidatedStub).to.be.calledOnce;
|
||||
expect(iapIsValidatedStub).to.be.calledWith({});
|
||||
expect(iapGetPurchaseDataStub).to.be.calledOnce;
|
||||
|
||||
expect(paymentsCreateSubscritionStub).to.be.calledOnce;
|
||||
expect(paymentsCreateSubscritionStub).to.be.calledWith({
|
||||
user,
|
||||
customerId: token,
|
||||
paymentMethod: applePayments.constants.PAYMENT_METHOD_APPLE,
|
||||
sub,
|
||||
headers,
|
||||
additionalData: receipt,
|
||||
nextPaymentProcessing,
|
||||
updatedFrom: oldSub,
|
||||
});
|
||||
});
|
||||
}
|
||||
if (option !== subOptions[0]) {
|
||||
const newOption = subOptions[0];
|
||||
it(`downgrades a subscription from ${option.sku} to ${newOption.sku}`, async () => {
|
||||
const oldSub = common.content.subscriptionBlocks[option.subKey];
|
||||
user.profile.name = 'sender';
|
||||
user.purchased.plan.paymentMethod = applePayments.constants.PAYMENT_METHOD_APPLE;
|
||||
user.purchased.plan.customerId = token;
|
||||
user.purchased.plan.planId = option.subKey;
|
||||
user.purchased.plan.additionalData = receipt;
|
||||
iap.getPurchaseData.restore();
|
||||
iapGetPurchaseDataStub = sinon.stub(iap, 'getPurchaseData')
|
||||
.returns([{
|
||||
expirationDate: moment.utc().add({ day: 2 }).toDate(),
|
||||
productId: newOption.sku,
|
||||
transactionId: `${token}new`,
|
||||
originalTransactionId: token,
|
||||
}]);
|
||||
sub = common.content.subscriptionBlocks[newOption.subKey];
|
||||
|
||||
await applePayments.subscribe(newOption.sku,
|
||||
user,
|
||||
receipt,
|
||||
headers,
|
||||
nextPaymentProcessing);
|
||||
|
||||
expect(iapSetupStub).to.be.calledOnce;
|
||||
expect(iapValidateStub).to.be.calledOnce;
|
||||
expect(iapValidateStub).to.be.calledWith(iap.APPLE, receipt);
|
||||
expect(iapIsValidatedStub).to.be.calledOnce;
|
||||
expect(iapIsValidatedStub).to.be.calledWith({});
|
||||
expect(iapGetPurchaseDataStub).to.be.calledOnce;
|
||||
|
||||
expect(paymentsCreateSubscritionStub).to.be.calledOnce;
|
||||
expect(paymentsCreateSubscritionStub).to.be.calledWith({
|
||||
user,
|
||||
customerId: token,
|
||||
paymentMethod: applePayments.constants.PAYMENT_METHOD_APPLE,
|
||||
sub,
|
||||
headers,
|
||||
additionalData: receipt,
|
||||
nextPaymentProcessing,
|
||||
updatedFrom: oldSub,
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
it('errors when a user is using the same subscription', async () => {
|
||||
it('errors when a user is already subscribed', async () => {
|
||||
payments.createSubscription.restore();
|
||||
user = new User();
|
||||
await user.save();
|
||||
iap.getPurchaseData.restore();
|
||||
|
||||
iapGetPurchaseDataStub = sinon.stub(iap, 'getPurchaseData')
|
||||
.returns([{
|
||||
expirationDate: moment.utc().add({ day: 1 }).toDate(),
|
||||
productId: sku,
|
||||
transactionId: token,
|
||||
originalTransactionId: token,
|
||||
}]);
|
||||
|
||||
await applePayments.subscribe(sku, user, receipt, headers, nextPaymentProcessing);
|
||||
|
||||
|
||||
@@ -445,89 +445,6 @@ describe('payments/index', () => {
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
context('Upgrades subscription', () => {
|
||||
it('from basic_earned to basic_6mo', async () => {
|
||||
data.sub.key = 'basic_earned';
|
||||
expect(user.purchased.plan.planId).to.not.exist;
|
||||
|
||||
await api.createSubscription(data);
|
||||
|
||||
expect(user.purchased.plan.planId).to.eql('basic_earned');
|
||||
expect(user.purchased.plan.customerId).to.eql('customer-id');
|
||||
const created = user.purchased.plan.dateCreated;
|
||||
const updated = user.purchased.plan.dateUpdated;
|
||||
|
||||
data.sub.key = 'basic_6mo';
|
||||
data.updatedFrom = { key: 'basic_earned' };
|
||||
await api.createSubscription(data);
|
||||
expect(user.purchased.plan.planId).to.eql('basic_6mo');
|
||||
expect(user.purchased.plan.dateCreated).to.eql(created);
|
||||
expect(user.purchased.plan.dateUpdated).to.not.eql(updated);
|
||||
expect(user.purchased.plan.customerId).to.eql('customer-id');
|
||||
});
|
||||
|
||||
it('from basic_3mo to basic_12mo', async () => {
|
||||
expect(user.purchased.plan.planId).to.not.exist;
|
||||
|
||||
await api.createSubscription(data);
|
||||
|
||||
expect(user.purchased.plan.planId).to.eql('basic_3mo');
|
||||
expect(user.purchased.plan.customerId).to.eql('customer-id');
|
||||
const created = user.purchased.plan.dateCreated;
|
||||
const updated = user.purchased.plan.dateUpdated;
|
||||
|
||||
data.sub.key = 'basic_12mo';
|
||||
data.updatedFrom = { key: 'basic_3mo' };
|
||||
await api.createSubscription(data);
|
||||
expect(user.purchased.plan.planId).to.eql('basic_12mo');
|
||||
expect(user.purchased.plan.dateCreated).to.eql(created);
|
||||
expect(user.purchased.plan.dateUpdated).to.not.eql(updated);
|
||||
expect(user.purchased.plan.customerId).to.eql('customer-id');
|
||||
});
|
||||
});
|
||||
|
||||
context('Downgrades subscription', () => {
|
||||
it('from basic_6mo to basic_earned', async () => {
|
||||
data.sub.key = 'basic_6mo';
|
||||
expect(user.purchased.plan.planId).to.not.exist;
|
||||
|
||||
await api.createSubscription(data);
|
||||
|
||||
expect(user.purchased.plan.planId).to.eql('basic_6mo');
|
||||
expect(user.purchased.plan.customerId).to.eql('customer-id');
|
||||
const created = user.purchased.plan.dateCreated;
|
||||
const updated = user.purchased.plan.dateUpdated;
|
||||
|
||||
data.sub.key = 'basic_earned';
|
||||
data.updatedFrom = { key: 'basic_6mo' };
|
||||
await api.createSubscription(data);
|
||||
expect(user.purchased.plan.planId).to.eql('basic_earned');
|
||||
expect(user.purchased.plan.dateCreated).to.eql(created);
|
||||
expect(user.purchased.plan.dateUpdated).to.not.eql(updated);
|
||||
expect(user.purchased.plan.customerId).to.eql('customer-id');
|
||||
});
|
||||
|
||||
it('from basic_12mo to basic_3mo', async () => {
|
||||
expect(user.purchased.plan.planId).to.not.exist;
|
||||
|
||||
data.sub.key = 'basic_12mo';
|
||||
await api.createSubscription(data);
|
||||
|
||||
expect(user.purchased.plan.planId).to.eql('basic_12mo');
|
||||
expect(user.purchased.plan.customerId).to.eql('customer-id');
|
||||
const created = user.purchased.plan.dateCreated;
|
||||
const updated = user.purchased.plan.dateUpdated;
|
||||
|
||||
data.sub.key = 'basic_3mo';
|
||||
data.updatedFrom = { key: 'basic_12mo' };
|
||||
await api.createSubscription(data);
|
||||
expect(user.purchased.plan.planId).to.eql('basic_3mo');
|
||||
expect(user.purchased.plan.dateCreated).to.eql(created);
|
||||
expect(user.purchased.plan.dateUpdated).to.not.eql(updated);
|
||||
expect(user.purchased.plan.customerId).to.eql('customer-id');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
context('Block subscription perks', () => {
|
||||
@@ -551,6 +468,7 @@ describe('payments/index', () => {
|
||||
|
||||
it('adds 10 to plan.consecutive.gemCapExtra for 6 month block', async () => {
|
||||
data.sub.key = 'basic_6mo';
|
||||
|
||||
await api.createSubscription(data);
|
||||
|
||||
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(10);
|
||||
@@ -558,6 +476,7 @@ describe('payments/index', () => {
|
||||
|
||||
it('adds 20 to plan.consecutive.gemCapExtra for 12 month block', async () => {
|
||||
data.sub.key = 'basic_12mo';
|
||||
|
||||
await api.createSubscription(data);
|
||||
|
||||
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(20);
|
||||
@@ -593,134 +512,6 @@ describe('payments/index', () => {
|
||||
|
||||
expect(user.purchased.plan.consecutive.trinkets).to.eql(4);
|
||||
});
|
||||
|
||||
context('Upgrades subscription', () => {
|
||||
it('Adds 10 to plan.consecutive.gemCapExtra from basic_earned to basic_6mo', async () => {
|
||||
data.sub.key = 'basic_earned';
|
||||
expect(user.purchased.plan.planId).to.not.exist;
|
||||
|
||||
await api.createSubscription(data);
|
||||
|
||||
expect(user.purchased.plan.planId).to.eql('basic_earned');
|
||||
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(0);
|
||||
|
||||
data.sub.key = 'basic_6mo';
|
||||
data.updatedFrom = { key: 'basic_earned' };
|
||||
await api.createSubscription(data);
|
||||
expect(user.purchased.plan.planId).to.eql('basic_6mo');
|
||||
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(10);
|
||||
});
|
||||
|
||||
it('Adds 15 to plan.consecutive.gemCapExtra when upgrading from basic_3mo to basic_12mo', async () => {
|
||||
expect(user.purchased.plan.planId).to.not.exist;
|
||||
|
||||
await api.createSubscription(data);
|
||||
|
||||
expect(user.purchased.plan.planId).to.eql('basic_3mo');
|
||||
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(5);
|
||||
|
||||
data.sub.key = 'basic_12mo';
|
||||
data.updatedFrom = { key: 'basic_3mo' };
|
||||
await api.createSubscription(data);
|
||||
expect(user.purchased.plan.planId).to.eql('basic_12mo');
|
||||
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(20);
|
||||
});
|
||||
|
||||
it('Adds 2 to plan.consecutive.trinkets from basic_earned to basic_6mo', async () => {
|
||||
data.sub.key = 'basic_earned';
|
||||
expect(user.purchased.plan.planId).to.not.exist;
|
||||
|
||||
await api.createSubscription(data);
|
||||
|
||||
expect(user.purchased.plan.planId).to.eql('basic_earned');
|
||||
expect(user.purchased.plan.consecutive.trinkets).to.eql(0);
|
||||
|
||||
data.sub.key = 'basic_6mo';
|
||||
data.updatedFrom = { key: 'basic_earned' };
|
||||
await api.createSubscription(data);
|
||||
expect(user.purchased.plan.planId).to.eql('basic_6mo');
|
||||
expect(user.purchased.plan.consecutive.trinkets).to.eql(2);
|
||||
});
|
||||
|
||||
it('Adds 3 to plan.consecutive.trinkets when upgrading from basic_3mo to basic_12mo', async () => {
|
||||
expect(user.purchased.plan.planId).to.not.exist;
|
||||
|
||||
await api.createSubscription(data);
|
||||
|
||||
expect(user.purchased.plan.planId).to.eql('basic_3mo');
|
||||
expect(user.purchased.plan.consecutive.trinkets).to.eql(1);
|
||||
|
||||
data.sub.key = 'basic_12mo';
|
||||
data.updatedFrom = { key: 'basic_3mo' };
|
||||
await api.createSubscription(data);
|
||||
expect(user.purchased.plan.planId).to.eql('basic_12mo');
|
||||
expect(user.purchased.plan.consecutive.trinkets).to.eql(4);
|
||||
});
|
||||
});
|
||||
|
||||
context('Downgrades subscription', () => {
|
||||
it('does not remove from plan.consecutive.gemCapExtra from basic_6mo to basic_earned', async () => {
|
||||
data.sub.key = 'basic_6mo';
|
||||
expect(user.purchased.plan.planId).to.not.exist;
|
||||
|
||||
await api.createSubscription(data);
|
||||
|
||||
expect(user.purchased.plan.planId).to.eql('basic_6mo');
|
||||
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(10);
|
||||
|
||||
data.sub.key = 'basic_earned';
|
||||
data.updatedFrom = { key: 'basic_6mo' };
|
||||
await api.createSubscription(data);
|
||||
expect(user.purchased.plan.planId).to.eql('basic_earned');
|
||||
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(10);
|
||||
});
|
||||
|
||||
it('does not remove from plan.consecutive.gemCapExtra from basic_12mo to basic_3mo', async () => {
|
||||
expect(user.purchased.plan.planId).to.not.exist;
|
||||
|
||||
data.sub.key = 'basic_12mo';
|
||||
await api.createSubscription(data);
|
||||
|
||||
expect(user.purchased.plan.planId).to.eql('basic_12mo');
|
||||
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(20);
|
||||
|
||||
data.sub.key = 'basic_3mo';
|
||||
data.updatedFrom = { key: 'basic_12mo' };
|
||||
await api.createSubscription(data);
|
||||
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(20);
|
||||
});
|
||||
|
||||
it('does not remove from plan.consecutive.trinkets from basic_6mo to basic_earned', async () => {
|
||||
data.sub.key = 'basic_6mo';
|
||||
expect(user.purchased.plan.planId).to.not.exist;
|
||||
|
||||
await api.createSubscription(data);
|
||||
|
||||
expect(user.purchased.plan.planId).to.eql('basic_6mo');
|
||||
expect(user.purchased.plan.consecutive.trinkets).to.eql(2);
|
||||
|
||||
data.sub.key = 'basic_earned';
|
||||
data.updatedFrom = { key: 'basic_6mo' };
|
||||
await api.createSubscription(data);
|
||||
expect(user.purchased.plan.planId).to.eql('basic_earned');
|
||||
expect(user.purchased.plan.consecutive.trinkets).to.eql(2);
|
||||
});
|
||||
|
||||
it('does not remove from plan.consecutive.trinkets from basic_12mo to basic_3mo', async () => {
|
||||
expect(user.purchased.plan.planId).to.not.exist;
|
||||
|
||||
data.sub.key = 'basic_12mo';
|
||||
await api.createSubscription(data);
|
||||
|
||||
expect(user.purchased.plan.planId).to.eql('basic_12mo');
|
||||
expect(user.purchased.plan.consecutive.trinkets).to.eql(4);
|
||||
|
||||
data.sub.key = 'basic_3mo';
|
||||
data.updatedFrom = { key: 'basic_12mo' };
|
||||
await api.createSubscription(data);
|
||||
expect(user.purchased.plan.consecutive.trinkets).to.eql(4);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
context('Mystery Items', () => {
|
||||
|
||||
@@ -11,12 +11,6 @@ describe('PUT /group', () => {
|
||||
const groupName = 'Test Public Guild';
|
||||
const groupType = 'guild';
|
||||
const groupUpdatedName = 'Test Public Guild Updated';
|
||||
const groupCategories = [
|
||||
{
|
||||
slug: 'initialCat',
|
||||
name: 'Initial Category',
|
||||
},
|
||||
];
|
||||
|
||||
beforeEach(async () => {
|
||||
const { group, groupLeader, members } = await createAndPopulateGroup({
|
||||
@@ -24,7 +18,6 @@ describe('PUT /group', () => {
|
||||
name: groupName,
|
||||
type: groupType,
|
||||
privacy: 'public',
|
||||
categories: groupCategories,
|
||||
},
|
||||
members: 1,
|
||||
});
|
||||
@@ -68,35 +61,6 @@ describe('PUT /group', () => {
|
||||
expect(updatedGroup.categories[0].name).to.eql(categories[0].name);
|
||||
});
|
||||
|
||||
it('removes the initial group category', async () => {
|
||||
const categories = [];
|
||||
|
||||
const updatedGroup = await leader.put(`/groups/${groupToUpdate._id}`, {
|
||||
categories,
|
||||
});
|
||||
|
||||
expect(updatedGroup.categories.length).to.equal(0);
|
||||
});
|
||||
|
||||
it('removes duplicate group categories', async () => {
|
||||
const categories = [
|
||||
{
|
||||
slug: 'newCat',
|
||||
name: 'New Category',
|
||||
},
|
||||
{
|
||||
slug: 'newCat',
|
||||
name: 'New Category',
|
||||
},
|
||||
];
|
||||
|
||||
const updatedGroup = await leader.put(`/groups/${groupToUpdate._id}`, {
|
||||
categories,
|
||||
});
|
||||
|
||||
expect(updatedGroup.categories.length).to.equal(1);
|
||||
});
|
||||
|
||||
it('allows an admin to update a guild', async () => {
|
||||
const updatedGroup = await adminUser.put(`/groups/${groupToUpdate._id}`, {
|
||||
name: groupUpdatedName,
|
||||
|
||||
Generated
+129
-354
@@ -22,9 +22,9 @@
|
||||
}
|
||||
},
|
||||
"@amplitude/analytics-connector": {
|
||||
"version": "1.4.5",
|
||||
"resolved": "https://registry.npmjs.org/@amplitude/analytics-connector/-/analytics-connector-1.4.5.tgz",
|
||||
"integrity": "sha512-ELAP6ivg+13uSk+TOirGZE/92M+tTbeiQ/i7eXgDO4Hiy00Abf/UxO/rp9WovtxCyeFYTILrujEYxPv5cRQmFw==",
|
||||
"version": "1.4.4",
|
||||
"resolved": "https://registry.npmjs.org/@amplitude/analytics-connector/-/analytics-connector-1.4.4.tgz",
|
||||
"integrity": "sha512-6JcE1nxrprJt6pHqqDQb7FXRqJmFHG7KJPe0jNZaAvfll4mWKVqZu8W9IV3XiN1P+xgHIV1NN+i3PLOAZWEhXg==",
|
||||
"requires": {
|
||||
"@amplitude/ua-parser-js": "0.7.31"
|
||||
}
|
||||
@@ -4356,9 +4356,9 @@
|
||||
"integrity": "sha512-1dVNHT76Uu5N3eJNTYcvxee+jzX4Z9lfciqRRHCU27ihbUcYi+iSc2iml5Ke1LXe1SyJCLA0+14Jh4tXJgOppA=="
|
||||
},
|
||||
"@hapi/hoek": {
|
||||
"version": "8.5.1",
|
||||
"resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-8.5.1.tgz",
|
||||
"integrity": "sha512-yN7kbciD87WzLGc5539Tn0sApjyiGHAJgKvG9W8C7O+6c7qmoQMfVs0W4bX17eqz6C78QJqqFrtgdK5EWf6Qow=="
|
||||
"version": "8.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-8.3.1.tgz",
|
||||
"integrity": "sha512-75ocgnI7HG/I01iGA3/rs0y6PXydUA/kxhFZM0HoT8NLSTnt/J8Gq03iKl4a4B/2A3iMG0ctXtxr5Hg9SGr1gw=="
|
||||
},
|
||||
"@hapi/joi": {
|
||||
"version": "15.1.1",
|
||||
@@ -4818,31 +4818,6 @@
|
||||
"warning": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"@sideway/address": {
|
||||
"version": "4.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.4.tgz",
|
||||
"integrity": "sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw==",
|
||||
"requires": {
|
||||
"@hapi/hoek": "^9.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@hapi/hoek": {
|
||||
"version": "9.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz",
|
||||
"integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"@sideway/formula": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.0.tgz",
|
||||
"integrity": "sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg=="
|
||||
},
|
||||
"@sideway/pinpoint": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz",
|
||||
"integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ=="
|
||||
},
|
||||
"@soda/friendly-errors-webpack-plugin": {
|
||||
"version": "1.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@soda/friendly-errors-webpack-plugin/-/friendly-errors-webpack-plugin-1.8.0.tgz",
|
||||
@@ -12389,39 +12364,29 @@
|
||||
}
|
||||
},
|
||||
"@vue/cli-plugin-router": {
|
||||
"version": "5.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@vue/cli-plugin-router/-/cli-plugin-router-5.0.8.tgz",
|
||||
"integrity": "sha512-Gmv4dsGdAsWPqVijz3Ux2OS2HkMrWi1ENj2cYL75nUeL+Xj5HEstSqdtfZ0b1q9NCce+BFB6QnHfTBXc/fCvMg==",
|
||||
"version": "4.5.15",
|
||||
"resolved": "https://registry.npmjs.org/@vue/cli-plugin-router/-/cli-plugin-router-4.5.15.tgz",
|
||||
"integrity": "sha512-q7Y6kP9b3k55Ca2j59xJ7XPA6x+iSRB+N4ac0ZbcL1TbInVQ4j5wCzyE+uqid40hLy4fUdlpl4X9fHJEwuVxPA==",
|
||||
"requires": {
|
||||
"@vue/cli-shared-utils": "^5.0.8"
|
||||
"@vue/cli-shared-utils": "^4.5.15"
|
||||
},
|
||||
"dependencies": {
|
||||
"@achrinza/node-ipc": {
|
||||
"version": "9.2.5",
|
||||
"resolved": "https://registry.npmjs.org/@achrinza/node-ipc/-/node-ipc-9.2.5.tgz",
|
||||
"integrity": "sha512-kBX7Ay911iXZ3VZ1pYltj3Rfu7Ow9H7sK4H4RSfWIfWR2JKNB40K808wppoRIEzE2j2hXLU+r6TJgCAliCGhyQ==",
|
||||
"requires": {
|
||||
"@node-ipc/js-queue": "2.0.3",
|
||||
"event-pubsub": "4.3.0",
|
||||
"js-message": "1.0.7"
|
||||
}
|
||||
},
|
||||
"@vue/cli-shared-utils": {
|
||||
"version": "5.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@vue/cli-shared-utils/-/cli-shared-utils-5.0.8.tgz",
|
||||
"integrity": "sha512-uK2YB7bBVuQhjOJF+O52P9yFMXeJVj7ozqJkwYE9PlMHL1LMHjtCYm4cSdOebuPzyP+/9p0BimM/OqxsevIopQ==",
|
||||
"version": "4.5.15",
|
||||
"resolved": "https://registry.npmjs.org/@vue/cli-shared-utils/-/cli-shared-utils-4.5.15.tgz",
|
||||
"integrity": "sha512-SKaej9hHzzjKSOw1NlFmc6BSE0vcqUQMQiv1cxQ2DhVyy4QxZXBmzmiLBUBe+hYZZs1neXW7n//udeN9bCAY+Q==",
|
||||
"requires": {
|
||||
"@achrinza/node-ipc": "^9.2.5",
|
||||
"chalk": "^4.1.2",
|
||||
"@hapi/joi": "^15.0.1",
|
||||
"chalk": "^2.4.2",
|
||||
"execa": "^1.0.0",
|
||||
"joi": "^17.4.0",
|
||||
"launch-editor": "^2.2.1",
|
||||
"lru-cache": "^6.0.0",
|
||||
"node-fetch": "^2.6.7",
|
||||
"open": "^8.0.2",
|
||||
"ora": "^5.3.0",
|
||||
"lru-cache": "^5.1.1",
|
||||
"node-ipc": "^9.1.1",
|
||||
"open": "^6.3.0",
|
||||
"ora": "^3.4.0",
|
||||
"read-pkg": "^5.1.1",
|
||||
"semver": "^7.3.4",
|
||||
"request": "^2.88.2",
|
||||
"semver": "^6.1.0",
|
||||
"strip-ansi": "^6.0.0"
|
||||
}
|
||||
},
|
||||
@@ -12430,152 +12395,10 @@
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
|
||||
},
|
||||
"ansi-styles": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"requires": {
|
||||
"color-convert": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"chalk": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
|
||||
"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",
|
||||
"integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
|
||||
"requires": {
|
||||
"restore-cursor": "^3.1.0"
|
||||
}
|
||||
},
|
||||
"cli-spinners": {
|
||||
"version": "2.6.1",
|
||||
"resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.1.tgz",
|
||||
"integrity": "sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g=="
|
||||
},
|
||||
"color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"requires": {
|
||||
"color-name": "~1.1.4"
|
||||
}
|
||||
},
|
||||
"color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
|
||||
},
|
||||
"has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
|
||||
},
|
||||
"is-docker": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz",
|
||||
"integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ=="
|
||||
},
|
||||
"is-wsl": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
|
||||
"integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==",
|
||||
"requires": {
|
||||
"is-docker": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"js-message": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/js-message/-/js-message-1.0.7.tgz",
|
||||
"integrity": "sha512-efJLHhLjIyKRewNS9EGZ4UpI8NguuL6fKkhRxVuMmrGV2xN/0APGdQYwLFky5w9naebSZ0OwAGp0G6/2Cg90rA=="
|
||||
},
|
||||
"log-symbols": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
|
||||
"integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==",
|
||||
"requires": {
|
||||
"chalk": "^4.1.0",
|
||||
"is-unicode-supported": "^0.1.0"
|
||||
}
|
||||
},
|
||||
"lru-cache": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
||||
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
|
||||
"requires": {
|
||||
"yallist": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"mimic-fn": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
|
||||
"integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="
|
||||
},
|
||||
"node-fetch": {
|
||||
"version": "2.6.7",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
|
||||
"integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
|
||||
"requires": {
|
||||
"whatwg-url": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"onetime": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
|
||||
"integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
|
||||
"requires": {
|
||||
"mimic-fn": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"open": {
|
||||
"version": "8.4.0",
|
||||
"resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz",
|
||||
"integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==",
|
||||
"requires": {
|
||||
"define-lazy-prop": "^2.0.0",
|
||||
"is-docker": "^2.1.1",
|
||||
"is-wsl": "^2.2.0"
|
||||
}
|
||||
},
|
||||
"ora": {
|
||||
"version": "5.4.1",
|
||||
"resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz",
|
||||
"integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==",
|
||||
"requires": {
|
||||
"bl": "^4.1.0",
|
||||
"chalk": "^4.1.0",
|
||||
"cli-cursor": "^3.1.0",
|
||||
"cli-spinners": "^2.5.0",
|
||||
"is-interactive": "^1.0.0",
|
||||
"is-unicode-supported": "^0.1.0",
|
||||
"log-symbols": "^4.1.0",
|
||||
"strip-ansi": "^6.0.0",
|
||||
"wcwidth": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"restore-cursor": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
|
||||
"integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
|
||||
"requires": {
|
||||
"onetime": "^5.1.0",
|
||||
"signal-exit": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"semver": {
|
||||
"version": "7.3.7",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz",
|
||||
"integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==",
|
||||
"requires": {
|
||||
"lru-cache": "^6.0.0"
|
||||
}
|
||||
"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.1",
|
||||
@@ -12584,38 +12407,6 @@
|
||||
"requires": {
|
||||
"ansi-regex": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
||||
"requires": {
|
||||
"has-flag": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"tr46": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
|
||||
},
|
||||
"webidl-conversions": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
|
||||
},
|
||||
"whatwg-url": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
|
||||
"requires": {
|
||||
"tr46": "~0.0.3",
|
||||
"webidl-conversions": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"yallist": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -12821,6 +12612,11 @@
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
||||
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
|
||||
},
|
||||
"minimist": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
|
||||
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
|
||||
},
|
||||
"semver": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||
@@ -13338,11 +13134,11 @@
|
||||
"integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM="
|
||||
},
|
||||
"amplitude-js": {
|
||||
"version": "8.21.1",
|
||||
"resolved": "https://registry.npmjs.org/amplitude-js/-/amplitude-js-8.21.1.tgz",
|
||||
"integrity": "sha512-02S0EWLTkCYurpKx6o6K7+BbtVHzhCTHDM+jgvCAV5VbbsXdhLqVY7Q6NtAF+Wb8phb9K0GSW1SuDKPy4TY9OA==",
|
||||
"version": "8.21.0",
|
||||
"resolved": "https://registry.npmjs.org/amplitude-js/-/amplitude-js-8.21.0.tgz",
|
||||
"integrity": "sha512-kC01TmmCdDrtms8LhvC/r65FtbmCbNHZ1/jiezXmTH82TsWI/SkN47jKs8CCwjZNakqTBN/hmficiZBUKv4myw==",
|
||||
"requires": {
|
||||
"@amplitude/analytics-connector": "^1.4.5",
|
||||
"@amplitude/analytics-connector": "1.4.4",
|
||||
"@amplitude/ua-parser-js": "0.7.31",
|
||||
"@amplitude/utils": "^1.10.1",
|
||||
"@babel/runtime": "^7.3.4",
|
||||
@@ -13425,6 +13221,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"ansi-html": {
|
||||
"version": "0.0.7",
|
||||
"resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz",
|
||||
"integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4="
|
||||
},
|
||||
"ansi-html-community": {
|
||||
"version": "0.0.8",
|
||||
"resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz",
|
||||
@@ -14037,29 +13838,11 @@
|
||||
"integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ=="
|
||||
},
|
||||
"axios": {
|
||||
"version": "0.27.2",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz",
|
||||
"integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==",
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.25.0.tgz",
|
||||
"integrity": "sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g==",
|
||||
"requires": {
|
||||
"follow-redirects": "^1.14.9",
|
||||
"form-data": "^4.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"follow-redirects": {
|
||||
"version": "1.15.2",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
|
||||
"integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA=="
|
||||
},
|
||||
"form-data": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
|
||||
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
|
||||
"requires": {
|
||||
"asynckit": "^0.4.0",
|
||||
"combined-stream": "^1.0.8",
|
||||
"mime-types": "^2.1.12"
|
||||
}
|
||||
}
|
||||
"follow-redirects": "^1.14.7"
|
||||
}
|
||||
},
|
||||
"axios-progress-bar": {
|
||||
@@ -14701,37 +14484,6 @@
|
||||
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz",
|
||||
"integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw=="
|
||||
},
|
||||
"bl": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
|
||||
"integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
|
||||
"requires": {
|
||||
"buffer": "^5.5.0",
|
||||
"inherits": "^2.0.4",
|
||||
"readable-stream": "^3.4.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"buffer": {
|
||||
"version": "5.7.1",
|
||||
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
|
||||
"integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
|
||||
"requires": {
|
||||
"base64-js": "^1.3.1",
|
||||
"ieee754": "^1.1.13"
|
||||
}
|
||||
},
|
||||
"readable-stream": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
|
||||
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
|
||||
"requires": {
|
||||
"inherits": "^2.0.3",
|
||||
"string_decoder": "^1.1.1",
|
||||
"util-deprecate": "^1.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"bluebird": {
|
||||
"version": "3.7.0",
|
||||
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.0.tgz",
|
||||
@@ -16135,9 +15887,9 @@
|
||||
}
|
||||
},
|
||||
"core-js": {
|
||||
"version": "3.25.5",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.25.5.tgz",
|
||||
"integrity": "sha512-nbm6eZSjm+ZuBQxCUPQKQCoUEfFOXjUZ8dTTyikyKaWrTYmAVbykQfwsKE5dBK88u3QCkCrzsx/PPlKfhsvgpw=="
|
||||
"version": "3.24.1",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.24.1.tgz",
|
||||
"integrity": "sha512-0QTBSYSUZ6Gq21utGzkfITDylE8jWC9Ne1D2MrhvlsZBI1x39OdDIVbzSqtgMndIy6BlHxBXpMGqzZmnztg2rg=="
|
||||
},
|
||||
"core-js-compat": {
|
||||
"version": "3.11.0",
|
||||
@@ -16755,11 +16507,6 @@
|
||||
"clone": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"define-lazy-prop": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz",
|
||||
"integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og=="
|
||||
},
|
||||
"define-properties": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
|
||||
@@ -18715,6 +18462,11 @@
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
},
|
||||
"minimist": {
|
||||
"version": "0.0.8",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"minipass": {
|
||||
"version": "2.3.5",
|
||||
"bundled": true,
|
||||
@@ -18732,6 +18484,14 @@
|
||||
"minipass": "^2.2.1"
|
||||
}
|
||||
},
|
||||
"mkdirp": {
|
||||
"version": "0.5.1",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"minimist": "0.0.8"
|
||||
}
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.1.1",
|
||||
"bundled": true,
|
||||
@@ -18854,6 +18614,13 @@
|
||||
"ini": "~1.3.0",
|
||||
"minimist": "^1.2.0",
|
||||
"strip-json-comments": "~2.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"minimist": {
|
||||
"version": "1.2.0",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"readable-stream": {
|
||||
@@ -20387,9 +20154,9 @@
|
||||
"integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA=="
|
||||
},
|
||||
"intro.js": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/intro.js/-/intro.js-6.0.0.tgz",
|
||||
"integrity": "sha512-ZUiR6BoLSvPSlLG0boewnWVgji1fE1gBvP/pyw5pgCKXEDQz1mMeUxarggClPNs71UTq364LwSk9zxz17A9gaQ=="
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/intro.js/-/intro.js-5.1.0.tgz",
|
||||
"integrity": "sha512-zwWl/duTh00eeNcZRU4o4/xxloNYPFKs4n4lMRDNx59jZr+qRI0jSOnzqYMOuVftD4beGrmxBHz4k8qp9/dCMA=="
|
||||
},
|
||||
"invariant": {
|
||||
"version": "2.2.4",
|
||||
@@ -20651,11 +20418,6 @@
|
||||
"resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.3.tgz",
|
||||
"integrity": "sha512-zxQ9//Q3D/34poZf8fiy3m3XVpbQc7ren15iKqrTtLPwkPD/t3Scy9Imp63FujULGxuK0ZlCwoo5xNpktFgbOA=="
|
||||
},
|
||||
"is-interactive": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz",
|
||||
"integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w=="
|
||||
},
|
||||
"is-map": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz",
|
||||
@@ -20795,11 +20557,6 @@
|
||||
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
|
||||
"integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo="
|
||||
},
|
||||
"is-unicode-supported": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
|
||||
"integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw=="
|
||||
},
|
||||
"is-weakref": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz",
|
||||
@@ -20917,33 +20674,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"joi": {
|
||||
"version": "17.6.0",
|
||||
"resolved": "https://registry.npmjs.org/joi/-/joi-17.6.0.tgz",
|
||||
"integrity": "sha512-OX5dG6DTbcr/kbMFj0KGYxuew69HPcAE3K/sZpEV2nP6e/j/C0HV+HNiBPCASxdx5T7DMoa0s8UeHWMnb6n2zw==",
|
||||
"requires": {
|
||||
"@hapi/hoek": "^9.0.0",
|
||||
"@hapi/topo": "^5.0.0",
|
||||
"@sideway/address": "^4.1.3",
|
||||
"@sideway/formula": "^3.0.0",
|
||||
"@sideway/pinpoint": "^2.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@hapi/hoek": {
|
||||
"version": "9.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz",
|
||||
"integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ=="
|
||||
},
|
||||
"@hapi/topo": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz",
|
||||
"integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==",
|
||||
"requires": {
|
||||
"@hapi/hoek": "^9.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"jquery": {
|
||||
"version": "3.6.1",
|
||||
"resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.1.tgz",
|
||||
@@ -21137,6 +20867,13 @@
|
||||
"integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==",
|
||||
"requires": {
|
||||
"minimist": "^1.2.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"minimist": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
|
||||
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"jsonfile": {
|
||||
@@ -21799,9 +21536,9 @@
|
||||
}
|
||||
},
|
||||
"minimist": {
|
||||
"version": "1.2.7",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz",
|
||||
"integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g=="
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
|
||||
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
|
||||
},
|
||||
"minipass": {
|
||||
"version": "3.1.1",
|
||||
@@ -21895,11 +21632,18 @@
|
||||
}
|
||||
},
|
||||
"mkdirp": {
|
||||
"version": "0.5.6",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
|
||||
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
|
||||
"version": "0.5.1",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
|
||||
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
|
||||
"requires": {
|
||||
"minimist": "^1.2.6"
|
||||
"minimist": "0.0.8"
|
||||
},
|
||||
"dependencies": {
|
||||
"minimist": {
|
||||
"version": "0.0.8",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
|
||||
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
|
||||
}
|
||||
}
|
||||
},
|
||||
"mocha": {
|
||||
@@ -21970,6 +21714,11 @@
|
||||
"path-exists": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"minimist": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
|
||||
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
|
||||
},
|
||||
"mkdirp": {
|
||||
"version": "0.5.4",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.4.tgz",
|
||||
@@ -23433,6 +23182,19 @@
|
||||
"requires": {
|
||||
"ms": "^2.1.1"
|
||||
}
|
||||
},
|
||||
"minimist": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
|
||||
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
|
||||
},
|
||||
"mkdirp": {
|
||||
"version": "0.5.5",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
|
||||
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
|
||||
"requires": {
|
||||
"minimist": "^1.2.5"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -28900,9 +28662,9 @@
|
||||
}
|
||||
},
|
||||
"vue-router": {
|
||||
"version": "3.6.5",
|
||||
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.6.5.tgz",
|
||||
"integrity": "sha512-VYXZQLtjuvKxxcshuRAwjHnciqZVoXAjTjcqBTz4rKc8qih9g9pI3hbDjmqXaHdgL3v8pV6P8Z335XvHzESxLQ=="
|
||||
"version": "3.5.4",
|
||||
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.5.4.tgz",
|
||||
"integrity": "sha512-x+/DLAJZv2mcQ7glH2oV9ze8uPwcI+H+GgTgTmb5I55bCgY3+vXWIsqbYUzbBSZnwFHEJku4eoaH/x98veyymQ=="
|
||||
},
|
||||
"vue-style-loader": {
|
||||
"version": "4.1.3",
|
||||
@@ -29148,6 +28910,19 @@
|
||||
"webpack-sources": "^1.4.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"minimist": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
|
||||
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
|
||||
},
|
||||
"mkdirp": {
|
||||
"version": "0.5.5",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
|
||||
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
|
||||
"requires": {
|
||||
"minimist": "^1.2.5"
|
||||
}
|
||||
},
|
||||
"serialize-javascript": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz",
|
||||
@@ -29241,11 +29016,11 @@
|
||||
}
|
||||
},
|
||||
"webpack-dev-server": {
|
||||
"version": "3.11.3",
|
||||
"resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.11.3.tgz",
|
||||
"integrity": "sha512-3x31rjbEQWKMNzacUZRE6wXvUFuGpH7vr0lIEbYpMAG9BOxi0928QU1BBswOAP3kg3H1O4hiS+sq4YyAn6ANnA==",
|
||||
"version": "3.11.2",
|
||||
"resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.11.2.tgz",
|
||||
"integrity": "sha512-A80BkuHRQfCiNtGBS1EMf2ChTUs0x+B3wGDFmOeT4rmJOHhHTCH2naNxIHhmkr0/UillP4U3yeIyv1pNp+QDLQ==",
|
||||
"requires": {
|
||||
"ansi-html-community": "0.0.8",
|
||||
"ansi-html": "0.0.7",
|
||||
"bonjour": "^3.5.0",
|
||||
"chokidar": "^2.1.8",
|
||||
"compression": "^1.7.4",
|
||||
@@ -29283,7 +29058,7 @@
|
||||
"ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
|
||||
"integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA=="
|
||||
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
|
||||
},
|
||||
"http-proxy-middleware": {
|
||||
"version": "0.19.1",
|
||||
@@ -29309,7 +29084,7 @@
|
||||
"strip-ansi": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
|
||||
"integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==",
|
||||
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
|
||||
"requires": {
|
||||
"ansi-regex": "^2.0.0"
|
||||
}
|
||||
|
||||
@@ -21,18 +21,18 @@
|
||||
"@storybook/vue": "6.3.13",
|
||||
"@vue/cli-plugin-babel": "^4.5.15",
|
||||
"@vue/cli-plugin-eslint": "^4.5.19",
|
||||
"@vue/cli-plugin-router": "^5.0.8",
|
||||
"@vue/cli-plugin-router": "^4.5.15",
|
||||
"@vue/cli-plugin-unit-mocha": "^4.5.15",
|
||||
"@vue/cli-service": "^4.5.15",
|
||||
"@vue/test-utils": "1.0.0-beta.29",
|
||||
"amplitude-js": "^8.21.1",
|
||||
"axios": "^0.27.2",
|
||||
"amplitude-js": "^8.21.0",
|
||||
"axios": "^0.25.0",
|
||||
"axios-progress-bar": "^1.2.0",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"bootstrap": "^4.6.0",
|
||||
"bootstrap-vue": "^2.22.0",
|
||||
"chai": "^4.3.6",
|
||||
"core-js": "^3.25.5",
|
||||
"core-js": "^3.24.1",
|
||||
"dompurify": "^2.4.0",
|
||||
"eslint": "^6.8.0",
|
||||
"eslint-config-habitrpg": "^6.2.0",
|
||||
@@ -41,7 +41,7 @@
|
||||
"habitica-markdown": "^3.0.0",
|
||||
"hellojs": "^1.19.5",
|
||||
"inspectpack": "^4.7.1",
|
||||
"intro.js": "^6.0.0",
|
||||
"intro.js": "^5.1.0",
|
||||
"jquery": "^3.6.1",
|
||||
"lodash": "^4.17.21",
|
||||
"moment": "^2.29.4",
|
||||
@@ -58,7 +58,7 @@
|
||||
"vue": "^2.7.10",
|
||||
"vue-cli-plugin-storybook": "2.1.0",
|
||||
"vue-mugen-scroll": "^0.2.6",
|
||||
"vue-router": "^3.6.5",
|
||||
"vue-router": "^3.5.4",
|
||||
"vue-template-compiler": "^2.7.10",
|
||||
"vuedraggable": "^2.24.3",
|
||||
"vuejs-datepicker": "git://github.com/habitrpg/vuejs-datepicker.git#153d339e4dbebb73733658aeda1d5b7fcc55b0a0",
|
||||
|
||||
@@ -280,7 +280,6 @@ import markdownDirective from '@/directives/markdown';
|
||||
import { userStateMixin } from '../../mixins/userState';
|
||||
|
||||
import { TAVERN_ID, MIN_SHORTNAME_SIZE_FOR_CHALLENGES, MAX_SUMMARY_SIZE_FOR_CHALLENGES } from '@/../../common/script/constants';
|
||||
import CategoryOptions from '@/../../common/script/content/categoryOptions';
|
||||
|
||||
export default {
|
||||
directives: {
|
||||
@@ -289,7 +288,64 @@ export default {
|
||||
mixins: [userStateMixin],
|
||||
props: ['groupId'],
|
||||
data () {
|
||||
const categoryOptions = CategoryOptions;
|
||||
const categoryOptions = [
|
||||
{
|
||||
label: 'habitica_official',
|
||||
key: 'habitica_official',
|
||||
},
|
||||
{
|
||||
label: 'academics',
|
||||
key: 'academics',
|
||||
},
|
||||
{
|
||||
label: 'advocacy_causes',
|
||||
key: 'advocacy_causes',
|
||||
},
|
||||
{
|
||||
label: 'creativity',
|
||||
key: 'creativity',
|
||||
},
|
||||
{
|
||||
label: 'entertainment',
|
||||
key: 'entertainment',
|
||||
},
|
||||
{
|
||||
label: 'finance',
|
||||
key: 'finance',
|
||||
},
|
||||
{
|
||||
label: 'health_fitness',
|
||||
key: 'health_fitness',
|
||||
},
|
||||
{
|
||||
label: 'hobbies_occupations',
|
||||
key: 'hobbies_occupations',
|
||||
},
|
||||
{
|
||||
label: 'location_based',
|
||||
key: 'location_based',
|
||||
},
|
||||
{
|
||||
label: 'mental_health',
|
||||
key: 'mental_health',
|
||||
},
|
||||
{
|
||||
label: 'getting_organized',
|
||||
key: 'getting_organized',
|
||||
},
|
||||
{
|
||||
label: 'self_improvement',
|
||||
key: 'self_improvement',
|
||||
},
|
||||
{
|
||||
label: 'spirituality',
|
||||
key: 'spirituality',
|
||||
},
|
||||
{
|
||||
label: 'time_management',
|
||||
key: 'time_management',
|
||||
},
|
||||
];
|
||||
const hashedCategories = {};
|
||||
categoryOptions.forEach(category => {
|
||||
hashedCategories[category.key] = category.label;
|
||||
|
||||
@@ -89,14 +89,70 @@
|
||||
import throttle from 'lodash/throttle';
|
||||
import FilterSidebar from '@/components/ui/filterSidebar';
|
||||
import FilterGroup from '@/components/ui/filterGroup';
|
||||
import CategoryOptions from '@/../../common/script/content/categoryOptions';
|
||||
|
||||
export default {
|
||||
components: { FilterGroup, FilterSidebar },
|
||||
data () {
|
||||
return {
|
||||
categoryFilters: [],
|
||||
categoryOptions: CategoryOptions,
|
||||
categoryOptions: [
|
||||
{
|
||||
label: 'habitica_official',
|
||||
key: 'habitica_official',
|
||||
},
|
||||
{
|
||||
label: 'academics',
|
||||
key: 'academics',
|
||||
},
|
||||
{
|
||||
label: 'advocacy_causes',
|
||||
key: 'advocacy_causes',
|
||||
},
|
||||
{
|
||||
label: 'creativity',
|
||||
key: 'creativity',
|
||||
},
|
||||
{
|
||||
label: 'entertainment',
|
||||
key: 'entertainment',
|
||||
},
|
||||
{
|
||||
label: 'finance',
|
||||
key: 'finance',
|
||||
},
|
||||
{
|
||||
label: 'health_fitness',
|
||||
key: 'health_fitness',
|
||||
},
|
||||
{
|
||||
label: 'hobbies_occupations',
|
||||
key: 'hobbies_occupations',
|
||||
},
|
||||
{
|
||||
label: 'location_based',
|
||||
key: 'location_based',
|
||||
},
|
||||
{
|
||||
label: 'mental_health',
|
||||
key: 'mental_health',
|
||||
},
|
||||
{
|
||||
label: 'getting_organized',
|
||||
key: 'getting_organized',
|
||||
},
|
||||
{
|
||||
label: 'self_improvement',
|
||||
key: 'self_improvement',
|
||||
},
|
||||
{
|
||||
label: 'spirituality',
|
||||
key: 'spirituality',
|
||||
},
|
||||
{
|
||||
label: 'time_management',
|
||||
key: 'time_management',
|
||||
},
|
||||
],
|
||||
membershipFilters: [],
|
||||
membershipOptions: [
|
||||
{
|
||||
|
||||
@@ -379,7 +379,6 @@ import informationIcon from '@/assets/svg/information.svg';
|
||||
|
||||
import { MAX_SUMMARY_SIZE_FOR_GUILDS } from '@/../../common/script/constants';
|
||||
import { userStateMixin } from '../../mixins/userState';
|
||||
import CategoryOptions from '@/../../common/script/content/categoryOptions';
|
||||
|
||||
// @TODO: Not sure the best way to pass party creating status
|
||||
// Since we need the modal in the header, passing props doesn't work
|
||||
@@ -411,7 +410,64 @@ export default {
|
||||
allowGuildInvitationsFromNonMembers: true,
|
||||
bannedWordsAllowed: null,
|
||||
},
|
||||
categoryOptions: CategoryOptions,
|
||||
categoryOptions: [
|
||||
{
|
||||
label: 'habitica_official',
|
||||
key: 'habitica_official',
|
||||
},
|
||||
{
|
||||
label: 'academics',
|
||||
key: 'academics',
|
||||
},
|
||||
{
|
||||
label: 'advocacy_causes',
|
||||
key: 'advocacy_causes',
|
||||
},
|
||||
{
|
||||
label: 'creativity',
|
||||
key: 'creativity',
|
||||
},
|
||||
{
|
||||
label: 'entertainment',
|
||||
key: 'entertainment',
|
||||
},
|
||||
{
|
||||
label: 'finance',
|
||||
key: 'finance',
|
||||
},
|
||||
{
|
||||
label: 'health_fitness',
|
||||
key: 'health_fitness',
|
||||
},
|
||||
{
|
||||
label: 'hobbies_occupations',
|
||||
key: 'hobbies_occupations',
|
||||
},
|
||||
{
|
||||
label: 'location_based',
|
||||
key: 'location_based',
|
||||
},
|
||||
{
|
||||
label: 'mental_health',
|
||||
key: 'mental_health',
|
||||
},
|
||||
{
|
||||
label: 'getting_organized',
|
||||
key: 'getting_organized',
|
||||
},
|
||||
{
|
||||
label: 'recovery_support_groups',
|
||||
key: 'recovery_support_groups',
|
||||
},
|
||||
{
|
||||
label: 'spirituality',
|
||||
key: 'spirituality',
|
||||
},
|
||||
{
|
||||
label: 'time_management',
|
||||
key: 'time_management',
|
||||
},
|
||||
],
|
||||
showCategorySelect: false,
|
||||
members: [],
|
||||
inviteMembers: false,
|
||||
|
||||
@@ -86,7 +86,6 @@
|
||||
import throttle from 'lodash/throttle';
|
||||
import FilterSidebar from '@/components/ui/filterSidebar';
|
||||
import FilterGroup from '@/components/ui/filterGroup';
|
||||
import CategoryOptions from '@/../../common/script/content/categoryOptions';
|
||||
|
||||
// TODO use checkbox-component to add/remove entries to *Filters, but without the v-model binding
|
||||
|
||||
@@ -95,7 +94,64 @@ export default {
|
||||
data () {
|
||||
return {
|
||||
categoryFilters: [],
|
||||
categoryOptions: CategoryOptions,
|
||||
categoryOptions: [
|
||||
{
|
||||
label: 'habitica_official',
|
||||
key: 'habitica_official',
|
||||
},
|
||||
{
|
||||
label: 'academics',
|
||||
key: 'academics',
|
||||
},
|
||||
{
|
||||
label: 'advocacy_causes',
|
||||
key: 'advocacy_causes',
|
||||
},
|
||||
{
|
||||
label: 'creativity',
|
||||
key: 'creativity',
|
||||
},
|
||||
{
|
||||
label: 'entertainment',
|
||||
key: 'entertainment',
|
||||
},
|
||||
{
|
||||
label: 'finance',
|
||||
key: 'finance',
|
||||
},
|
||||
{
|
||||
label: 'health_fitness',
|
||||
key: 'health_fitness',
|
||||
},
|
||||
{
|
||||
label: 'hobbies_occupations',
|
||||
key: 'hobbies_occupations',
|
||||
},
|
||||
{
|
||||
label: 'location_based',
|
||||
key: 'location_based',
|
||||
},
|
||||
{
|
||||
label: 'mental_health',
|
||||
key: 'mental_health',
|
||||
},
|
||||
{
|
||||
label: 'getting_organized',
|
||||
key: 'getting_organized',
|
||||
},
|
||||
{
|
||||
label: 'recovery_support_groups',
|
||||
key: 'recovery_support_groups',
|
||||
},
|
||||
{
|
||||
label: 'spirituality',
|
||||
key: 'spirituality',
|
||||
},
|
||||
{
|
||||
label: 'time_management',
|
||||
key: 'time_management',
|
||||
},
|
||||
],
|
||||
roleFilters: [],
|
||||
roleOptions: [
|
||||
{
|
||||
|
||||
@@ -241,14 +241,6 @@ const NOTIFICATIONS = {
|
||||
achievement: 'mountColorAchievs',
|
||||
},
|
||||
},
|
||||
ACHIEVEMENT_PET_SET_COMPLETE: {
|
||||
achievement: true,
|
||||
label: $t => `${$t('achievement')}: ${$t('achievementPetSetComplete')}`,
|
||||
modalId: 'generic-achievement',
|
||||
data: {
|
||||
achievement: 'petSetCompleteAchievs',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default {
|
||||
@@ -321,7 +313,6 @@ export default {
|
||||
'ACHIEVEMENT_ANIMAL_SET',
|
||||
'ACHIEVEMENT_PET_COLOR',
|
||||
'ACHIEVEMENT_MOUNT_COLOR',
|
||||
'ACHIEVEMENT_PET_SET_COMPLETE',
|
||||
].forEach(type => {
|
||||
handledNotifications[type] = true;
|
||||
});
|
||||
@@ -781,15 +772,6 @@ export default {
|
||||
Vue.set(this.user.achievements, achievement, true);
|
||||
break;
|
||||
}
|
||||
case 'ACHIEVEMENT_PET_SET_COMPLETE': {
|
||||
const { achievement } = notification.data;
|
||||
const upperCaseAchievement = achievement.charAt(0).toUpperCase() + achievement.slice(1);
|
||||
const achievementTitleKey = `achievement${upperCaseAchievement}`;
|
||||
NOTIFICATIONS.ACHIEVEMENT_PET_SET_COMPLETE.label = $t => `${$t('achievement')}: ${$t(achievementTitleKey)}`;
|
||||
this.showNotificationWithModal(notification);
|
||||
Vue.set(this.user.achievements, achievement, true);
|
||||
break;
|
||||
}
|
||||
case 'ACHIEVEMENT': { // generic achievement
|
||||
const { achievement } = notification.data;
|
||||
const upperCaseAchievement = achievement.charAt(0).toUpperCase() + achievement.slice(1);
|
||||
|
||||
@@ -75,7 +75,6 @@
|
||||
v-show="selectedPage === 'subscription'"
|
||||
class="subscribe-option"
|
||||
:userReceivingGift="userReceivingGift"
|
||||
:receiverName="receiverName"
|
||||
/>
|
||||
|
||||
<!-- gem block -->
|
||||
@@ -649,7 +648,6 @@ export default {
|
||||
},
|
||||
},
|
||||
giftReceiver: this.receiverName,
|
||||
toUserId: this.userReceivingGift._id,
|
||||
});
|
||||
}, 500);
|
||||
},
|
||||
|
||||
@@ -1,22 +1,11 @@
|
||||
<template>
|
||||
<b-modal
|
||||
id="payments-success-modal"
|
||||
:hide-footer="isNewGroup || isGems || isSubscription"
|
||||
:modal-class="isNewGroup || isGems || isSubscription
|
||||
? ['modal-hidden-footer'] : []"
|
||||
:title="$t('accountSuspendedTitle')"
|
||||
:hide-footer="isFromBalance || paymentData.newGroup"
|
||||
:modal-class="isFromBalance || paymentData.newGroup ? ['modal-hidden-footer'] : []"
|
||||
>
|
||||
<!-- HEADER -->
|
||||
<div slot="modal-header">
|
||||
<div
|
||||
class="modal-close"
|
||||
@click="close()"
|
||||
>
|
||||
<div
|
||||
class="icon-close"
|
||||
v-html="icons.close"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="check-container d-flex align-items-center justify-content-center">
|
||||
<div
|
||||
v-once
|
||||
@@ -24,127 +13,19 @@
|
||||
v-html="icons.check"
|
||||
></div>
|
||||
</div>
|
||||
<h2>{{ $t(isGemsBalance ? 'success' : 'paymentSuccessful') }}</h2>
|
||||
<h2>{{ $t(isFromBalance ? 'success' : 'paymentSuccessful') }}</h2>
|
||||
</div>
|
||||
<!-- BODY -->
|
||||
<div class="row">
|
||||
<div class="col-12 modal-body-col">
|
||||
<!-- buy gems for self -->
|
||||
<template v-if="isGems">
|
||||
<strong v-once>{{ $t('paymentYouReceived') }}</strong>
|
||||
<div class="details-block gems">
|
||||
<div
|
||||
v-once
|
||||
class="svg-icon"
|
||||
v-html="icons.gem"
|
||||
></div>
|
||||
<span>{{ paymentData.gemsBlock.gems }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<!-- buy gems to someone else OR send gems from balance-->
|
||||
<template
|
||||
v-if="isGiftGems || isGemsBalance"
|
||||
>
|
||||
<span v-html="$t('paymentYouSentGems', {name: paymentData.giftReceiver})"></span>
|
||||
<div class="details-block gems">
|
||||
<div
|
||||
v-once
|
||||
class="svg-icon"
|
||||
v-html="icons.gem"
|
||||
></div>
|
||||
<span>{{ paymentData.gift.gems.amount }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<!-- give gift subscription (non-recurring)-->
|
||||
<template v-if="paymentData.paymentType === 'gift-subscription'">
|
||||
<div>
|
||||
<span
|
||||
v-html="$t('paymentYouSentSubscription', {
|
||||
name: paymentData.giftReceiver, months: paymentData.subscription.months})"
|
||||
></span>
|
||||
</div>
|
||||
</template>
|
||||
<!-- buy self subscription (recurring) -->
|
||||
<template v-if="isSubscription">
|
||||
<strong v-once>{{ $t('nowSubscribed') }}</strong>
|
||||
<div class="details-block">
|
||||
<span
|
||||
v-html="$t('paymentSubBilling', {
|
||||
amount: paymentData.subscription.price, months: paymentData.subscription.months})"
|
||||
></span>
|
||||
</div>
|
||||
</template>
|
||||
<!-- group plan new or upgraded -->
|
||||
<template v-if="isGroupPlan">
|
||||
<span
|
||||
v-html="$t(isNewGroup
|
||||
? 'groupPlanCreated' : 'groupPlanUpgraded', {groupName: paymentData.group.name})"
|
||||
></span>
|
||||
<div
|
||||
v-if="isGroupPlan"
|
||||
class=""
|
||||
>
|
||||
<div class="details-block group-billing-date">
|
||||
<span
|
||||
v-html="$t('groupsPaymentSubBilling', { renewalDate })"
|
||||
>
|
||||
</span>
|
||||
</div>
|
||||
<div class="small-text group-auto-renew">
|
||||
<span
|
||||
v-once
|
||||
>{{ $t('groupsPaymentAutoRenew') }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<!-- buy self subscription auto renew -->
|
||||
<template
|
||||
v-if="isSubscription"
|
||||
>
|
||||
<span
|
||||
v-once
|
||||
class="small-text auto-renew"
|
||||
>{{ $t('paymentAutoRenew') }}</span>
|
||||
</template>
|
||||
<!-- buttons for subscriptions / new Group / buy Gems for self -->
|
||||
<button
|
||||
v-if="isNewGroup || isGems || isSubscription"
|
||||
v-once
|
||||
class="btn btn-primary"
|
||||
@click="submit()"
|
||||
>
|
||||
{{ $t('onwards') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- FOOTER -->
|
||||
<div slot="modal-footer">
|
||||
<!-- gift gems balance & buy, gift subscription -->
|
||||
<!-- everyone else -->
|
||||
<div
|
||||
v-if="isGemsBalance || isGiftGems || isGiftSubscription"
|
||||
class="message mx-auto"
|
||||
v-if="paymentData.paymentType !== 'groupPlan' || paymentData.newGroup"
|
||||
class="small-text"
|
||||
>
|
||||
<lockable-label
|
||||
:text="$t('sendGiftLabel')"
|
||||
class="mx-auto label-text"
|
||||
/>
|
||||
<textarea
|
||||
v-model="gift.message"
|
||||
class="form-control mx-auto"
|
||||
:placeholder="$t('sendGiftMessagePlaceholder')"
|
||||
></textarea>
|
||||
<button
|
||||
:disabled="!gift.message || sendingInProgress"
|
||||
class="btn btn-primary mx-auto"
|
||||
@click="sendMessage()"
|
||||
>
|
||||
{{ $t('sendMessage') }}
|
||||
</button>
|
||||
{{ $t('giftSubscriptionText4') }}
|
||||
</div>
|
||||
<!-- upgradedGroup -->
|
||||
<div
|
||||
v-else-if="isUpgradedGroup"
|
||||
v-else
|
||||
class="demographics d-flex flex-column justify-content-center"
|
||||
>
|
||||
<lockable-label
|
||||
@@ -175,137 +56,108 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-12 modal-body-col">
|
||||
<!-- buy gems for self -->
|
||||
<template v-if="paymentData.paymentType === 'gems'">
|
||||
<strong v-once>{{ $t('paymentYouReceived') }}</strong>
|
||||
<div class="details-block gems">
|
||||
<div
|
||||
v-once
|
||||
class="svg-icon"
|
||||
v-html="icons.gem"
|
||||
></div>
|
||||
<span>{{ paymentData.gemsBlock.gems }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<!-- buy or gift gems to someone else -->
|
||||
<template
|
||||
v-if="paymentData.paymentType === 'gift-gems'
|
||||
|| paymentData.paymentType === 'gift-gems-balance'"
|
||||
>
|
||||
<span v-html="$t('paymentYouSentGems', {name: paymentData.giftReceiver})"></span>
|
||||
<div class="details-block gems">
|
||||
<div
|
||||
v-once
|
||||
class="svg-icon"
|
||||
v-html="icons.gem"
|
||||
></div>
|
||||
<span>{{ paymentData.gift.gems.amount }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<!-- give gift subscription (non-recurring)-->
|
||||
<template v-if="paymentData.paymentType === 'gift-subscription'">
|
||||
<span
|
||||
v-html="$t('paymentYouSentSubscription', {
|
||||
name: paymentData.giftReceiver, months: paymentData.subscription.months})"
|
||||
></span>
|
||||
</template>
|
||||
<!-- buy self subscription (recurring) -->
|
||||
<template v-if="paymentData.paymentType === 'subscription'">
|
||||
<strong v-once>{{ $t('nowSubscribed') }}</strong>
|
||||
<div class="details-block">
|
||||
<span
|
||||
v-html="$t('paymentSubBilling', {
|
||||
amount: paymentData.subscription.price, months: paymentData.subscription.months})"
|
||||
></span>
|
||||
</div>
|
||||
</template>
|
||||
<!-- group plan new or upgraded -->
|
||||
<template v-if="paymentData.paymentType === 'groupPlan'">
|
||||
<span
|
||||
v-html="$t(paymentData.newGroup
|
||||
? 'groupPlanCreated' : 'groupPlanUpgraded', {groupName: paymentData.group.name})"
|
||||
></span>
|
||||
<div
|
||||
v-if="!paymentData.newGroup || paymentData.newGroup"
|
||||
class=""
|
||||
>
|
||||
<div class="details-block group-billing-date">
|
||||
<span
|
||||
v-html="$t('groupsPaymentSubBilling', { renewalDate })"
|
||||
>
|
||||
</span>
|
||||
</div>
|
||||
<div class="small-text group-auto-renew">
|
||||
<span
|
||||
v-once
|
||||
>{{ $t('groupsPaymentAutoRenew') }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<!-- buy self subscription auto renew -->
|
||||
<template
|
||||
v-if="paymentData.paymentType === 'subscription'"
|
||||
>
|
||||
<span
|
||||
v-once
|
||||
class="small-text auto-renew"
|
||||
>{{ $t('paymentAutoRenew') }}</span>
|
||||
</template>
|
||||
<!-- buttons for subscriptions -->
|
||||
<button
|
||||
v-if="paymentData.paymentType !== 'groupPlan'"
|
||||
v-once
|
||||
class="btn btn-primary"
|
||||
@click="submit()"
|
||||
>
|
||||
{{ $t('onwards') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</b-modal>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@import '~@/assets/scss/colors.scss';
|
||||
|
||||
#payments-success-modal {
|
||||
.modal-md {
|
||||
max-width: 448px;
|
||||
min-width: 330px;
|
||||
#payments-success-modal .modal-md {
|
||||
max-width: 448px;
|
||||
}
|
||||
|
||||
.modal-close {
|
||||
position: absolute;
|
||||
right: 16px;
|
||||
top: 16px;
|
||||
cursor: pointer;
|
||||
|
||||
.icon-close {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
vertical-align: middle;
|
||||
|
||||
& svg path {
|
||||
fill: $green-1;
|
||||
}
|
||||
& :hover {
|
||||
fill: $green-1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
justify-content: center;
|
||||
padding-top: 24px;
|
||||
padding-bottom: 0px;
|
||||
background: $green-100;
|
||||
border-top-right-radius: 8px;
|
||||
border-top-left-radius: 8px;
|
||||
border-bottom: none;
|
||||
|
||||
h2 {
|
||||
color: $green-1;
|
||||
}
|
||||
|
||||
.check-container {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
border-radius: 50%;
|
||||
background: $green-1;
|
||||
margin: 0 auto;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.check {
|
||||
width: 35.1px;
|
||||
height: 28px;
|
||||
color: $white;
|
||||
}
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
padding: 16px 32px 24px 32px;
|
||||
background: $white;
|
||||
|
||||
.modal-body-col {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
|
||||
.btn.btn-primary {
|
||||
margin-top: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
.details-block {
|
||||
background: $gray-700;
|
||||
border-radius: 4px;
|
||||
padding: 8px 16px;
|
||||
margin-top: 16px;
|
||||
display: inline-flex;
|
||||
flex-direction: row;
|
||||
text-align: center;
|
||||
|
||||
&.gems {
|
||||
padding: 12px 16px 12px 20px;
|
||||
color: $green-10;
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
line-height: 1.33;
|
||||
|
||||
.svg-icon {
|
||||
margin-right: 8px;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.auto-renew {
|
||||
margin-top: 16px;
|
||||
color: $orange-10;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.group-auto-renew {
|
||||
margin: 12px 20px -8px 20px;
|
||||
color: $yellow-5;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.group-billing-date {
|
||||
width: 269px;
|
||||
}
|
||||
}
|
||||
.modal-footer {
|
||||
background: $gray-700;
|
||||
border-bottom-right-radius: 8px;
|
||||
border-bottom-left-radius: 8px;
|
||||
justify-content: center;
|
||||
border-top: none;
|
||||
|
||||
.small-text {
|
||||
font-style: normal;
|
||||
}
|
||||
}
|
||||
}
|
||||
#payments-success-modal .modal-content {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
#payments-success-modal.modal-hidden-footer .modal-body {
|
||||
@@ -313,6 +165,102 @@
|
||||
border-bottom-left-radius: 8px;
|
||||
}
|
||||
|
||||
|
||||
#payments-success-modal .modal-header {
|
||||
justify-content: center;
|
||||
padding-top: 24px;
|
||||
padding-bottom: 0px;
|
||||
background: $green-100;
|
||||
border-top-right-radius: 8px;
|
||||
border-top-left-radius: 8px;
|
||||
border-bottom: none;
|
||||
|
||||
h2 {
|
||||
color: $green-1;
|
||||
}
|
||||
|
||||
.check-container {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
border-radius: 50%;
|
||||
background: $green-1;
|
||||
margin: 0 auto;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.check {
|
||||
width: 35.1px;
|
||||
height: 28px;
|
||||
color: $white;
|
||||
}
|
||||
}
|
||||
|
||||
#payments-success-modal .modal-body {
|
||||
padding: 16px 32px 24px 32px;
|
||||
background: $white;
|
||||
|
||||
.modal-body-col {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
|
||||
.btn.btn-primary {
|
||||
margin-top: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
.details-block {
|
||||
background: $gray-700;
|
||||
border-radius: 4px;
|
||||
padding: 8px 16px;
|
||||
margin-top: 16px;
|
||||
display: inline-flex;
|
||||
flex-direction: row;
|
||||
text-align: center;
|
||||
|
||||
&.gems {
|
||||
padding: 12px 16px 12px 20px;
|
||||
color: $green-10;
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
line-height: 1.33;
|
||||
|
||||
.svg-icon {
|
||||
margin-right: 8px;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.auto-renew {
|
||||
margin-top: 16px;
|
||||
color: $orange-10;
|
||||
font-style: normal;
|
||||
}
|
||||
.group-auto-renew {
|
||||
margin: 12px 20px -8px 20px;
|
||||
color: $yellow-5;
|
||||
font-style: normal;
|
||||
}
|
||||
.group-billing-date {
|
||||
width: 269px;
|
||||
}
|
||||
}
|
||||
|
||||
#payments-success-modal .modal-footer {
|
||||
background: $gray-700;
|
||||
border-bottom-right-radius: 8px;
|
||||
border-bottom-left-radius: 8px;
|
||||
justify-content: center;
|
||||
border-top: none;
|
||||
|
||||
.small-text {
|
||||
font-style: normal;
|
||||
}
|
||||
}
|
||||
|
||||
.demographics {
|
||||
background-color: $gray-700;
|
||||
|
||||
@@ -330,42 +278,23 @@
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
}
|
||||
.message {
|
||||
margin-bottom: 8px;
|
||||
width: 378px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
textarea.form-control {
|
||||
height: 56px;
|
||||
margin: 0 24px 24px 24px;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss">
|
||||
@import '~@/assets/scss/mixins.scss';
|
||||
|
||||
|
||||
</style>
|
||||
|
||||
|
||||
<script>
|
||||
// icons
|
||||
import checkIcon from '@/assets/svg/check.svg';
|
||||
import gemIcon from '@/assets/svg/gem.svg';
|
||||
import closeIcon from '@/assets/svg/close.svg';
|
||||
|
||||
// components
|
||||
import { mapState } from '@/libs/store';
|
||||
import subscriptionBlocks from '@/../../common/script/content/subscriptionBlocks';
|
||||
import selectTranslatedArray from '@/components/tasks/modal-controls/selectTranslatedArray';
|
||||
import lockableLabel from '@/components/tasks/modal-controls/lockableLabel';
|
||||
|
||||
// mixins
|
||||
import notificationsMixin from '@/mixins/notifications';
|
||||
import paymentsMixin from '@/mixins/payments';
|
||||
|
||||
// analytics
|
||||
import * as Analytics from '@/libs/analytics';
|
||||
|
||||
export default {
|
||||
@@ -373,30 +302,18 @@ export default {
|
||||
selectTranslatedArray,
|
||||
lockableLabel,
|
||||
},
|
||||
mixins: [
|
||||
paymentsMixin,
|
||||
notificationsMixin,
|
||||
],
|
||||
mixins: [paymentsMixin],
|
||||
data () {
|
||||
return {
|
||||
icons: Object.freeze({
|
||||
check: checkIcon,
|
||||
gem: gemIcon,
|
||||
close: closeIcon,
|
||||
}),
|
||||
paymentData: {},
|
||||
upgradedGroup: {
|
||||
name: '',
|
||||
demographics: null,
|
||||
},
|
||||
sendingInProgress: false,
|
||||
gift: {
|
||||
message: '',
|
||||
},
|
||||
receiverName: {
|
||||
name: null,
|
||||
uuid: null,
|
||||
},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@@ -406,30 +323,9 @@ export default {
|
||||
const memberCount = this.paymentData.group.memberCount || 1;
|
||||
return sub.price + 3 * (memberCount - 1);
|
||||
},
|
||||
isGemsBalance () {
|
||||
isFromBalance () {
|
||||
return this.paymentData.paymentType === 'gift-gems-balance';
|
||||
},
|
||||
isGems () {
|
||||
return this.paymentData.paymentType === 'gems';
|
||||
},
|
||||
isGiftGems () {
|
||||
return this.paymentData.paymentType === 'gift-gems';
|
||||
},
|
||||
isGiftSubscription () {
|
||||
return this.paymentData.paymentType === 'gift-subscription';
|
||||
},
|
||||
isSubscription () {
|
||||
return this.paymentData.paymentType === 'subscription';
|
||||
},
|
||||
isGroupPlan () {
|
||||
return this.paymentData.paymentType === 'groupPlan';
|
||||
},
|
||||
isUpgradedGroup () {
|
||||
return this.paymentData.paymentType === 'groupPlan' && !this.paymentData.newGroup;
|
||||
},
|
||||
isNewGroup () {
|
||||
return this.paymentData.paymentType === 'groupPlan' && this.paymentData.newGroup;
|
||||
},
|
||||
},
|
||||
mounted () {
|
||||
this.$root.$on('habitica:payment-success', data => {
|
||||
@@ -445,19 +341,6 @@ export default {
|
||||
this.$root.$off('habitica:payments-success');
|
||||
},
|
||||
methods: {
|
||||
async sendMessage () {
|
||||
this.sendingInProgress = true;
|
||||
await this.$store.dispatch('members:sendPrivateMessage', {
|
||||
message: this.gift.message,
|
||||
toUserId: this.paymentData.gift.uuid || this.paymentData.toUserId,
|
||||
});
|
||||
this.close();
|
||||
},
|
||||
close () {
|
||||
this.gift.message = '';
|
||||
this.sendingInProgress = false;
|
||||
this.$root.$emit('bv::hide::modal', 'payments-success-modal');
|
||||
},
|
||||
submit () {
|
||||
if (this.paymentData.group && !this.paymentData.newGroup) {
|
||||
Analytics.track({
|
||||
|
||||
@@ -125,10 +125,6 @@ export default {
|
||||
type: Object,
|
||||
default () {},
|
||||
},
|
||||
receiverName: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
@@ -139,6 +135,7 @@ export default {
|
||||
type: 'subscription',
|
||||
subscription: { key: 'basic_earned' },
|
||||
},
|
||||
receiverName: '',
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
||||
@@ -31,11 +31,6 @@
|
||||
class="svg-icon inline lock"
|
||||
v-html="icons.lock"
|
||||
></span>
|
||||
<span
|
||||
v-if="item.completed"
|
||||
class="svg-icon inline check"
|
||||
v-html="icons.check"
|
||||
></span>
|
||||
<span
|
||||
v-if="item.isSuggested"
|
||||
class="suggestedDot"
|
||||
@@ -205,16 +200,6 @@
|
||||
color: $gray-200;
|
||||
}
|
||||
|
||||
span.svg-icon.inline.check {
|
||||
height: 12px;
|
||||
width: 10px;
|
||||
position: absolute;
|
||||
left: 8px;
|
||||
top: 8px;
|
||||
margin-top: 0;
|
||||
color: $gray-200;
|
||||
}
|
||||
|
||||
span.badge.badge-round.badge-item.badge-clock {
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
@@ -261,7 +246,6 @@ import svgGem from '@/assets/svg/gem.svg';
|
||||
import svgGold from '@/assets/svg/gold.svg';
|
||||
import svgHourglasses from '@/assets/svg/hourglass.svg';
|
||||
import svgLock from '@/assets/svg/lock.svg';
|
||||
import svgCheck from '@/assets/svg/check.svg';
|
||||
import svgClock from '@/assets/svg/clock.svg';
|
||||
|
||||
import EquipmentAttributesPopover from '@/components/inventory/equipment/attributesPopover';
|
||||
@@ -313,7 +297,6 @@ export default {
|
||||
gems: svgGem,
|
||||
gold: svgGold,
|
||||
lock: svgLock,
|
||||
check: svgCheck,
|
||||
hourglasses: svgHourglasses,
|
||||
clock: svgClock,
|
||||
}),
|
||||
@@ -367,7 +350,6 @@ export default {
|
||||
'highlight-border': this.highlightBorder,
|
||||
suggested: this.item.isSuggested,
|
||||
locked: this.item.locked,
|
||||
completed: this.item.completed,
|
||||
};
|
||||
},
|
||||
countdownString () {
|
||||
|
||||
@@ -138,8 +138,5 @@
|
||||
"achievementGroupsBeta2022ModalText":"You and your groups helped Habitica by testing and providing feedback!",
|
||||
"achievementWoodlandWizard": "Woodland Wizard",
|
||||
"achievementWoodlandWizardText": "Has hatched all standard colors of forest creatures: Badger, Bear, Deer, Fox, Frog, Hedgehog, Owl, Snail, Squirrel, and Treeling!",
|
||||
"achievementWoodlandWizardModalText": "You collected all the forest pets!",
|
||||
"achievementBoneToPick": "Bone to Pick",
|
||||
"achievementBoneToPickText": "Has hatched all the Classic and Quest Skeleton Pets!",
|
||||
"achievementBoneToPickModalText": "You collected all the Classic and Quest Skeleton Pets!"
|
||||
"achievementWoodlandWizardModalText": "You collected all the forest pets!"
|
||||
}
|
||||
|
||||
@@ -71,5 +71,5 @@
|
||||
"webFaqStillNeedHelp": "If you have a question that isn't on this list or on the [Wiki FAQ](https://habitica.fandom.com/wiki/FAQ), come ask in the [Habitica Help guild](https://habitica.com/groups/guild/5481ccf3-5d2d-48a9-a871-70a7380cee5a)! We're happy to help.",
|
||||
|
||||
"faqQuestion13": "What is a Group Plan?",
|
||||
"webFaqAnswer13": "## How do Group Plans work?\n\nA [Group Plan](/group-plans) gives your Party or Guild access to a shared task board that’s similar to your personal task board! It’s a shared Habitica experience where tasks can be created and checked off by anyone in the group.\n\nThere are also features available like member roles, status view, and task assigning that give you a more controlled experience. [Visit our wiki](https://habitica.fandom.com/wiki/Group_Plans) to learn more about our Group Plans’ features!\n\n## Who benefits from a Group Plan?\n\nGroup Plans work best when you have a small team of people who want to collaborate together. We recommend 2-5 members.\n\nGroup Plans are great for families, whether it’s a parent and child or you and a partner. Shared goals, chores, or responsibilities are easy to keep track of on one board.\n\nGroup Plans can also be useful for teams of colleagues that have shared goals, or managers that want to introduce their employees to gamification.\n\n## Quick tips for using Groups\n\nHere are some quick tips to get you started with your new Group. We’ll provide more details in the following sections:\n\n* Make a member a manager to give them the ability to create and edit tasks\n* Leave tasks unassigned if anyone can complete it and it only needs done once\n* Assign a task to one person to make sure no one else can complete their task\n* Assign a task to multiple people if they all need to complete it\n* Toggle the ability to display shared tasks on your personal board to not miss anything\n* You get rewarded for the tasks you complete, even multi-assigned\n* Task completion rewards aren’t shared or split between Team members\n* Use task color on the team board to judge the average completion rate of tasks\n* Regularly review the tasks on your Team Board to make sure they are still relevant\n* Missing a Daily won’t damage you or your team, but the task will degrade in color\n\n## How can others in the group create tasks?\n\nOnly the group leader and managers can create tasks. If you’d like a group member to be able to create tasks, then you should promote them to be a manager by going to the Group Information tab, viewing the member list, and clicking the dot icon by their name.\n\n## How does assigning a task work?\n\nGroup Plans give you the unique ability to assign tasks to other group members. Assigning a task is great for delegating. If you assign a task to someone, then other members are prevented from completing it.\n\nYou can also assign a task to multiple people if it needs to be completed by more than one member. For example, if everyone has to brush their teeth, create a task and assign it to each group member. They will all be able to check it off and get their individual rewards for doing so. The main task will show as complete once everyone checks it off.\n\n## How do unassigned tasks work?\n\nUnassigned tasks can be completed by anyone in the group, so leave a task unassigned to allow any member to complete it. For example, taking out the trash. Whoever takes out the trash can check off the unassigned task and it will show as completed for everyone.\n\n## How does the synchronized day reset work?\n\nShared tasks will reset at the same time for everyone to keep the shared task board in sync. This time is visible on the shared task board and is determined by the group leader’s day start time. Because shared tasks reset automatically, you will not get a chance to complete yesterday’s uncompleted shared Dailies when you check in the next morning.\n\nShared Dailies will not do damage if they are missed, however they will degrade in color to help visualize progress. We don’t want the shared experience to be a negative one!\n\n## How do I use my Group on the mobile apps?\n\nWhile the mobile apps don’t fully support all Group Plans functionality yet, you can still complete shared tasks from the iOS and Android app by copying the tasks onto your personal task board. You can switch this preference on from Settings in the mobile apps or from the group task board on the browser version. Now open and assigned shared tasks will display on your personal task board across all platforms.\n\n## What’s the difference between a Group’s shared tasks and Challenges?\n\nGroup Plan shared task boards are more dynamic than Challenges, in that they can constantly be updated and interacted with. Challenges are great if you have one set of tasks to send out to many people.\n\nGroup Plans are also a paid feature, while Challenges are available free to everyone.\n\nYou cannot assign specific tasks in Challenges, and Challenges do not have a shared day reset. In general, Challenges offer less control and direct interaction."
|
||||
"webFaqAnswer13": "## How do Group Plans work?\n\nA [Group Plan](/group-plans) gives your Party or Guild access to a shared task board that’s similar to your personal task board! It’s a shared Habitica experience where tasks can be created and checked off by anyone in the group.\n\nThere are also features available like member roles, status view, and task assigning that give you a more controlled experience. [Visit our wiki](https://habitica.fandom.com/wiki/Group_Plans) to learn more about our Group Plans’ features!\n\n## Who benefits from a Group Plan?\n\nGroup Plans work best when you have a small team of people who want to collaborate together. We recommend 2-5 members.\n\nGroup Plans are great for families, whether it’s a parent and child or you and a partner. Shared goals, chores, or responsibilities are easy to keep track of on one board.\n\nGroup Plans can also be useful for teams of colleagues that have shared goals, or managers that want to introduce their employees to gamification.\n\n## Quick tips for using Groups\n\nHere are some quick tips to get you started with your new Group. We’ll provide more details in the following sections:\n\n* Make a member a manager to give them the ability to create and edit tasks\n* Leave tasks unassigned if anyone can complete it and it only needs done once\n* Assign a task to one person to make sure no one else can complete their task\n* Assign a task to multiple people if they all need to complete it\n* Toggle the ability to display shared tasks on your personal board to not miss anything\n* You get rewarded for the tasks you complete, even multi-assigned\n* Task completion rewards aren’t shared or split between Team members\n* Use task color on the team board to judge the average completion rate of tasks\n* Regularly review the tasks on your Team Board to make sure they are still relevant\n* Missing a Daily won’t damage you or your team, but the task will degrade in color\n\n## How can others in the group create tasks?\n\nOnly the group leader and managers can create tasks. If you’d like a group member to be able to create tasks, then you should promote them to be a manager by going to the Group Information tab, viewing the member list, and clicking the dot icon by their name.\n\n## How does assigning a task work?\n\nGroup Plans give you the unique ability to assign tasks to other group members. Assigning a task is great for delegating. If you assign a task to someone, then other members are prevented from completing it.\n\nYou can also assign a task to multiple people if it needs to be completed by more than one member. For example, if everyone has to brush their teeth, create a task and assign it to each group member. They will all be able to check it off and get their individual rewards for doing so. The main task will show as complete once everyone checks it off.\n\n## How do unassigned tasks work?\n\nUnassigned tasks can be completed by anyone in the group, so leave a task unassigned to allow any member to complete it. For example, taking out the trash. Whoever takes out the trash can check off the unassigned task and it will show as completed for everyone.\n\n## How does the synchronized day reset work?\n\nShared tasks will reset at the same time for everyone to keep the shared task board in sync. This time is visible on the shared task board and is determined by the group leader’s day start time. Because shared tasks reset automatically, you will not get a chance to complete yesterday’s uncompleted shared Dailies when you check in the next morning.\n\nShared Dailies will not do damage if they are missed, however they will degrade in color to help visualize progress. We don’t want the shared experience to be a negative one!\n\n## How do I use my Group on the mobile apps?\n\nWhile the mobile apps don’t fully support all Group Plans functionality yet, you can still complete shared tasks from the iOS and Android app. On the browser version of Habitica, go to your group’s shared task board and turn on the copy tasks toggle. Now all open and assigned shared tasks will display on your personal task board across all platforms.\n\n## What’s the difference between a Group’s shared tasks and Challenges?\n\nGroup Plan shared task boards are more dynamic than Challenges, in that they can constantly be updated and interacted with. Challenges are great if you have one set of tasks to send out to many people.\n\nGroup Plans are also a paid feature, while Challenges are available free to everyone.\n\nYou cannot assign specific tasks in Challenges, and Challenges do not have a shared day reset. In general, Challenges offer less control and direct interaction."
|
||||
}
|
||||
|
||||
@@ -132,8 +132,7 @@
|
||||
"sendGiftTotal": "Total:",
|
||||
"sendGiftFromBalance": "From Balance",
|
||||
"sendGiftPurchase": "Purchase",
|
||||
"sendGiftLabel": "Would you like to send a gift message?",
|
||||
"sendGiftMessagePlaceholder": "Add a gift message",
|
||||
"sendGiftMessagePlaceholder": "Personal message (optional)",
|
||||
"sendGiftSubscription": "<%= months %> Month(s): $<%= price %> USD",
|
||||
"gemGiftsAreOptional": "Please note that Habitica will never require you to gift gems to other players. Begging people for gems is a <strong>violation of the Community Guidelines</strong>, and all such instances should be reported to <%= hrefTechAssistanceEmail %>.",
|
||||
"giftMessageTooLong": "The maximum length for gift messages is <%= maxGiftMessageLength %>.",
|
||||
|
||||
@@ -92,7 +92,7 @@
|
||||
"paymentSuccessful": "Your payment was successful!",
|
||||
"paymentYouReceived": "You received:",
|
||||
"paymentYouSentGems": "You sent <strong><%- name %></strong>:",
|
||||
"paymentYouSentSubscription": "You sent <strong><%- name %></strong><br> a <%= months %> month(s) Habitica subscription.",
|
||||
"paymentYouSentSubscription": "You sent <strong><%- name %></strong> a <%= months %>-months Habitica subscription.",
|
||||
"paymentSubBilling": "Your subscription will be billed <strong>$<%= amount %></strong> every <strong><%= months %> months</strong>.",
|
||||
"groupsPaymentSubBilling": "Your next billing date is <strong><%= renewalDate %></strong>.",
|
||||
"paymentSubBillingWithMethod": "Your subscription will be billed <strong>$<%= amount %></strong> every <strong><%= months %> months</strong> via <strong><%= paymentMethod %></strong>.",
|
||||
|
||||
@@ -310,15 +310,6 @@ const mountColorAchievs = {
|
||||
};
|
||||
Object.assign(achievementsData, mountColorAchievs);
|
||||
|
||||
const petSetCompleteAchievs = {
|
||||
boneToPick: {
|
||||
icon: 'achievement-boneToPick',
|
||||
titleKey: 'achievementBoneToPick',
|
||||
textKey: 'achievementBoneToPickText',
|
||||
},
|
||||
};
|
||||
Object.assign(achievementsData, petSetCompleteAchievs);
|
||||
|
||||
const onboardingAchievs = {
|
||||
createdTask: {
|
||||
icon: 'achievement-createdTask',
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
const categoryOptions = [
|
||||
{
|
||||
label: 'habitica_official',
|
||||
key: 'habitica_official',
|
||||
},
|
||||
{
|
||||
label: 'academics',
|
||||
key: 'academics',
|
||||
},
|
||||
{
|
||||
label: 'advocacy_causes',
|
||||
key: 'advocacy_causes',
|
||||
},
|
||||
{
|
||||
label: 'creativity',
|
||||
key: 'creativity',
|
||||
},
|
||||
{
|
||||
label: 'entertainment',
|
||||
key: 'entertainment',
|
||||
},
|
||||
{
|
||||
label: 'finance',
|
||||
key: 'finance',
|
||||
},
|
||||
{
|
||||
label: 'health_fitness',
|
||||
key: 'health_fitness',
|
||||
},
|
||||
{
|
||||
label: 'hobbies_occupations',
|
||||
key: 'hobbies_occupations',
|
||||
},
|
||||
{
|
||||
label: 'location_based',
|
||||
key: 'location_based',
|
||||
},
|
||||
{
|
||||
label: 'mental_health',
|
||||
key: 'mental_health',
|
||||
},
|
||||
{
|
||||
label: 'getting_organized',
|
||||
key: 'getting_organized',
|
||||
},
|
||||
{
|
||||
label: 'self_improvement',
|
||||
key: 'self_improvement',
|
||||
},
|
||||
{
|
||||
label: 'spirituality',
|
||||
key: 'spirituality',
|
||||
},
|
||||
{
|
||||
label: 'time_management',
|
||||
key: 'time_management',
|
||||
},
|
||||
];
|
||||
|
||||
export default categoryOptions;
|
||||
@@ -32,7 +32,6 @@ export { default as SEASONAL_SETS } from './seasonalSets';
|
||||
export { default as ANIMAL_COLOR_ACHIEVEMENTS } from './animalColorAchievements';
|
||||
export { default as ANIMAL_SET_ACHIEVEMENTS } from './animalSetAchievements';
|
||||
export { default as QUEST_SERIES_ACHIEVEMENTS } from './questSeriesAchievements';
|
||||
export { default as PET_SET_COMPLETE_ACHIEVEMENTS } from './petCompleteSetAchievements'; // eslint-disable-line import/no-cycle
|
||||
export { default as STABLE_ACHIEVEMENTS } from './stableAchievements';
|
||||
export { default as ITEM_LIST } from './itemList';
|
||||
export { default as QUEST_SERIES } from '../quests/series';
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
// this achievement covers all pets of a specific type--classic & quest
|
||||
|
||||
const PET_SET_COMPLETE_ACHIEVEMENTS = [
|
||||
{
|
||||
color: 'Skeleton',
|
||||
petAchievement: 'boneToPick',
|
||||
petNotificationType: 'ACHIEVEMENT_PET_SET_COMPLETE',
|
||||
},
|
||||
];
|
||||
|
||||
export default PET_SET_COMPLETE_ACHIEVEMENTS;
|
||||
@@ -13,7 +13,6 @@ import {
|
||||
ANIMAL_COLOR_ACHIEVEMENTS,
|
||||
ANIMAL_SET_ACHIEVEMENTS,
|
||||
STABLE_ACHIEVEMENTS,
|
||||
PET_SET_COMPLETE_ACHIEVEMENTS,
|
||||
} from './constants';
|
||||
|
||||
import achievements from './achievements';
|
||||
@@ -44,7 +43,6 @@ api.questSeriesAchievements = QUEST_SERIES_ACHIEVEMENTS;
|
||||
api.animalColorAchievements = ANIMAL_COLOR_ACHIEVEMENTS;
|
||||
api.animalSetAchievements = ANIMAL_SET_ACHIEVEMENTS;
|
||||
api.stableAchievements = STABLE_ACHIEVEMENTS;
|
||||
api.petSetCompleteAchievs = PET_SET_COMPLETE_ACHIEVEMENTS;
|
||||
|
||||
api.quests = quests;
|
||||
api.questsByLevel = questsByLevel;
|
||||
|
||||
@@ -219,7 +219,6 @@ function _getBasicAchievements (user, language) {
|
||||
_addSimple(result, user, { path: 'birdsOfAFeather', language });
|
||||
_addSimple(result, user, { path: 'reptacularRumble', language });
|
||||
_addSimple(result, user, { path: 'woodlandWizard', language });
|
||||
_addSimple(result, user, { path: 'boneToPick', language });
|
||||
|
||||
_addSimpleWithMasterCount(result, user, { path: 'beastMaster', language });
|
||||
_addSimpleWithMasterCount(result, user, { path: 'mountMaster', language });
|
||||
|
||||
@@ -152,7 +152,6 @@ export default function getItemInfo (user, type, item, officialPinnedItems, lang
|
||||
? content.quests[item.previous].text(language)
|
||||
: null,
|
||||
unlockCondition: item.unlockCondition,
|
||||
completed: user.achievements.quests[item.key] !== undefined,
|
||||
drop: item.drop,
|
||||
boss: item.boss,
|
||||
collect: item.collect ? _mapValues(item.collect, o => ({
|
||||
|
||||
@@ -113,34 +113,6 @@ export default function hatch (user, req = {}, analytics) {
|
||||
});
|
||||
}
|
||||
|
||||
if (content.dropEggs[egg] || content.questEggs[egg]) {
|
||||
forEach(content.petSetCompleteAchievs, achievement => {
|
||||
if (hatchingPotion !== achievement.color) return;
|
||||
if (!user.achievements[achievement.petAchievement]) {
|
||||
const dropPetIndex = findIndex(
|
||||
keys(content.dropEggs),
|
||||
animal => !user.items.pets[`${animal}-${achievement.color}`] || user.items.pets[`${animal}-${achievement.color}`] <= 0,
|
||||
);
|
||||
const questPetIndex = findIndex(
|
||||
keys(content.questEggs),
|
||||
animal => !user.items.pets[`${animal}-${achievement.color}`] || user.items.pets[`${animal}-${achievement.color}`] <= 0,
|
||||
);
|
||||
if (dropPetIndex === -1 && questPetIndex === -1) {
|
||||
user.achievements[achievement.petAchievement] = true;
|
||||
if (user.addNotification) {
|
||||
const achievementString = `achievement${upperFirst(achievement.petAchievement)}`;
|
||||
user.addNotification(achievement.petNotificationType, {
|
||||
label: `${'achievement'}: ${achievementString}`,
|
||||
achievement: achievement.petAchievement,
|
||||
message: `${i18n.t('modalAchievement')} ${i18n.t(achievementString)}`,
|
||||
modalText: i18n.t(`${achievementString}ModalText`),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (analytics && moment().diff(user.auth.timestamps.created, 'days') < 7) {
|
||||
analytics.track('pet hatch', {
|
||||
uuid: user._id,
|
||||
|
||||
@@ -492,17 +492,7 @@ api.updateGroup = {
|
||||
|
||||
if (req.body.leader !== user._id && group.hasNotCancelled()) throw new NotAuthorized(res.t('cannotChangeLeaderWithActiveGroupPlan'));
|
||||
|
||||
const handleArrays = (currentValue, updatedValue) => {
|
||||
if (!_.isArray(currentValue)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Previously, categories could get duplicated. By making the updated category list unique,
|
||||
// the duplication issue is fixed on every group edit
|
||||
return _.uniqBy(updatedValue, 'slug');
|
||||
};
|
||||
|
||||
_.assign(group, _.mergeWith(group.toObject(), Group.sanitizeUpdate(req.body), handleArrays));
|
||||
_.assign(group, _.merge(group.toObject(), Group.sanitizeUpdate(req.body)));
|
||||
|
||||
const savedGroup = await group.save();
|
||||
const response = await Group.toJSONCleanChat(savedGroup, user);
|
||||
|
||||
@@ -106,6 +106,10 @@ api.verifyGemPurchase = async function verifyGemPurchase (options) {
|
||||
};
|
||||
|
||||
api.subscribe = async function subscribe (sku, user, receipt, headers, nextPaymentProcessing) {
|
||||
if (user && user.isSubscribed()) {
|
||||
throw new NotAuthorized(this.constants.RESPONSE_ALREADY_USED);
|
||||
}
|
||||
|
||||
if (!sku) throw new BadRequest(shared.i18n.t('missingSubscriptionCode'));
|
||||
|
||||
let subCode;
|
||||
@@ -136,49 +140,33 @@ api.subscribe = async function subscribe (sku, user, receipt, headers, nextPayme
|
||||
throw new NotAuthorized(api.constants.RESPONSE_NO_ITEM_PURCHASED);
|
||||
}
|
||||
|
||||
let originalTransactionId;
|
||||
let newTransactionId;
|
||||
let transactionId;
|
||||
|
||||
for (const purchaseData of purchaseDataList) {
|
||||
const dateTerminated = new Date(Number(purchaseData.expirationDate));
|
||||
if (purchaseData.productId === sku && dateTerminated > new Date()) {
|
||||
originalTransactionId = purchaseData.originalTransactionId;
|
||||
newTransactionId = purchaseData.transactionId;
|
||||
transactionId = purchaseData.transactionId;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (originalTransactionId) {
|
||||
let existingSub;
|
||||
if (user && user.isSubscribed()) {
|
||||
if (user.purchased.plan.customerId !== originalTransactionId) {
|
||||
throw new NotAuthorized(this.constants.RESPONSE_ALREADY_USED);
|
||||
}
|
||||
existingSub = shared.content.subscriptionBlocks[user.purchased.plan.planId];
|
||||
}
|
||||
if (transactionId) {
|
||||
const existingUser = await User.findOne({
|
||||
'purchased.plan.customerId': originalTransactionId,
|
||||
'purchased.plan.customerId': transactionId,
|
||||
}).exec();
|
||||
if (existingUser
|
||||
&& (originalTransactionId === newTransactionId || existingUser._id !== user._id)) {
|
||||
throw new NotAuthorized(this.constants.RESPONSE_ALREADY_USED);
|
||||
}
|
||||
if (existingUser) throw new NotAuthorized(this.constants.RESPONSE_ALREADY_USED);
|
||||
|
||||
nextPaymentProcessing = nextPaymentProcessing || moment.utc().add({ days: 2 }); // eslint-disable-line max-len, no-param-reassign
|
||||
|
||||
const data = {
|
||||
await payments.createSubscription({
|
||||
user,
|
||||
customerId: originalTransactionId,
|
||||
customerId: transactionId,
|
||||
paymentMethod: this.constants.PAYMENT_METHOD_APPLE,
|
||||
sub,
|
||||
headers,
|
||||
nextPaymentProcessing,
|
||||
additionalData: receipt,
|
||||
};
|
||||
if (existingSub) {
|
||||
data.updatedFrom = existingSub;
|
||||
}
|
||||
await payments.createSubscription(data);
|
||||
});
|
||||
} else {
|
||||
throw new NotAuthorized(api.constants.RESPONSE_INVALID_RECEIPT);
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ import { // eslint-disable-line import/no-cycle
|
||||
model as Group,
|
||||
basicFields as basicGroupFields,
|
||||
} from '../../models/group';
|
||||
import { model as User } from '../../models/user'; // eslint-disable-line import/no-cycle
|
||||
import {
|
||||
NotAuthorized,
|
||||
NotFound,
|
||||
@@ -71,15 +70,7 @@ async function createSubscription (data) {
|
||||
? data.gift.subscription.key
|
||||
: data.sub.key];
|
||||
const autoRenews = data.autoRenews !== undefined ? data.autoRenews : true;
|
||||
const updatedFrom = data.updatedFrom
|
||||
? shared.content.subscriptionBlocks[data.updatedFrom.key]
|
||||
: undefined;
|
||||
let months;
|
||||
if (updatedFrom && Number(updatedFrom.months) !== 1) {
|
||||
months = Math.max(0, Number(block.months) - Number(updatedFrom.months));
|
||||
} else {
|
||||
months = Number(block.months);
|
||||
}
|
||||
const months = Number(block.months);
|
||||
const today = new Date();
|
||||
let group;
|
||||
let groupId;
|
||||
@@ -88,22 +79,6 @@ async function createSubscription (data) {
|
||||
let emailType = 'subscription-begins';
|
||||
let recipientIsSubscribed = recipient.isSubscribed();
|
||||
|
||||
if (data.user && !data.gift && !data.groupId) {
|
||||
const unlockedUser = await User.findOneAndUpdate(
|
||||
{
|
||||
_id: data.user._id,
|
||||
$or: [
|
||||
{ _subSignature: 'NOT_RUNNING' },
|
||||
{ _subSignature: { $exists: false } },
|
||||
],
|
||||
},
|
||||
{ $set: { _subSignature: 'SUB_IN_PROGRESS' } },
|
||||
);
|
||||
if (!unlockedUser) {
|
||||
throw new NotFound('User not found or subscription already processing.');
|
||||
}
|
||||
}
|
||||
|
||||
// If we are buying a group subscription
|
||||
if (data.groupId) {
|
||||
const groupFields = basicGroupFields.concat(' purchased');
|
||||
@@ -307,6 +282,10 @@ async function createSubscription (data) {
|
||||
}
|
||||
}
|
||||
|
||||
if (group) await group.save();
|
||||
if (data.user && data.user.isModified()) await data.user.save();
|
||||
if (data.gift) await data.gift.member.save();
|
||||
|
||||
slack.sendSubscriptionNotification({
|
||||
buyer: {
|
||||
id: data.user._id,
|
||||
@@ -323,24 +302,6 @@ async function createSubscription (data) {
|
||||
groupId,
|
||||
autoRenews,
|
||||
});
|
||||
|
||||
if (group) {
|
||||
await group.save();
|
||||
}
|
||||
if (data.user) {
|
||||
if (data.user.isModified()) {
|
||||
await data.user.save();
|
||||
}
|
||||
if (!data.gift && !data.groupId) {
|
||||
await User.findOneAndUpdate(
|
||||
{ _id: data.user._id },
|
||||
{ $set: { _subSignature: 'NOT_RUNNING' } },
|
||||
);
|
||||
}
|
||||
}
|
||||
if (data.gift) {
|
||||
await data.gift.member.save();
|
||||
}
|
||||
}
|
||||
|
||||
// Cancels a subscription or group plan, setting termination to happen later
|
||||
|
||||
@@ -151,7 +151,6 @@ export default new Schema({
|
||||
birdsOfAFeather: Boolean,
|
||||
reptacularRumble: Boolean,
|
||||
woodlandWizard: Boolean,
|
||||
boneToPick: Boolean,
|
||||
// Onboarding Guide
|
||||
createdTask: Boolean,
|
||||
completedTask: Boolean,
|
||||
@@ -431,8 +430,6 @@ export default new Schema({
|
||||
|
||||
lastCron: { $type: Date, default: Date.now },
|
||||
_cronSignature: { $type: String, default: 'NOT_RUNNING' }, // Private property used to avoid double cron
|
||||
// Lock property to avoid double subscription. Not strictly private because we query on it
|
||||
_subSignature: { $type: String, default: 'NOT_RUNNING' },
|
||||
|
||||
// {GROUP_ID: Boolean}, represents whether they have unseen chat messages
|
||||
newMessages: {
|
||||
|
||||
@@ -46,7 +46,6 @@ const NOTIFICATION_TYPES = [
|
||||
'ACHIEVEMENT_ANIMAL_SET',
|
||||
'ACHIEVEMENT_PET_COLOR',
|
||||
'ACHIEVEMENT_MOUNT_COLOR',
|
||||
'ACHIEVEMENT_PET_SET_COMPLETE',
|
||||
// Deprecated notification types. Can be removed once old data is cleaned out
|
||||
'BOSS_DAMAGE', // deprecated
|
||||
'ACHIEVEMENT_ALL_YOUR_BASE', // deprecated
|
||||
|
||||
Reference in New Issue
Block a user