mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-16 06:07:21 +01:00
Compare commits
42 Commits
v5.38.3
...
fiz/fix-or
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9dd57d7707 | ||
|
|
df030d50e3 | ||
|
|
ae4ecc72ef | ||
|
|
78df6cd657 | ||
|
|
2dcfdd29dd | ||
|
|
4e4bd4714e | ||
|
|
84bc8ad08f | ||
|
|
1582add0c2 | ||
|
|
99e48c1e30 | ||
|
|
d7aa6e7d7c | ||
|
|
6e7cae90b7 | ||
|
|
fe8f74f2d9 | ||
|
|
04c8d40e5b | ||
|
|
ffd34a0967 | ||
|
|
80d29664f0 | ||
|
|
8fab51d27e | ||
|
|
2a69f98906 | ||
|
|
5d821a15c2 | ||
|
|
5d1d70b72e | ||
|
|
b9764ddfcf | ||
|
|
fdbae638bb | ||
|
|
949809a994 | ||
|
|
ef59ecb3b1 | ||
|
|
4250fbc53f | ||
|
|
1d48439528 | ||
|
|
08ff95d015 | ||
|
|
ec91b674f1 | ||
|
|
45e1b97ebd | ||
|
|
c1ab9cb6ca | ||
|
|
5cf07d75f5 | ||
|
|
375cafc781 | ||
|
|
0c0dc20dcc | ||
|
|
ffa73698a5 | ||
|
|
f1ac0d5038 | ||
|
|
c0508a2f22 | ||
|
|
53aa960afd | ||
|
|
d16c9bc67a | ||
|
|
00a468bde9 | ||
|
|
c71528a478 | ||
|
|
338c633cdb | ||
|
|
b957d2a3b0 | ||
|
|
b2efb8286b |
Submodule habitica-images updated: e3215f16f9...992d838120
76
package-lock.json
generated
76
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "habitica",
|
||||
"version": "5.38.3",
|
||||
"version": "5.38.1",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "habitica",
|
||||
"version": "5.38.3",
|
||||
"version": "5.38.1",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.22.10",
|
||||
@@ -59,6 +59,7 @@
|
||||
"morgan": "^1.10.0",
|
||||
"nconf": "^0.12.1",
|
||||
"node-gcm": "^1.0.5",
|
||||
"nodemon": "^3.1.9",
|
||||
"on-headers": "^1.0.2",
|
||||
"passport": "^0.5.3",
|
||||
"passport-facebook": "^3.0.0",
|
||||
@@ -16010,6 +16011,55 @@
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz",
|
||||
"integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw=="
|
||||
},
|
||||
"node_modules/nodemon": {
|
||||
"version": "3.1.10",
|
||||
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.10.tgz",
|
||||
"integrity": "sha512-WDjw3pJ0/0jMFmyNDp3gvY2YizjLmmOUQo6DEBY+JgdvW/yQ9mEeSw6H5ythl5Ny2ytb7f9C2nIbjSxMNzbJXw==",
|
||||
"dependencies": {
|
||||
"chokidar": "^3.5.2",
|
||||
"debug": "^4",
|
||||
"ignore-by-default": "^1.0.1",
|
||||
"minimatch": "^3.1.2",
|
||||
"pstree.remy": "^1.1.8",
|
||||
"semver": "^7.5.3",
|
||||
"simple-update-notifier": "^2.0.0",
|
||||
"supports-color": "^5.5.0",
|
||||
"touch": "^3.1.0",
|
||||
"undefsafe": "^2.0.5"
|
||||
},
|
||||
"bin": {
|
||||
"nodemon": "bin/nodemon.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/nodemon"
|
||||
}
|
||||
},
|
||||
"node_modules/nodemon/node_modules/semver": {
|
||||
"version": "7.7.2",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
|
||||
"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/nodemon/node_modules/supports-color": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
||||
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
|
||||
"dependencies": {
|
||||
"has-flag": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/noop-logger": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/noop-logger/-/noop-logger-0.1.1.tgz",
|
||||
@@ -19373,6 +19423,28 @@
|
||||
"is-arrayish": "^0.3.1"
|
||||
}
|
||||
},
|
||||
"node_modules/simple-update-notifier": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz",
|
||||
"integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==",
|
||||
"dependencies": {
|
||||
"semver": "^7.5.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/simple-update-notifier/node_modules/semver": {
|
||||
"version": "7.7.2",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
|
||||
"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/sinon": {
|
||||
"version": "15.2.0",
|
||||
"resolved": "https://registry.npmjs.org/sinon/-/sinon-15.2.0.tgz",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "habitica",
|
||||
"description": "A habit tracker app which treats your goals like a Role Playing Game.",
|
||||
"version": "5.38.3",
|
||||
"version": "5.38.1",
|
||||
"main": "./website/server/index.js",
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.22.10",
|
||||
@@ -106,8 +106,8 @@
|
||||
"start": "node --watch ./website/server/index.js",
|
||||
"start:simple": "node ./website/server/index.js",
|
||||
"debug": "node --watch --inspect ./website/server/index.js",
|
||||
"mongo:dev": "run-rs -v 7.0.23 -l ubuntu2404 --keep --dbpath mongodb-data --number 1 --quiet",
|
||||
"mongo:test": "run-rs -v 7.0.23 -l ubuntu2404 --keep --dbpath mongodb-data-testing --number 1 --quiet",
|
||||
"mongo:dev": "run-rs -v 5.0.23 -l ubuntu1804 --keep --dbpath mongodb-data --number 1 --quiet",
|
||||
"mongo:test": "run-rs -v 5.0.23 -l ubuntu1804 --keep --dbpath mongodb-data-testing --number 1 --quiet",
|
||||
"postinstall": "git config --global url.\"https://\".insteadOf git:// && gulp build && cd website/client && npm install",
|
||||
"apidoc": "gulp apidoc",
|
||||
"heroku-postbuild": ".heroku/report_deploy.sh"
|
||||
|
||||
@@ -47,6 +47,12 @@ describe('highlightMentions', () => {
|
||||
expect(result[0]).to.equal('[@user-dash](/profile/444): message [@user_underscore](/profile/555)');
|
||||
});
|
||||
|
||||
it('highlights users with case-insensitive matching', async () => {
|
||||
const text = '@USER: message @User2 @USER3';
|
||||
const result = await highlightMentions(text);
|
||||
expect(result[0]).to.equal('[@USER](/profile/111): message [@User2](/profile/222) [@USER3](/profile/333)');
|
||||
});
|
||||
|
||||
it('doesn\'t highlight nonexisting users', async () => {
|
||||
const text = '@nouser message';
|
||||
const result = await highlightMentions(text);
|
||||
|
||||
@@ -1,197 +0,0 @@
|
||||
import nconf from 'nconf';
|
||||
import requireAgain from 'require-again';
|
||||
import {
|
||||
generateRes,
|
||||
generateReq,
|
||||
generateNext,
|
||||
} from '../../../helpers/api-unit.helper';
|
||||
import { Forbidden } from '../../../../website/server/libs/errors';
|
||||
import { apiError } from '../../../../website/server/libs/apiError';
|
||||
import { model as Blocker } from '../../../../website/server/models/blocker';
|
||||
|
||||
function checkIPBlockedErrorThrown (next) {
|
||||
expect(next).to.have.been.calledOnce;
|
||||
const calledWith = next.getCall(0).args;
|
||||
expect(calledWith[0].message).to.equal(apiError('ipAddressBlocked'));
|
||||
expect(calledWith[0] instanceof Forbidden).to.equal(true);
|
||||
}
|
||||
|
||||
function checkClientBlockedErrorThrown (next) {
|
||||
expect(next).to.have.been.calledOnce;
|
||||
const calledWith = next.getCall(0).args;
|
||||
expect(calledWith[0].message).to.equal(apiError('clientBlocked'));
|
||||
expect(calledWith[0] instanceof Forbidden).to.equal(true);
|
||||
}
|
||||
|
||||
function checkErrorNotThrown (next) {
|
||||
expect(next).to.have.been.calledOnce;
|
||||
const calledWith = next.getCall(0).args;
|
||||
expect(typeof calledWith[0] === 'undefined').to.equal(true);
|
||||
}
|
||||
|
||||
describe('Blocker middleware', () => {
|
||||
const pathToBlocker = '../../../../website/server/middlewares/blocker';
|
||||
|
||||
let res; let req; let next;
|
||||
|
||||
beforeEach(() => {
|
||||
res = generateRes();
|
||||
req = generateReq();
|
||||
next = generateNext();
|
||||
});
|
||||
|
||||
describe('Blocking IPs', () => {
|
||||
it('is disabled when the env var is not defined', () => {
|
||||
sandbox.stub(nconf, 'get').withArgs('BLOCKED_IPS').returns(undefined);
|
||||
const attachBlocker = requireAgain(pathToBlocker).default;
|
||||
attachBlocker(req, res, next);
|
||||
|
||||
checkErrorNotThrown(next);
|
||||
});
|
||||
|
||||
it('is disabled when the env var is an empty string', () => {
|
||||
sandbox.stub(nconf, 'get').withArgs('BLOCKED_IPS').returns('');
|
||||
const attachBlocker = requireAgain(pathToBlocker).default;
|
||||
attachBlocker(req, res, next);
|
||||
|
||||
checkErrorNotThrown(next);
|
||||
});
|
||||
|
||||
it('is disabled when the env var contains comma separated empty strings', () => {
|
||||
sandbox.stub(nconf, 'get').withArgs('BLOCKED_IPS').returns(' , , ');
|
||||
const attachBlocker = requireAgain(pathToBlocker).default;
|
||||
attachBlocker(req, res, next);
|
||||
|
||||
checkErrorNotThrown(next);
|
||||
});
|
||||
|
||||
it('does not throw when the ip does not match', () => {
|
||||
req.ip = '192.168.1.1';
|
||||
sandbox.stub(nconf, 'get').withArgs('BLOCKED_IPS').returns('192.168.1.2');
|
||||
const attachBlocker = requireAgain(pathToBlocker).default;
|
||||
attachBlocker(req, res, next);
|
||||
|
||||
checkErrorNotThrown(next);
|
||||
});
|
||||
|
||||
it('does not throw when the blocker IP does not match', async () => {
|
||||
req.ip = '192.168.1.1';
|
||||
sandbox.stub(Blocker, 'watchBlockers').returns({
|
||||
on: (event, callback) => {
|
||||
if (event === 'change') {
|
||||
callback({ operation: 'add', blocker: { type: 'ipaddress', area: 'full', value: '192.168.1.2' } });
|
||||
}
|
||||
},
|
||||
});
|
||||
const attachBlocker = requireAgain(pathToBlocker).default;
|
||||
attachBlocker(req, res, next);
|
||||
|
||||
checkErrorNotThrown(next);
|
||||
});
|
||||
|
||||
it('does not throw when a client is blocked', async () => {
|
||||
sandbox.stub(Blocker, 'watchBlockers').returns({
|
||||
on: (event, callback) => {
|
||||
if (event === 'change') {
|
||||
callback({ operation: 'add', blocker: { type: 'client', area: 'full', value: '192.168.1.1' } });
|
||||
}
|
||||
},
|
||||
});
|
||||
const attachBlocker = requireAgain(pathToBlocker).default;
|
||||
attachBlocker(req, res, next);
|
||||
|
||||
checkErrorNotThrown(next);
|
||||
});
|
||||
|
||||
it('throws when the blocker IP is blocked', async () => {
|
||||
req.ip = '192.168.1.1';
|
||||
sandbox.stub(Blocker, 'watchBlockers').returns({
|
||||
on: (event, callback) => {
|
||||
if (event === 'change') {
|
||||
callback({ operation: 'add', blocker: { type: 'ipaddress', area: 'full', value: '192.168.1.1' } });
|
||||
}
|
||||
},
|
||||
});
|
||||
const attachBlocker = requireAgain(pathToBlocker).default;
|
||||
attachBlocker(req, res, next);
|
||||
|
||||
checkIPBlockedErrorThrown(next);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Blocking clients', () => {
|
||||
beforeEach(() => {
|
||||
sandbox.stub(nconf, 'get').withArgs('BLOCKED_IPS').returns('');
|
||||
req.headers['x-client'] = 'test-client';
|
||||
});
|
||||
it('is disabled when no clients are blocked', () => {
|
||||
const attachBlocker = requireAgain(pathToBlocker).default;
|
||||
attachBlocker(req, res, next);
|
||||
|
||||
checkErrorNotThrown(next);
|
||||
});
|
||||
|
||||
it('does not throw when the client does not match', async () => {
|
||||
sandbox.stub(Blocker, 'watchBlockers').returns({
|
||||
on: (event, callback) => {
|
||||
if (event === 'change') {
|
||||
callback({ operation: 'add', blocker: { type: 'client', area: 'full', value: 'another-client' } });
|
||||
}
|
||||
},
|
||||
});
|
||||
const attachBlocker = requireAgain(pathToBlocker).default;
|
||||
attachBlocker(req, res, next);
|
||||
|
||||
checkErrorNotThrown(next);
|
||||
});
|
||||
|
||||
it('throws when the client is blocked', async () => {
|
||||
sandbox.stub(Blocker, 'watchBlockers').returns({
|
||||
on: (event, callback) => {
|
||||
if (event === 'change') {
|
||||
callback({ operation: 'add', blocker: { type: 'client', area: 'full', value: 'test-client' } });
|
||||
}
|
||||
},
|
||||
});
|
||||
const attachBlocker = requireAgain(pathToBlocker).default;
|
||||
attachBlocker(req, res, next);
|
||||
|
||||
checkClientBlockedErrorThrown(next);
|
||||
});
|
||||
|
||||
it('does not throw when an ip is blocked', async () => {
|
||||
sandbox.stub(Blocker, 'watchBlockers').returns({
|
||||
on: (event, callback) => {
|
||||
if (event === 'change') {
|
||||
callback({ operation: 'add', blocker: { type: 'ipaddress', area: 'full', value: 'test-client' } });
|
||||
}
|
||||
},
|
||||
});
|
||||
const attachBlocker = requireAgain(pathToBlocker).default;
|
||||
attachBlocker(req, res, next);
|
||||
|
||||
checkErrorNotThrown(next);
|
||||
});
|
||||
|
||||
it('updates the list when data changes', async () => {
|
||||
let blockCallback;
|
||||
sandbox.stub(Blocker, 'watchBlockers').returns({
|
||||
on: (event, callback) => {
|
||||
blockCallback = callback;
|
||||
if (event === 'change') {
|
||||
callback({ operation: 'add', blocker: { type: 'client', area: 'full', value: 'another-client' } });
|
||||
}
|
||||
},
|
||||
});
|
||||
const attachBlocker = requireAgain(pathToBlocker).default;
|
||||
attachBlocker(req, res, next);
|
||||
checkErrorNotThrown(next);
|
||||
blockCallback({ operation: 'add', blocker: { type: 'client', area: 'full', value: 'test-client' } });
|
||||
attachBlocker(req, res, next);
|
||||
expect(next).to.have.been.calledTwice;
|
||||
const calledWith = next.getCall(1).args;
|
||||
expect(calledWith[0].message).to.equal(apiError('clientBlocked'));
|
||||
expect(calledWith[0] instanceof Forbidden).to.equal(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
76
test/api/unit/middlewares/ipBlocker.test.js
Normal file
76
test/api/unit/middlewares/ipBlocker.test.js
Normal file
@@ -0,0 +1,76 @@
|
||||
import nconf from 'nconf';
|
||||
import requireAgain from 'require-again';
|
||||
import {
|
||||
generateRes,
|
||||
generateReq,
|
||||
generateNext,
|
||||
} from '../../../helpers/api-unit.helper';
|
||||
import { Forbidden } from '../../../../website/server/libs/errors';
|
||||
import { apiError } from '../../../../website/server/libs/apiError';
|
||||
|
||||
function checkErrorThrown (next) {
|
||||
expect(next).to.have.been.calledOnce;
|
||||
const calledWith = next.getCall(0).args;
|
||||
expect(calledWith[0].message).to.equal(apiError('ipAddressBlocked'));
|
||||
expect(calledWith[0] instanceof Forbidden).to.equal(true);
|
||||
}
|
||||
|
||||
function checkErrorNotThrown (next) {
|
||||
expect(next).to.have.been.calledOnce;
|
||||
const calledWith = next.getCall(0).args;
|
||||
expect(typeof calledWith[0] === 'undefined').to.equal(true);
|
||||
}
|
||||
|
||||
describe('ipBlocker middleware', () => {
|
||||
const pathToIpBlocker = '../../../../website/server/middlewares/ipBlocker';
|
||||
|
||||
let res; let req; let next;
|
||||
|
||||
beforeEach(() => {
|
||||
res = generateRes();
|
||||
req = generateReq();
|
||||
next = generateNext();
|
||||
});
|
||||
|
||||
it('is disabled when the env var is not defined', () => {
|
||||
sandbox.stub(nconf, 'get').withArgs('BLOCKED_IPS').returns(undefined);
|
||||
const attachIpBlocker = requireAgain(pathToIpBlocker).default;
|
||||
attachIpBlocker(req, res, next);
|
||||
|
||||
checkErrorNotThrown(next);
|
||||
});
|
||||
|
||||
it('is disabled when the env var is an empty string', () => {
|
||||
sandbox.stub(nconf, 'get').withArgs('BLOCKED_IPS').returns('');
|
||||
const attachIpBlocker = requireAgain(pathToIpBlocker).default;
|
||||
attachIpBlocker(req, res, next);
|
||||
|
||||
checkErrorNotThrown(next);
|
||||
});
|
||||
|
||||
it('is disabled when the env var contains comma separated empty strings', () => {
|
||||
sandbox.stub(nconf, 'get').withArgs('BLOCKED_IPS').returns(' , , ');
|
||||
const attachIpBlocker = requireAgain(pathToIpBlocker).default;
|
||||
attachIpBlocker(req, res, next);
|
||||
|
||||
checkErrorNotThrown(next);
|
||||
});
|
||||
|
||||
it('does not throw when the ip does not match', () => {
|
||||
req.ip = '192.168.1.1';
|
||||
sandbox.stub(nconf, 'get').withArgs('BLOCKED_IPS').returns('192.168.1.2');
|
||||
const attachIpBlocker = requireAgain(pathToIpBlocker).default;
|
||||
attachIpBlocker(req, res, next);
|
||||
|
||||
checkErrorNotThrown(next);
|
||||
});
|
||||
|
||||
it('throws when the ip is blocked', () => {
|
||||
req.ip = '192.168.1.1';
|
||||
sandbox.stub(nconf, 'get').withArgs('BLOCKED_IPS').returns('192.168.1.1');
|
||||
const attachIpBlocker = requireAgain(pathToIpBlocker).default;
|
||||
attachIpBlocker(req, res, next);
|
||||
|
||||
checkErrorThrown(next);
|
||||
});
|
||||
});
|
||||
@@ -1,13 +1,9 @@
|
||||
import moment from 'moment';
|
||||
import requireAgain from 'require-again';
|
||||
import { model as User } from '../../../../website/server/models/user';
|
||||
import { model as NewsPost } from '../../../../website/server/models/newsPost';
|
||||
import { model as Group } from '../../../../website/server/models/group';
|
||||
import { model as Blocker } from '../../../../website/server/models/blocker';
|
||||
import common from '../../../../website/common';
|
||||
|
||||
const pathToUserSchema = '../../../../website/server/models/user/schema';
|
||||
|
||||
describe('User Model', () => {
|
||||
describe('.toJSON()', () => {
|
||||
it('keeps user._tmp when calling .toJSON', () => {
|
||||
@@ -916,73 +912,4 @@ describe('User Model', () => {
|
||||
expect(user.toJSON().flags.newStuff).to.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('validates email', () => {
|
||||
it('does not throw an error for a valid email', () => {
|
||||
const user = new User();
|
||||
user.auth.local.email = 'hello@example.com';
|
||||
const errors = user.validateSync();
|
||||
expect(errors.errors['auth.local.email']).to.not.exist;
|
||||
});
|
||||
|
||||
it('throws an error if email is not valid', () => {
|
||||
const user = new User();
|
||||
user.auth.local.email = 'invalid-email';
|
||||
const errors = user.validateSync();
|
||||
expect(errors.errors['auth.local.email'].message).to.equal(common.i18n.t('invalidEmail'));
|
||||
});
|
||||
|
||||
it('throws an error if email is using a restricted domain', () => {
|
||||
const user = new User();
|
||||
user.auth.local.email = 'scammer@habitica.com';
|
||||
const errors = user.validateSync();
|
||||
expect(errors.errors['auth.local.email'].message).to.equal(common.i18n.t('invalidEmailDomain', { domains: 'habitica.com, habitrpg.com' }));
|
||||
});
|
||||
|
||||
it('throws an error if email was blocked specifically', () => {
|
||||
sandbox.stub(Blocker, 'watchBlockers').returns({
|
||||
on: (event, callback) => {
|
||||
callback({ operation: 'add', blocker: { type: 'email', area: 'full', value: 'blocked@example.com' } });
|
||||
},
|
||||
});
|
||||
const schema = requireAgain(pathToUserSchema).UserSchema;
|
||||
const valid = schema.paths['auth.local.email'].options.validate.every(v => v.validator('blocked@example.com'));
|
||||
expect(valid).to.equal(false);
|
||||
});
|
||||
|
||||
it('throws an error if email domain was blocked', () => {
|
||||
sandbox.stub(Blocker, 'watchBlockers').returns({
|
||||
on: (event, callback) => {
|
||||
callback({ operation: 'add', blocker: { type: 'email', area: 'full', value: '@example.com' } });
|
||||
},
|
||||
});
|
||||
const schema = requireAgain(pathToUserSchema).UserSchema;
|
||||
const valid = schema.paths['auth.local.email'].options.validate.every(v => v.validator('blocked@example.com'));
|
||||
expect(valid).to.equal(false);
|
||||
});
|
||||
|
||||
it('throws an error if user portion of email was blocked', () => {
|
||||
sandbox.stub(Blocker, 'watchBlockers').returns({
|
||||
on: (event, callback) => {
|
||||
callback({ operation: 'add', blocker: { type: 'email', area: 'full', value: 'blocked@' } });
|
||||
},
|
||||
});
|
||||
const schema = requireAgain(pathToUserSchema).UserSchema;
|
||||
const valid = schema.paths['auth.local.email'].options.validate.every(v => v.validator('blocked@example.com'));
|
||||
expect(valid).to.equal(false);
|
||||
});
|
||||
|
||||
it('does not throw an error if email is not blocked', () => {
|
||||
sandbox.stub(Blocker, 'watchBlockers').returns({
|
||||
on: (event, callback) => {
|
||||
callback({ operation: 'add', blocker: { type: 'email', area: 'full', value: '@example.com' } });
|
||||
callback({ operation: 'add', blocker: { type: 'email', area: 'full', value: 'blocked@' } });
|
||||
callback({ operation: 'add', blocker: { type: 'email', area: 'full', value: 'bad@test.com' } });
|
||||
},
|
||||
});
|
||||
const schema = requireAgain(pathToUserSchema).UserSchema;
|
||||
const valid = schema.paths['auth.local.email'].options.validate.every(v => v.validator('good@test.com'));
|
||||
expect(valid).to.equal(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -238,6 +238,18 @@ describe('POST /chat', () => {
|
||||
expect(groupMessages[0].id).to.exist;
|
||||
});
|
||||
|
||||
it('creates a chat with case-insensitive mentions', async () => {
|
||||
const originalUsername = member.auth.local.username;
|
||||
const uppercaseUsername = originalUsername.toUpperCase();
|
||||
const messageWithMentions = `hi @${uppercaseUsername}`;
|
||||
const newMessage = await user.post(`/groups/${groupWithChat._id}/chat`, { message: messageWithMentions });
|
||||
const groupMessages = await user.get(`/groups/${groupWithChat._id}/chat`);
|
||||
|
||||
expect(newMessage.message.id).to.exist;
|
||||
expect(newMessage.message.text).to.include(`[@${uppercaseUsername}](/profile/${member._id})`);
|
||||
expect(groupMessages[0].id).to.exist;
|
||||
});
|
||||
|
||||
it('creates a chat with a max length of 3000 chars', async () => {
|
||||
const veryLongMessage = `
|
||||
123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789.
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import {
|
||||
requester,
|
||||
translate as t,
|
||||
generateUser,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
import i18n from '../../../../../website/common/script/i18n';
|
||||
|
||||
@@ -56,4 +57,28 @@ describe('GET /content', () => {
|
||||
const res = await requester().get('/content?filter=backgroundsFlat,invalid');
|
||||
expect(res).to.not.have.property('backgroundsFlat');
|
||||
});
|
||||
|
||||
describe('authenticated user', () => {
|
||||
let user;
|
||||
it('returns content in user\'s preferred language when no language parameter is provided', async () => {
|
||||
user = await generateUser({ 'preferences.language': 'de' });
|
||||
const res = await user.get('/content');
|
||||
expect(res).to.have.nested.property('backgrounds.backgrounds062014.beach');
|
||||
expect(res.backgrounds.backgrounds062014.beach.text).to.equal(i18n.t('backgroundBeachText', 'de'));
|
||||
});
|
||||
|
||||
it('respects language parameter over user\'s preferred language', async () => {
|
||||
user = await generateUser({ 'preferences.language': 'de' });
|
||||
const res = await user.get('/content?language=fr');
|
||||
expect(res).to.have.nested.property('backgrounds.backgrounds062014.beach');
|
||||
expect(res.backgrounds.backgrounds062014.beach.text).to.equal(i18n.t('backgroundBeachText', 'fr'));
|
||||
});
|
||||
|
||||
it('falls back to English if user\'s preferred language is invalid', async () => {
|
||||
user = await generateUser({ 'preferences.language': 'invalid_lang' });
|
||||
const res = await user.get('/content');
|
||||
expect(res).to.have.nested.property('backgrounds.backgrounds062014.beach');
|
||||
expect(res.backgrounds.backgrounds062014.beach.text).to.equal(t('backgroundBeachText'));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -208,6 +208,9 @@ export default {
|
||||
|
||||
return response;
|
||||
}, error => { // Set up Error interceptors
|
||||
if (!error.response) {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
if (error.response.status >= 400) {
|
||||
const isBanned = this.checkForBannedUser(error);
|
||||
if (isBanned === true) return null; // eslint-disable-line consistent-return
|
||||
|
||||
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 5.4 KiB |
@@ -0,0 +1,29 @@
|
||||
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M58.1792 31.6843L46.8536 22.3769L23.918 28.6988L18.861 42.5218L44.341 58.5813L58.1792 31.6843Z" fill="#FF944C"/>
|
||||
<path opacity="0.25" fill-rule="evenodd" clip-rule="evenodd" d="M46.6218 34.5148L46.1108 26.1328L36.2812 28.8422L46.6218 34.5148Z" fill="white"/>
|
||||
<path opacity="0.25" fill-rule="evenodd" clip-rule="evenodd" d="M30.2393 39.0304L26.4518 31.5515L36.2813 28.8422L30.2393 39.0304Z" fill="white"/>
|
||||
<path opacity="0.5" fill-rule="evenodd" clip-rule="evenodd" d="M46.6218 34.5148L36.2813 28.8422L30.2393 39.0304L46.6218 34.5148Z" fill="white"/>
|
||||
<path opacity="0.5" fill-rule="evenodd" clip-rule="evenodd" d="M53.8301 32.5279L46.1108 26.1328L46.6218 34.5148L53.8301 32.5279Z" fill="white"/>
|
||||
<path opacity="0.35" fill-rule="evenodd" clip-rule="evenodd" d="M23.0309 41.0173L26.4518 31.5516L30.2393 39.0304L23.0309 41.0173Z" fill="#FA8537"/>
|
||||
<path opacity="0.35" fill-rule="evenodd" clip-rule="evenodd" d="M53.8301 32.5279L46.6218 34.5148L43.0424 53.79L53.8301 32.5279Z" fill="#FA8537"/>
|
||||
<path opacity="0.5" fill-rule="evenodd" clip-rule="evenodd" d="M23.0309 41.0173L30.2393 39.0304L43.0425 53.79L23.0309 41.0173Z" fill="white"/>
|
||||
<path opacity="0.25" fill-rule="evenodd" clip-rule="evenodd" d="M46.6218 34.5148L30.2393 39.0304L43.0425 53.79L46.6218 34.5148Z" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M50.555 4.15937L47.026 0.420004L38.7773 1.59601L36.4144 6.17539L44.5675 12.8919L50.555 4.15937Z" fill="#FFBE5D"/>
|
||||
<path opacity="0.25" fill-rule="evenodd" clip-rule="evenodd" d="M46.414 4.62854L46.6034 1.6924L43.0682 2.1964L46.414 4.62854Z" fill="white"/>
|
||||
<path opacity="0.25" fill-rule="evenodd" clip-rule="evenodd" d="M40.5221 5.46854L39.5331 2.7004L43.0682 2.1964L40.5221 5.46854Z" fill="white"/>
|
||||
<path opacity="0.5" fill-rule="evenodd" clip-rule="evenodd" d="M46.414 4.62854L43.0683 2.1964L40.5221 5.46855L46.414 4.62854Z" fill="white"/>
|
||||
<path opacity="0.5" fill-rule="evenodd" clip-rule="evenodd" d="M49.0064 4.25894L46.6034 1.6924L46.414 4.62854L49.0064 4.25894Z" fill="white"/>
|
||||
<path opacity="0.35" fill-rule="evenodd" clip-rule="evenodd" d="M37.9296 5.83815L39.5331 2.70041L40.5221 5.46855L37.9296 5.83815Z" fill="#FFA624"/>
|
||||
<path opacity="0.35" fill-rule="evenodd" clip-rule="evenodd" d="M49.0064 4.25893L46.414 4.62853L44.3259 11.1688L49.0064 4.25893Z" fill="#FFA624"/>
|
||||
<path opacity="0.5" fill-rule="evenodd" clip-rule="evenodd" d="M37.9297 5.83815L40.5221 5.46855L44.326 11.1688L37.9297 5.83815Z" fill="white"/>
|
||||
<path opacity="0.25" fill-rule="evenodd" clip-rule="evenodd" d="M46.414 4.62854L40.5221 5.46855L44.326 11.1688L46.414 4.62854Z" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M27.2986 16.7775L24.6513 8.36623L11.1016 3.94533L4.07056 9.19883L11.614 25.6769L27.2986 16.7775Z" fill="#FF6165"/>
|
||||
<path opacity="0.25" fill-rule="evenodd" clip-rule="evenodd" d="M20.5864 14.3719L23.0573 10.0026L17.2502 8.10789L20.5864 14.3719Z" fill="white"/>
|
||||
<path opacity="0.25" fill-rule="evenodd" clip-rule="evenodd" d="M10.908 11.2141L11.4432 6.21322L17.2502 8.10789L10.908 11.2141Z" fill="white"/>
|
||||
<path opacity="0.5" fill-rule="evenodd" clip-rule="evenodd" d="M20.5864 14.3719L17.2502 8.10789L10.9081 11.2141L20.5864 14.3719Z" fill="white"/>
|
||||
<path opacity="0.5" fill-rule="evenodd" clip-rule="evenodd" d="M24.8449 15.7613L23.0573 10.0026L20.5864 14.3719L24.8449 15.7613Z" fill="white"/>
|
||||
<path opacity="0.35" fill-rule="evenodd" clip-rule="evenodd" d="M6.64955 9.82464L11.4432 6.21321L10.908 11.2141L6.64955 9.82464Z" fill="#F23035"/>
|
||||
<path opacity="0.35" fill-rule="evenodd" clip-rule="evenodd" d="M24.8449 15.7613L20.5864 14.3719L12.5221 22.8464L24.8449 15.7613Z" fill="#F23035"/>
|
||||
<path opacity="0.5" fill-rule="evenodd" clip-rule="evenodd" d="M6.64959 9.82464L10.9081 11.2141L12.5221 22.8463L6.64959 9.82464Z" fill="white"/>
|
||||
<path opacity="0.25" fill-rule="evenodd" clip-rule="evenodd" d="M20.5864 14.3719L10.9081 11.2141L12.5221 22.8463L20.5864 14.3719Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.0 KiB |
@@ -0,0 +1,29 @@
|
||||
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.82083 31.6843L17.1464 22.3769L40.082 28.6988L45.139 42.5218L19.659 58.5813L5.82083 31.6843Z" fill="#24CC8F"/>
|
||||
<path opacity="0.25" fill-rule="evenodd" clip-rule="evenodd" d="M17.3782 34.5148L17.8892 26.1328L27.7188 28.8422L17.3782 34.5148Z" fill="white"/>
|
||||
<path opacity="0.25" fill-rule="evenodd" clip-rule="evenodd" d="M33.7607 39.0304L37.5482 31.5515L27.7187 28.8422L33.7607 39.0304Z" fill="white"/>
|
||||
<path opacity="0.5" fill-rule="evenodd" clip-rule="evenodd" d="M17.3782 34.5148L27.7187 28.8422L33.7607 39.0304L17.3782 34.5148Z" fill="white"/>
|
||||
<path opacity="0.5" fill-rule="evenodd" clip-rule="evenodd" d="M10.1699 32.5279L17.8892 26.1328L17.3782 34.5148L10.1699 32.5279Z" fill="white"/>
|
||||
<path opacity="0.35" fill-rule="evenodd" clip-rule="evenodd" d="M40.9691 41.0173L37.5482 31.5516L33.7607 39.0304L40.9691 41.0173Z" fill="#1CA372"/>
|
||||
<path opacity="0.35" fill-rule="evenodd" clip-rule="evenodd" d="M10.1699 32.5279L17.3782 34.5148L20.9576 53.79L10.1699 32.5279Z" fill="#1CA372"/>
|
||||
<path opacity="0.5" fill-rule="evenodd" clip-rule="evenodd" d="M40.9691 41.0173L33.7607 39.0304L20.9575 53.79L40.9691 41.0173Z" fill="white"/>
|
||||
<path opacity="0.25" fill-rule="evenodd" clip-rule="evenodd" d="M17.3782 34.5148L33.7607 39.0304L20.9575 53.79L17.3782 34.5148Z" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.445 4.15937L16.974 0.420004L25.2227 1.59601L27.5856 6.17539L19.4325 12.8919L13.445 4.15937Z" fill="#925CF3"/>
|
||||
<path opacity="0.25" fill-rule="evenodd" clip-rule="evenodd" d="M17.586 4.62854L17.3966 1.6924L20.9318 2.1964L17.586 4.62854Z" fill="white"/>
|
||||
<path opacity="0.25" fill-rule="evenodd" clip-rule="evenodd" d="M23.4779 5.46854L24.4669 2.7004L20.9318 2.1964L23.4779 5.46854Z" fill="white"/>
|
||||
<path opacity="0.5" fill-rule="evenodd" clip-rule="evenodd" d="M17.586 4.62854L20.9317 2.1964L23.4779 5.46855L17.586 4.62854Z" fill="white"/>
|
||||
<path opacity="0.5" fill-rule="evenodd" clip-rule="evenodd" d="M14.9936 4.25894L17.3966 1.6924L17.586 4.62854L14.9936 4.25894Z" fill="white"/>
|
||||
<path opacity="0.35" fill-rule="evenodd" clip-rule="evenodd" d="M26.0704 5.83815L24.4669 2.70041L23.4779 5.46855L26.0704 5.83815Z" fill="#4F2A93"/>
|
||||
<path opacity="0.35" fill-rule="evenodd" clip-rule="evenodd" d="M14.9936 4.25893L17.586 4.62853L19.6741 11.1688L14.9936 4.25893Z" fill="#4F2A93"/>
|
||||
<path opacity="0.5" fill-rule="evenodd" clip-rule="evenodd" d="M26.0703 5.83815L23.4779 5.46855L19.674 11.1688L26.0703 5.83815Z" fill="white"/>
|
||||
<path opacity="0.25" fill-rule="evenodd" clip-rule="evenodd" d="M17.586 4.62854L23.4779 5.46855L19.674 11.1688L17.586 4.62854Z" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M36.7014 16.7775L39.3487 8.36623L52.8984 3.94533L59.9294 9.19883L52.386 25.6769L36.7014 16.7775Z" fill="#50B5E9"/>
|
||||
<path opacity="0.25" fill-rule="evenodd" clip-rule="evenodd" d="M43.4136 14.3719L40.9427 10.0026L46.7498 8.10789L43.4136 14.3719Z" fill="white"/>
|
||||
<path opacity="0.25" fill-rule="evenodd" clip-rule="evenodd" d="M53.092 11.2141L52.5568 6.21322L46.7498 8.10789L53.092 11.2141Z" fill="white"/>
|
||||
<path opacity="0.5" fill-rule="evenodd" clip-rule="evenodd" d="M43.4136 14.3719L46.7498 8.10789L53.0919 11.2141L43.4136 14.3719Z" fill="white"/>
|
||||
<path opacity="0.5" fill-rule="evenodd" clip-rule="evenodd" d="M39.1551 15.7613L40.9427 10.0026L43.4136 14.3719L39.1551 15.7613Z" fill="white"/>
|
||||
<path opacity="0.35" fill-rule="evenodd" clip-rule="evenodd" d="M57.3504 9.82464L52.5568 6.21321L53.092 11.2141L57.3504 9.82464Z" fill="#46A7D9"/>
|
||||
<path opacity="0.35" fill-rule="evenodd" clip-rule="evenodd" d="M39.1551 15.7613L43.4136 14.3719L51.4779 22.8464L39.1551 15.7613Z" fill="#46A7D9"/>
|
||||
<path opacity="0.5" fill-rule="evenodd" clip-rule="evenodd" d="M57.3504 9.82464L53.0919 11.2141L51.4779 22.8463L57.3504 9.82464Z" fill="white"/>
|
||||
<path opacity="0.25" fill-rule="evenodd" clip-rule="evenodd" d="M43.4136 14.3719L53.0919 11.2141L51.4779 22.8463L43.4136 14.3719Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.0 KiB |
@@ -117,7 +117,7 @@ export default {
|
||||
closeWithAction () {
|
||||
this.close();
|
||||
setTimeout(() => {
|
||||
this.$router.push({ name: 'achievements' });
|
||||
this.$router.push(`/profile/${this.$store.state.user.data._id}#achievements`);
|
||||
}, 200);
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="row standard-page col-12 d-flex justify-content-center">
|
||||
<div class="admin-panel-content">
|
||||
<h1>{{ $t("adminPanel") }}</h1>
|
||||
<h1>Admin Panel</h1>
|
||||
<form
|
||||
class="form-inline"
|
||||
@submit.prevent="searchUsers(userIdentifier)"
|
||||
@@ -72,7 +72,7 @@ export default {
|
||||
},
|
||||
mounted () {
|
||||
this.$store.dispatch('common:setTitle', {
|
||||
section: this.$t('adminPanel'),
|
||||
section: 'Admin Panel',
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
@@ -55,7 +55,7 @@
|
||||
<script>
|
||||
import VueRouter from 'vue-router';
|
||||
import { mapState } from '@/libs/store';
|
||||
import LoadingSpinner from '../../ui/loadingSpinner';
|
||||
import LoadingSpinner from '../ui/loadingSpinner';
|
||||
|
||||
const { isNavigationFailure, NavigationFailureType } = VueRouter;
|
||||
|
||||
@@ -38,17 +38,12 @@
|
||||
>
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input
|
||||
:id="permission.key"
|
||||
v-model="hero.permissions[permission.key]"
|
||||
:disabled="!hasPermission(user, permission.key)
|
||||
|| (hero.permissions.fullAccess && permission.key !== 'fullAccess')"
|
||||
:disabled="!hasPermission(user, permission.key)"
|
||||
class="custom-control-input"
|
||||
type="checkbox"
|
||||
>
|
||||
<label
|
||||
class="custom-control-label"
|
||||
:for="permission.key"
|
||||
>
|
||||
<label class="custom-control-label">
|
||||
{{ permission.name }}<br>
|
||||
<small class="text-secondary">{{ permission.description }}</small>
|
||||
</label>
|
||||
@@ -129,10 +124,7 @@
|
||||
value="Save"
|
||||
class="btn btn-primary mt-1"
|
||||
>
|
||||
<b
|
||||
v-if="hasUnsavedChanges"
|
||||
class="text-warning float-right"
|
||||
>
|
||||
<b v-if="hasUnsavedChanges" class="text-warning float-right">
|
||||
Unsaved changes
|
||||
</b>
|
||||
</div>
|
||||
@@ -155,7 +147,7 @@ import markdownDirective from '@/directives/markdown';
|
||||
import saveHero from '../mixins/saveHero';
|
||||
|
||||
import { mapState } from '@/libs/store';
|
||||
import { userStateMixin } from '../../../../mixins/userState';
|
||||
import { userStateMixin } from '../../../mixins/userState';
|
||||
|
||||
const permissionList = [
|
||||
{
|
||||
@@ -183,11 +175,6 @@ const permissionList = [
|
||||
name: 'Challenge Admin',
|
||||
description: 'Can create official habitica challenges and admin all challenges',
|
||||
},
|
||||
{
|
||||
key: 'accessControl',
|
||||
name: 'Access Control',
|
||||
description: 'Can manage IP-Address, Client and E-Mail blockers',
|
||||
},
|
||||
{
|
||||
key: 'coupons',
|
||||
name: 'Coupon Creator',
|
||||
@@ -126,7 +126,7 @@
|
||||
@click="changeApiToken()"
|
||||
>
|
||||
Change API Token
|
||||
</a>
|
||||
</a>
|
||||
<div
|
||||
v-if="tokenModified"
|
||||
>
|
||||
@@ -46,7 +46,7 @@
|
||||
:
|
||||
<span :class="{ ownedItem: !item.neverOwned }">{{ item.text }}</span>
|
||||
</span>
|
||||
- {{ itemType }}.{{ item.key }} - <i> {{ item.set }}</i>
|
||||
- {{ itemType }}.{{item.key}} - <i> {{ item.set }}</i>
|
||||
|
||||
<div
|
||||
v-if="item.modified"
|
||||
@@ -16,9 +16,9 @@
|
||||
:hero="hero"
|
||||
:reset-counter="resetCounter"
|
||||
:has-unsaved-changes="hasUnsavedChanges([hero.flags, unModifiedHero.flags],
|
||||
[hero.auth, unModifiedHero.auth],
|
||||
[hero.balance, unModifiedHero.balance],
|
||||
[hero.secret, unModifiedHero.secret])"
|
||||
[hero.auth, unModifiedHero.auth],
|
||||
[hero.balance, unModifiedHero.balance],
|
||||
[hero.secret, unModifiedHero.secret])"
|
||||
/>
|
||||
|
||||
<subscription-and-perks
|
||||
@@ -88,7 +88,7 @@
|
||||
|
||||
<contributor-details
|
||||
:hero="hero"
|
||||
:has-unsaved-changes="hasUnsavedChanges(
|
||||
:hasUnsavedChanges="hasUnsavedChanges(
|
||||
[hero.contributor, unModifiedHero.contributor],
|
||||
[hero.permissions, unModifiedHero.permissions],
|
||||
[hero.secret, unModifiedHero.secret],
|
||||
@@ -149,7 +149,7 @@ import Achievements from './achievements.vue';
|
||||
import UserHistory from './userHistory.vue';
|
||||
import Stats from './stats.vue';
|
||||
|
||||
import { userStateMixin } from '../../../../mixins/userState';
|
||||
import { userStateMixin } from '../../../mixins/userState';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -32,43 +32,38 @@
|
||||
></p>
|
||||
</div>
|
||||
<div v-if="userHasParty">
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-3 col-form-label">
|
||||
Party ID
|
||||
</label>
|
||||
<strong class="col-sm-9 col-form-label">
|
||||
{{ groupPartyData._id }}
|
||||
</strong>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-3 col-form-label">
|
||||
Estimated Member Count
|
||||
</label>
|
||||
<strong class="col-sm-9 col-form-label">
|
||||
{{ groupPartyData.memberCount }}
|
||||
</strong>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-3 col-form-label">
|
||||
Leader
|
||||
</label>
|
||||
<strong class="col-sm-9 col-form-label">
|
||||
<span v-if="userIsPartyLeader">User is the party leader</span>
|
||||
<span v-else>Party leader is
|
||||
<router-link
|
||||
:to="{'name': 'userProfile', 'params': {'userId': groupPartyData.leader}}"
|
||||
>
|
||||
{{ groupPartyData.leader }}
|
||||
</router-link>
|
||||
</span>
|
||||
</strong>
|
||||
</div>
|
||||
<div
|
||||
class="btn btn-danger"
|
||||
@click="removeFromParty()"
|
||||
>
|
||||
Remove from Party
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-3 col-form-label">
|
||||
Party ID
|
||||
</label>
|
||||
<strong class="col-sm-9 col-form-label">
|
||||
{{ groupPartyData._id }}
|
||||
</strong>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-3 col-form-label">
|
||||
Estimated Member Count
|
||||
</label>
|
||||
<strong class="col-sm-9 col-form-label">
|
||||
{{ groupPartyData.memberCount }}
|
||||
</strong>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-3 col-form-label">
|
||||
Leader
|
||||
</label>
|
||||
<strong class="col-sm-9 col-form-label">
|
||||
<span v-if="userIsPartyLeader">User is the party leader</span>
|
||||
<span v-else>Party leader is
|
||||
<router-link :to="{'name': 'userProfile', 'params': {'userId': groupPartyData.leader}}">
|
||||
{{ groupPartyData.leader }}
|
||||
</router-link>
|
||||
</span>
|
||||
</strong>
|
||||
</div>
|
||||
<div
|
||||
class="btn btn-danger"
|
||||
@click="removeFromParty()">Remove from Party</div>
|
||||
</div>
|
||||
<strong v-else>User is not in a party.</strong>
|
||||
<div class="subsection-start">
|
||||
@@ -1,13 +1,11 @@
|
||||
<template>
|
||||
<form
|
||||
@submit.prevent="saveHero({hero: {
|
||||
_id: hero._id,
|
||||
flags: hero.flags,
|
||||
balance: hero.balance,
|
||||
auth: hero.auth,
|
||||
secret: hero.secret,
|
||||
}, msg: 'Privileges or Gems or Moderation Notes'})"
|
||||
>
|
||||
<form @submit.prevent="saveHero({hero: {
|
||||
_id: hero._id,
|
||||
flags: hero.flags,
|
||||
balance: hero.balance,
|
||||
auth: hero.auth,
|
||||
secret: hero.secret,
|
||||
}, msg: 'Privileges or Gems or Moderation Notes'})">
|
||||
<div class="card mt-2">
|
||||
<div class="card-header">
|
||||
<h3
|
||||
@@ -16,12 +14,9 @@
|
||||
@click="expand = !expand"
|
||||
>
|
||||
Privileges, Gem Balance
|
||||
<b
|
||||
v-if="hasUnsavedChanges && !expand"
|
||||
class="text-warning float-right"
|
||||
>
|
||||
Unsaved changes
|
||||
</b>
|
||||
<b v-if="hasUnsavedChanges && !expand" class="text-warning float-right">
|
||||
Unsaved changes
|
||||
</b>
|
||||
</h3>
|
||||
</div>
|
||||
<div
|
||||
@@ -138,10 +133,7 @@
|
||||
value="Save"
|
||||
class="btn btn-primary mt-1"
|
||||
>
|
||||
<b
|
||||
v-if="hasUnsavedChanges"
|
||||
class="text-warning float-right"
|
||||
>
|
||||
<b v-if="hasUnsavedChanges" class="text-warning float-right">
|
||||
Unsaved changes
|
||||
</b>
|
||||
</div>
|
||||
@@ -8,12 +8,9 @@
|
||||
@click="expand = !expand"
|
||||
>
|
||||
Stats
|
||||
<b
|
||||
v-if="hasUnsavedChanges && !expand"
|
||||
class="text-warning float-right"
|
||||
>
|
||||
Unsaved changes
|
||||
</b>
|
||||
<b v-if="hasUnsavedChanges && !expand" class="text-warning float-right">
|
||||
Unsaved changes
|
||||
</b>
|
||||
</h3>
|
||||
</div>
|
||||
<div
|
||||
@@ -21,60 +18,47 @@
|
||||
class="card-body"
|
||||
>
|
||||
<stats-row
|
||||
v-model="hero.stats.hp"
|
||||
label="Health"
|
||||
color="red-label"
|
||||
:max="maxHealth"
|
||||
/>
|
||||
v-model="hero.stats.hp" />
|
||||
<stats-row
|
||||
v-model="hero.stats.exp"
|
||||
label="Experience"
|
||||
color="yellow-label"
|
||||
min="0"
|
||||
:max="maxFieldHardCap"
|
||||
/>
|
||||
v-model="hero.stats.exp" />
|
||||
<stats-row
|
||||
v-model="hero.stats.mp"
|
||||
label="Mana"
|
||||
color="blue-label"
|
||||
min="0"
|
||||
:max="maxFieldHardCap"
|
||||
/>
|
||||
v-model="hero.stats.mp" />
|
||||
<stats-row
|
||||
v-model="hero.stats.lvl"
|
||||
label="Level"
|
||||
step="1"
|
||||
min="0"
|
||||
:max="maxLevelHardCap"
|
||||
/>
|
||||
v-model="hero.stats.lvl" />
|
||||
<stats-row
|
||||
v-model="hero.stats.gp"
|
||||
label="Gold"
|
||||
min="0"
|
||||
:max="maxFieldHardCap"
|
||||
/>
|
||||
v-model="hero.stats.gp" />
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-3 col-form-label">Selected Class</label>
|
||||
<div class="col-sm-9">
|
||||
<select
|
||||
id="selectedClass"
|
||||
v-model="hero.stats.class"
|
||||
class="form-control"
|
||||
:disabled="hero.stats.lvl < 10"
|
||||
>
|
||||
<option value="warrior">
|
||||
Warrior
|
||||
</option>
|
||||
<option value="wizard">
|
||||
Mage
|
||||
</option>
|
||||
<option value="healer">
|
||||
Healer
|
||||
</option>
|
||||
<option value="rogue">
|
||||
Rogue
|
||||
</option>
|
||||
</select>
|
||||
id="selectedClass"
|
||||
v-model="hero.stats.class"
|
||||
class="form-control"
|
||||
:disabled="hero.stats.lvl < 10"
|
||||
>
|
||||
<option value="warrior">Warrior</option>
|
||||
<option value="wizard">Mage</option>
|
||||
<option value="healer">Healer</option>
|
||||
<option value="rogue">Rogue</option>
|
||||
</select>
|
||||
<small>
|
||||
When changing class, players usually need stat points deallocated as well.
|
||||
</small>
|
||||
@@ -83,59 +67,50 @@
|
||||
|
||||
<h3>Stat Points</h3>
|
||||
<stats-row
|
||||
v-model="hero.stats.points"
|
||||
label="Unallocated"
|
||||
min="0"
|
||||
step="1"
|
||||
:max="maxStatPoints"
|
||||
/>
|
||||
v-model="hero.stats.points" />
|
||||
<stats-row
|
||||
v-model="hero.stats.str"
|
||||
label="Strength"
|
||||
color="red-label"
|
||||
min="0"
|
||||
:max="maxStatPoints"
|
||||
step="1"
|
||||
/>
|
||||
v-model="hero.stats.str" />
|
||||
<stats-row
|
||||
v-model="hero.stats.int"
|
||||
label="Intelligence"
|
||||
color="blue-label"
|
||||
min="0"
|
||||
:max="maxStatPoints"
|
||||
step="1"
|
||||
/>
|
||||
v-model="hero.stats.int" />
|
||||
<stats-row
|
||||
v-model="hero.stats.per"
|
||||
label="Perception"
|
||||
color="purple-label"
|
||||
min="0"
|
||||
:max="maxStatPoints"
|
||||
step="1"
|
||||
/>
|
||||
v-model="hero.stats.per" />
|
||||
<stats-row
|
||||
v-model="hero.stats.con"
|
||||
label="Constitution"
|
||||
color="yellow-label"
|
||||
min="0"
|
||||
:max="maxStatPoints"
|
||||
step="1"
|
||||
/>
|
||||
v-model="hero.stats.con" />
|
||||
<div class="form-group row">
|
||||
<div class="offset-sm-3 col-sm-9">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-warning btn-sm"
|
||||
@click="deallocateStatPoints"
|
||||
>
|
||||
@click="deallocateStatPoints">
|
||||
Deallocate all stat points
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="statPointsIncorrect"
|
||||
class="form-group row"
|
||||
>
|
||||
<div class="form-group row" v-if="statPointsIncorrect">
|
||||
<div class="offset-sm-3 col-sm-9 text-danger">
|
||||
Error: Sum of stat points should equal the users level
|
||||
</div>
|
||||
@@ -143,40 +118,35 @@
|
||||
|
||||
<h3>Buffs</h3>
|
||||
<stats-row
|
||||
v-model="hero.stats.buffs.str"
|
||||
label="Strength"
|
||||
color="red-label"
|
||||
min="0"
|
||||
step="1"
|
||||
/>
|
||||
v-model="hero.stats.buffs.str" />
|
||||
<stats-row
|
||||
v-model="hero.stats.buffs.int"
|
||||
label="Intelligence"
|
||||
color="blue-label"
|
||||
min="0"
|
||||
step="1"
|
||||
/>
|
||||
v-model="hero.stats.buffs.int" />
|
||||
<stats-row
|
||||
v-model="hero.stats.buffs.per"
|
||||
label="Perception"
|
||||
color="purple-label"
|
||||
min="0"
|
||||
step="1"
|
||||
/>
|
||||
v-model="hero.stats.buffs.per" />
|
||||
<stats-row
|
||||
v-model="hero.stats.buffs.con"
|
||||
label="Constitution"
|
||||
color="yellow-label"
|
||||
min="0"
|
||||
step="1"
|
||||
/>
|
||||
v-model="hero.stats.buffs.con" />
|
||||
<div class="form-group row">
|
||||
<div class="offset-sm-3 col-sm-9">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-warning btn-sm"
|
||||
@click="resetBuffs"
|
||||
>
|
||||
@click="resetBuffs">
|
||||
Reset Buffs
|
||||
</button>
|
||||
</div>
|
||||
@@ -191,10 +161,7 @@
|
||||
value="Save"
|
||||
class="btn btn-primary mt-1"
|
||||
>
|
||||
<b
|
||||
v-if="hasUnsavedChanges"
|
||||
class="text-warning float-right"
|
||||
>
|
||||
<b v-if="hasUnsavedChanges" class="text-warning float-right">
|
||||
Unsaved changes
|
||||
</b>
|
||||
</div>
|
||||
@@ -222,7 +189,7 @@ import markdownDirective from '@/directives/markdown';
|
||||
import saveHero from '../mixins/saveHero';
|
||||
|
||||
import { mapState } from '@/libs/store';
|
||||
import { userStateMixin } from '../../../../mixins/userState';
|
||||
import { userStateMixin } from '../../../mixins/userState';
|
||||
|
||||
import StatsRow from './stats-row';
|
||||
|
||||
@@ -6,71 +6,49 @@
|
||||
}, msg: 'Subscription Perks' })"
|
||||
>
|
||||
<div class="card mt-2">
|
||||
<div
|
||||
class="card-header"
|
||||
@click="expand = !expand"
|
||||
>
|
||||
<div class="card-header"
|
||||
@click="expand = !expand">
|
||||
<h3
|
||||
class="mb-0 mt-0"
|
||||
:class="{ 'open': expand }"
|
||||
>
|
||||
Subscription, Monthly Perks
|
||||
<b
|
||||
v-if="hasUnsavedChanges && !expand"
|
||||
class="text-warning float-right"
|
||||
>
|
||||
Unsaved changes
|
||||
</b>
|
||||
<b v-if="hasUnsavedChanges && !expand" class="text-warning float-right">
|
||||
Unsaved changes
|
||||
</b>
|
||||
</h3>
|
||||
</div>
|
||||
<div
|
||||
v-if="expand"
|
||||
class="card-body"
|
||||
>
|
||||
<div
|
||||
<div
|
||||
class="form-group row"
|
||||
>
|
||||
<label class="col-sm-3 col-form-label">
|
||||
Payment method:
|
||||
</label>
|
||||
<div class="col-sm-9">
|
||||
<input
|
||||
<input v-model="hero.purchased.plan.paymentMethod"
|
||||
class="form-control"
|
||||
type="text"
|
||||
v-if="!isRegularPaymentMethod"
|
||||
v-model="hero.purchased.plan.paymentMethod"
|
||||
class="form-control"
|
||||
type="text"
|
||||
>
|
||||
<select
|
||||
<select
|
||||
v-else
|
||||
v-model="hero.purchased.plan.paymentMethod"
|
||||
class="form-control"
|
||||
type="text"
|
||||
>
|
||||
<option value="groupPlan">
|
||||
Group Plan
|
||||
</option>
|
||||
<option value="Stripe">
|
||||
Stripe
|
||||
</option>
|
||||
<option value="Apple">
|
||||
Apple
|
||||
</option>
|
||||
<option value="Google">
|
||||
Google
|
||||
</option>
|
||||
<option value="Amazon Payments">
|
||||
Amazon
|
||||
</option>
|
||||
<option value="PayPal">
|
||||
PayPal
|
||||
</option>
|
||||
<option value="Gift">
|
||||
Gift
|
||||
</option>
|
||||
<option value="">
|
||||
Clear out
|
||||
</option>
|
||||
</select>
|
||||
v-model="hero.purchased.plan.paymentMethod"
|
||||
class="form-control"
|
||||
type="text"
|
||||
>
|
||||
<option value="groupPlan">Group Plan</option>
|
||||
<option value="Stripe">Stripe</option>
|
||||
<option value="Apple">Apple</option>
|
||||
<option value="Google">Google</option>
|
||||
<option value="Amazon Payments">Amazon</option>
|
||||
<option value="PayPal">PayPal</option>
|
||||
<option value="Gift">Gift</option>
|
||||
<option value="">Clear out</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
@@ -80,40 +58,25 @@
|
||||
Payment schedule:
|
||||
</label>
|
||||
<div class="col-sm-9">
|
||||
<input
|
||||
<input v-model="hero.purchased.plan.planId"
|
||||
class="form-control"
|
||||
type="text"
|
||||
v-if="!isRegularPlanId"
|
||||
v-model="hero.purchased.plan.planId"
|
||||
class="form-control"
|
||||
type="text"
|
||||
>
|
||||
<select
|
||||
<select
|
||||
v-else
|
||||
v-model="hero.purchased.plan.planId"
|
||||
class="form-control"
|
||||
type="text"
|
||||
>
|
||||
<option value="basic_earned">
|
||||
Monthly recurring
|
||||
</option>
|
||||
<option value="basic_3mo">
|
||||
3 Months recurring
|
||||
</option>
|
||||
<option value="basic_6mo">
|
||||
6 Months recurring
|
||||
</option>
|
||||
<option value="basic_12mo">
|
||||
12 Months recurring
|
||||
</option>
|
||||
<option value="group_monthly">
|
||||
Group Plan (legacy)
|
||||
</option>
|
||||
<option value="group_plan_auto">
|
||||
Group Plan (auto)
|
||||
</option>
|
||||
<option value="">
|
||||
Clear out
|
||||
</option>
|
||||
</select>
|
||||
v-model="hero.purchased.plan.planId"
|
||||
class="form-control"
|
||||
type="text"
|
||||
>
|
||||
<option value="basic_earned">Monthly recurring</option>
|
||||
<option value="basic_3mo">3 Months recurring</option>
|
||||
<option value="basic_6mo">6 Months recurring</option>
|
||||
<option value="basic_12mo">12 Months recurring</option>
|
||||
<option value="group_monthly">Group Plan (legacy)</option>
|
||||
<option value="group_plan_auto">Group Plan (auto)</option>
|
||||
<option value="">Clear out</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
@@ -123,50 +86,43 @@
|
||||
Customer ID:
|
||||
</label>
|
||||
<div class="col-sm-9">
|
||||
<input
|
||||
v-model="hero.purchased.plan.customerId"
|
||||
class="form-control"
|
||||
type="text"
|
||||
>
|
||||
<input
|
||||
v-model="hero.purchased.plan.customerId"
|
||||
class="form-control"
|
||||
type="text"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="hero.purchased.plan.planId === 'group_plan_auto'"
|
||||
class="form-group row"
|
||||
>
|
||||
<div class="form-group row"
|
||||
v-if="hero.purchased.plan.planId === 'group_plan_auto'">
|
||||
<label class="col-sm-3 col-form-label">
|
||||
Group Plan Memberships:
|
||||
</label>
|
||||
<div class="col-sm-9 col-form-label">
|
||||
<loading-spinner
|
||||
v-if="!groupPlans"
|
||||
dark-color="true"
|
||||
/>
|
||||
v-if="!groupPlans"
|
||||
dark-color=true
|
||||
/>
|
||||
<b
|
||||
v-else-if="groupPlans.length === 0"
|
||||
class="text-danger col-form-label"
|
||||
v-else-if="groupPlans.length === 0"
|
||||
class="text-danger col-form-label"
|
||||
>User is not part of an active group plan!</b>
|
||||
<div
|
||||
v-else
|
||||
v-for="group in groupPlans"
|
||||
v-else
|
||||
:key="group._id"
|
||||
class="card mb-2"
|
||||
>
|
||||
class="card mb-2">
|
||||
<div class="card-body">
|
||||
<h6 class="card-title">
|
||||
{{ group.name }}
|
||||
<h6 class="card-title">{{ group.name }}
|
||||
<small class="float-right">{{ group._id }}</small>
|
||||
</h6>
|
||||
<p class="card-text">
|
||||
<strong>Leader: </strong>
|
||||
<a
|
||||
v-if="group.leader !== hero._id"
|
||||
@click="switchUser(group.leader)"
|
||||
>{{ group.leader }}</a>
|
||||
<strong
|
||||
v-else
|
||||
class="text-success"
|
||||
>This user</strong>
|
||||
<a
|
||||
v-if="group.leader !== hero._id"
|
||||
@click="switchUser(group.leader)"
|
||||
>{{ group.leader }}</a>
|
||||
<strong v-else class="text-success">This user</strong>
|
||||
</p>
|
||||
<p class="card-text">
|
||||
<strong>Members: </strong> {{ group.memberCount }}
|
||||
@@ -234,21 +190,16 @@
|
||||
<strong class="input-group-text">
|
||||
{{ dateFormat(hero.purchased.plan.dateTerminated) }}
|
||||
</strong>
|
||||
<a
|
||||
v-if="!hero.purchased.plan.dateTerminated && hero.purchased.plan.planId"
|
||||
<a class="btn btn-danger"
|
||||
href="#"
|
||||
v-b-modal.sub_termination_modal
|
||||
class="btn btn-danger"
|
||||
href="#"
|
||||
>
|
||||
v-if="!hero.purchased.plan.dateTerminated && hero.purchased.plan.planId">
|
||||
Terminate
|
||||
</a>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<small
|
||||
v-if="!hero.purchased.plan.dateTerminated
|
||||
&& hero.purchased.plan.planId"
|
||||
class="text-success"
|
||||
>
|
||||
<small v-if="!hero.purchased.plan.dateTerminated
|
||||
&& hero.purchased.plan.planId" class="text-success">
|
||||
The subscription does not have a termination date and is active.
|
||||
</small>
|
||||
</div>
|
||||
@@ -284,13 +235,11 @@
|
||||
step="any"
|
||||
>
|
||||
<div class="input-group-append">
|
||||
<a
|
||||
v-if="hero.purchased.plan.dateTerminated && hero.purchased.plan.extraMonths > 0"
|
||||
class="btn btn-warning"
|
||||
<a class="btn btn-warning"
|
||||
@click="applyExtraMonths"
|
||||
>
|
||||
v-if="hero.purchased.plan.dateTerminated && hero.purchased.plan.extraMonths > 0">
|
||||
Apply Credit
|
||||
</a>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<small class="text-secondary">
|
||||
@@ -390,24 +339,19 @@
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="!isConvertingToGroupPlan && hero.purchased.plan.planId !== 'group_plan_auto'"
|
||||
class="form-group row"
|
||||
>
|
||||
<div class="form-group row"
|
||||
v-if="!isConvertingToGroupPlan && hero.purchased.plan.planId !== 'group_plan_auto'">
|
||||
<div class="offset-sm-3 col-sm-9">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-secondary btn-sm"
|
||||
@click="beginGroupPlanConvert"
|
||||
>
|
||||
@click="beginGroupPlanConvert">
|
||||
Begin converting to group plan subscription
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="isConvertingToGroupPlan"
|
||||
class="form-group row"
|
||||
>
|
||||
<div class="form-group row"
|
||||
v-if="isConvertingToGroupPlan">
|
||||
<label class="col-sm-3 col-form-label">
|
||||
Group Plan group ID:
|
||||
</label>
|
||||
@@ -430,40 +374,25 @@
|
||||
class="btn btn-primary mt-1"
|
||||
@click="saveClicked"
|
||||
>
|
||||
<b
|
||||
v-if="hasUnsavedChanges"
|
||||
class="text-warning float-right"
|
||||
>
|
||||
<b v-if="hasUnsavedChanges" class="text-warning float-right">
|
||||
Unsaved changes
|
||||
</b>
|
||||
</div>
|
||||
</div>
|
||||
<b-modal
|
||||
id="sub_termination_modal"
|
||||
title="Set Termination Date"
|
||||
>
|
||||
<b-modal id="sub_termination_modal" title="Set Termination Date">
|
||||
<p>
|
||||
You can set the sub benefit termination date to today or to the last
|
||||
day of the current billing cycle. Any extra subscription credit will
|
||||
then be processed and automatically added onto the selected date.
|
||||
</p>
|
||||
<template #modal-footer>
|
||||
<div
|
||||
class="mt-3 btn btn-secondary"
|
||||
@click="$bvModal.hide('sub_termination_modal')"
|
||||
>
|
||||
<div class="mt-3 btn btn-secondary" @click="$bvModal.hide('sub_termination_modal')">
|
||||
Close
|
||||
</div>
|
||||
<div
|
||||
class="mt-3 btn btn-danger"
|
||||
@click="terminateSubscription()"
|
||||
>
|
||||
<div class="mt-3 btn btn-danger" @click="terminateSubscription()">
|
||||
Set to Today
|
||||
</div>
|
||||
<div
|
||||
class="mt-3 btn btn-danger"
|
||||
@click="terminateSubscription(todayWithRemainingCycle)"
|
||||
>
|
||||
<div class="mt-3 btn btn-danger" @click="terminateSubscription(todayWithRemainingCycle)">
|
||||
Set to {{ todayWithRemainingCycle.utc().format('MM/DD/YYYY') }}
|
||||
</div>
|
||||
</template>
|
||||
@@ -491,15 +420,15 @@
|
||||
import isUUID from 'validator/es/lib/isUUID';
|
||||
import moment from 'moment';
|
||||
import { getPlanContext } from '@/../../common/script/cron';
|
||||
import subscriptionBlocks from '@/../../common/script/content/subscriptionBlocks';
|
||||
import saveHero from '../mixins/saveHero';
|
||||
import subscriptionBlocks from '../../../../../common/script/content/subscriptionBlocks';
|
||||
import LoadingSpinner from '@/components/ui/loadingSpinner';
|
||||
|
||||
export default {
|
||||
mixins: [saveHero],
|
||||
components: {
|
||||
LoadingSpinner,
|
||||
},
|
||||
mixins: [saveHero],
|
||||
props: {
|
||||
hero: {
|
||||
type: Object,
|
||||
@@ -22,8 +22,8 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import PurchaseHistoryTable from '../../../ui/purchaseHistoryTable.vue';
|
||||
import { userStateMixin } from '../../../../mixins/userState';
|
||||
import PurchaseHistoryTable from '../../ui/purchaseHistoryTable.vue';
|
||||
import { userStateMixin } from '../../../mixins/userState';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -180,7 +180,7 @@
|
||||
|
||||
<script>
|
||||
import moment from 'moment';
|
||||
import { userStateMixin } from '../../../../mixins/userState';
|
||||
import { userStateMixin } from '../../../mixins/userState';
|
||||
|
||||
export default {
|
||||
filters: {
|
||||
@@ -13,12 +13,9 @@
|
||||
@click="expand = !expand"
|
||||
>
|
||||
User Profile
|
||||
<b
|
||||
v-if="hasUnsavedChanges && !expand"
|
||||
class="text-warning float-right"
|
||||
>
|
||||
Unsaved changes
|
||||
</b>
|
||||
<b v-if="hasUnsavedChanges && !expand" class="text-warning float-right">
|
||||
Unsaved changes
|
||||
</b>
|
||||
</h3>
|
||||
</div>
|
||||
<div
|
||||
@@ -69,10 +66,7 @@
|
||||
value="Save"
|
||||
class="btn btn-primary mt-1"
|
||||
>
|
||||
<b
|
||||
v-if="hasUnsavedChanges"
|
||||
class="text-warning float-right"
|
||||
>
|
||||
<b v-if="hasUnsavedChanges" class="text-warning float-right">
|
||||
Unsaved changes
|
||||
</b>
|
||||
</div>
|
||||
@@ -92,7 +86,7 @@ import markdownDirective from '@/directives/markdown';
|
||||
import saveHero from '../mixins/saveHero';
|
||||
|
||||
import { mapState } from '@/libs/store';
|
||||
import { userStateMixin } from '../../../../mixins/userState';
|
||||
import { userStateMixin } from '../../../mixins/userState';
|
||||
|
||||
function resetData (self) {
|
||||
self.expand = false;
|
||||
@@ -1,133 +0,0 @@
|
||||
<template>
|
||||
<div style="display: contents">
|
||||
<td>
|
||||
<select
|
||||
v-model="blocker.type"
|
||||
class="form-control"
|
||||
@change="onTypeChanged"
|
||||
>
|
||||
<option value="ipaddress">
|
||||
IP-Address
|
||||
</option>
|
||||
<option value="client">
|
||||
Client Identifier
|
||||
</option>
|
||||
<option value="email">
|
||||
E-Mail
|
||||
</option>
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
<select
|
||||
v-model="blocker.area"
|
||||
class="form-control"
|
||||
>
|
||||
<option value="full">
|
||||
Full
|
||||
</option>
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
<input
|
||||
v-model="blocker.value"
|
||||
class="form-control"
|
||||
autocorrect="off"
|
||||
autocapitalize="off"
|
||||
:class="{ 'is-invalid input-invalid': !isValid }"
|
||||
@input="validateValue"
|
||||
>
|
||||
</td>
|
||||
<td>
|
||||
<input
|
||||
v-model="blocker.reason"
|
||||
class="form-control"
|
||||
>
|
||||
</td>
|
||||
<td
|
||||
colspan="3"
|
||||
class="text-right"
|
||||
>
|
||||
<button
|
||||
class="btn btn-primary mr-2"
|
||||
:disabled="!isValid"
|
||||
:class="{ disabled: !isValid }"
|
||||
@click="$emit('save', blocker)"
|
||||
>
|
||||
<span>Save</span>
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-danger"
|
||||
@click="$emit('cancel')"
|
||||
>
|
||||
<span>Cancel</span>
|
||||
</button>
|
||||
</td>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.btn-primary.disabled {
|
||||
background: #4F2A93;
|
||||
color: white;
|
||||
cursor: not-allowed;
|
||||
opacity: 0.5;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import isIP from 'validator/es/lib/isIP';
|
||||
|
||||
export default {
|
||||
name: 'BlockerForm',
|
||||
props: {
|
||||
isNew: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
blocker: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
type: '',
|
||||
area: '',
|
||||
value: '',
|
||||
reason: '',
|
||||
}),
|
||||
},
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
isValid: false,
|
||||
};
|
||||
},
|
||||
mounted () {
|
||||
this.validateValue();
|
||||
},
|
||||
methods: {
|
||||
onTypeChanged () {
|
||||
if (this.blocker.type === 'email') {
|
||||
this.blocker.area = 'full';
|
||||
}
|
||||
this.validateValue();
|
||||
},
|
||||
validateValue () {
|
||||
if (this.blocker.type === 'ipaddress') {
|
||||
this.validateValueAsIpAddress();
|
||||
} else if (this.blocker.type === 'client') {
|
||||
this.validateValueAsClient();
|
||||
} else if (this.blocker.type === 'email') {
|
||||
this.validateValueAsEmail();
|
||||
}
|
||||
},
|
||||
validateValueAsEmail () {
|
||||
const emailRegex = /^([a-zA-Z0-9._%+-]*)@(?:[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})?$/;
|
||||
this.isValid = emailRegex.test(this.blocker.value) && this.blocker.value.length > 3;
|
||||
},
|
||||
validateValueAsIpAddress () {
|
||||
this.isValid = isIP(this.blocker.value);
|
||||
},
|
||||
validateValueAsClient () {
|
||||
this.isValid = this.blocker.value.length > 0;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -1,238 +0,0 @@
|
||||
<template>
|
||||
<div class="row standard-page col-12 d-flex justify-content-center">
|
||||
<div class="blocker-content">
|
||||
<h1>
|
||||
Blockers
|
||||
<button
|
||||
class="btn btn-primary float-right"
|
||||
@click="showCreateForm = true"
|
||||
>
|
||||
Create
|
||||
</button>
|
||||
</h1>
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
Type <span
|
||||
id="type_tooltip"
|
||||
class="info-icon"
|
||||
>?</span>
|
||||
<b-tooltip
|
||||
target="type_tooltip"
|
||||
>
|
||||
<b>IP-Address</b> - Block access for a specific IP-Address
|
||||
<br>
|
||||
<br>
|
||||
<b>Client</b> - Block access for a client based on the "x-client" header.
|
||||
<br>
|
||||
<br>
|
||||
<b>E-Mail</b> - Blocks e-mails from being used for signup.
|
||||
</b-tooltip>
|
||||
</th>
|
||||
<th>
|
||||
Area <span
|
||||
id="area_tooltip"
|
||||
class="info-icon"
|
||||
>?</span>
|
||||
<b-tooltip
|
||||
target="area_tooltip"
|
||||
>
|
||||
<b>Full</b> - Block access to the entire site.
|
||||
<br>
|
||||
<br>
|
||||
<b>Payments</b> - Block access to any payment related functionality.
|
||||
</b-tooltip>
|
||||
</th>
|
||||
<th>Value</th>
|
||||
<th>Reason</th>
|
||||
<th>Source</th>
|
||||
<th>Created at</th>
|
||||
<th class="btncol"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-if="showCreateForm">
|
||||
<BlockerForm
|
||||
:is-new="true"
|
||||
:blocker="newBlocker"
|
||||
@save="createBlocker"
|
||||
@cancel="showCreateForm = false"
|
||||
/>
|
||||
</tr>
|
||||
<tr
|
||||
v-for="blocker in blockers"
|
||||
:key="blocker._id"
|
||||
>
|
||||
<BlockerForm
|
||||
v-if="blocker._id === editedBlockerId"
|
||||
:blocker="blocker"
|
||||
@save="saveBlocker(blocker)"
|
||||
@cancel="editedBlockerId = null"
|
||||
/>
|
||||
<template v-else>
|
||||
<td>{{ getTypeName(blocker.type) }}</td>
|
||||
<td>{{ getAreaName(blocker.area) }}</td>
|
||||
<td>{{ blocker.value }}</td>
|
||||
<td>{{ blocker.reason || "--" }}</td>
|
||||
<td>{{ blocker.blockSource }}</td>
|
||||
<td>{{ blocker.createdAt }}</td>
|
||||
<td>
|
||||
<button
|
||||
class="btn btn-primary mr-2"
|
||||
@click="editBlocker(blocker._id)"
|
||||
>
|
||||
<span
|
||||
v-once
|
||||
class="svg-icon icon-16"
|
||||
v-html="icons.editIcon"
|
||||
></span>
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-danger"
|
||||
@click="deleteBlocker(blocker._id)"
|
||||
>
|
||||
<span
|
||||
v-once
|
||||
class="svg-icon icon-16"
|
||||
v-html="icons.deleteIcon"
|
||||
></span>
|
||||
</button>
|
||||
</td>
|
||||
</template>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '@/assets/scss/colors.scss';
|
||||
|
||||
.blocker-content {
|
||||
flex: 0 0 100%;
|
||||
max-width: 1200px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
padding: 0.4rem 0.75rem;
|
||||
}
|
||||
|
||||
.btncol {
|
||||
width: 123px;
|
||||
}
|
||||
|
||||
td {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.info-icon {
|
||||
font-size: 0.8rem;
|
||||
color: $purple-400;
|
||||
cursor: pointer;
|
||||
margin-left: 0.5rem;
|
||||
background-color: $gray-500;
|
||||
padding: 0.1rem 0.3rem;
|
||||
border-radius: 0.2rem;
|
||||
}
|
||||
|
||||
.info-icon:hover {
|
||||
background-color: $purple-400;
|
||||
color: white;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import { mapState } from '@/libs/store';
|
||||
|
||||
import editIcon from '@/assets/svg/edit.svg?raw';
|
||||
import deleteIcon from '@/assets/svg/delete.svg?raw';
|
||||
import BlockerForm from './blocker_form.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
BlockerForm,
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
showCreateForm: false,
|
||||
newBlocker: {
|
||||
type: '',
|
||||
area: 'full',
|
||||
value: '',
|
||||
reason: '',
|
||||
},
|
||||
blockers: [],
|
||||
editedBlockerId: null,
|
||||
icons: Object.freeze({
|
||||
editIcon,
|
||||
deleteIcon,
|
||||
}),
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState({ user: 'user.data' }),
|
||||
},
|
||||
mounted () {
|
||||
this.$store.dispatch('common:setTitle', {
|
||||
section: this.$t('siteBlockers'),
|
||||
});
|
||||
this.loadBlockers();
|
||||
},
|
||||
methods: {
|
||||
async loadBlockers () {
|
||||
this.blockers = await this.$store.dispatch('blockers:getBlockers');
|
||||
},
|
||||
editBlocker (id) {
|
||||
this.editedBlockerId = id;
|
||||
},
|
||||
async saveBlocker (blocker) {
|
||||
await this.$store.dispatch('blockers:updateBlocker', { blocker });
|
||||
this.editedBlockerId = null;
|
||||
this.loadBlockers();
|
||||
},
|
||||
async deleteBlocker (blockerId) {
|
||||
if (!window.confirm('Are you sure you want to delete this blocker?')) {
|
||||
return;
|
||||
}
|
||||
await this.$store.dispatch('blockers:deleteBlocker', { blockerId });
|
||||
this.loadBlockers();
|
||||
},
|
||||
async createBlocker (blocker) {
|
||||
await this.$store.dispatch('blockers:createBlocker', { blocker });
|
||||
this.showCreateForm = false;
|
||||
this.newBlocker = {
|
||||
type: '',
|
||||
area: 'full',
|
||||
value: '',
|
||||
reason: '',
|
||||
};
|
||||
this.loadBlockers();
|
||||
},
|
||||
|
||||
getTypeName (type) {
|
||||
switch (type) {
|
||||
case 'ipaddress':
|
||||
return 'IP-Address';
|
||||
case 'email':
|
||||
return 'E-Mail';
|
||||
case 'client':
|
||||
return 'Client Identifier';
|
||||
default:
|
||||
return type;
|
||||
}
|
||||
},
|
||||
getAreaName (area) {
|
||||
switch (area) {
|
||||
case 'full':
|
||||
return 'Full';
|
||||
case 'payments':
|
||||
return 'Payments';
|
||||
default:
|
||||
return area;
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -1,40 +0,0 @@
|
||||
<template>
|
||||
<div class="row">
|
||||
<secondary-menu class="col-12">
|
||||
<router-link
|
||||
v-if="hasPermission(user, 'userSupport')"
|
||||
class="nav-link"
|
||||
:to="{name: 'adminPanel'}"
|
||||
>
|
||||
{{ $t('adminPanel') }}
|
||||
</router-link>
|
||||
<router-link
|
||||
v-if="hasPermission(user, 'accessControl')"
|
||||
class="nav-link"
|
||||
:to="{name: 'blockers'}"
|
||||
>
|
||||
{{ $t('siteBlockers') }}
|
||||
</router-link>
|
||||
</secondary-menu><div class="col-12">
|
||||
<router-view />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from '@/libs/store';
|
||||
import SecondaryMenu from '@/components/secondaryMenu';
|
||||
import { userStateMixin } from '../../mixins/userState';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
SecondaryMenu,
|
||||
},
|
||||
mixins: [
|
||||
userStateMixin,
|
||||
],
|
||||
computed: {
|
||||
...mapState({ user: 'user.data' }),
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -276,9 +276,9 @@
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="time-travel"
|
||||
v-if="TIME_TRAVEL_ENABLED && user?.permissions?.fullAccess"
|
||||
:key="lastTimeJump"
|
||||
class="time-travel"
|
||||
>
|
||||
<a
|
||||
class="btn btn-secondary mr-1"
|
||||
@@ -299,7 +299,7 @@
|
||||
@click="resetTime()"
|
||||
>
|
||||
Reset
|
||||
</a>
|
||||
</a>
|
||||
</div>
|
||||
<a
|
||||
class="btn btn-secondary mr-1"
|
||||
|
||||
@@ -75,29 +75,37 @@
|
||||
class="box member-count"
|
||||
@click="showMemberModal()"
|
||||
>
|
||||
<div
|
||||
class="svg-icon member-icon"
|
||||
v-html="icons.memberIcon"
|
||||
></div>
|
||||
{{ challenge.memberCount }}
|
||||
<div
|
||||
v-once
|
||||
class="details"
|
||||
>
|
||||
{{ $t('participantsTitle') }}
|
||||
<div class="box-content">
|
||||
<div class="icon-number-row">
|
||||
<div
|
||||
class="svg-icon member-icon"
|
||||
v-html="icons.memberIcon"
|
||||
></div>
|
||||
<span class="number">{{ challenge.memberCount }}</span>
|
||||
</div>
|
||||
<div
|
||||
v-once
|
||||
class="details"
|
||||
>
|
||||
{{ $t('participantsTitle') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div
|
||||
class="svg-icon gem-icon"
|
||||
v-html="icons.gemIcon"
|
||||
></div>
|
||||
{{ challenge.prize || 0 }}
|
||||
<div
|
||||
v-once
|
||||
class="details"
|
||||
>
|
||||
{{ $t('prize') }}
|
||||
<div class="box prize-count">
|
||||
<div class="box-content">
|
||||
<div class="icon-number-row">
|
||||
<div
|
||||
class="svg-icon gem-icon"
|
||||
v-html="icons.gemIcon"
|
||||
></div>
|
||||
<span class="number">{{ challenge.prize || 0 }}</span>
|
||||
</div>
|
||||
<div
|
||||
v-once
|
||||
class="details"
|
||||
>
|
||||
{{ $t('prize') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -304,7 +312,7 @@
|
||||
|
||||
.box {
|
||||
display: inline-block;
|
||||
padding: 1em;
|
||||
padding: 0.5em;
|
||||
border-radius: 2px;
|
||||
background-color: $white;
|
||||
box-shadow: 0 2px 2px 0 rgba(26, 24, 29, 0.16), 0 1px 4px 0 rgba(26, 24, 29, 0.12);
|
||||
@@ -314,22 +322,88 @@
|
||||
text-align: center;
|
||||
font-size: 20px;
|
||||
vertical-align: bottom;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
|
||||
&.member-count:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.box-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.icon-number-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 0.1em;
|
||||
|
||||
.number {
|
||||
font-size: 20px;
|
||||
font-weight: normal;
|
||||
margin-left: 0.2em;
|
||||
}
|
||||
}
|
||||
|
||||
.svg-icon {
|
||||
width: 30px;
|
||||
display: inline-block;
|
||||
margin-right: .2em;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
.details {
|
||||
font-size: 12px;
|
||||
margin-top: 0.4em;
|
||||
color: $gray-200;
|
||||
width: 100%;
|
||||
padding: 0 4px;
|
||||
line-height: 1.15;
|
||||
word-break: break-word;
|
||||
max-height: 2.3em;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
&.member-count {
|
||||
.icon-number-row {
|
||||
.svg-icon {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.number {
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
.details {
|
||||
font-size: 11px;
|
||||
line-height: 1.1;
|
||||
max-height: 2.2em;
|
||||
}
|
||||
}
|
||||
|
||||
&.prize-count {
|
||||
.icon-number-row {
|
||||
.svg-icon {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.number {
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
.details {
|
||||
font-size: 11px;
|
||||
line-height: 1.1;
|
||||
max-height: 2.2em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
id="close-challenge-modal"
|
||||
:title="$t('endChallenge')"
|
||||
size="md"
|
||||
:hide-header="false"
|
||||
>
|
||||
<div
|
||||
slot="modal-header"
|
||||
@@ -15,6 +16,9 @@
|
||||
>
|
||||
{{ $t('endChallenge') }}
|
||||
</h2>
|
||||
<close-x
|
||||
@close="$root.$emit('bv::hide::modal', 'close-challenge-modal')"
|
||||
/>
|
||||
</div>
|
||||
<div class="row text-center">
|
||||
<span
|
||||
@@ -28,28 +32,67 @@
|
||||
class="col-12"
|
||||
>
|
||||
<div class="col-12">
|
||||
<div class="support-habitica">
|
||||
<!-- @TODO: Add challenge achievement badge here-->
|
||||
<div class="badge-section">
|
||||
<div
|
||||
class="gems-left"
|
||||
v-html="icons.gemsOrange"
|
||||
></div>
|
||||
<div
|
||||
class="challenge-badge"
|
||||
v-html="icons.endChallengeBadge"
|
||||
></div>
|
||||
<div
|
||||
class="gems-right"
|
||||
v-html="icons.gemsPurple"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<strong v-once>{{ $t('selectChallengeWinnersDescription') }}</strong>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<member-search-dropdown
|
||||
:text="winnerText"
|
||||
:members="members"
|
||||
:challenge-id="challengeId"
|
||||
@member-selected="selectMember"
|
||||
/>
|
||||
<div class="search-input-wrapper">
|
||||
<div
|
||||
class="search-icon"
|
||||
v-html="icons.search"
|
||||
></div>
|
||||
<input
|
||||
v-model="searchTerm"
|
||||
class="search-input"
|
||||
type="text"
|
||||
:placeholder="'@' + $t('username')"
|
||||
@input="searchMembers"
|
||||
@focus="showResults = true"
|
||||
@blur="handleBlur"
|
||||
>
|
||||
<div
|
||||
v-if="showResults && filteredMembers.length > 0"
|
||||
class="search-results"
|
||||
>
|
||||
<div
|
||||
v-for="member in filteredMembers"
|
||||
:key="member._id"
|
||||
class="search-result-item"
|
||||
@mousedown="selectMember(member)"
|
||||
>
|
||||
{{ getMemberDisplayName(member) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<button
|
||||
v-once
|
||||
class="btn btn-primary"
|
||||
class="btn award-winner-btn"
|
||||
:class="{'has-winner': winner._id}"
|
||||
:disabled="!winner._id"
|
||||
@click="closeChallenge"
|
||||
>
|
||||
{{ $t('awardWinners') }}
|
||||
<span>{{ $t('awardWinners') }}</span>
|
||||
<div
|
||||
class="gem-icon"
|
||||
v-html="icons.gem"
|
||||
></div>
|
||||
<span>{{ prize }} {{ prize === 1 ? $t('gem') : $t('gems') }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</span>
|
||||
@@ -60,14 +103,24 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<strong v-once>{{ $t('doYouWantedToDeleteChallenge') }}</strong>
|
||||
<strong
|
||||
v-once
|
||||
class="delete-challenge-text"
|
||||
>{{ $t('doYouWantedToDeleteChallenge') }}</strong>
|
||||
</div>
|
||||
<div class="col-12 refund-text">
|
||||
{{ $t('deleteChallengeRefundDescription') }}
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<button
|
||||
v-once
|
||||
class="btn btn-danger"
|
||||
class="btn btn-danger delete-challenge-btn"
|
||||
@click="deleteChallenge()"
|
||||
>
|
||||
<div
|
||||
class="svg-icon color delete-icon"
|
||||
v-html="icons.deleteIcon"
|
||||
></div>
|
||||
{{ $t('deleteChallenge') }}
|
||||
</button>
|
||||
</div>
|
||||
@@ -95,13 +148,163 @@
|
||||
.header-wrap {
|
||||
width: 100%;
|
||||
padding-top: 2em;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.support-habitica {
|
||||
background-image: url('@/assets/svg/for-css/support-habitica-gems.svg?raw');
|
||||
width: 325px;
|
||||
height: 89px;
|
||||
.search-input-wrapper {
|
||||
position: relative;
|
||||
width: 384px;
|
||||
margin: 0 auto;
|
||||
|
||||
.search-icon {
|
||||
position: absolute;
|
||||
left: 12px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
color: $gray-200;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
width: 100%;
|
||||
height: 32px;
|
||||
padding-left: 36px;
|
||||
padding-right: 12px;
|
||||
border: 2px solid $gray-400;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
transition: border-color 0.2s ease;
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
border-color: $purple-300;
|
||||
}
|
||||
|
||||
&::placeholder {
|
||||
color: $gray-300;
|
||||
}
|
||||
}
|
||||
|
||||
.search-results {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: $white;
|
||||
border: 1px solid $gray-400;
|
||||
border-top: none;
|
||||
border-radius: 0 0 4px 4px;
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
z-index: 1000;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
|
||||
.search-result-item {
|
||||
padding: 8px 12px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
background-color: $purple-600;
|
||||
color: $purple-300;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.delete-challenge-text {
|
||||
color: $maroon-50;
|
||||
}
|
||||
|
||||
.refund-text {
|
||||
font-family: 'Roboto', sans-serif;
|
||||
font-size: 14px;
|
||||
line-height: 24px;
|
||||
font-weight: 400;
|
||||
color: $gray-50;
|
||||
margin-top: 0.5em !important;
|
||||
}
|
||||
|
||||
.delete-challenge-btn {
|
||||
font-family: 'Roboto', sans-serif;
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
line-height: 24px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
|
||||
.delete-icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
display: inline-flex;
|
||||
}
|
||||
}
|
||||
|
||||
.award-winner-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 8px 16px;
|
||||
background-color: $white;
|
||||
color: $gray-200;
|
||||
border: 1px solid $gray-400;
|
||||
box-shadow: 0 2px 2px 0 rgba(26, 24, 29, 0.16), 0 1px 4px 0 rgba(26, 24, 29, 0.12);
|
||||
transition: all 0.2s ease;
|
||||
|
||||
&:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
|
||||
&:hover {
|
||||
background-color: $white;
|
||||
}
|
||||
}
|
||||
|
||||
&.has-winner {
|
||||
background-color: $purple-200;
|
||||
color: $white;
|
||||
border-color: $purple-200;
|
||||
|
||||
.gem-icon {
|
||||
color: $white;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover:not(.has-winner):not(:disabled) {
|
||||
background-color: $gray-700;
|
||||
}
|
||||
|
||||
.gem-icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
color: $gems-color;
|
||||
}
|
||||
}
|
||||
|
||||
.badge-section {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 1.5rem;
|
||||
margin: 0 auto;
|
||||
padding: 0.5rem 0;
|
||||
|
||||
.gems-left, .gems-right {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.challenge-badge {
|
||||
width: 48px;
|
||||
height: 52px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.modal-footer, .modal-header {
|
||||
@@ -113,7 +316,11 @@
|
||||
}
|
||||
|
||||
.col-12 {
|
||||
margin-top: 2em;
|
||||
margin-top: 1.5em;
|
||||
}
|
||||
|
||||
.col-12:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.or {
|
||||
@@ -123,21 +330,39 @@
|
||||
margin-right: auto;
|
||||
margin-left: auto;
|
||||
font-weight: bold;
|
||||
color: $gray-100;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import memberSearchDropdown from '@/components/members/memberSearchDropdown';
|
||||
import searchIcon from '@/assets/svg/for-css/search.svg?raw';
|
||||
import deleteIcon from '@/assets/svg/delete.svg?raw';
|
||||
import gemIcon from '@/assets/svg/gem.svg?raw';
|
||||
import endChallengeBadge from '@/assets/svg/for-css/end_challenge_badge.svg?raw';
|
||||
import gemsOrange from '@/assets/svg/for-css/orange100_red100_yellow100_gems.svg?raw';
|
||||
import gemsPurple from '@/assets/svg/for-css/purple200_green10_blue100_gems.svg?raw';
|
||||
import closeX from '@/components/ui/closeX';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
memberSearchDropdown,
|
||||
closeX,
|
||||
},
|
||||
props: ['challengeId', 'members', 'prize', 'flagCount'],
|
||||
data () {
|
||||
return {
|
||||
winner: {},
|
||||
searchTerm: '',
|
||||
showResults: false,
|
||||
filteredMembers: [],
|
||||
icons: Object.freeze({
|
||||
search: searchIcon,
|
||||
deleteIcon,
|
||||
gem: gemIcon,
|
||||
endChallengeBadge,
|
||||
gemsOrange,
|
||||
gemsPurple,
|
||||
}),
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@@ -150,8 +375,35 @@ export default {
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
searchMembers () {
|
||||
if (!this.searchTerm) {
|
||||
this.filteredMembers = [];
|
||||
return;
|
||||
}
|
||||
|
||||
const searchLower = this.searchTerm.toLowerCase().replace('@', '');
|
||||
this.filteredMembers = this.members.filter(member => {
|
||||
const username = member.auth?.local?.username || '';
|
||||
const displayName = member.profile?.name || '';
|
||||
return username.toLowerCase().includes(searchLower)
|
||||
|| displayName.toLowerCase().includes(searchLower);
|
||||
}).slice(0, 10);
|
||||
},
|
||||
getMemberDisplayName (member) {
|
||||
if (member.auth?.local?.username) {
|
||||
return `@${member.auth.local.username}`;
|
||||
}
|
||||
return member.profile?.name || '';
|
||||
},
|
||||
selectMember (member) {
|
||||
this.winner = member;
|
||||
this.searchTerm = this.getMemberDisplayName(member);
|
||||
this.showResults = false;
|
||||
},
|
||||
handleBlur () {
|
||||
setTimeout(() => {
|
||||
this.showResults = false;
|
||||
}, 200);
|
||||
},
|
||||
async closeChallenge () {
|
||||
this.challenge = await this.$store.dispatch('challenges:selectChallengeWinner', {
|
||||
|
||||
@@ -227,8 +227,7 @@
|
||||
<div class="quest-icon">
|
||||
<Sprite
|
||||
class="quest"
|
||||
:image-name="`inventory_quest_scroll_${questData.key}`"
|
||||
/>
|
||||
:image-name="`inventory_quest_scroll_${questData.key}`" />
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
|
||||
@@ -286,7 +286,7 @@
|
||||
:to="{ name: 'adminPanelUser',
|
||||
params: { userIdentifier: hero._id } }"
|
||||
>
|
||||
{{ $t("adminPanel") }}
|
||||
admin panel
|
||||
</router-link>
|
||||
</span>
|
||||
</td>
|
||||
|
||||
@@ -295,6 +295,14 @@
|
||||
{{ $t('help') }}
|
||||
</router-link>
|
||||
<div class="topbar-dropdown">
|
||||
<router-link
|
||||
v-if="user.permissions.fullAccess ||
|
||||
user.permissions.userSupport"
|
||||
class="topbar-dropdown-item dropdown-item"
|
||||
:to="{name: 'adminPanel'}"
|
||||
>
|
||||
Admin Panel
|
||||
</router-link>
|
||||
<router-link
|
||||
class="topbar-dropdown-item dropdown-item"
|
||||
:to="{name: 'faq'}"
|
||||
@@ -328,61 +336,6 @@
|
||||
>{{ $t('requestFeature') }}</a>
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
v-if="hasElevatedPrivileges"
|
||||
class="topbar-item droppable"
|
||||
:class="{
|
||||
'active': $route.path.startsWith('/admin')}"
|
||||
>
|
||||
<div
|
||||
class="chevron rotate"
|
||||
@click="dropdownMobile($event)"
|
||||
>
|
||||
<div
|
||||
v-once
|
||||
class="chevron-icon-down"
|
||||
v-html="icons.chevronDown"
|
||||
></div>
|
||||
</div>
|
||||
<router-link
|
||||
v-if="hasPermission(user, 'userSupport')"
|
||||
class="nav-link"
|
||||
:to="{name: 'adminPanel'}"
|
||||
>
|
||||
{{ $t('admin') }}
|
||||
</router-link>
|
||||
<a
|
||||
v-else
|
||||
href="#"
|
||||
class="nav-link"
|
||||
>
|
||||
{{ $t('admin') }}
|
||||
</a>
|
||||
<div class="topbar-dropdown">
|
||||
<router-link
|
||||
v-if="hasPermission(user, 'userSupport')"
|
||||
class="topbar-dropdown-item dropdown-item"
|
||||
:to="{name: 'adminPanel'}"
|
||||
>
|
||||
{{ $t("adminPanel") }}
|
||||
</router-link>
|
||||
<router-link
|
||||
v-if="hasPermission(user, 'accessControl')"
|
||||
class="topbar-dropdown-item dropdown-item"
|
||||
:to="{name: 'blockers'}"
|
||||
>
|
||||
{{ $t("siteBlockers") }}
|
||||
</router-link>
|
||||
<a
|
||||
v-if="hasPermission(user, 'news')"
|
||||
class="topbar-dropdown-item dropdown-item"
|
||||
target="_blank"
|
||||
href="https://panel.habitica.com"
|
||||
>
|
||||
{{ $t('newsroom') }}
|
||||
</a>
|
||||
</div>
|
||||
</li>
|
||||
</b-navbar-nav>
|
||||
<div class="currency-tray form-inline">
|
||||
<div
|
||||
@@ -804,7 +757,6 @@ import selectUserModal from '@/components/payments/selectUserModal';
|
||||
import sync from '@/mixins/sync';
|
||||
import userDropdown from './userDropdown';
|
||||
import reportBug from '@/mixins/reportBug.js';
|
||||
import { userStateMixin } from '../../mixins/userState';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -817,7 +769,7 @@ export default {
|
||||
selectUserModal,
|
||||
userDropdown,
|
||||
},
|
||||
mixins: [sync, reportBug, userStateMixin],
|
||||
mixins: [sync, reportBug],
|
||||
data () {
|
||||
return {
|
||||
isUserDropdownOpen: false,
|
||||
@@ -850,12 +802,6 @@ export default {
|
||||
params: { groupId: this.groupPlans[0]._id },
|
||||
};
|
||||
},
|
||||
hasElevatedPrivileges () {
|
||||
return this.user.permissions.fullAccess
|
||||
|| this.user.permissions.userSupport
|
||||
|| this.user.permissions.accessControl
|
||||
|| this.user.permissions.news;
|
||||
},
|
||||
},
|
||||
async mounted () {
|
||||
await this.getUserGroupPlans();
|
||||
|
||||
@@ -25,24 +25,32 @@
|
||||
{{ $t('sendGift') }}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="notification-remove"
|
||||
@click.stop="remove()"
|
||||
>
|
||||
<div
|
||||
v-once
|
||||
class="svg-icon"
|
||||
v-html="icons.close"
|
||||
></div>
|
||||
</div>
|
||||
<close-x
|
||||
@close="remove()"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
@import '@/assets/scss/colors.scss';
|
||||
|
||||
small, strong {
|
||||
small {
|
||||
color: $white;
|
||||
font-family: 'Roboto', sans-serif;
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
font-size: 14px;
|
||||
line-height: 24px;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
strong {
|
||||
color: $white;
|
||||
font-family: 'Roboto', sans-serif;
|
||||
font-weight: 700;
|
||||
font-style: normal;
|
||||
font-size: 14px;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
.notification {
|
||||
@@ -55,50 +63,42 @@
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.notification-remove {
|
||||
position: absolute;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
padding: 4px;
|
||||
right: 24px;
|
||||
top: 24px;
|
||||
|
||||
.svg-icon {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
width: 5.75rem;
|
||||
min-height: 1.5rem;
|
||||
min-width: 5.75rem;
|
||||
width: auto;
|
||||
max-width: calc(100% - 2rem);
|
||||
min-height: 32px;
|
||||
padding: 0.25rem 0.75rem;
|
||||
border-radius: 2px;
|
||||
border-color: $white;
|
||||
box-shadow: 0 2px 2px 0 rgba(26, 24, 29, 0.16), 0 1px 4px 0 rgba(26, 24, 29, 0.12);
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
font-family: 'Roboto', sans-serif;
|
||||
font-weight: 700;
|
||||
font-style: normal;
|
||||
font-size: 14px;
|
||||
line-height: 24px;
|
||||
color: $gray-50;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import closeIcon from '@/assets/svg/close-teal.svg?raw';
|
||||
import { mapActions } from '@/libs/store';
|
||||
import closeX from '@/components/ui/closeX';
|
||||
|
||||
export default {
|
||||
props: ['notification'],
|
||||
data () {
|
||||
return {
|
||||
icons: Object.freeze({
|
||||
close: closeIcon,
|
||||
}),
|
||||
};
|
||||
components: {
|
||||
closeX,
|
||||
},
|
||||
props: ['notification', 'eventKey'],
|
||||
methods: {
|
||||
...mapActions({
|
||||
readNotification: 'notifications:readNotification',
|
||||
}),
|
||||
remove () {
|
||||
this.readNotification({ notificationId: this.notification.id });
|
||||
if (this.eventKey) {
|
||||
window.sessionStorage.setItem(`hide-g1g1-${this.eventKey}`, 'true');
|
||||
}
|
||||
this.$emit('notification-removed');
|
||||
},
|
||||
showSelectUser () {
|
||||
this.$root.$emit('bv::show::modal', 'select-user-modal');
|
||||
|
||||
@@ -15,8 +15,7 @@
|
||||
<Sprite
|
||||
slot="icon"
|
||||
class="mt-3"
|
||||
:image-name="notification.data.icon"
|
||||
/>
|
||||
:image-name="notification.data.icon" />
|
||||
</base-notification>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -12,8 +12,7 @@
|
||||
></div>
|
||||
<Sprite
|
||||
slot="icon"
|
||||
:image-name="mysteryClass"
|
||||
/>
|
||||
:image-name="mysteryClass" />
|
||||
</base-notification>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ export default {
|
||||
props: ['notification', 'canRemove'],
|
||||
methods: {
|
||||
action () {
|
||||
this.$router.push({ name: 'achievements' });
|
||||
this.$router.push(`/profile/${this.$store.state.user.data._id}#achievements`);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -43,7 +43,7 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
action () {
|
||||
this.$router.push({ name: 'stats' });
|
||||
this.$router.push(`/profile/${this.$store.state.user.data._id}#stats`);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -49,6 +49,12 @@
|
||||
v-if="showOnboardingGuide"
|
||||
:never-seen="hasSpecialBadge"
|
||||
/>
|
||||
<gift-one-get-one-notification
|
||||
v-if="shouldShowG1g1"
|
||||
:notification="g1g1Notification"
|
||||
:event-key="g1g1EventKey"
|
||||
@notification-removed="handleG1g1Removed"
|
||||
/>
|
||||
<component
|
||||
:is="notification.type"
|
||||
v-for="notification in notifications"
|
||||
@@ -114,6 +120,7 @@
|
||||
<script>
|
||||
import * as quests from '@/../../common/script/content/quests';
|
||||
import { hasCompletedOnboarding } from '@/../../common/script/libs/onboarding';
|
||||
import find from 'lodash/find';
|
||||
import { mapState, mapActions } from '@/libs/store';
|
||||
import notificationsIcon from '@/assets/svg/notifications.svg?raw';
|
||||
import MenuDropdown from '../ui/customMenuDropdown';
|
||||
@@ -151,6 +158,7 @@ export default {
|
||||
CARD_RECEIVED,
|
||||
CHALLENGE_INVITATION,
|
||||
GIFT_ONE_GET_ONE,
|
||||
GiftOneGetOneNotification: GIFT_ONE_GET_ONE,
|
||||
GROUP_TASK_ASSIGNED,
|
||||
GROUP_TASK_CLAIMED,
|
||||
GROUP_TASK_NEEDS_WORK,
|
||||
@@ -178,17 +186,14 @@ export default {
|
||||
hasSpecialBadge: false,
|
||||
quests,
|
||||
openStatus: undefined,
|
||||
g1g1Hidden: false,
|
||||
actionableNotifications: [
|
||||
'GUILD_INVITATION', 'PARTY_INVITATION', 'CHALLENGE_INVITATION',
|
||||
'QUEST_INVITATION',
|
||||
],
|
||||
// A list of notifications handled by this component,
|
||||
// listed in the order they should appear in the notifications panel.
|
||||
// NOTE: Those not listed here won't be shown in the notification panel!
|
||||
handledNotifications: [
|
||||
'NEW_STUFF',
|
||||
'ITEM_RECEIVED',
|
||||
'GIFT_ONE_GET_ONE',
|
||||
'GROUP_TASK_NEEDS_WORK',
|
||||
'GUILD_INVITATION',
|
||||
'PARTY_INVITATION',
|
||||
@@ -207,7 +212,10 @@ export default {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState({ user: 'user.data' }),
|
||||
...mapState({
|
||||
user: 'user.data',
|
||||
currentEventList: 'worldState.data.currentEventList',
|
||||
}),
|
||||
notificationsOrder () {
|
||||
// Returns a map of NOTIFICATION_TYPE -> POSITION
|
||||
const orderMap = {};
|
||||
@@ -286,9 +294,9 @@ export default {
|
||||
|
||||
return notifications;
|
||||
},
|
||||
// The total number of notification, shown inside the dropdown
|
||||
notificationsCount () {
|
||||
return this.notifications.length;
|
||||
const g1g1Count = this.shouldShowG1g1 ? 1 : 0;
|
||||
return this.notifications.length + g1g1Count;
|
||||
},
|
||||
hasUnseenNotifications () {
|
||||
return this.notifications.some(notification => (notification.seen === false));
|
||||
@@ -299,6 +307,30 @@ export default {
|
||||
showOnboardingGuide () {
|
||||
return !hasCompletedOnboarding(this.user);
|
||||
},
|
||||
currentG1g1Event () {
|
||||
return find(this.currentEventList, event => event.promo === 'g1g1');
|
||||
},
|
||||
g1g1EventKey () {
|
||||
if (!this.currentG1g1Event || !this.currentG1g1Event.start) return null;
|
||||
const startDate = new Date(this.currentG1g1Event.start);
|
||||
return `${startDate.getFullYear()}-${startDate.getMonth()}`;
|
||||
},
|
||||
shouldShowG1g1 () {
|
||||
if (!this.currentG1g1Event) return false;
|
||||
const eventKey = this.g1g1EventKey;
|
||||
if (eventKey && window.sessionStorage.getItem(`hide-g1g1-${eventKey}`) === 'true') {
|
||||
return false;
|
||||
}
|
||||
return !this.g1g1Hidden;
|
||||
},
|
||||
g1g1Notification () {
|
||||
return {
|
||||
type: 'GIFT_ONE_GET_ONE',
|
||||
id: `g1g1-event-${this.currentG1g1Event?.start || 'default'}`,
|
||||
data: {},
|
||||
seen: false,
|
||||
};
|
||||
},
|
||||
},
|
||||
mounted () {
|
||||
const onboardingPanelState = getLocalSetting(CONSTANTS.keyConstants.ONBOARDING_PANEL_STATE);
|
||||
@@ -364,6 +396,9 @@ export default {
|
||||
isActionable (notification) {
|
||||
return this.actionableNotifications.indexOf(notification.type) !== -1;
|
||||
},
|
||||
handleG1g1Removed () {
|
||||
this.g1g1Hidden = true;
|
||||
},
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
@@ -176,7 +176,12 @@ export default {
|
||||
}
|
||||
},
|
||||
showProfile (startingPage) {
|
||||
this.$router.push({ name: startingPage });
|
||||
const userId = this.$store.state.user.data._id;
|
||||
let path = `/profile/${userId}`;
|
||||
if (startingPage !== 'profile') {
|
||||
path += `#${startingPage}`;
|
||||
}
|
||||
this.$router.push(path);
|
||||
},
|
||||
toLearnMore () {
|
||||
this.$router.push({ name: 'subscription' });
|
||||
|
||||
@@ -156,7 +156,7 @@
|
||||
background-color: rgba(213, 200, 255, 0.32);
|
||||
padding: 0.1rem;
|
||||
}
|
||||
|
||||
|
||||
.at-text {
|
||||
color: #6133b4;
|
||||
}
|
||||
@@ -460,9 +460,8 @@ export default {
|
||||
}
|
||||
|
||||
const { user } = this;
|
||||
const displayName = user.profile.name;
|
||||
const { username } = user.auth.local;
|
||||
const pattern = `@(${escapeRegExp(displayName)}|${escapeRegExp(username)})(\\b)`;
|
||||
const pattern = `@${escapeRegExp(username)}(\\b)`;
|
||||
message.highlight = new RegExp(pattern, 'i').test(message.text);
|
||||
|
||||
return message.highlight;
|
||||
|
||||
@@ -81,10 +81,9 @@ import moment from 'moment';
|
||||
import habiticaMarkdown from 'habitica-markdown';
|
||||
import { mapState } from '@/libs/store';
|
||||
import seasonalNPC from '@/mixins/seasonalNPC';
|
||||
import { userStateMixin } from '../../mixins/userState';
|
||||
|
||||
export default {
|
||||
mixins: [seasonalNPC, userStateMixin],
|
||||
mixins: [seasonalNPC],
|
||||
data () {
|
||||
return {
|
||||
posts: [],
|
||||
@@ -108,7 +107,7 @@ export default {
|
||||
if (lastPublishedPost) this.posts.push(lastPublishedPost);
|
||||
|
||||
// If the user is authorized, show any draft
|
||||
if (this.user && (this.hasPermission(this.user, 'news'))) {
|
||||
if (this.user && (this.user.permissions.news || this.user.permissions.fullAccess)) {
|
||||
this.posts.unshift(
|
||||
...postsFromServer
|
||||
.filter(p => !p.published || moment().isBefore(p.publishDate)),
|
||||
|
||||
@@ -851,7 +851,7 @@ export default {
|
||||
return;
|
||||
}
|
||||
if (this.genericPurchase) {
|
||||
this.makeGenericPurchase(this.item, 'buyModal', this.selectedAmountToBuy);
|
||||
await this.makeGenericPurchase(this.item, 'buyModal', this.selectedAmountToBuy);
|
||||
await this.purchased(this.item.text);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,15 +7,10 @@
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-12 mb-5 mb-md-0">
|
||||
<img
|
||||
:src="makeUrl('features_taskboard.png')"
|
||||
class="img-fluid"
|
||||
>
|
||||
<img :src="makeUrl('features_taskboard.png')" class="img-fluid">
|
||||
<h2>{{ $t('marketing1Lead1Title') }}</h2>
|
||||
<div class="row justify-content-md-center">
|
||||
<p class="col col-lg-8 col-xl-6 margin-auto description">
|
||||
{{ $t('marketing1Lead1') }}
|
||||
</p>
|
||||
<p class="col col-lg-8 col-xl-6 margin-auto description">{{ $t('marketing1Lead1') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -23,16 +18,12 @@
|
||||
<div class="col-md-6 mb-5 mb-md-0">
|
||||
<img :src="makeUrl('features_gear.png')">
|
||||
<h2>{{ $t('marketing1Lead2Title') }}</h2>
|
||||
<p class="description">
|
||||
{{ $t('marketing1Lead2') }}
|
||||
</p>
|
||||
<p class="description">{{ $t('marketing1Lead2') }}</p>
|
||||
</div>
|
||||
<div class="col-md-6 mb-5 mb-md-0">
|
||||
<img :src="makeUrl('features_items.png')">
|
||||
<h2>{{ $t('marketing1Lead3Title') }}</h2>
|
||||
<p class="description">
|
||||
{{ $t('marketing1Lead3') }}
|
||||
</p>
|
||||
<p class="description">{{ $t('marketing1Lead3') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
@@ -44,26 +35,19 @@
|
||||
<div class="row mb-5">
|
||||
<div class="col-12">
|
||||
<h2>{{ $t('marketing2Lead1Title') }}</h2>
|
||||
<p class="description">
|
||||
{{ $t('marketing2Lead1') }}
|
||||
</p>
|
||||
<p class="description">{{ $t('marketing2Lead1') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-5 mb-md-0">
|
||||
<img :src="makeUrl('features_monsters.png')">
|
||||
<h2>{{ $t('marketing2Lead2Title') }}</h2>
|
||||
<p
|
||||
v-markdown="$t('marketing2Lead2')"
|
||||
class="description"
|
||||
></p>
|
||||
<p class="description" v-markdown="$t('marketing2Lead2')"></p>
|
||||
</div>
|
||||
<div class="col-md-6 mb-5 mb-md-0">
|
||||
<img :src="makeUrl('features_challenges.png')">
|
||||
<h2>{{ $t('marketing2Lead3Title') }}</h2>
|
||||
<p class="description">
|
||||
{{ $t('marketing2Lead3') }}
|
||||
</p>
|
||||
<p class="description">{{ $t('marketing2Lead3') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
@@ -76,18 +60,12 @@
|
||||
<div class="col-md-6 mb-5 mb-md-0">
|
||||
<img :src="makeUrl('features_mobile.png')">
|
||||
<h2>{{ $t('marketing3Lead1Title') }}</h2>
|
||||
<p
|
||||
v-markdown="$t('marketing3Lead1')"
|
||||
class="description"
|
||||
></p>
|
||||
<p class="description" v-markdown="$t('marketing3Lead1')"></p>
|
||||
</div>
|
||||
<div class="col-md-6 mb-5 mb-md-0">
|
||||
<img :src="makeUrl('features_opensource.png')">
|
||||
<h2>{{ $t('marketing3Lead2Title') }}</h2>
|
||||
<p
|
||||
v-markdown="$t('marketing3Lead2')"
|
||||
class="description"
|
||||
></p>
|
||||
<p class="description" v-markdown="$t('marketing3Lead2')"></p>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
@@ -102,9 +80,7 @@
|
||||
<img src="@/assets/images/marketing/education.png">
|
||||
<div class="media-body">
|
||||
<h2>{{ $t('marketing4Lead1Title') }}</h2>
|
||||
<p class="description">
|
||||
{{ $t('marketing4Lead1') }}
|
||||
</p>
|
||||
<p class="description">{{ $t('marketing4Lead1') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -113,9 +89,7 @@
|
||||
<img src="@/assets/images/marketing/wellness.png">
|
||||
<div class="media-body">
|
||||
<h2>{{ $t('marketing4Lead2Title') }}</h2>
|
||||
<p class="description">
|
||||
{{ $t('marketing4Lead2') }}
|
||||
</p>
|
||||
<p class="description">{{ $t('marketing4Lead2') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,819 +0,0 @@
|
||||
<template>
|
||||
<!-- eslint-disable max-len -->
|
||||
<div class="container-fluid">
|
||||
<h1>HabitRPG Privacy Policy</h1>
|
||||
<p class="strong pagemeta">
|
||||
Last Updated September 1, 2025.
|
||||
</p>
|
||||
<p>
|
||||
This Privacy Policy applies when you interact with us through <a href="https://habitica.com">Habitica.com</a> (the “<strong>Site</strong>”), our mobile apps, and/or through any other feature or service owned or controlled by HabitRPG, Inc. ("<strong>HabitRPG</strong>", "<strong>we</strong>", or "<strong>us</strong>") that posts, links to, or references this Privacy Policy (collectively, the "<strong>Service(s)</strong>"). This Privacy Policy informs you of our practices regarding the collection, use, and disclosure of personal information we receive from users of our Services. By accessing or using the Services, you consent to our Privacy Policy and our collection, use, and disclosure of your information as described in this policy, our <a href="https://habitica.com/static/terms">Terms of Use</a>, and any additional policies and terms you may agree to in connection with the Services.
|
||||
</p>
|
||||
<h1>Table of Contents</h1>
|
||||
<ol>
|
||||
<li>
|
||||
<a href="#section_1">Collection of Information</a>
|
||||
<ol>
|
||||
<li><a href="#section_1_1">Information you Provide Directly</a></li>
|
||||
<li><a href="#section_1_2">Information We Collect Automatically</a></li>
|
||||
<li><a href="#section_1_3">Location Data</a></li>
|
||||
<li><a href="#section_1_2">Information Collected By and/or Disclosed to Third Parties</a></li>
|
||||
</ol>
|
||||
</li>
|
||||
<li><a href="#section_2">Purpose and Use of Information We Collect</a></li>
|
||||
<li><a href="#section_3">Service Providers</a></li>
|
||||
<li><a href="#section_4">Sweepstakes, Contests, and Promotions</a></li>
|
||||
<li><a href="#section_5">Security</a></li>
|
||||
<li><a href="#section_6">Data Retention</a></li>
|
||||
<li><a href="#section_7">General Audience Services</a></li>
|
||||
<li><a href="#section_8">Consent to International Transfer</a></li>
|
||||
<li><a href="#section_9">Your Choices</a></li>
|
||||
<li><a href="#section_10">Changes to This Privacy Policy</a></li>
|
||||
<li><a href="#section_11">Jurisdiction-Specific Rights</a></li>
|
||||
<li><a href="#section_12">Contact Us</a></li>
|
||||
</ol>
|
||||
<h2 id="section_1">
|
||||
1. Collection of Information
|
||||
</h2>
|
||||
<p>
|
||||
We and our third-party service providers and business partners may collect information from you directly and/or automatically when you visit the Site or use the Services. We and our third-party service providers may also collect information about you from third parties. Some of this information may be considered "personal information" or "personal data" under applicable laws (collectively, “personal information”). We consider information that identifies you as a specific, identified individual (such as your name, phone number, and email address) to be personal information. We also, where required by applicable law, consider additional types of identifying data, like IP addresses and cookie identifiers, to be personal information.
|
||||
</p>
|
||||
<p>
|
||||
We may, in accordance with applicable law, take your personal information and de-identify or pseudonymize it to make it non-personally identifiable, either by combining it with information about other individuals and/or by hashing the information or otherwise removing characteristics that make the information personally identifiable. We maintain and use de-identified or pseudonymized data without attempting to re-identify it, except where permitted by applicable law, such as to determine whether our de-identification processes satisfy legal requirements. We will treat de-identified or pseudonymized information as non-personal to the fullest extent allowed by applicable law.
|
||||
</p>
|
||||
<p>
|
||||
We may collect the following categories of personal information from or about you:
|
||||
</p>
|
||||
<p>
|
||||
<u>Identifiers and Contact Information</u>. This category includes names, addresses, telephone numbers, mobile numbers, email addresses, signatures, account names, dates of birth, bank account information, and other similar contact information and identifiers.
|
||||
</p>
|
||||
<p>
|
||||
<u>Commercial Information</u>. This category includes, without limitation, products and services purchased, obtained, or considered, or other purchasing or consuming histories or tendencies.
|
||||
</p>
|
||||
<p>
|
||||
<u>Internet or Other Electronic Network Activity Information</u>. This category includes, without limitation, browsing history, search history, or a consumer’s interactions with a website, application, or advertisement.
|
||||
</p>
|
||||
<p>
|
||||
<u>Geolocation Data</u>. This category includes, without limitation, location information collected when using our Services.
|
||||
</p>
|
||||
<p>
|
||||
<u>Sensitive Personal Information</u>. This category includes:
|
||||
</p>
|
||||
<ul>
|
||||
<li>racial or ethnic origin, citizenship or immigration status, or religious beliefs;</li>
|
||||
<li>mental or physical health diagnosis;</li>
|
||||
<li>sexual orientation; and</li>
|
||||
<li>information collected from a known child.</li>
|
||||
</ul>
|
||||
|
||||
<h3 id="section_1_1">
|
||||
1.1 Information You Provide Directly
|
||||
</h3>
|
||||
<p>
|
||||
We may ask you to provide certain personal information when you use the Services. This information may include contact information (such as your name and email), account information (such as your email address and, if you choose to log in through Google or Apple, the associated user ID and email address), transaction information (such as your billing address and mailing address), or user content you choose to upload (such as photos and task lists). Note that all payments are handled by our third-party payment providers who may collect relevant information in order to complete your transaction (such as your payment card, billing address, and phone number). Our current providers are Apple, Google, PayPal, and Stripe. We encourage you to review their respective privacy policies for additional information regarding their privacy practices.
|
||||
</p>
|
||||
<p>
|
||||
We may also ask you to provide the contact information of another individual, such as when you invite another user to the Services. By providing us this information, you represent to us that you do so with the consent of the individual to whom it relates. We will only use this information for the specific reason for which it was provided.
|
||||
</p>
|
||||
<h3 id="section_1_2">
|
||||
1.2 Information We Collect Automatically
|
||||
</h3>
|
||||
<p>
|
||||
We and third-party companies and business partners may use a variety of technologies, including, without limitation, cookies, pixels, embedded scripts, and session events (collectively, “cookies”) that automatically or passively collect certain information whenever you visit our Site, use our Services, or otherwise interact with us or our content (“<strong>Usage Information</strong>”). Usage Information may include the hardware model, browser, and operating system you are using, the URL or advertisement that referred you to the Site you are visiting or Service you are using, all of the areas within the Site that you visit or Services that you use, your time zone, non-precise location information, and mobile network (if applicable), among other information. In addition, we automatically collect your IP address or other unique identifier ("<strong>Device Identifier</strong>") for any computer, mobile phone or other device you use to access our Services. In some cases, we may directly collect location information through your device. You may be able to turn off the collection of location information through the settings on your device. Usage Information is generally non-identifying, but if HabitRPG associates it with you as a specific and identifiable person, HabitRPG treats it as personal information.
|
||||
</p>
|
||||
<h4>Types of Cookies</h4>
|
||||
<ul>
|
||||
<li><u>First and Third-Party Cookies</u>. First party cookies are generally placed on your computer or device by the website you are visiting. For example, we may use a first party cookie to improve Site security. Third-party cookies are placed on your computer or device by a source other than the website you are visiting in order to enable third-party features such as advertising, analytics, videos, or interactive content.</li>
|
||||
<li><u>Essential or Strictly Necessary Cookies</u>. These cookies are necessary for the Site to function and cannot be switched off in our systems. They are usually set in response to actions taken by you which amount to a request for Services, such as setting your privacy preferences, logging in, or filling in forms.</li>
|
||||
<li><u>Performance and Functionality Cookies</u>. Although these are non-essential cookies, they help the Site perform and function as designed. For example, performance and functionality cookies may help the Site display videos, enable chat sessions, or recognize whether you visited the Site before.</li>
|
||||
<li><u>Analytics Cookies</u>. These cookies track your usage of the Site. The information these cookies collect can be used for various purposes such as understanding how visitors use the Site, Site and content customization, or advertising and marketing.</li>
|
||||
<li><u>Advertising Cookies</u>. Advertising and marketing cookies perform functions such as helping customize your Site experience, personalizing ads based on your online activities and interests, measuring the effectiveness of ads, preventing an ad from reappearing, and serving you targeted advertisements. Information from these cookies may be disclosed to third parties or third parties may place these cookies on your computer or device. Advertising and marketing cookies may track your online activities.</li>
|
||||
</ul>
|
||||
<p>
|
||||
The Site uses essential, functional, and performance cookies to function and perform as designed; analytics cookies to understand how you use the Site, improve its functionality, and other related purposes; and advertising cookies to help us with our advertising and marketing activities. We and our third-party partners and service providers may collect and track information about your online activities over time and across different websites, applications, and devices.
|
||||
</p>
|
||||
<h4>Google Analytics</h4>
|
||||
<p>
|
||||
We use Google Analytics, a service which uses cookies to collect and analyze data about the use of the Services and report on activities and trends. This service may also collect data about the use of other websites, apps, and online services. You can <a
|
||||
href="https://policies.google.com/technologies/partner-sites"
|
||||
target="_blank"
|
||||
>learn about</a> Google's practices, and opt out of them, by downloading the <a
|
||||
href="https://tools.google.com/dlpage/gaoptout"
|
||||
target="_blank"
|
||||
>Google Analytics opt-out browser add-on</a>.
|
||||
</p>
|
||||
<h4>Controlling Cookies</h4>
|
||||
<p>
|
||||
You may control cookies, including preventing or stopping the installation and storage of cookies, through your browser settings and other tools. Most browsers will allow you to block or refuse cookies. However, you may need to manually adjust your preferences each time you visit a site. For more information, see the Help section of your browser. Please note that if you block certain cookies, some of the services and functionalities of our Site may not work.
|
||||
</p>
|
||||
<p class="important">
|
||||
IMPORTANT: BY USING THE SITE, YOU CONSENT TO THE PROCESSING OF ANY PERSONAL INFORMATION FOR THE PURPOSES AND FUNCTIONS DESCRIBED ABOVE.
|
||||
</p>
|
||||
<h4>Do Not Track</h4>
|
||||
<p>
|
||||
“Do Not Track” is a privacy preference that you can set in your Internet search browser that sends a signal to a website that you do not want the website operator to track certain browsing information about you. However, because our Site is not configured to detect Do Not Track signals from a user’s computer, we are unable to respond to Do Not Track requests.
|
||||
</p>
|
||||
<h3 id="section_1_3">
|
||||
1.3 Location Data
|
||||
</h3>
|
||||
<p>
|
||||
We do not collect your precise location. However, please note that we may still be able to collect or infer your approximate location through other information we collect, such as IP address. In addition, some mobile service providers may also provide us or our third-party service providers with information regarding the non-precise physical location of the device you use to access our Services.
|
||||
</p>
|
||||
<h3 id="section_1_4">
|
||||
1.4 Information Collected By and/or Disclosed to Third Parties
|
||||
</h3>
|
||||
<p>
|
||||
We may receive information about you from third parties. For example, you may have the opportunity to log in through or otherwise connect your Apple and Google accounts. Additionally, when you interact with us through social media, you will be choosing to share information about your interactions with HabitRPG with that social media service.
|
||||
</p>
|
||||
<p>
|
||||
The following chart sets out by category the personal information we may collect ("<strong>Category</strong>"), the purposes for which we may collect it ("<strong>A. Purposes</strong>"), the categories of third parties to which we may disclose it for business purposes ("<strong>B. Disclosed To</strong>"), and the categories of third parties to which we may sell it for monetary value or other valuable consideration or share it for cross-context behavioral advertising/targeted marketing ("<strong>C. Sold/Shared To</strong>")
|
||||
</p>
|
||||
|
||||
<button
|
||||
for="showThirdParties"
|
||||
class="btn btn-primary"
|
||||
@click="toggleThirdPartyInfo"
|
||||
>
|
||||
{{ showThirdPartyInfo ? "Hide Information from Third Parties" : "Show Information from Third Parties" }}
|
||||
</button>
|
||||
<div v-if="showThirdPartyInfo">
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th
|
||||
rowspan="3"
|
||||
class="first-column"
|
||||
>
|
||||
Category of Data
|
||||
</th>
|
||||
<th>A. Purpose for Collection;</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>B. Categories of Third Parties to which Personal Information may be Disclosed;</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>C. Categories of Third Parties to which the Personal Information may be Sold/Shared for Targeted Advertising</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td rowspan="3">
|
||||
<strong>Contact Information</strong> (such as your name, email address, mailing address)
|
||||
</td>
|
||||
<td>
|
||||
<h6>A. Purposes for Collection:</h6>
|
||||
<p>
|
||||
<strong>(1) Provide You the Service</strong> (to process and fulfill your purchases of products; enable you to use the Services and other products or services we offer, including maintaining your account information and verifying information you provide to us, such as that your email address is active and valid; send you transactional messages regarding your use of the Services, your purchases, and other account-related communications; respond to your inquiries and provide customer support or request feedback from you); <strong>(2) Advertising Activities and Marketing Messages</strong> (to customize and optimize content for you, such as to send you marketing communications if you have provided us your email address, as permitted by applicable law); <strong>(3) Administrative</strong> (to verify your identity, recognize you across the Services and your devices; contact you regarding your use of the Services, content, features, or products you use or request, and, in our discretion, changes to our policies); <strong>(4) Internal Business Purposes</strong> (to operate and improve the Services and our products, services, and marketing endeavors; market research; analyze the effectiveness of our marketing efforts through our third-party service providers; detect and troubleshoot problems; monetize our Services); <strong>(5) Security</strong> (to protect integrity of the Services); <strong>(6) Legal</strong> (to comply with the law; resolve disputes; enforce our agreements and policies; cooperate in governmental or other legal inquiries; fulfill regulatory reporting obligations; protect our or others' rights, assets, safety or security); <strong>(7) Business Transitions</strong> (in connection with a merger, acquisition, consolidation, bankruptcy, or other corporate transition, including during due diligence); <strong>(8) Other Purposes</strong> as disclosed at the time of collection, with your consent (where required by law), or as permitted by applicable law.
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<h6>B. Categories of Third Parties to which Personal Information may be Disclosed:</h6>
|
||||
<p>
|
||||
Analytics Partners (such as analytics companies); Social Media Platforms; Business Partners; Technology Systems Service Providers (such as webhosts, database hosts, cloud computing providers, software-as-a-service providers, and technology maintenance and repair vendors); Security Vendors; Communications Service Providers (such as email delivery and direct mail vendors); Customer Service Providers; Government Agencies and Law Enforcement/Where Required by Law (including third parties in connection with a court or other legal proceeding); Third Parties in connection with court or other legal actions; Third Parties in connection with business transitions; Third Parties to whom you agree or direct us to share your data; Other Service Providers we engage to provide services to us and/or you.
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<h6>C. Categories of Third Parties to which Personal Information may be Sold/Shared for Targeted Advertising:</h6>
|
||||
<p>
|
||||
Analytics Partners.
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th
|
||||
rowspan="3"
|
||||
class="first-column"
|
||||
>
|
||||
Category of Data
|
||||
</th>
|
||||
<th>A. Purpose for Collection;</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>B. Categories of Third Parties to which Personal Information may be Disclosed;</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>C. Categories of Third Parties to which the Personal Information may be Sold/Shared for Targeted Advertising</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td rowspan="3">
|
||||
<strong>User Account Information</strong> (such as account name, communication preferences, and account settings)
|
||||
</td>
|
||||
<td>
|
||||
<h6>A. Purposes for Collection:</h6>
|
||||
<p>
|
||||
<strong>(1) Provide You the Service</strong> (to process your purchases; to enable you to use the Services and other products or services we offer, including maintaining your account information and verifying information you provide to us, such as that your email address is active and valid; send you transactional messages regarding your use of the Services and other account-related communications; respond to your inquiries and provide customer support or request feedback from you); <strong>(2) Advertising Activities and Marketing Messages</strong> (to customize and optimize content for you, such as to send you in-app announcements and marketing communications if you have provided us your email address, as permitted by applicable law); <strong>(3) Personalization</strong> (to tailor the content we display to you on the Services, including content in emails we may send to you, to tell you about new products, promotions, opportunities, or other general information about HabitRPG or our products that we believe will be of interest to you); <strong>(4) Administrative</strong> (to verify your identity, recognize you across the Services and your devices; contact you regarding your use of the Services, content, features, or products you use or request, and, in our discretion, changes to our policies); <strong>(5) Internal Business Purposes</strong> (to operate and improve the Services and our products, services, and marketing endeavors; market research; analyze the effectiveness of our marketing efforts; detect and troubleshoot problems; understand how you use the Services including tracking traffic, usage, trends, and navigation patterns); <strong>(6) Security</strong> (to protect integrity of the Services; to investigate, prevent, and detect, and protect against misuse of our systems, abuse, fraud or other crime, or illegal activities or those that violate our policies; to detect and troubleshoot problems); <strong>(7) Legal</strong> (to comply with the law; resolve disputes; enforce our agreements and policies; cooperate in governmental or other legal inquiries; fulfill regulatory reporting obligations; protect our or others' rights, assets, safety or security); <strong>(8) Business Transitions</strong> (in connection with a merger, acquisition, consolidation, bankruptcy, or other corporate transition, including during due diligence); <strong>(9) Other Purposes</strong> as disclosed at the time of collection, with your consent (where required by applicable law), or as permitted by applicable law.
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<h6>B. Categories of Third Parties to which Personal Information may be Disclosed:</h6>
|
||||
<p>
|
||||
Technology Systems Service Providers (such as webhosts, database hosts, cloud computing providers, software- as-a-service providers, and technology maintenance and repair vendors); Security Vendors; Communications Service Providers; Customer Service Providers; Government Agencies and Law Enforcement/Where Required by Law (including third parties in connection with a court or other legal proceeding); Third Parties in connection with court or other legal actions; Third Parties in connection with business transitions; Third Parties to whom you agree or direct us to share your data; Other Service Providers we engage to provide services to us and/or you.
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<h6>C. Categories of Third Parties to which Personal Information may be Sold/Shared for Targeted Advertising:</h6>
|
||||
<p>
|
||||
This category of data is not sold or shared for targeted advertising.
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th
|
||||
rowspan="3"
|
||||
class="first-column"
|
||||
>
|
||||
Category of Data
|
||||
</th>
|
||||
<th>A. Purpose for Collection;</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>B. Categories of Third Parties to which Personal Information may be Disclosed;</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>C. Categories of Third Parties to which the Personal Information may be Sold/Shared for Targeted Advertising</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td rowspan="3">
|
||||
<strong>Demographic Information</strong> (such as your zip code, birth day/month)
|
||||
</td>
|
||||
<td>
|
||||
<h6>A. Purposes for Collection:</h6>
|
||||
<p>
|
||||
<strong>(1) Provide You the Service</strong> (to enable you to use the Services and other products or services we offer, including verifying information you provide to us); <strong>(2) Advertising Activities and Marketing Messages</strong> (to customize and optimize content for you); <strong>(3) Personalization</strong> (to tailor the content and advertising we display to you or others, on the Services or elsewhere, including content in emails we may send to you); <strong>(4) Administrative</strong> (to verify your identity, recognize you across the Services and your devices); <strong>(5) Internal Business Purposes</strong> (to operate and improve the Services and our products, services, and marketing endeavors; market research; analyze the effectiveness of our marketing efforts; detect and troubleshoot problems; understand how you use the Services including tracking traffic, usage, trends, and navigation patterns); <strong>(6) Security</strong> (to protect integrity of the Services; to investigate, prevent, and detect, and protect against misuse of our systems, abuse, fraud or other crime, or illegal activities or those that violate our policies; to detect and troubleshoot problems); <strong>(7) Legal</strong> (to comply with the law; resolve disputes; enforce our agreements and policies; cooperate in governmental or other legal inquiries; fulfill regulatory reporting obligations; protect our or others' rights, assets, safety or security); <strong>(8) Business Transitions</strong> (in connection with a merger, acquisition, consolidation, bankruptcy, or other corporate transition, including during due diligence); <strong>(9) Other Purposes</strong> as disclosed at the time of collection, with your consent (where required by applicable law), or as permitted by applicable law.
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<h6>B. Categories of Third Parties to which Personal Information may be Disclosed:</h6>
|
||||
<p>
|
||||
Social Media Platforms and Other Third Party Platforms; Third Party Business Partners; Order Fulfillment Vendors (such as payment processors); Technology Systems Service Providers (such as webhosts, database hosts, cloud computing providers, software-as-a-service providers, and technology maintenance and repair vendors); Security Vendors; Customer Service Providers; Government Agencies and Law Enforcement/Where Required by Law (including third parties in connection with a court or other legal proceeding); Third Parties in connection with court or other legal actions; Third Parties in connection with business transitions; Third Parties to whom you agree or direct us to share your data; Other Service Providers we engage to provide services to us and/or you.
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<h6>C. Categories of Third Parties to which Personal Information may be Sold/Shared for Targeted Advertising:</h6>
|
||||
<p>
|
||||
This category of data is not sold or shared for targeted advertising.
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th
|
||||
rowspan="3"
|
||||
class="first-column"
|
||||
>
|
||||
Category of Data
|
||||
</th>
|
||||
<th>A. Purpose for Collection;</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>B. Categories of Third Parties to which Personal Information may be Disclosed;</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>C. Categories of Third Parties to which the Personal Information may be Sold/Shared for Targeted Advertising</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td rowspan="3">
|
||||
<strong>Device ID/Interaction Information</strong> (such as IP address or other unique ID for any computer, mobile phone or other device used to access the Services, device information (model, browser, operating system version), URL or advertisement that referred you to the Services, any search terms you entered into a search engine that led you to the Services; areas within Services that you visit, time of day you visited the Services, your time zone, location information, and mobile network (if applicable) information)
|
||||
</td>
|
||||
<td>
|
||||
<h6>A. Purposes for Collection:</h6>
|
||||
<p>
|
||||
<strong>(1) Provide You the Service</strong> (to process and fulfill your purchases of products; to enable you to use the Services and other products or services we offer, including maintaining your account information and verifying information you provide to us, such as that your email address is active and valid; send you transactional messages regarding your use of the Services and other account-related communications); <strong>(2) Advertising Activities and Marketing Messages</strong> (to customize and optimize content for you, such as to send you in-app announcements and marketing communications if you have provided us your email address, as permitted by applicable law); <strong>(3) Personalization</strong> (to tailor the content and advertising we display to you or others, on the Services or elsewhere, including content in emails we may send to you, to tell you about new products, promotions, opportunities, or other general information about HabitRPG or our products that we believe will be of interest to you); <strong>(4) Administrative</strong> (to verify your identity, recognize you across the Services and your devices; contact you regarding your use of the Services, content, features, or products you use or request, and, in our discretion, changes to our policies); <strong>(5) Internal Business Purposes</strong> (to operate and improve the Services and our products, services, and marketing endeavors; market research; analyze the effectiveness of our marketing efforts; detect and troubleshoot problems; monetize our Services; understand how you use the Services including tracking traffic, usage, trends, and navigation patterns); <strong>(6) Security</strong> (to protect integrity of the Services; to investigate, prevent, and detect, and protect against misuse of our systems, abuse, fraud or other crime, or illegal activities or those that violate our policies; to detect and troubleshoot problems); <strong>(7) Legal</strong> (to comply with the law; resolve disputes; enforce our agreements and policies; cooperate in governmental or other legal inquiries; fulfill regulatory reporting obligations; protect our or others' rights, assets, safety or security); <strong>(8) Business Transitions</strong> (in connection with a merger, acquisition, consolidation, bankruptcy, or other corporate transition, including during due diligence); <strong>(9) Other Purposes</strong> as disclosed at the time of collection, with your consent (where required by law), or as permitted by applicable law.
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<h6>B. Categories of Third Parties to which Personal Information may be Disclosed:</h6>
|
||||
<p>
|
||||
Analytics Partners; Social Media Platforms and Other Third Party Platforms; Third Party Business Partners; Order Fulfillment Vendors (such as payment processors, fulfillment centers, delivery services, order tracking vendors); Technology Systems Service Providers (such as webhosts, database hosts, cloud computing providers, software-as- a-service providers, and technology maintenance and repair vendors); Security Vendors; Communications Service Providers; Customer Service Providers; Government Agencies and Law Enforcement/Where Required by Law (including third parties in connection with a court or other legal proceeding); Third Parties in connection with court or other legal actions; Third Parties in connection with business transitions; Third Parties to whom you agree or direct us to share your data; Other Service Providers we engage to provide services to us and/or you.
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<h6>C. Categories of Third Parties to which Personal Information may be Sold/Shared for Targeted Advertising:</h6>
|
||||
<p>
|
||||
Analytics Partners.
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th
|
||||
rowspan="3"
|
||||
class="first-column"
|
||||
>
|
||||
Category of Data
|
||||
</th>
|
||||
<th>A. Purpose for Collection;</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>B. Categories of Third Parties to which Personal Information may be Disclosed;</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>C. Categories of Third Parties to which the Personal Information may be Sold/Shared for Targeted Advertising</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td rowspan="3">
|
||||
<strong>Transaction Information</strong> (such as data about the products you purchase, obtained, or considered, customer service contacts)
|
||||
</td>
|
||||
<td>
|
||||
<h6>A. Purposes for Collection:</h6>
|
||||
<p>
|
||||
<strong>(1) Provide You the Service</strong> (to enable you to use the Services and other products or services we offer, including maintaining your account information and verifying information you provide to us; send you transactional messages regarding your use of the Services, your purchases, and other account-related communications; respond to your inquiries and provide customer support or request feedback from you); <strong>(2) Advertising Activities and Marketing Messages</strong> (to customize and optimize content for you, such as to send you in-app announcements and marketing communications if you have provided us your email address, as permitted by applicable law); <strong>(3) Personalization</strong> (to tailor the content we display to you on the Services, including content in emails we may send to you, to tell you about new products, promotions, opportunities, or other general information about HabitRPG or our products that we believe will be of interest to you); <strong>(4) Administrative</strong> (to verify your identity, recognize you across the Services and your devices; contact you regarding your use of the Services, content, features, or products you use or request, and, in our discretion, changes to our policies); <strong>(5) Internal Business Purposes</strong> (to operate and improve the Services and our products, services, and marketing endeavors; market research; analyze the effectiveness of our marketing efforts; detect and troubleshoot problems; monetize our Services; understand how you use the Services including tracking traffic, usage, trends, and navigation patterns); <strong>(6) Security</strong> (to protect integrity of the Services; to investigate, prevent, and detect, and protect against misuse of our systems, abuse, fraud or other crime, or illegal activities or those that violate our policies; to detect and troubleshoot problems); <strong>(7) Legal</strong> (to comply with the law; resolve disputes; enforce our agreements and policies; cooperate in governmental or other legal inquiries; fulfill regulatory reporting obligations; protect our or others' rights, assets, safety or security); <strong>(8) Business Transitions</strong> (in connection with a merger, acquisition, consolidation, bankruptcy, or other corporate transition, including during due diligence); <strong>(9) Other Purposes</strong> as disclosed at the time of collection, with your consent (where required by applicable law), or as permitted by applicable law.
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<h6>B. Categories of Third Parties to which Personal Information may be Disclosed:</h6>
|
||||
<p>
|
||||
Order Fulfillment Vendors (such as payment processors, fulfillment centers, delivery services, order tracking vendors); Technology Systems Service Providers (such as webhosts, database hosts, cloud computing providers, software-as- a-service providers, and technology maintenance and repair vendors); Security Vendors; Communications Service Providers; Customer Service Providers; Government Agencies and Law Enforcement/Where Required by Law (including third parties in connection with a court or other legal proceeding); Third Parties in connection with court or other legal actions; Third Parties in connection with business transitions; Third Parties to whom you agree or direct us to share your data; Other Service Providers we engage to provide services to us and/or you.
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<h6>C. Categories of Third Parties to which Personal Information may be Sold/Shared for Targeted Advertising:</h6>
|
||||
<p>
|
||||
This category of data is not sold or shared for targeted advertising.
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th
|
||||
rowspan="3"
|
||||
class="first-column"
|
||||
>
|
||||
Category of Data
|
||||
</th>
|
||||
<th>A. Purpose for Collection;</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>B. Categories of Third Parties to which Personal Information may be Disclosed;</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>C. Categories of Third Parties to which the Personal Information may be Sold/Shared for Targeted Advertising</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td rowspan="3">
|
||||
<strong>Information about your Interests and Preferences</strong> (such as information about the products you purchased, obtained, or considered)
|
||||
</td>
|
||||
<td>
|
||||
<h6>A. Purposes for Collection:</h6>
|
||||
<p>
|
||||
<strong>(1) Provide You the Service</strong> (to enable you to use the Services and other products or services we offer, including maintaining your account information and verifying information you provide to us; send you transactional messages regarding your use of the Services and other account-related communications; respond to your inquiries and provide customer support or request feedback from you); <strong>(2) Personalization</strong> (to tailor the content we display to you or others, on the Services or elsewhere, including content in emails we may send to you, to tell you about new products, promotions, opportunities, or other general information about HabitRPG or our products that we believe will be of interest to you); <strong>(3) Internal Business Purposes</strong> (to operate and improve the Services and our products, services, and marketing endeavors; market research; analyze the effectiveness of our marketing efforts; detect and troubleshoot problems; monetize our Services; understand how you use the Services including tracking traffic, usage, trends, and navigation patterns); <strong>(4) Security</strong> (to protect integrity of the Services; to investigate, prevent, and detect, and protect against misuse of our systems, abuse, fraud or other crime, or illegal activities or those that violate our policies; to detect and troubleshoot problems); <strong>(5) Legal</strong> (to comply with the law; resolve disputes; enforce our agreements and policies; cooperate in governmental or other legal inquiries; fulfill regulatory reporting obligations; protect our or others' rights, assets, safety or security); <strong>(6) Business Transitions</strong> (in connection with a merger, acquisition, consolidation, bankruptcy, or other corporate transition, including during due diligence); <strong>(7) Other Purposes</strong> as disclosed at the time of collection, with your consent (where required by applicable law), or as permitted by applicable law.
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<h6>B. Categories of Third Parties to which Personal Information may be Disclosed:</h6>
|
||||
<p>
|
||||
Social Media Platforms and Other Third Party Platforms; Third Party Business Partners; Technology Systems Service Providers (such as webhosts, database hosts, cloud computing providers, software-as-a-service providers, and technology maintenance and repair vendors); Security Vendors; Communications Service Providers; Customer Service Providers; Government Agencies and Law Enforcement/Where Required by Law (including third parties in connection with a court or other legal proceeding); Third Parties in connection with court or other legal actions; Third Parties in connection with business transitions; Third Parties to whom you agree or direct us to share your data; Other Service Providers we engage to provide services to us and/or you.
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<h6>C. Categories of Third Parties to which Personal Information may be Sold/Shared for Targeted Advertising:</h6>
|
||||
<p>
|
||||
This category of data is not sold or shared for targeted advertising.
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th
|
||||
rowspan="3"
|
||||
class="first-column"
|
||||
>
|
||||
Category of Data
|
||||
</th>
|
||||
<th>A. Purpose for Collection;</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>B. Categories of Third Parties to which Personal Information may be Disclosed;</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>C. Categories of Third Parties to which the Personal Information may be Sold/Shared for Targeted Advertising</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td rowspan="3">
|
||||
<strong>Photo/Video Data, and Other User Generated Content</strong> (such as tasks, profile, chat messages)
|
||||
</td>
|
||||
<td>
|
||||
<h6>A. Purposes for Collection:</h6>
|
||||
<p>
|
||||
<strong>(1) Provide You the Service</strong> (to enable you to use the Services and other products or services we offer, including maintaining your account information and permitting you to create to do lists and upload related content; send you transactional messages regarding your use of the Services and other account-related communications; respond to your inquiries and provide customer support or request feedback from you); <strong>(2) Administrative</strong> (to verify your identity, recognize you across the Services and your devices; contact you regarding your use of the Services, content, features, or products you use or request, and, in our discretion, changes to our policies); <strong>(3) Internal Business Purposes</strong> (to operate and improve the Services and our products, services, and marketing endeavors; market research; detect and troubleshoot problems; monetize our Services; understand how you use the Services including tracking traffic, usage, trends, and navigation patterns); <strong>(4) Security</strong> (to protect integrity of the Services; to investigate, prevent, and detect, and protect against misuse of our systems, abuse, fraud or other crime, or illegal activities or those that violate our policies; to detect and troubleshoot problems); <strong>(5) Legal</strong> (to comply with the law; resolve disputes; enforce our agreements and policies; cooperate in governmental or other legal inquiries; fulfill regulatory reporting obligations; protect our or others' rights, assets, safety or security); <strong>(6) Business Transitions</strong> (in connection with a merger, acquisition, consolidation, bankruptcy, or other corporate transition, including during due diligence); <strong>(7) Other Purposes</strong> as disclosed at the time of collection, with your consent (where required by applicable law), or as permitted by applicable law.
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<h6>B. Categories of Third Parties to which Personal Information may be Disclosed:</h6>
|
||||
<p>
|
||||
Third Party Business Partners; Technology Systems Service Providers (such as webhosts, database hosts, cloud computing providers, software-as-a-service providers, and technology maintenance and repair vendors); Security Vendors; Communications Service Providers; Customer Service Providers; Government Agencies and Law Enforcement/Where Required by Law (including third parties in connection with a court or other legal proceeding); Third Parties in connection with court or other legal actions; Third Parties in connection with business transitions; Third Parties to whom you agree or direct us to share your data; Other Service Providers we engage to provide services to us and/or you.
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<h6>C. Categories of Third Parties to which Personal Information may be Sold/Shared for Targeted Advertising:</h6>
|
||||
<p>
|
||||
This category of data is not sold or shared for targeted advertising.
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<h2 id="section_2">
|
||||
2. Purpose and Use of Information We Collect
|
||||
</h2>
|
||||
<p>
|
||||
We may use non-personal information for any purpose, including for research and marketing purposes. We also use information that we collect, including personal information and Usage Information, as disclosed in this Privacy Policy and as follows:
|
||||
</p>
|
||||
<ul>
|
||||
<li>to provide the Services to you and allow you to use the features we offer;</li>
|
||||
<li>to verify your identity and to otherwise manage your user account;</li>
|
||||
<li>to tailor and target content, recommendations, and offers we display to you via the Services;</li>
|
||||
<li>to send you communications with information about our products and Services;</li>
|
||||
<li>to fulfill your order, send you an order confirmation, process your payment, and communicate with you about your order;</li>
|
||||
<li>to respond to your inquiries, customer service questions, feedback, or requests;</li>
|
||||
<li>to provide you with technical support;</li>
|
||||
<li>to improve our Services and for legal, regulatory, and internal business purposes; and</li>
|
||||
<li>to fulfill any other purpose consistent with this Privacy Policy.</li>
|
||||
</ul>
|
||||
<p>
|
||||
We may also use your personal information for any other purpose disclosed to you at the time of collection, with your consent (where required by applicable law), or permitted by applicable law.
|
||||
</p>
|
||||
|
||||
<h2 id="section_3">
|
||||
3. Service Providers
|
||||
</h2>
|
||||
<p>
|
||||
Our service providers may collect information on our behalf and at our direction, in order to provide Services on our behalf to help with our business activities.
|
||||
</p>
|
||||
<p>
|
||||
These companies are authorized to use your personal information only as necessary to provide these Services to us and for other purposes permitted by applicable law.
|
||||
</p>
|
||||
<p>
|
||||
Our Service Providers include, without limitation:
|
||||
</p>
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<th>
|
||||
Service Provider Name
|
||||
</th>
|
||||
<th>
|
||||
Product(s)
|
||||
</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Google Cloud</td>
|
||||
<td>cloud computing; storage</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>MongoDB</td>
|
||||
<td>database</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Heroku</td>
|
||||
<td>cloud-based testing</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Amazon Web Services</td>
|
||||
<td>content storage</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Hetzner</td>
|
||||
<td>translations and push notifications</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Stripe</td>
|
||||
<td>payment processing</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>PayPal</td>
|
||||
<td>payment processing</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Amazon Payments</td>
|
||||
<td>payment processing</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Apple App Store</td>
|
||||
<td>app host</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Google Play Store</td>
|
||||
<td>app host</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Mailchimp</td>
|
||||
<td>email marketing</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Gmail</td>
|
||||
<td>internal communications</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Redislabs</td>
|
||||
<td>rate limiting</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Loggly</td>
|
||||
<td>log management and analytics</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Slack</td>
|
||||
<td>internal communications</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Amplitude</td>
|
||||
<td>analytics</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h2 id="section_4">
|
||||
4. Sweepstakes, Contents and Promotions
|
||||
</h2>
|
||||
<p>
|
||||
We may offer sweepstakes, contests, or other promotions (collectively, "<strong>Promotion(s)</strong>") that may require registration. By participating in a Promotion, you agree to the provisions, conditions, or official rules that govern the Promotion, which may contain specific requirements of you (including, except where prohibited by law, allowing the sponsor(s) of the Promotion to use your name, voice, likeness, or other indicia of persona in advertising or marketing materials). If you choose to enter a Promotion, personal information may be disclosed to co-promotion partners, third parties or the public in connection with the administration of such Promotion, including in connection with winner selection, prize fulfillment, as required by law, or as permitted by the Promotion's terms or official rules.
|
||||
</p>
|
||||
|
||||
<h2 id="section_5">
|
||||
5. Security
|
||||
</h2>
|
||||
<p>
|
||||
HabitRPG maintains commercially reasonable administrative, physical, and technical safeguards that are designed to secure your personal information; however, no data transmission over the Internet, wireless transmission, or electronic storage of data can be guaranteed to be 100% secure. HabitRPG cannot ensure or warrant the security of any data we collect. You use the Services and provide us your data at your own risk.
|
||||
</p>
|
||||
|
||||
<h2 id="section_6">
|
||||
6. Data Retention
|
||||
</h2>
|
||||
<p>
|
||||
We retain your personal information for as long as necessary to provide you Services and in accordance with our data retention schedule. We may retain your personal information for longer if it is necessary to comply with our legal or reporting obligations, resolve disputes, enforce contracts, or address other legitimate business needs, or as permitted or required by applicable law. We may also retain your personal information in a deidentified or aggregated form so that it can no longer be associated with you. To determine the appropriate retention period for your personal information, we consider various factors, such as the amount, nature, and sensitivity of your information; the potential risk of unauthorized access, use, or disclosure; the purposes for which we collect or process your personal information; and applicable legal requirements.
|
||||
</p>
|
||||
|
||||
<h2 id="section_7">
|
||||
7. General Audience Services
|
||||
</h2>
|
||||
<p>
|
||||
The Service are intended for users 13 years or older; you are not permitted to access or use the Service if you are younger than 13. We do not knowingly collect personal information from children under the age of 13 through the Service. We encourage parents and legal guardians to monitor their children's Internet usage and to help enforce our Privacy Policy by instructing their children to never provide personal information without their permission. If you have reason to believe that a child under the age of 13 has provided personal information to us, please contact us at <a href="mailto:privacy@habitica.com">privacy@habitica.com</a>, and we will delete that information from our databases.
|
||||
</p>
|
||||
|
||||
<h2 id="section_8">
|
||||
8. Consent to International Transfer
|
||||
</h2>
|
||||
<p>
|
||||
HabitRPG is based in the United States. Please be aware that information we collect will be transferred to and processed in the United States and other countries. HabitRPG makes no representation that this Privacy Policy or the practices described in it comply with the laws of any other jurisdiction. By using the Services, or providing us with any information, you fully understand and unambiguously consent to this transfer, processing, and storage of your information in the United States and other jurisdictions for which the privacy laws may not be as comprehensive as those in the country where you reside and/or are a citizen. As a result, this information may be subject to access requests from governments, courts, or law enforcement in the United States and other countries according to laws in those jurisdictions.
|
||||
</p>
|
||||
|
||||
<h2 id="section_9">
|
||||
9. Your Choices
|
||||
</h2>
|
||||
<p>
|
||||
<em>Edit Your Information</em>: On the website, you can update the information in your user profile at any time by going to the user icon in the upper right, selecting the "Profile" option, then clicking "Edit Profile". You can update your username and email address by going to the user icon in the upper right and selecting the "Settings" option. If you are using the mobile app, you can update your user profile, username, and email by tapping the Settings gear in the menu and then selecting the "My Account" option.
|
||||
</p>
|
||||
<p>
|
||||
<em>Reset Your Account</em>: You can fully delete or reset your account by selecting the user icon in the upper right, selecting the "Settings", and then looking under "General Settings". You can fully delete or reset your account on the mobile apps by tapping the Settings gear in the menu and then selecting the "My Account" submenu. Please note that in order to fully delete all data associated with your account, you will need to email us at <a href="mailto:privacy@habitica.com">privacy@habitica.com</a>. Note that we may be required to retain certain data about you to comply with applicable laws.
|
||||
</p>
|
||||
<p>
|
||||
<em>Newsletter</em>: You may also sign-up to receive our email newsletter. If you would like to discontinue receiving this information, you may update your email preferences by using the "Unsubscribe" link found in emails we send to you, or by contacting us. Please note that we reserve the right to send you certain communications relating to your account or use of the Services, such as administrative and services announcements. These transactional account messages may be unaffected if you choose to opt out from marketing emails.
|
||||
</p>
|
||||
<p>
|
||||
<em>Push Notifications</em>: With your consent, we may send promotional and non-promotional push notifications or alerts to your mobile device. You can elect to stop receiving those messages by changing the notification settings in the app or on your mobile device.
|
||||
</p>
|
||||
<p>
|
||||
<em>Other Privacy Rights</em>: Certain jurisdictions provide additional rights. Please see the "<a href="#section_11">Jurisdiction-Specific Rights</a>" section below for more information.
|
||||
</p>
|
||||
|
||||
<h2 id="section_10">
|
||||
10. Changes to This Privacy Policy
|
||||
</h2>
|
||||
<p>
|
||||
To the extent permitted by applicable law, we reserve the right to change or modify this Privacy Policy at our discretion at any time. We will notify you of any material changes by posting the changed or modified Privacy Policy on our Services. We may also provide notice to you in other ways, such as through contact information you have provided. Any changes will be effective immediately upon the posting of the revised Privacy Policy unless otherwise specified. Your continued use of the Services after the effective date of the revised Privacy Policy (or such other act as specified in the revised Privacy Policy) will, to the fullest extent permitted by applicable law, constitute your consent to those changes. However, we will provide notice and obtain your consent (opt-in or opt-out) if required by law. We encourage you to regularly review this Privacy Policy for the latest information on our privacy practices.
|
||||
</p>
|
||||
|
||||
<h2 id="section_11">
|
||||
11. Jurisdiction-Specific Rights
|
||||
</h2>
|
||||
<p><strong><u>California, Nebraska, and Texas Residents</u></strong></p>
|
||||
<p>
|
||||
This section applies only to California, Nebraska, and Texas residents. It supplements and amends the information contained in the Policy with respect to such individuals. The other provisions of the Policy continue to apply, except as modified in this section.
|
||||
</p>
|
||||
<p>
|
||||
<em>Shine the Light [California residents only]</em>. California Civil Code Section 1798.83 permits you to request information regarding the disclosure of your personal information by us to third parties for the third parties’ direct marketing purposes. Such requests must be submitted to us in accordance with the instructions in the Contact Us section of this Policy. Please mention when contacting us that you are making a “California Shine the Light” inquiry. Within 30 days of receiving such a request, we will provide a list of the categories of personal information disclosed to third parties for third-party direct marketing purposes during the immediately preceding calendar year, along with the names and addresses of these third parties. This request may be made no more than once per calendar year. We reserve our right not to respond to requests submitted other than in accordance with the instructions specified in this paragraph.
|
||||
</p>
|
||||
<p>
|
||||
<em>Eraser Law [California residents only]</em>. If you are a California resident under the age of 18, and a registered user of any site where this policy is posted, California law permits you to request and obtain removal of content or information you have publicly posted. You may submit your request using the contact information at the end of this Policy. Please be aware that such request does not ensure complete or comprehensive removal of the content or information you have posted and that there may be circumstances in which the law does not require or allow removal even if requested.
|
||||
</p>
|
||||
<p>
|
||||
<u><em>Your Rights</em></u>
|
||||
</p>
|
||||
<p>
|
||||
You may have certain rights related to your personal information, subject to certain exceptions (including, as applicable and without limitation, safety, security, and protecting the rights of other users). Specifically:
|
||||
</p>
|
||||
<p>
|
||||
<em>Right to Confirm and Access</em>. You may have the right to request that we confirm whether we process your personal information and, if we do, that we grant you access to that information. If you previously provided us your personal information, and that information is available in a digital format, you may have the right to obtain a copy of the information in a portable and, to the extent technically feasible, readily usable format that allows you to transmit the information to another person without hindrance.
|
||||
</p>
|
||||
<p>
|
||||
<em>Right to Delete</em>. You may have the right to request that we delete your personal information from our records, subject to certain exceptions.
|
||||
</p>
|
||||
<p>
|
||||
<em>Right to Correct</em>. You may have the right, subject to certain limitations, to request that we correct any inaccurate personal information we maintain about you.
|
||||
</p>
|
||||
<p>
|
||||
<em>Opt - Out of the “ Sale” of Personal Information or Use of Such Information for “Targeted Advertising” or “Profiling”</em>. We engage in common marketing and advertising practices to provide more relevant content and ads to users of our Site and Services. Certain of these practices may involve the “selling” of personal information, or the use of such information for “targeted advertising” or “profiling,” as those terms are defined in the Texas Data Privacy and Security Act (“TDPSA”) and the Nebraska Data Privacy Act. We do not sell personal information under the more commonly understood meaning of that word—i.e., providing personal information to third parties in exchange for money. Nor do we have actual knowledge of selling personal information of minors under the age of 16. To opt-out of the selling of your personal information, or use of that information for targeted advertising or profiling, please submit a request to <a href="mailto:privacy@habitica.com">privacy@habitica.com</a>. Note: We also treat Global Privacy Control browser signals as opt-out of sale/disclosure for targeted advertising or profiling requests. To opt-out via the Global Privacy Control, please follow the instructions available <a
|
||||
href="https://globalprivacycontrol.org/"
|
||||
target="_blank"
|
||||
>here</a>.
|
||||
</p>
|
||||
<p>
|
||||
<em>Right to Consent to Use or Disclosure of Sensitive Personal Information</em>. Where required by applicable law, we will process your sensitive personal information only with your consent.
|
||||
</p>
|
||||
<p>
|
||||
<em>Right Against Discrimination</em>. You have the right not to be discriminated against for exercising any of the rights described in this section. For example, we generally will not provide you a different level or quality of goods or services if you exercise these rights.
|
||||
</p>
|
||||
<p>
|
||||
<em>Submitting Data Subject Rights Requests</em>. To submit a data subject rights request, please email us at <a href="mailto:privacy@habitica.com">privacy@habitica.com</a>. We reserve the right to only respond to verifiable requests. To verify your identity, we may ask you to verify personal information we already have on file for you. If we cannot verify your identity from the information we have on file, we may request additional information from you, which we will only use to verify your identity, and for security or fraud-prevention purposes. You will need to describe your request with sufficient detail to allow us to review, understand, assess, and respond to it, and we may not be able to respond to your request, or provide you with personal information, if we cannot verify your identity or authority to make the request and confirm the personal information relates to you. You may authorize another person to act on your behalf with respect to your rights under this section. We reserve the right to deny requests from persons claiming to be authorized agents that do not submit sufficient proof of their authorization.
|
||||
</p>
|
||||
<p>
|
||||
<em>Our Response</em> . You may have the right, subject to applicable law, to submit up to two (2) requests per year free of charge. We reserve the right to charge a fee to process or respond to your request if it is excessive, repetitive, or manifestly unfounded. If we determine that a request warrants a fee, we will attempt to notify you as to why we made that decision and provide a cost estimate before completing your request. We will endeavor to respond to verifiable requests within forty-five (45) calendar days of receipt, but we may require an extension of up to forty-five (45) additional calendar days to respond and we will notify you of the need for the extension.
|
||||
</p>
|
||||
<p>
|
||||
If you have an account with us, we will deliver our written response to that account. If you do not have an account with us, we will deliver our written response by email. In the event we deny your request, in whole or in part, we will explain the bases for that denial.
|
||||
</p>
|
||||
<p>
|
||||
<em>Appeal of Our Response</em>. In the event you believe we have erroneously denied your request, in full or in part, you may, within 60 days of receipt of that denial, submit an appeal to <a href="mailto:privacy@habitica.com">privacy@habitica.com</a>. In your appeal submission, please explain why you believe our decision to deny your request was incorrect and please provide any additional information you believe we should consider in connection with your appeal. Within 60 days of receipt of your appeal, we will advise you in writing of any action we have taken, or refrained from taking, in response to your appeal, along with an explanation of why we have taken, or refrained from taking, such action.
|
||||
</p>
|
||||
|
||||
<p><strong><u>Nevada Residents</u></strong></p>
|
||||
<p>
|
||||
Nevada residents may opt out of the sale of certain “covered information” collected by operators of websites or online services. We currently do not sell covered information, as “sale” is defined by such law, and do not have plans to do so. In accordance with Nevada law, you may submit to us a verified request instructing us not to sell your covered information by sending an email to <a href='mailto:privacy@habitica.com'>privacy@habitica.com</a>.
|
||||
</p>
|
||||
<p><strong><u>Notice to United Kingdom/European/Switzerland Residents.</u></strong></p>
|
||||
<p>
|
||||
If you are a resident of the United Kingdom (UK), European Economic Area (EEA), or of Switzerland, the following information applies.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Purposes of processing and legal basis for processing:</strong> As explained above, we process personal information in various ways depending upon your use of our Services. We process personal information on the following legal bases: (1) with your consent; (2) as necessary to perform our agreement to provide the Services; (3) compliance with our legal obligations; and (4) as necessary for our legitimate interests in providing the Service where those interests do not override your fundamental rights and freedoms related to data privacy such as for:
|
||||
<ul>
|
||||
<li>preventing fraud;</li>
|
||||
<li>ensuring network and information security, including preventing unauthorized access to our computer and electronic communication systems and preventing malicious software distribution;</li>
|
||||
<li>supporting internal administration;</li>
|
||||
<li>improving and developing the Services; and</li>
|
||||
<li>conducting data analytics analyses to review and better understand consumer interaction.</li>
|
||||
</ul>
|
||||
</p>
|
||||
<p>
|
||||
<strong>Right to lodge a complaint:</strong> Users that reside in the UK, EEA, or Switzerland have the right to seek information and assistance or lodge a complaint about our data collection and processing actions with the supervisory authority where they reside. Contact details for data protection authorities are available here. UK: <a
|
||||
href="https://ico.org.uk/"
|
||||
target="_blank"
|
||||
>https://ico.org.uk/</a> EEA: <a
|
||||
href="https://edpb.europa.eu/about-edpb/board/members_en"
|
||||
target="_blank"
|
||||
>https://edpb.europa.eu/about-edpb/board/members_en</a> Switzerland: <a
|
||||
href="https//www.edoeb.admin.ch/edoeb/en/home/deredoeb/kontakt.html"
|
||||
target="_blank"
|
||||
>https//www.edoeb.admin.ch/edoeb/en/home/deredoeb/kontakt.html</a>.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Transfers:</strong> Personal information we collect may be transferred to, and stored and processed in, the United States or any other country in which we or our affiliates or subcontractors maintain facilities. Transfers of personal data to a third country without an adequacy decision (as that term is understood pursuant to Article 45 of GDPR) are required to be subject to appropriate safeguards such as standard contractual clauses. In certain cases, we rely on your consent to facilitate transfer, processing, and storage of your data in the United States and other jurisdictions, where laws regarding processing of personal information may be less stringent than the laws in the EEA, UK, and Switzerland.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Withdraw consent:</strong> If we have collected personal information with your consent, you have the right to withdraw that consent at any time.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Access:</strong> You have the right to request access to personal information we collected about you and information about its sources, purposes, and sharing.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Correction:</strong> You have the right to request that we correct the personal information we hold about you if it is inaccurate or incomplete.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Erasure:</strong> You have the right to request that we erase data we have collected from you. Please note that we may have a reason to deny your deletion request or delete data in a more limited way than you anticipated, e.g., because of a legal obligation to retain it.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Portability:</strong> You have the right, in certain circumstances, to request that we provide your personal information to you in a format that can be transferred to another entity.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Restrict Processing:</strong> You have the right, in certain circumstances, to request that we limit our processing of your personal information if you are (1) contesting the accuracy of your personal information, (2) asserting that our processing is unlawful; (3) asserting that we no longer need to keep the information for reasons related to the establishment, exercise, or defense of legal claims, or you object to our processing. You have the right, in certain circumstances, to request that we limit our processing of your personal information if you are contesting the accuracy of your personal information; asserting that our processing is unlawful; asserting that we no longer need to keep the information for reasons related to the establishment, exercise, or defense of legal claims, or you object to our processing
|
||||
</p>
|
||||
<p>
|
||||
<strong>Objection:</strong> You have the right to object to our processing if we are processing your personal information based on legitimate interests, using your personal information for direct marketing (including profiling), or processing your personal information for purposes of scientific or historical research and statistics.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Verification Procedures:</strong> We must verify your identity for everyone's protection, so we may require you to provide us with verification information prior to accessing any records containing personal information about you. We do this by asking you to provide personal identifiers we can match against information we may have collected from you previously and confirm your request using the email stated in the request.
|
||||
</p>
|
||||
<p>
|
||||
We will use the information you provide for verification only for the purpose of verification. We may have a reason under the law why we do not have to respond to your request or respond to it in a more limited way than you anticipated. If we do, we will explain that to you in our response.
|
||||
</p>
|
||||
|
||||
<h2 id="section_12">
|
||||
12. Contact Us
|
||||
</h2>
|
||||
<p>
|
||||
If you have any questions or concerns about this Privacy Policy, please contact us at <a href="mailto:privacy@habitica.com">privacy@habitica.com</a> with “Privacy Policy” in the subject line. You may also write to us at:
|
||||
</p>
|
||||
<address class="ml-4">
|
||||
HabitRPG, Inc. c/o Workbar<br>
|
||||
120 Washington Street Suite 202<br>
|
||||
Salem, MA 01970-6396
|
||||
</address>
|
||||
</div>
|
||||
<!-- eslint-enable max-len -->
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '@/assets/scss/colors.scss';
|
||||
h4 {
|
||||
text-decoration: underline;
|
||||
font-style: italic;
|
||||
font-weight: normal;
|
||||
color: $gray-50;
|
||||
}
|
||||
|
||||
.important {
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
table {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.first-column {
|
||||
width: 25%
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'PrivacyReview',
|
||||
data () {
|
||||
return {
|
||||
showThirdPartyInfo: true,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
toggleThirdPartyInfo () {
|
||||
this.showThirdPartyInfo = !this.showThirdPartyInfo;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -1,244 +0,0 @@
|
||||
<template>
|
||||
<!-- eslint-disable max-len -->
|
||||
<div class="container-fluid">
|
||||
<h1>Terms of Service</h1>
|
||||
<p class="strong pagemeta">
|
||||
Last Updated September 1, 2025.
|
||||
</p>
|
||||
<p>Thanks for choosing Habitica!</p>
|
||||
<p>
|
||||
Our Service is provided by HabitRPG, Inc. ("HabitRPG"). By accepting these Terms of Service and our Privacy Policy located at: <a
|
||||
href="https://habitica.com/static/privacy"
|
||||
target="_blank"
|
||||
>https://habitica.com/static/privacy</a> (collectively, the "Agreement"), using our website, Habitica.com, or our other features or services (collectively, “the Services”, or otherwise manifesting your assent to the Agreement, you acknowledge that you have read, understood, and agree to be legally bound by the Agreement. If you do not agree to (or cannot comply with) the Agreement, you are not permitted to access or use the Service. By accepting or agreeing to this Agreement on behalf of a company or other legal entity, you represent and warrant that you have the authority to bind that company or other legal entity to the Agreement and, in such event, "you" and "your" will refer and apply to that company or other legal entity. You further represent and warrant that your assent to this Agreement constitutes an electronic signature as defined by the Electronic Signatures in Global and National Commerce Act (“E-Sign”) and the Uniform Electronic Transactions Act (“UETA”) and that you have formed, executed, entered into, and accepted the terms of and otherwise authenticated the Agreement and acknowledged and agreed that the Agreement is an electronic record for purposes of E- Sign, UETA, and the Uniform Computer Information Transactions Act and, as such, is completely valid, has legal effect, is enforceable, and is binding on, and non- refutable by, you and/or any entity on whose behalf you are acting.
|
||||
</p>
|
||||
<p class="strong">
|
||||
THE SECTIONS BELOW TITLED "BINDING ARBITRATION' AND "CLASS ACTION WAIVER" CONTAIN A BINDING ARBITRATION AGREEMENT AND CLASS ACTION WAIVER. THEY AFFECT YOUR LEGAL RIGHTS. PLEASE READ THEM CAREFULLY.
|
||||
</p>
|
||||
<h2>Changes to the Terms of Service</h2>
|
||||
<p>
|
||||
These Terms of Service are effective as of the last updated date stated at the top of this page. We may change these Terms of Service from time to time with or without notice to you. By accessing the Service after we make any such changes to this Terms of Service, you are deemed to have accepted such changes. Please be aware that, to the extent permitted by applicable law, our use of the information collected is governed by the Terms of Service in effect at the time we collect the information. Please refer back to this Terms of Service on a regular basis.
|
||||
</p>
|
||||
<h2>Intellectual Property</h2>
|
||||
<p>
|
||||
Our Services allow you to upload, store, send, download, or receive content, including but not limited to information, text, graphics, artwork, or other material ("Content"). You retain ownership of any intellectual property rights that you had in your Content prior to using it in connection with the Service. You hereby grant HabitRPG a worldwide, perpetual, irrevocable, sublicenseable, transferable, assignable, non-exclusive, and royalty-free right and license to use, reproduce, distribute, adapt, modify, translate, create derivative works of, publicly perform, publicly display, digitally perform, make, have made, sell, offer for sale, and import your Content, including all intellectual property rights therein. You represent, warrant, and agree that your Content does not and will not violate any third-party intellectual property, privacy, or other rights, and that you have all right, title and interest in and to your Content required to grant us the license above. We reserve the right at all times, but have no obligation, to delete or refuse to use or distribute any Content on or through the Service, including your Content.
|
||||
</p>
|
||||
<p>
|
||||
HabitRPG appreciates receiving your ideas, comments, suggestions and requests regarding the Service ("Unsolicited Ideas"). By submitting your Unsolicited Ideas (in any form or medium), you are transferring all your right, title and interest therein exclusively to HabitRPG. As the owner of Unsolicited Ideas, we have unrestricted rights to use, disclose and process the Unsolicited Ideas for any purpose whatsoever without any compensation to you. You also give up any claim that any use, disclosure, and/or processing by us or our licensees of your Unsolicited Ideas violates any of your rights, including moral rights, privacy rights, rights to publicity, proprietary or other rights, and rights to credit for the material or ideas set forth therein.
|
||||
</p>
|
||||
<p>
|
||||
<strong>DMCA Notice</strong>. We respect the intellectual property rights of third parties. We respond to notices of alleged copyright infringement according to the Digital Millennium Copyright Act (“DMCA”) at 17 U.S.C. § 512 et seq. Regardless of whether or not the we believe that we are liable for any copyright infringement for which we are provided notice, our response may include removing or disabling access to material claimed to be the subject of infringing activity and/or terminating an individual’s access to the Service, in our sole discretion and operating within the parameters of the DMCA.
|
||||
</p>
|
||||
<p>
|
||||
If you believe that your work has been copied in a manner that constitutes copyright infringement, please contact us at <a href="mailto:admin@habitica.com">admin@habitica.com</a> with the following information:
|
||||
</p>
|
||||
<ul>
|
||||
<li>Your name, address, telephone number, and email address;</li>
|
||||
<li>A description of the copyrighted work that you claim has been infringed;</li>
|
||||
<li>A description of the allegedly infringing material and where it is located on the Service;</li>
|
||||
<li>A statement by you that you have a good faith belief that the disputed use is not authorized by the copyright owner, its agents, or the law;</li>
|
||||
<li>A statement by you under penalty of perjury that your notice is accurate, that you are the copyright owner, or that the copyright holder has authorized you to act on its behalf; and</li>
|
||||
<li>Your written or electronic signature attesting to the above.</li>
|
||||
</ul>
|
||||
<p>
|
||||
If your content has been removed from the Service in response to our receipt of a DMCA Notification as outlined above, and you believe the removal was inappropriate, you may submit a DMCA Counter-Notification by contacting us using the information above with the following information:
|
||||
</p>
|
||||
<ul>
|
||||
<li>Your name, address, telephone number, and email address;</li>
|
||||
<li>A statement that you consent to the jurisdiction of Federal District Court for the judicial district in which your address is located, or if your address is outside of the United States, for any judicial district for which jurisdiction for us would be appropriate, and that you will accept service of process from the person who submitted the DMCA notification or an agent of such person;</li>
|
||||
<li>A description of the material that has been removed or to which access has been disabled and the location at which the material appeared before it was removed or access to it was disabled;</li>
|
||||
<li>A statement by you under penalty of perjury that you have a good faith belief that the material was removed or disabled as a result of mistake or misidentification of the material to be removed or disabled; and</li>
|
||||
<li>Your written or electronic signature attesting to the above.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Software in our Service</h2>
|
||||
<p>
|
||||
When the Service requires or includes downloadable software ("Software"), it may update automatically on your device once a new version or features become available to you. Some platforms may let you adjust your automatic update settings.
|
||||
</p>
|
||||
<p>
|
||||
HabitRPG hereby grants you a non-commercial, worldwide, royalty-free, non-assignable and non-exclusive license to use the Software provided by HabitRPG as part of the Service. You may not copy, modify, distribute, sell, or lease any part of our Service or included Software unless you are explicitly allowed to do so by the GPL-3.0 license, or you have our written permission to do so with respect to those parts not covered by the open-source license.
|
||||
</p>
|
||||
<p>
|
||||
Third party applications may use one of the permitted logos and signifiers in order to represent their compatibility with the Service, but may not claim formal association with and/or impersonate HabitRPG or our staff without prior written consent. Third Party applications, the companies that own or provide them, and their employees and agents, are not authorized to make any promises or representations on our behalf, or change the terms of this Agreement.
|
||||
</p>
|
||||
<p>
|
||||
We allow for personal, non-commercial uses like fanart under Commercial Commons License CC-NC-SA 3.0 terms.
|
||||
</p>
|
||||
<p>
|
||||
Outside the above explicitly allowed use cases, you may not use our trademarks, service marks, trade names, logos, domain names, taglines, or trade dress without a signed written contract with us granting you a license to do so.
|
||||
</p>
|
||||
|
||||
<h2>Modifying and Termination of Service</h2>
|
||||
<p>
|
||||
HabitRPG reserves the right, in its sole discretion, to add, modify, or remove functionalities or features from the Service, and improve, change and/or update the Service. We may also suspend or terminate the Service at any time, with or without notice to you.
|
||||
</p>
|
||||
<p>
|
||||
You can choose to stop using our Service at any time. We may suspend or cease providing the Service to you at any time, including if we determine in our sole discretion, that:
|
||||
</p>
|
||||
<ul>
|
||||
<li>You have violated any part of this Agreement, the Privacy Policy, or the Community Guidelines;</li>
|
||||
<li>We have stopped offering the Service in your region; or</li>
|
||||
<li>Doing so would be in the best interests of our community, the Service, or the rights of a third party.</li>
|
||||
</ul>
|
||||
<p>
|
||||
If your account is terminated, you will no longer have access to it, including to any of the associated data or Content. You will not be entitled to any refunds and we will have no liability to you. We also reserve the right to terminate any other accounts you may have created, as well as access to any other HabitRPG Service (also without refunds or liability to you).
|
||||
You understand and agree that using the Service comes with the risk that your account may be terminated or suspended at our discretion and at any time. Please keep this risk in mind and comport yourself appropriately.
|
||||
</p>
|
||||
|
||||
<h2>API</h2>
|
||||
<p>
|
||||
You may access your Service data via the Application Program Interface ("API"). By using API you are automatically bound by the Agreement.
|
||||
</p>
|
||||
|
||||
<h2>Links to Third-Party Sites</h2>
|
||||
<p>
|
||||
The Service may contain links to other web sites (“Linked Sites”). The Linked Sites are not under our control and we are not responsible for the contents of any Linked Site, including, without limitation, any link contained in a Linked Site, or any changes or updates to a Linked Site.
|
||||
</p>
|
||||
<p>
|
||||
By providing these links, we do not endorse, sponsor, or recommend such sites or the materials disseminated by or services provided by them, and are not responsible for the materials, services, or other situations at or related to or from any other site. We are not responsible for webcasting or any other form of transmission received from any Linked Site. We are providing these links to you only as a convenience, and the inclusion of any link does not imply endorsement by us of the Linked Site or any association with its operators. We reserve the right to disable links from any third-party sites to the Service.
|
||||
</p>
|
||||
<p>
|
||||
Please exercise discretion while using the Service. You should be aware that when you are using the Service, you could be directed to other sites that are beyond our control. There are links to other sites from the Service pages that take you outside of the Service. These other sites may send their own cookies to users, collect data, solicit personal information, or contain information that you may find inappropriate or offensive.
|
||||
</p>
|
||||
|
||||
<h2>Links to Third-Party Integrations</h2>
|
||||
<p>
|
||||
We may provide links to third-party integrations. Third-party integrations are websites or platforms that synchronize with our Service to provide you with additional functionality, tools, or services, such as delivering content based on your location.
|
||||
</p>
|
||||
<p>
|
||||
You acknowledge and agree that we are not responsible for the availability of such sites or resources and do not endorse and are not responsible or liable for any content, advertising, goods, services, or other materials on, available through, or provided by such sites or resources.
|
||||
</p>
|
||||
<p>
|
||||
We are not responsible for the privacy or other practices of such sites and cannot guarantee the security of any personal information that you provide to such sites or that such sites collect. We encourage you to review the privacy policies and terms and conditions on those linked sites.
|
||||
</p>
|
||||
|
||||
<h2>Using Our Service</h2>
|
||||
<p>
|
||||
You must follow any policies made available to you within the Service, including but not limited to the Terms of Service, Privacy Policy, and Community Guidelines. You may only use our Service as permitted by law. HabitRPG may investigate and/or suspend or terminate our Service to you at any time if we find your use of our Service violates the Agreement, applicable law, and/or any of our policies.
|
||||
</p>
|
||||
<p>
|
||||
Using our Service does not grant you ownership of any intellectual property rights in our Service or the content you may have access to. You may not use any copyrighted content in our Service unless you obtain permission from the content owner and/or are otherwise permitted by law. The Agreement does not grant you the right to use any branding or logos used in our Service. Our Service may display some logos, trademarks, or branding materials that are not the property of HabitRPG. Such content is the sole property of the entity that makes it available.
|
||||
</p>
|
||||
<p>
|
||||
You may not abuse and/or misuse our Service, including but not limited to the following actions:
|
||||
</p>
|
||||
<ul>
|
||||
<li>Using the Service for any unlawful purposes or activities;</li>
|
||||
<li>Uploading any content to the Service in violation of any applicable law, including but not limited to intellectual property laws, privacy laws, and publicity laws;</li>
|
||||
<li>Sending unsolicited promotions or advertisements;</li>
|
||||
<li>Accessing or tampering with the Service's server systems;</li>
|
||||
<li>Interfering with or disrupting the access of any user, host, or network;</li>
|
||||
<li>Abusing or submitting excessively frequent requests to the Service via the API</li>
|
||||
<li>Spamming chat, whether for personal or commercial purposes, by disrupting the flow of conversation with repeated postings;</li>
|
||||
<li>Impersonating any person, business, or entity, including an employee of HabitRPG, or member of the Habitica moderation team, or communicating in any way that makes it appear that the communication originates from Habitica staff or HabitRPG;</li>
|
||||
<li>Transmitting or communicating any content which, in the sole and exclusive discretion of HabitRPG, is deemed offensive, including language that is unlawful, harmful, threatening, abusive, harassing, defamatory, vulgar, obscene, sexually explicit, or racially, ethically, or otherwise objectionable,</li>
|
||||
<li>Participating in any action which, in the sole and exclusive judgment of HabitRPG, defrauds any other user of the Service, including by scamming or social engineering; or</li>
|
||||
<li>Inducing or encouraging others to violate the Community Guidelines or the Agreement.</li>
|
||||
</ul>
|
||||
<p>
|
||||
HabitRPG, in its sole discretion, will determine what constitutes abuse and/or misuse of our Service.
|
||||
</p>
|
||||
|
||||
<h2>Premium Service and Payments</h2>
|
||||
<p>
|
||||
You may choose our free Service or paid Service ("Premium") depending on your needs. We do not guarantee when, if ever, Premium features will be available in the free Service. You may upgrade from free Service to Premium at any time by any of the following methods:
|
||||
</p>
|
||||
<ul>
|
||||
<li>Web: Selecting the user icon in the top right corner and selecting "Subscription" from the dropdown menu,</li>
|
||||
<li>Web: Clicking the green gem icon in the navigation bar at the top of the screen and following instructions to Subscribe or Buy Gems,</li>
|
||||
<li>Android: Tap the menu icon in the top left corner of the main screen and select “Purchase Gems” or “Subscription”. Follow the instructions to make a purchase.</li>
|
||||
<li>iOS: Tap the menu icon in the lower right and select “Purchase Gems” or “Subscription”. Follow the instructions to make a purchase.</li>
|
||||
</ul>
|
||||
<p>
|
||||
You will be charged the amount shown on Pricing before you can access Premium Service. All prices shown on Pricing are inclusive of any applicable sales taxes, levies, value-added taxes, or duties imposed by taxing authorities, and you are responsible for payment of all such taxes, levies, or duties. We may revise the Pricing at any time and may, from time to time, modify, amend, or supplement our fees and fee-billing methods. Such changes shall be effective upon posting on the Pricing page or elsewhere in the Service. If there is a dispute regarding payment of fees to us, we reserve the right to terminate or suspend your account at our sole discretion.
|
||||
</p>
|
||||
<p>
|
||||
BY PURCHASING PREMIUM YOU EXPRESSLY UNDERSTAND AND AGREE TO OUR REFUND POLICY:
|
||||
</p>
|
||||
<p>
|
||||
WITHIN THIRTY (30) DAYS OF YOUR PREMIUM PAYMENT DATE AS SHOWN ON YOUR PAYMENT BILL, YOU CAN REQUEST A FULL REFUND BY CONTACTING US AT <a href="mailto:admin@habitica.com">ADMIN@HABITICA.COM</a>. AFTER THIRTY (30) DAYS OF YOUR PREMIUM PAYMENT DATE, ANY PAYMENT REFUND IS SOLELY SUBJECT TO OUR DISCRETION. THE REFUND SHALL BE YOUR SOLE AND EXCLUSIVE REMEDY.
|
||||
</p>
|
||||
<p>
|
||||
FOR ANY CUSTOMER WHO PURCHASED PREMIUM IN APPLE INC.'s APP STORE ("APP STORE"), PLEASE CONTACT APPLE INC.'s SUPPORT TEAM: <a
|
||||
href="https://reportaproblem.apple.com"
|
||||
target="_blank"
|
||||
>https://reportaproblem.apple.com</a>. APPLE'S APP STORE DOES NOT ALLOW DEVELOPERS TO ISSUE REFUNDS FOR APP STORE PURCHASES MADE BY CUSTOMERS.
|
||||
</p>
|
||||
|
||||
<h2>Warranty Disclaimer and Limitation on Liability</h2>
|
||||
<p>
|
||||
THE SERVICE AND ANY CONTENT MADE AVAILABLE BY HABITRPG VIA THE SERVICE IS PROVIDED "AS IS" AND "AS AVAILABLE" WITHOUT ANY WARRANTIES OF ANY KIND, INCLUDING, WITHOUT LIMITATION, THAT THE SERVICE OR CONTENT WILL OPERATE ERROR-FREE OR THAT THE SERVICE OR CONTENT OR ITS SERVERS ARE FREE OF COMPUTER VIRUSES OR SIMILAR CONTAMINATION OR DESTRUCTIVE FEATURES.
|
||||
</p>
|
||||
<p>
|
||||
WE DISCLAIM ALL WARRANTIES, INCLUDING, BUT NOT LIMITED TO, WARRANTIES OF TITLE, MERCHANTABILITY, NON-INFRINGEMENT OF THIRD PARTIES' RIGHTS, AND FITNESS FOR PARTICULAR PURPOSE AND ANY WARRANTIES ARISING FROM A COURSE OF DEALING, COURSE OF PERFORMANCE, OR USAGE OF TRADE.
|
||||
</p>
|
||||
<p>
|
||||
WE RESERVE THE RIGHT TO MAKE CHANGES, CORRECTIONS, AND/OR IMPROVEMENTS TO THE SERVICE OR THE CONTENT AT ANY TIME WITHOUT NOTICE. IN CONNECTION WITH ANY WARRANTY, CONTRACT, OR COMMON LAW TORT CLAIMS: (I) WE AND OUR LICENSORS SHALL NOT BE LIABLE FOR ANY INCIDENTAL OR CONSEQUENTIAL DAMAGES, LOST PROFITS, OR DAMAGES RESULTING FROM LOST DATA OR BUSINESS INTERRUPTION RESULTING FROM THE USE OR INABILITY TO ACCESS AND USE THE SERVICE OR CONTENT POSTED BY HABITRPG, EVEN IF WE HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES; AND (II) ANY DIRECT DAMAGES THAT YOU MAY SUFFER AS A RESULT OF YOUR USE OF THE PLATFORM SHALL BE LIMITED TO THE GREATER OF (I) MONIES YOU HAVE PAID US IN CONNECTION WITH YOUR USE OF THE PLATFORM DURING THE TWELVE (12) MONTHS IMMEDIATELY PRECEDING THE EVENTS GIVING RISE TO THE CLAIM, OR (II) ONE HUNDRED US DOLLARS ($100).
|
||||
</p>
|
||||
<p>
|
||||
SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF CERTAIN WARRANTIES. THEREFORE, SOME OF THE ABOVE LIMITATIONS ON WARRANTIES IN THIS SECTION MAY NOT APPLY TO YOU. NOTHING IN THIS AGREEMENT SHALL AFFECT ANY NON-WAIVABLE STATUTORY RIGHTS THAT APPLY TO YOU.
|
||||
</p>
|
||||
|
||||
<h2>Indemnification</h2>
|
||||
<p>
|
||||
You agree to defend, indemnify, and hold us and our officers, directors, employees, agents, successors, licensees, licensors, and assigns harmless from and against any damages, liabilities, losses, expenses, claims, actions, and/or demands, including, without limitation, reasonable legal and accounting fees, arising or resulting from: (i) your breach of any of your representations, warranties or other obligations under this Agreement; (ii) your use or misuse of the Service or content posted or made available by HabitRPG; and/or (iii) your violation of any third-party rights in connection with your use of the Service, including without limitation any copyright, trademark, property, publicity, or privacy right. We shall provide notice to you of any such claim, suit, or proceeding and shall assist you, at your expense, in defending any such claim, suit, or proceeding. We reserve the right to participate in the defense (at our expense) of any matter that is subject to indemnification under this section. In such case, you agree to cooperate with any reasonable requests assisting our defense of such matter.
|
||||
</p>
|
||||
|
||||
<h2>Electronic Communications Privacy Act Notice (18 U.S.C. 2701-2711)</h2>
|
||||
<p>
|
||||
We make no guaranty of confidentiality or privacy with respect to any communication or information transmitted via the Service. We will not be liable for the privacy of the information, e-mail addresses, registration, and identification information, disk space, communications, confidential or trade-secret information, or any other content transmitted over networks accessed by the Service, or otherwise connected with your use of the Service.
|
||||
</p>
|
||||
|
||||
<h2>Compliance with Applicable Laws</h2>
|
||||
<p>
|
||||
The Service is based in the United States. We make no claims concerning whether the Service or posted content may be downloaded, viewed, or be appropriate for use outside of the United States. If you access the Service or such content from outside of the United States, you do so at your own risk. Whether inside or outside of the United States, you are solely responsible for ensuring compliance with the laws of your specific jurisdiction.
|
||||
</p>
|
||||
|
||||
<h2>Binding Arbitration</h2>
|
||||
<p>
|
||||
In the event of a dispute arising under or relating to this Agreement or the Service (each, a "Dispute"), such dispute will be finally and exclusively resolved by binding arbitration governed by the Federal Arbitration Act ("FAA"). Any election to arbitrate, at any time, shall be final and binding on the other party. NEITHER PARTY SHALL HAVE THE RIGHT TO LITIGATE SUCH CLAIM IN COURT OR TO HAVE A JURY TRIAL, EXCEPT EITHER PARTY MAY BRING ITS CLAIM IN ITS LOCAL SMALL CLAIMS COURT, IF PERMITTED BY THAT SMALL CLAIMS COURT RULES AND IF WITHIN SUCH COURT'S JURISDICTION. ARBITRATION IS DIFFERENT FROM COURT, AND DISCOVERY AND APPEAL RIGHTS MAY ALSO BE LIMITED IN ARBITRATION.
|
||||
</p>
|
||||
<p>
|
||||
All disputes will be resolved before a neutral arbitrator selected jointly by the parties, whose decision will be final, except for a limited right of appeal under the FAA. The arbitration shall be commenced and conducted by JAMS pursuant to its then current Comprehensive Arbitration Rules and Procedures and in accordance with the Expedited Procedures in those rules, or, where appropriate, pursuant to JAMS' Streamlined Arbitration Rules and Procedures. All applicable JAMS rules and procedures are available at the JAMS website <a
|
||||
href="https://www.jamsadr.com"
|
||||
target="_blank"
|
||||
>www.jamsadr.com</a>. Each party will be responsible for paying any JAMS filing, administrative, and arbitrator fees in accordance with JAMS rules. Judgment on the arbitrator's award may be entered in any court having jurisdiction. This clause shall not preclude parties from seeking provisional remedies in aid of arbitration from a court of appropriate jurisdiction. The arbitration may be conducted in person, through the submission of documents, by phone, or online. If conducted in person, the arbitration shall take place in the United States county where you reside. The parties may litigate in court to compel arbitration, to stay a proceeding pending arbitration, or to confirm, modify, vacate, or enter judgment on the award entered by the arbitrator. The parties shall cooperate in good faith in the voluntary and informal exchange of all non-privileged documents and other information (including electronically stored information) relevant to the Dispute immediately after commencement of the arbitration. As set forth below, nothing in this Agreement will prevent us from seeking injunctive relief in any court of competent jurisdiction as necessary to protect our proprietary interests.
|
||||
</p>
|
||||
<p>
|
||||
ANY CLAIMS, ACTIONS OR PROCEEDINGS BY YOU MUST BE COMMENCED WITHIN ONE YEAR AFTER THE EVENT THAT GAVE RISE TO YOUR CLAIM OCCURS. ALL OTHER CLAIMS YOU MAY HAVE ARE PERMANENTLY BARRED.
|
||||
</p>
|
||||
|
||||
<h2>Class Action Waiver</h2>
|
||||
<p>
|
||||
You agree that any arbitration or proceeding shall be limited to the Dispute between us and you individually. To the full extent permitted by law, (i) no arbitration or proceeding shall be joined with any other; (ii) there is no right or authority for any Dispute to be arbitrated or resolved on a class action-basis or to utilize class action procedures; and (iii) there is no right or authority for any Dispute to be brought in a purported representative capacity on behalf of the general public or any other persons. YOU AGREE THAT YOU MAY BRING CLAIMS AGAINST US ONLY IN YOUR INDIVIDUAL CAPACITY AND NOT AS A PLAINTIFF OR CLASS MEMBER IN ANY PURPORTED CLASS OR REPRESENTATIVE PROCEEDING.
|
||||
</p>
|
||||
|
||||
<h2>Jury Trial Waiver</h2>
|
||||
<p>
|
||||
IF FOR ANY REASON A DISPUTE OR CLAIM MAY PROCEED IN COURT RATHER THAN IN ARBITRATION, EACH PARTY TO THIS AGREEMENT IRREVOCABLY WAIVES, TO THE FULLEST EXTENT PERMITTED BY APPLICABLE LAW, ANY RIGHT IT MAY HAVE TO A TRIAL BY JURY IN ANY LEGAL PROCEEDING DIRECTLY OR INDIRECTLY ARISING OUT OF OR RELATING TO THESE TERMS OR THE SERVICE (WHETHER BASED ON CONTRACT, TORT, OR ANY OTHER THEORY), UNLESS SUCH WAIVERS ARE UNENFORCEABLE.
|
||||
</p>
|
||||
|
||||
<h2>Equitable Relief</h2>
|
||||
<p>
|
||||
You acknowledge and agree that in the event of a breach or threatened violation by you of our intellectual property rights and/or rights related to our confidential and proprietary information, we will suffer irreparable harm and will therefore be entitled to injunctive relief to enforce this Agreement. We may, without waiving any other remedies under this Agreement, seek from any court having jurisdiction any interim, equitable, provisional, or injunctive relief that is necessary to protect our rights and property pending the outcome of the arbitration referenced above.
|
||||
</p>
|
||||
|
||||
<h2>Contact Us</h2>
|
||||
<p>
|
||||
If you have any questions about the Agreement, or want to report a violation, please contact us at <a href="mailto:admin@habitica.com">admin@habitica.com</a>.
|
||||
</p>
|
||||
|
||||
<h2>Miscellaneous</h2>
|
||||
<ul>
|
||||
<li>This Agreement and any action related thereto will be governed by the laws of the State of California without regard to its conflict of laws provisions.</li>
|
||||
<li>Our failure to act on or enforce any provision of this Agreement shall not be construed as a waiver of that provision or any other provision therein. No waiver shall be effective against us unless made in writing, and no such waiver shall be construed as a waiver in any other or subsequent instance.</li>
|
||||
<li>Except as expressly agreed by us and you in writing, this Agreement constitutes the entire agreement between you and us with respect to the subject matter, and supersedes all previous or contemporaneous agreements, whether written or oral, between the parties with respect to the subject matter.</li>
|
||||
<li>The section headings are provided merely for convenience and shall not be given any legal import. This Agreement will inure to the benefit of our successors, assigns, licensees, and sublicensees.</li>
|
||||
<li>You agree that no joint venture, partnership, employment, or agency relationship exists between you and us as a result of the Agreement or your use of the Service.</li>
|
||||
<li>Our performance under the Agreement is subject to existing laws and legal process, and nothing contained in the Agreement is in derogation of our right to comply with governmental, court, and law enforcement requests, or requirements relating to your use of the Service or information provided to or gathered by us with respect to such use.</li>
|
||||
<li>If any part of the Agreement is determined to be invalid or unenforceable pursuant to applicable law, including, without limitation, the warranty disclaimers and liability limitations set forth above, then the invalid or unenforceable provision will be deemed superseded by a valid, enforceable provision that most closely matches the intent of the original provision and the remainder of the Agreement shall continue in effect.</li>
|
||||
<li>A printed version of the Agreement and of any notice given in electronic form shall be admissible in judicial or administrative proceedings based upon or relating to the Agreement to the same extent and subject to the same conditions as other business documents and records originally generated and maintained in printed form.</li>
|
||||
</ul>
|
||||
</div>
|
||||
<!-- eslint-enable max-len -->
|
||||
</template>
|
||||
@@ -1126,7 +1126,12 @@ export default {
|
||||
this.loadUser();
|
||||
this.oldTitle = this.$store.state.title;
|
||||
this.handleExternalLinks();
|
||||
this.selectPage(this.startingPage);
|
||||
// Check if there's a hash in the URL to determine the starting page
|
||||
let pageToSelect = this.startingPage;
|
||||
if (window.location.hash && (window.location.hash === '#stats' || window.location.hash === '#achievements')) {
|
||||
pageToSelect = window.location.hash.substring(1);
|
||||
}
|
||||
this.selectPage(pageToSelect);
|
||||
this.$root.$on('habitica:report-profile-result', () => {
|
||||
this.loadUser();
|
||||
});
|
||||
@@ -1211,10 +1216,15 @@ export default {
|
||||
},
|
||||
selectPage (page) {
|
||||
this.selectedPage = page || 'profile';
|
||||
window.history.replaceState(null, null, '');
|
||||
const profileUserId = this.userId || this.userLoggedIn._id;
|
||||
let newPath = `/profile/${profileUserId}`;
|
||||
if (page !== 'profile') {
|
||||
newPath += `#${page}`;
|
||||
}
|
||||
window.history.replaceState(null, null, newPath);
|
||||
this.$store.dispatch('common:setTitle', {
|
||||
section: this.$t('user'),
|
||||
subSection: this.$t(this.startingPage),
|
||||
subSection: this.$t(page),
|
||||
});
|
||||
},
|
||||
getNextIncentive () {
|
||||
|
||||
@@ -3,5 +3,14 @@ import habiticaMarkdown from 'habitica-markdown/withMentions';
|
||||
export default function renderWithMentions (text, user) {
|
||||
if (!text) return null;
|
||||
const env = { userName: user.auth.local.username, displayName: user.profile.name };
|
||||
return habiticaMarkdown.render(String(text), env);
|
||||
let html = habiticaMarkdown.render(String(text), env);
|
||||
if (user.profile.name && user.profile.name !== user.auth.local.username) {
|
||||
const escapedDisplayName = user.profile.name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
const displayNameRegex = new RegExp(
|
||||
`(<span class="at-text) at-highlight(">@${escapedDisplayName}</span>)`,
|
||||
'gi',
|
||||
);
|
||||
html = html.replace(displayNameRegex, '$1$2');
|
||||
}
|
||||
return html;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
class="balance-info"
|
||||
:currency-needed="currencyNeeded"
|
||||
:amount-needed="amountNeeded"
|
||||
:needed-currency-only="true"
|
||||
:neededCurrencyOnly="true"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -19,12 +19,16 @@ const HallPage = () => import(/* webpackChunkName: "hall" */'@/components/hall/i
|
||||
const PatronsPage = () => import(/* webpackChunkName: "hall" */'@/components/hall/patrons');
|
||||
const HeroesPage = () => import(/* webpackChunkName: "hall" */'@/components/hall/heroes');
|
||||
|
||||
// Admin Pages
|
||||
const AdminContainerPage = () => import(/* webpackChunkName: "admin-panel" */'@/components/admin/container');
|
||||
const AdminPanelPage = () => import(/* webpackChunkName: "admin-panel" */'@/components/admin/admin-panel');
|
||||
const AdminPanelUserPage = () => import(/* webpackChunkName: "admin-panel" */'@/components/admin/admin-panel/user-support');
|
||||
const AdminPanelSearchPage = () => import(/* webpackChunkName: "admin-panel" */'@/components/admin/admin-panel/search');
|
||||
const BlockerPage = () => import(/* webpackChunkName: "admin-panel" */'@/components/admin/blocker');
|
||||
// Admin Panel
|
||||
const AdminPanelPage = () => import(/* webpackChunkName: "admin-panel" */'@/components/admin-panel');
|
||||
const AdminPanelUserPage = () => import(/* webpackChunkName: "admin-panel" */'@/components/admin-panel/user-support');
|
||||
const AdminPanelSearchPage = () => import(/* webpackChunkName: "admin-panel" */'@/components/admin-panel/search');
|
||||
|
||||
// Except for tasks that are always loaded all the other main level
|
||||
// All the main level
|
||||
// components are loaded in separate webpack chunks.
|
||||
// See https://webpack.js.org/guides/code-splitting-async/
|
||||
// for docs
|
||||
|
||||
// Tasks
|
||||
const UserTasks = () => import(/* webpackChunkName: "userTasks" */'@/components/tasks/user');
|
||||
@@ -94,6 +98,9 @@ const router = new VueRouter({
|
||||
path: '/profile/:userId',
|
||||
props: true,
|
||||
},
|
||||
{ name: 'profile', path: '/user/profile' },
|
||||
{ name: 'stats', path: '/user/stats' },
|
||||
{ name: 'achievements', path: '/user/achievements' },
|
||||
{
|
||||
path: '/inventory',
|
||||
component: InventoryContainer,
|
||||
@@ -180,55 +187,32 @@ const router = new VueRouter({
|
||||
},
|
||||
|
||||
{
|
||||
name: 'adminSection',
|
||||
path: '/admin',
|
||||
component: AdminContainerPage,
|
||||
name: 'adminPanel',
|
||||
path: '/admin-panel',
|
||||
component: AdminPanelPage,
|
||||
meta: {
|
||||
privilegeNeeded: [ // any one of these is enough to give access
|
||||
'userSupport',
|
||||
'accessControl',
|
||||
],
|
||||
},
|
||||
children: [
|
||||
{
|
||||
name: 'adminPanel',
|
||||
path: 'panel',
|
||||
component: AdminPanelPage,
|
||||
name: 'adminPanelSearch',
|
||||
path: 'search/:userIdentifier',
|
||||
component: AdminPanelSearchPage,
|
||||
meta: {
|
||||
privilegeNeeded: [ // any one of these is enough to give access
|
||||
privilegeNeeded: [
|
||||
'userSupport',
|
||||
],
|
||||
},
|
||||
children: [
|
||||
{
|
||||
name: 'adminPanelSearch',
|
||||
path: 'search/:userIdentifier',
|
||||
component: AdminPanelSearchPage,
|
||||
meta: {
|
||||
privilegeNeeded: [
|
||||
'userSupport',
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'adminPanelUser',
|
||||
path: ':userIdentifier',
|
||||
component: AdminPanelUserPage,
|
||||
meta: {
|
||||
privilegeNeeded: [
|
||||
'userSupport',
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'blockers',
|
||||
path: 'blockers',
|
||||
component: BlockerPage,
|
||||
name: 'adminPanelUser',
|
||||
path: ':userIdentifier',
|
||||
component: AdminPanelUserPage,
|
||||
meta: {
|
||||
privilegeNeeded: [ // any one of these is enough to give access
|
||||
'accessControl',
|
||||
privilegeNeeded: [
|
||||
'userSupport',
|
||||
],
|
||||
},
|
||||
},
|
||||
@@ -351,6 +335,10 @@ router.beforeEach(async (to, from, next) => {
|
||||
if (to.params.startingPage !== undefined) {
|
||||
startingPage = to.params.startingPage;
|
||||
}
|
||||
// Check if there's a hash in the URL for stats or achievements
|
||||
if (to.hash === '#stats' || to.hash === '#achievements') {
|
||||
startingPage = to.hash.substring(1);
|
||||
}
|
||||
if (from.name === null) {
|
||||
store.state.postLoadModal = `profile/${to.params.userId}`;
|
||||
return next({ name: 'tasks' });
|
||||
@@ -371,10 +359,18 @@ router.beforeEach(async (to, from, next) => {
|
||||
}
|
||||
|
||||
if ((to.name === 'stats' || to.name === 'achievements' || to.name === 'profile') && from.name !== null) {
|
||||
const userId = store.state.user.data._id;
|
||||
let redirectPath = `/profile/${userId}`;
|
||||
if (to.name === 'stats') {
|
||||
redirectPath += '#stats';
|
||||
} else if (to.name === 'achievements') {
|
||||
redirectPath += '#achievements';
|
||||
}
|
||||
router.app.$emit('habitica:show-profile', {
|
||||
userId,
|
||||
startingPage: to.name,
|
||||
fromPath: from.path,
|
||||
toPath: to.path,
|
||||
toPath: redirectPath,
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -22,9 +22,7 @@ const NewsPage = () => import('@/components/static/newStuff');
|
||||
const OverviewPage = () => import('@/components/static/overview');
|
||||
const PressKitPage = () => import('@/components/static/pressKit');
|
||||
const PrivacyPage = () => import('@/components/static/privacy');
|
||||
const PrivacyReviewPage = () => import('@/components/static/privacyReview');
|
||||
const TermsPage = () => import('@/components/static/terms');
|
||||
const TermsReviewPage = () => import('@/components/static/termsReview');
|
||||
|
||||
export const STATIC_ROUTES = {
|
||||
path: '/static',
|
||||
@@ -81,15 +79,9 @@ export const STATIC_ROUTES = {
|
||||
{
|
||||
name: 'privacy', path: 'privacy', component: PrivacyPage, meta: { requiresLogin: false },
|
||||
},
|
||||
{
|
||||
name: 'privacyReview', path: 'privacy-review', component: PrivacyReviewPage, meta: { requiresLogin: false },
|
||||
},
|
||||
{
|
||||
name: 'terms', path: 'terms', component: TermsPage, meta: { requiresLogin: false },
|
||||
},
|
||||
{
|
||||
name: 'termsReview', path: 'terms-review', component: TermsReviewPage, meta: { requiresLogin: false },
|
||||
},
|
||||
{
|
||||
name: 'notFound', path: 'not-found', component: NotFoundPage, meta: { requiresLogin: false },
|
||||
},
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
import axios from 'axios';
|
||||
|
||||
export async function getBlockers () {
|
||||
const response = await axios.get('/api/v4/admin/blockers');
|
||||
return response.data.data;
|
||||
}
|
||||
export async function createBlocker (store, payload) {
|
||||
const response = await axios.post('/api/v4/admin/blockers', payload.blocker);
|
||||
return response.data.data;
|
||||
}
|
||||
export async function updateBlocker (store, payload) {
|
||||
const response = await axios.put(`/api/v4/admin/blockers/${payload.blocker._id}`, payload.blocker);
|
||||
return response.data.data;
|
||||
}
|
||||
|
||||
export async function deleteBlocker (store, payload) {
|
||||
const response = await axios.delete(`/api/v4/admin/blockers/${payload.blockerId}`);
|
||||
return response.data.data;
|
||||
}
|
||||
@@ -20,7 +20,6 @@ import * as worldState from './worldState';
|
||||
import * as news from './news';
|
||||
import * as analytics from './analytics';
|
||||
import * as faq from './faq';
|
||||
import * as blockers from './blockers';
|
||||
|
||||
// Actions should be named as 'actionName' and can be accessed as 'namespace:actionName'
|
||||
// Example: fetch in user.js -> 'user:fetch'
|
||||
@@ -46,7 +45,6 @@ const actions = flattenAndNamespace({
|
||||
news,
|
||||
analytics,
|
||||
faq,
|
||||
blockers,
|
||||
});
|
||||
|
||||
export default actions;
|
||||
|
||||
@@ -182,7 +182,5 @@
|
||||
"incorrectResetPhrase": "Bitte tippe <%= magicWord %> in Großbuchstaben um deinen Account zurückzusetzen.",
|
||||
"translateHabitica": "Habitica übersetzen",
|
||||
"marketing3Lead1Title": "Android & iOS Apps",
|
||||
"marketing4Lead3Button": "Starte noch heute",
|
||||
"emailBlockedRegistration": "Diese E-Mail ist für die Registrierung blockiert. Wenn du denkst, dass das ein Fehler ist, kontaktiere uns bitte unter admin@habitica.com.",
|
||||
"missingClientHeader": "Fehlende X-Client Header."
|
||||
"marketing4Lead3Button": "Starte noch heute"
|
||||
}
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"adminPanel": "Admin Panel",
|
||||
"siteBlockers": "Site Blockers",
|
||||
"newsroom": "Newsroom",
|
||||
"adminBlockerTypeDescription": "<b>IP-Address</b> - Block access for a specific IP-Address\n\nClient - Block access for a client based on the \"x-client\" header.\n\nE-Mail - Blocks e-mails from being used for signup.",
|
||||
"adminBlockerAreaDescription": "A blocker can either apply to the full site, completely blocking any access. Or it can apply to purchases, which still allows the site to be accessed."
|
||||
}
|
||||
@@ -69,6 +69,7 @@
|
||||
"awardWinners": "Award Winner",
|
||||
"doYouWantedToDeleteChallenge": "Do you want to delete this Challenge?",
|
||||
"deleteChallenge": "Delete Challenge",
|
||||
"deleteChallengeRefundDescription": "If you delete this Challenge, you will be refunded the Gem prize and the Challenge tasks will remain on the participants' task boards.",
|
||||
"challengeNamePlaceholder": "What is your Challenge name?",
|
||||
"challengeSummary": "Summary",
|
||||
"challengeSummaryPlaceholder": "Write a short description advertising your Challenge to other Habiticans. What is the main purpose of your Challenge and why should people join it? Try to include useful keywords in the description so that Habiticans can easily find it when they search!",
|
||||
|
||||
@@ -91,7 +91,7 @@
|
||||
"sync": "Sync",
|
||||
"tasks": "Tasks",
|
||||
"teams": "Teams",
|
||||
"terms": "Terms of Service",
|
||||
"terms": "Terms and Conditions",
|
||||
"tumblr": "Tumblr",
|
||||
"localStorageTryFirst": "If you are experiencing problems with Habitica, click the button below to clear local storage and most cookies for this website (other websites will not be affected). You will need to log in again after doing this, so first be sure that you know your log-in details, which can be found at Settings -> <%= linkStart %>Site<%= linkEnd %>.",
|
||||
"localStorageTryNext": "If the problem persists, please <%= linkStart %>Report a Bug<%= linkEnd %> if you haven't already.",
|
||||
@@ -116,7 +116,6 @@
|
||||
"missingPassword": "Missing password.",
|
||||
"missingNewPassword": "Missing new password.",
|
||||
"invalidEmailDomain": "You cannot register with emails with the following domains: <%= domains %>",
|
||||
"emailBlockedRegistration": "This E-Mail is blocked from registration. If you think this is a mistake, please contact us at admin@habitica.com.",
|
||||
"wrongPassword": "Password is incorrect. If you forgot your password, click \"Forgot Password.\"",
|
||||
"incorrectDeletePhrase": "Please type <%= magicWord %> in all capital letters to delete your account.",
|
||||
"incorrectResetPhrase": "Please type <%= magicWord %> in all capital letters to reset your account.",
|
||||
|
||||
@@ -51,6 +51,7 @@
|
||||
"notEnoughGems": "Not enough Gems",
|
||||
"alreadyHave": "Whoops! You already have this item. No need to buy it again!",
|
||||
"delete": "Delete",
|
||||
"gem": "Gem",
|
||||
"gems": "Gems",
|
||||
"needMoreGems": "Need More Gems?",
|
||||
"needMoreGemsInfo": "Purchase Gems now, or become a subscriber to buy Gems with Gold, get monthly mystery items, enjoy increased drop caps and more!",
|
||||
|
||||
@@ -116,7 +116,7 @@
|
||||
"needsTextPlaceholder": "Type your message here.",
|
||||
"messageCopiedToClipboard": "Message copied to clipboard.",
|
||||
"leaderOnlyChallenges": "Only group leader can create challenges",
|
||||
"sendGift": "Send a Gift",
|
||||
"sendGift": "Send Gift",
|
||||
"selectGift": "Select Gift",
|
||||
"selectSubscription": "Select Subscription",
|
||||
"sendGiftToWhom": "Who would you like to send a gift to?",
|
||||
|
||||
@@ -183,6 +183,5 @@
|
||||
"translateHabitica": "Translate Habitica",
|
||||
"marketing3Lead1Title": "Android & iOS apps",
|
||||
"marketing4Lead3Button": "Get Started Today",
|
||||
"missingClientHeader": "Missing x-client headers.",
|
||||
"emailBlockedRegistration": "This E-Mail is blocked from registration. If you think this is a mistake, please contact us at admin@habitica.com."
|
||||
"missingClientHeader": "Missing x-client headers."
|
||||
}
|
||||
|
||||
@@ -1174,7 +1174,7 @@
|
||||
"headArmoireRedHairbowText": "Red Hairbow",
|
||||
"headArmoireRedHairbowNotes": "Become strong, tough, and smart while wearing this beautiful Red Hairbow! Increases Strength by <%= str %>, Constitution by <%= con %>, and Intelligence by <%= int %>. Enchanted Armoire: Red Hairbow Set (Item 1 of 2).",
|
||||
"headArmoireVioletFloppyHatText": "Violet Floppy Hat",
|
||||
"headArmoireVioletFloppyHatNotes": "Many spells have been sewn into this simple hat, giving it a pleasing purple color. Increases Perception by <%= per %>, Intelligence by <%= int %>, and Constitution by <%= con %>. Enchanted Armoire: Violet Loungewear Set (Item 1 of 3).",
|
||||
"headArmoireVioletFloppyHatNotes": "Many spells have been sewn into this simple hat, giving it a pleasing purple colour. Increases Perception by <%= per %>, Intelligence by <%= int %>, and Constitution by <%= con %>. Enchanted Armoire: Independent Item.",
|
||||
"headArmoireGladiatorHelmText": "Gladiator Helm",
|
||||
"headArmoireGladiatorHelmNotes": "To be a gladiator you must be not only strong.... but cunning. Increases Intelligence by <%= int %> and Perception by <%= per %>. Enchanted Armoire: Gladiator Set (Item 1 of 3).",
|
||||
"headArmoireRancherHatText": "Rancher Hat",
|
||||
@@ -1575,7 +1575,7 @@
|
||||
"bodyMystery201901Notes": "These shimmering pauldrons are strong, but will rest on your shoulders as weightlessly as a ray of dancing light. Confers no benefit. January 2019 Subscriber Item.",
|
||||
"bodyArmoireCozyScarfText": "Cozy Scarf",
|
||||
"bodyArmoireCozyScarfNotes": "This fine scarf will keep you warm as you go about your wintry business. Increases Constitution and Perception by <%= attrs %> each. Enchanted Armoire: Lamplighter's Set (Item 4 of 4).",
|
||||
"headAccessory": "Head Accessory",
|
||||
"headAccessory": "head accessory",
|
||||
"accessories": "Accessories",
|
||||
"animalEars": "Animal Ears",
|
||||
"headAccessoryBase0Text": "No Head Accessory",
|
||||
@@ -2252,12 +2252,12 @@
|
||||
"armorSpecialBirthday2021Text": "Extravagant Party Robes",
|
||||
"weaponMystery202102Notes": "The glowing pink gem in this wand holds the power to spread joy and friendship far and wide! Confers no benefit. February 2021 Subscriber Item.",
|
||||
"weaponMystery202102Text": "Charming Wand",
|
||||
"weaponSpecialSpring2024WarriorNotes": "This colourful crystal will help concentrate all your energy into an attack. Increases Strength by <%= str %>. Limited Edition Spring 2024 Gear.",
|
||||
"weaponSpecialSpring2024WarriorNotes": "This colorful crystal will help concentrate all your energy into an attack. Increases Strength by <%= str %>. Limited Edition Spring 2024 Gear.",
|
||||
"weaponArmoirePotionDesertNotes": "With this potion in hand, you don’t have to be stranded on a deserted island to find a desert-coloured pet to share your dessert with! Increases Strength by <%= str %> and Constitution by <%= con %>. Enchanted Armoire: Potion Set (Item 3 of 10)",
|
||||
"armorSpecialSummer2022HealerNotes": "Use your colourful fins to scoot about the reef and help those in need of rest and healing. Increases Constitution by <%= con %>. Limited Edition 2022 Summer Gear.",
|
||||
"weaponSpecialSpring2023HealerNotes": "With a puff and a sparkle, you deploy new growth, joy, and colour. Increases Intelligence by <%= int %>. Limited Edition 2023 Spring Gear.",
|
||||
"gearItemsCompleted": "You own all <%= klass %> gear! New gear is released during the seasonal Galas.",
|
||||
"moreArmoireGearAvailable": "Until then, there's <%= armoireCount %> pieces of gear in the Enchanted Armoire to find!",
|
||||
"moreArmoireGearAvailable": "Until then, there's <%= armoireCount %> pieces of gear in the Enchanted Wardrobe to find!",
|
||||
"moreArmoireGearComing": "The Enchanted Wardrobe gets new stock every month too!",
|
||||
"weaponSpecialSummer2021HealerText": "Staff of Corn",
|
||||
"weaponSpecialSpring2021WarriorNotes": "Harness the power of the sun against your enemies, and let the sunstone bring you luck! Increases Strength by <%= str %>. Limited Edition 2021 Spring Gear.",
|
||||
@@ -2348,30 +2348,5 @@
|
||||
"weaponSpecialFall2023HealerText": "Log Great Hammer",
|
||||
"weaponSpecialFall2023RogueNotes": "It takes an exceptionally strong stirrer to cook up bubbles and toiling troubles. Increases Strength by <%= str %>. Limited Edition 2023 Autumn Gear.",
|
||||
"weaponSpecialFall2023WarriorText": "Tasty Popcorn",
|
||||
"weaponSpecialFall2023WarriorNotes": "The most terrifying thing of all is the thought of a scary movie night with no snacks! Increases Strength by <%= str %>. Limited Edition 2023 Autumn Gear.",
|
||||
"armorSpecialSpring2021WarriorNotes": "Be careful you don't dazzle yourself as this sunstone armour catches the light! Increases Constitution by <%= con %>. Limited Edition 2021 Spring Gear.",
|
||||
"weaponArmoireShadyBeachUmbrellaNotes": "The shade of this rainbow-coloured umbrella conceals you briefly from the day star and any unwanted bothers. Increases Perception by <%= per %>. Enchanted Armoire: Beachside Set (Item 3 of 4).",
|
||||
"weaponSpecialWinter2025MageNotes": "This stunning, colourful show provides the perfect backdrop! You’ll be unstoppable! Increases Intelligence by <%= int %> and Perception by <%= per %>. Limited Edition Winter 2024-2025 Gear.",
|
||||
"armorArmoireAdmiralsUniformNotes": "We salute you! This naval uniform signals that you’re ready to take command of your tasks as well as a ship. Increases Constitution and Strength by <%= attrs %> each. Enchanted Armoire: Admiral’s Set (Item 2 of 2).",
|
||||
"weaponSpecialWinter2024RogueText": "Snowy Owl Bracer",
|
||||
"weaponSpecialWinter2024WarriorText": "Candy Club",
|
||||
"weaponSpecialWinter2024RogueNotes": "You are equipped with a flurry of feathers and talons! Hoot! Increases Strength by <%= str %>. Limited Edition Winter 2023-2024 Gear.",
|
||||
"weaponSpecialWinter2024WarriorNotes": "A fine weapon, so long as you can stop yourself from eating it. Increases Strength by <%= str %>. Limited Edition Winter 2023-2024 Gear.",
|
||||
"weaponSpecialWinter2024MageText": "Narwhal Wand",
|
||||
"armorArmoireJewelersApronText": "Jeweller's Apron",
|
||||
"armorSpecialSpring2021WarriorText": "Armour of the Sun",
|
||||
"armorSpecialSpring2021MageText": "White Swan's Splendour",
|
||||
"armorSpecialSpring2021MageNotes": "Your transformation is complete! Take to the sky, or to the lake, and sing for joy! Increases Intelligence by <%= int %>. Limited Edition 2021 Spring Gear.",
|
||||
"weaponMystery202306Notes": "Shine proud and bring a shimmering prism of colour wherever you go! Confers no benefit. June 2023 Subscriber Item.",
|
||||
"armorSpecialSummer2023MageNotes": "Feel protected and comfortable in these flowing robes, perfectly coloured for underwater adventures. Increases Intelligence by <%= int %>. Limited Edition 2023 Summer Gear.",
|
||||
"weaponMystery202104Notes": "Your enemies had better look out- you've got powerful and prickly defences! Confers no benefit. April 2021 Subscriber Item.",
|
||||
"weaponArmoireOrangeKiteNotes": "With colours like sunrise and sunset, let’s see how high your kite can get! Increases all stats by <%= attrs %> each. Enchanted Armoire: Kite Set (Item 3 of 5)",
|
||||
"armorArmoireJadeArmorText": "Jade Armour",
|
||||
"armorSpecialSpring2021HealerText": "Willow Bark Coat",
|
||||
"armorSpecialSpring2021HealerNotes": "This armour helps you bend instead of break when buffeted by wind or weapon. Increases Constitution by <%= con %>. Limited Edition 2021 Spring Gear.",
|
||||
"armorSpecialSpring2021RogueNotes": "No one will see you waiting in amBUSH with this cunning armour; you look like a plant from every angle. Increases Perception by <%= per %>. Limited Edition 2021 Spring Gear.",
|
||||
"weaponSpecialFall2023HealerNotes": "With slow, heavy attacks, this gnarled hammer deals out healing blows instead of damage. Increases Intelligence by <%= int %>. Limited Edition 2023 Autumn Gear.",
|
||||
"armorArmoireJadeArmorNotes": "This jade armour is both beautiful and functional. Protect yourself, and know that you look fabulous! Increases Perception by <%= per %>. Enchanted Armoire: Jade Warrior Set (Item 2 of 3).",
|
||||
"weaponSpecialWinter2024HealerText": "Torch",
|
||||
"weaponSpecialSpring2024RogueText": "Silver Blade"
|
||||
"weaponSpecialFall2023WarriorNotes": "The most terrifying thing of all is the thought of a scary movie night with no snacks! Increases Strength by <%= str %>. Limited Edition 2023 Autumn Gear."
|
||||
}
|
||||
|
||||
@@ -843,6 +843,5 @@
|
||||
"questAlpacaRageEffect": "The Overpacked Alpaca launches luggage at you! The boss regains 30% of its health!",
|
||||
"questAlpacaDropAlpacaEgg": "Alpaca (egg)",
|
||||
"questAlpacaNotes": "The sun beams down as you hike up the rocky trailheads of the Meandering Mountains. You’ve been planning this expedition for your friend group for months, researching every aspect of the trip. The weight of supplies on your back is so much to bear, each step feels more like a burden than an adventure.<br><br>You hear a soft crunch of hooves on the trail behind you. A fluffy alpaca approaches with a gigantic stack of luggage on her back.<br><br>“Seems like you’re dragging a bit, friend, and all you’re carrying is a little backpack!” she says as she passes by.<br><br>“You make it look so easy,” you sigh. “I planned this trip for so long, but now that we’re here, I’m not even having fun…”<br><br>“Don’t get down on yourself,” the alpaca snorts. “I’ll teach you a lesson I learned long ago!” She bucks, and suddenly a bundled bedroll is flying at you! How is this helping again?!",
|
||||
"questAlpacaCompletion": "Luckily none of the bags the alpaca threw your way were heavy, but your hands are definitely full. “What was that about?” you ask, annoyed.<br><br>“If you’re planning a trip with friends, you shouldn’t be carrying your burden alone! I’m sure your friends would rather you shake off a few things onto them than for you to collapse under the weight by yourself. Anyway, you can hand me those bags back. I’m a seasoned pack animal and I’ve made my point,” she says with a wink. “But keep that blue bundle as a reward for a hard lesson learned. I’ll see you at the peak!”",
|
||||
"questPlatypusText": "The Perfectionist Platypus"
|
||||
"questAlpacaCompletion": "Luckily none of the bags the alpaca threw your way were heavy, but your hands are definitely full. “What was that about?” you ask, annoyed.<br><br>“If you’re planning a trip with friends, you shouldn’t be carrying your burden alone! I’m sure your friends would rather you shake off a few things onto them than for you to collapse under the weight by yourself. Anyway, you can hand me those bags back. I’m a seasoned pack animal and I’ve made my point,” she says with a wink. “But keep that blue bundle as a reward for a hard lesson learned. I’ll see you at the peak!”"
|
||||
}
|
||||
|
||||
@@ -169,7 +169,7 @@
|
||||
"mysterySet202010": "Beguilingly Batty Set",
|
||||
"mysterySet202009": "Marvellous Moth Set",
|
||||
"cancelSubInfoApple": "Please follow <a href=\"https://support.apple.com/en-us/HT202039\">Apple's official instructions</a> to cancel your subscription or to see your subscription's termination date if you have already cancelled it. This screen is not able to show you whether your subscription has been cancelled.",
|
||||
"cancelSubInfoGoogle": "To cancel your subscription or to view your subscription's termination date, please use the <a href='https://play.google.com/store/account/subscriptions'>Google Play Store</a>. Any leftover months of subscription credit will be added to your end date after cancellation.",
|
||||
"cancelSubInfoGoogle": "Please go to the \"Account\" > \"Subscriptions\" section of the Google Play Store app to cancel your subscription or to see your subscription's termination date if you have already cancelled it. This screen is not able to show you whether your subscription has been cancelled.",
|
||||
"organization": "Organisation",
|
||||
"dropCapSubs": "Habitica subscribers can find twice as many random items each day and receive monthly mystery items!",
|
||||
"lookingForMoreItems": "Looking for More Items?",
|
||||
@@ -232,7 +232,5 @@
|
||||
"mysterySet202302": "Trickster Tabby Set",
|
||||
"mysterySet202304": "Tiptop Teapot Set",
|
||||
"mysterySet202310": "Ghostlight Wraith Set",
|
||||
"monthlyMysticHourglass": "Monthly Mystic Hourglass",
|
||||
"mysterySet202407": "Amiable Axolotl Set",
|
||||
"mysterySet202408": "Arcane Aegis Set"
|
||||
"monthlyMysticHourglass": "Monthly Mystic Hourglass"
|
||||
}
|
||||
|
||||
@@ -183,6 +183,5 @@
|
||||
"incorrectResetPhrase": "Por favor, teclea <%= magicWord %> en mayúsculas para reiniciar tu cuenta.",
|
||||
"marketing3Lead1Title": "Aplicaciones para Android y iOS",
|
||||
"marketing4Lead3Button": "Empieza Hoy Mismo",
|
||||
"missingClientHeader": "Faltan los encabezados x-client.",
|
||||
"emailBlockedRegistration": "Esta cuenta de E-Mail está bloqueada desde el registro. Si crees que es un error, por favor contacta con nosotros por medio de admin@habitica.com."
|
||||
"missingClientHeader": "Faltan los encabezados x-client."
|
||||
}
|
||||
|
||||
@@ -183,6 +183,5 @@
|
||||
"incorrectResetPhrase": "Merci de taper <%= magicWord %> en lettres capitales pour réinitialiser votre compte.",
|
||||
"marketing3Lead1Title": "Applications Android et iOS",
|
||||
"marketing4Lead3Button": "Commencez dès Aujourd'hui",
|
||||
"missingClientHeader": "En-têtes x-client manquants introuvables.",
|
||||
"emailBlockedRegistration": "Cette adresse mail a été bloquée par la modération. Si vous pensez qu'il s'agit d'une erreur, merci de nous contacter à admin@habitica.com."
|
||||
"missingClientHeader": "En-têtes x-client manquants."
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
"backgroundHauntedHouseNotes": "幽霊屋敷をそっと通りぬけましょう。",
|
||||
"backgroundPumpkinPatchText": "カボチャ畑",
|
||||
"backgroundPumpkinPatchNotes": "カボチャ畑でジャック・オ・ランタンを作りましょう。",
|
||||
"backgrounds112014": "セット6:2014年11月リリース",
|
||||
"backgrounds112014": "セット6: 2014年11月リリース",
|
||||
"backgroundHarvestFeastText": "収穫祭",
|
||||
"backgroundHarvestFeastNotes": "収穫祭を楽しみましょう。",
|
||||
"backgroundStarrySkiesText": "星空",
|
||||
@@ -131,7 +131,7 @@
|
||||
"backgroundSunsetOasisNotes": "夕焼けの沃地で休もう。",
|
||||
"backgrounds122015": "セット19:2015年12月リリース",
|
||||
"backgroundAlpineSlopesText": "雪の山",
|
||||
"backgroundAlpineSlopesNotes": "雪の山でスキーをしよう。",
|
||||
"backgroundAlpineSlopesNotes": "雪の山にスキーする。",
|
||||
"backgroundSnowySunriseText": "雪の日出",
|
||||
"backgroundSnowySunriseNotes": "雪の日の出を見よう。",
|
||||
"backgroundWinterTownText": "都市の冬",
|
||||
@@ -206,7 +206,7 @@
|
||||
"backgroundStrangeSewersNotes": "奇妙な下水道で滑りましょう。",
|
||||
"backgroundRainyCityText": "雨の街",
|
||||
"backgroundRainyCityNotes": "雨の街をピチャピチャ歩きましょう。",
|
||||
"backgrounds112016": "セット30:2016年11月リリース",
|
||||
"backgrounds112016": "セット30: 2016年11月リリース",
|
||||
"backgroundMidnightCloudsText": "闇夜の雲海",
|
||||
"backgroundMidnightCloudsNotes": "闇夜の雲海を飛びまわろう。",
|
||||
"backgroundStormyRooftopsText": "嵐の屋上",
|
||||
@@ -252,8 +252,8 @@
|
||||
"backgroundMagicBeanstalkNotes": "魔法の豆の木を登ろう。",
|
||||
"backgroundMeanderingCaveText": "曲がりくねった洞窟",
|
||||
"backgroundMeanderingCaveNotes": "曲がりくねった洞窟を探検しよう。",
|
||||
"backgroundMistiflyingCircusText": "マドワシティーのサーカス",
|
||||
"backgroundMistiflyingCircusNotes": "マドワシティーのサーカスで酔い騒ごう。",
|
||||
"backgroundMistiflyingCircusText": "幻想的なサーカス",
|
||||
"backgroundMistiflyingCircusNotes": "幻想的なサーカスで酔い騒ごう。",
|
||||
"backgrounds042017": "セット35: 2017年4月リリース",
|
||||
"backgroundBugCoveredLogText": "虫だらけの丸太",
|
||||
"backgroundBugCoveredLogNotes": "虫だらけの丸太を調査しよう。",
|
||||
@@ -264,9 +264,9 @@
|
||||
"backgrounds052017": "セット36:2017年5月リリース",
|
||||
"backgroundGuardianStatuesText": "ガーディアンの像",
|
||||
"backgroundGuardianStatuesNotes": "ガーディアンの像の前で寝ずの番をしよう。",
|
||||
"backgroundHabitCityStreetsText": "ハビットシティの街並み",
|
||||
"backgroundHabitCityStreetsNotes": "ハビットシティの街並みを探検しましょう。",
|
||||
"backgroundOnATreeBranchText": "木の枝の上",
|
||||
"backgroundHabitCityStreetsText": "Habit シティーの街並み",
|
||||
"backgroundHabitCityStreetsNotes": "Habit シティーの街並みを探検しましょう。",
|
||||
"backgroundOnATreeBranchText": "木の枝で",
|
||||
"backgroundOnATreeBranchNotes": "木の枝の上にとまろう。",
|
||||
"backgrounds062017": "セット37:2017年6月リリース",
|
||||
"backgroundBuriedTreasureText": "埋もれた宝",
|
||||
@@ -357,7 +357,7 @@
|
||||
"backgroundDocksNotes": "造船ドックの上で魚釣りをしましょう。",
|
||||
"backgroundRowboatText": "小舟",
|
||||
"backgroundRowboatNotes": "小舟の上で輪唱しましょう。",
|
||||
"backgroundPirateFlagText": "海賊の旗",
|
||||
"backgroundPirateFlagText": "海賊のフラッグ",
|
||||
"backgroundPirateFlagNotes": "見る者に恐怖を与える海賊旗を掲げましょう。",
|
||||
"backgrounds072018": "セット50:2018年7月リリース",
|
||||
"backgroundDarkDeepText": "暗い深海",
|
||||
@@ -451,7 +451,7 @@
|
||||
"backgroundUnderwaterVentsText": "海底の熱水噴出孔",
|
||||
"backgroundSeasideCliffsNotes": "そびえる断崖の美観とともに海辺に立ちましょう。",
|
||||
"backgroundTreehouseText": "ツリーハウス",
|
||||
"backgroundTreehouseNotes": "林の中に隠れた、自分専用のツリーハウスで遊びましょう。",
|
||||
"backgroundTreehouseNotes": "あなた達だけの樹木のアジト、あなた達専用のツリーハウスに集まって遊びましょう。",
|
||||
"backgroundGiantDandelionsNotes": "巨大なタンポポに囲まれてのんびり時を過ごしましょう。",
|
||||
"backgroundGiantDandelionsText": "巨大なタンポポ",
|
||||
"backgroundAmidAncientRuinsText": "古代遺跡の真ん中",
|
||||
@@ -516,8 +516,8 @@
|
||||
"backgroundRainyBarnyardText": "雨降る納屋の前庭",
|
||||
"backgroundHabitCityRooftopsNotes": "ハビットシティーの屋根と屋根の間を大胆に跳ぼう。",
|
||||
"backgroundHabitCityRooftopsText": "ハビットシティーの屋上",
|
||||
"backgroundHotAirBalloonNotes": "気球に乗って空から景色を眺めよう。",
|
||||
"backgroundHotAirBalloonText": "気球",
|
||||
"backgroundHotAirBalloonNotes": "熱気球で景色の上まで舞い上がろう。",
|
||||
"backgroundHotAirBalloonText": "熱気球",
|
||||
"backgrounds062020": "セット73:2020年6月リリース",
|
||||
"backgroundStrawberryPatchNotes": "いちご畑から新鮮な喜びを摘もう。",
|
||||
"backgroundStrawberryPatchText": "いちご畑",
|
||||
@@ -873,10 +873,10 @@
|
||||
"backgroundRiverBottomText": "川底",
|
||||
"backgrounds082024": "セット123: 2024年8月リリース",
|
||||
"monthlyBackgrounds": "月替わりの背景",
|
||||
"backgrounds092024": "セット124: 2024年9月リリース",
|
||||
"backgrounds092024": "セット124: 2024年9月リリース",
|
||||
"backgroundMagicDoorInForestText": "森の中の魔法の門",
|
||||
"backgroundMagicDoorInForestNotes": "思い切って森の魔法のゲートに足を踏み入れましょう。",
|
||||
"backgrounds102024": "セット125:2024年10月リリース",
|
||||
"backgrounds102024": "セット125: 2024年10月リリース",
|
||||
"backgroundSurroundedByGhostsText": "おばけに囲まれる",
|
||||
"backgroundSurroundedByGhostsNotes": "おばけに囲まれた不気味な夕べを過ごしましょう。",
|
||||
"backgrounds022025": "セット129: 2025年2月リリース",
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
"challenge": "チャレンジ",
|
||||
"challengeDetails": "チャレンジはプレイヤー同士で競争し、一連の関連したタスクを完了させることによって賞品を獲得するコミュニティのイベントです。",
|
||||
"brokenChaLink": "チャレンジのリンク切れ",
|
||||
"brokenTask": "チャレンジのリンク切れ:このタスクはもともとチャレンジの一部でしたが、チャレンジから削除されました。どうしますか?",
|
||||
"brokenTask": "チャレンジのリンク切れ: このタスクはもともとチャレンジの一部でしたが、チャレンジから削除されました。どうしますか?",
|
||||
"keepIt": "このまま残す",
|
||||
"removeIt": "消す",
|
||||
"brokenChallenge": "チャレンジのリンク切れ:このタスクはもともとチャレンジの一部でしたが、チャレンジ(もしくはグループ)が削除されました。残されたタスクはどうしますか?",
|
||||
"challengeCompleted": "チャレンジ終了です!<span class=\"badge\"><%- user %></span>が優勝しました!残ったタスクはどうしますか?",
|
||||
"challengeCompleted": "チャレンジ終了です! <span class=\"badge\"><%- user %></span>が優勝しました! 残ったタスクはどうしますか?",
|
||||
"unsubChallenge": "チャレンジのリンク切れ:このタスクはもともとチャレンジの一部でしたが、あなたがチャレンジ登録を取り消しました。残されたタスクはどうしますか?",
|
||||
"challenges": "チャレンジ",
|
||||
"endDate": "終了日",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"playerTiersDesc": "チャットで見られる色のついたユーザー名は、その人の貢献者段位を表しています。段位が高いほど、その人がHabiticaのピクセルアート、コード、コミュニティなどに貢献していることを示します!",
|
||||
"playerTiersDesc": "チャットで見られる色のついたユーザー名は、その人の貢献者段位を表わしています。段位が高いほど、その人がHabiticaのピクセルアート、コード、コミュニティなどに貢献していることを示します!",
|
||||
"tier1": "初段 (友人)",
|
||||
"tier2": "2段 (友人)",
|
||||
"tier3": "3段 (エリート)",
|
||||
@@ -10,7 +10,7 @@
|
||||
"tierModerator": "モデレーター",
|
||||
"tierStaff": "スタッフ",
|
||||
"tierNPC": "NPC",
|
||||
"friend": "友人",
|
||||
"friend": "友達",
|
||||
"elite": "エリート",
|
||||
"champion": "チャンピオン",
|
||||
"legendary": "伝説",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"lostAllHealth": "体力がなくなった!",
|
||||
"dontDespair": "がっかりしないで!",
|
||||
"deathPenaltyDetails": "レベル、ゴールド、装備の一部を失ってしまいました。しかし、努力すればすべて取り戻せます! あなたなら、きっとできる!頑張って!",
|
||||
"deathPenaltyDetails": "レベル、ゴールド、装備の一部を失ってしまいました。しかし、がんばればすべてを取り戻せます! あなたなら、きっとやれる――幸あらんことを。",
|
||||
"refillHealthTryAgain": "体力を復活させて、もう一度やってみよう",
|
||||
"dyingOftenTips": "よく体力がなくなってしまいますか? <a href='https://habitica.fandom.com/ja/wiki/死のしくみ#生き残るための戦略' target='_blank'>ここにヒントがあります! </a>",
|
||||
"losingHealthWarning": "気をつけて - 体力が減っています!",
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"defaultHabit1Text": "生産的な仕事 (鉛筆をクリックして編集)",
|
||||
"defaultHabit2Text": "ジャンクフードを食べる(鉛筆をクリックして編集)",
|
||||
"defaultHabit3Text": "階段・エレベーター(鉛筆をクリックして編集)",
|
||||
"defaultHabit1Text": "生産的な仕事 (鉛筆をクリックして編集)",
|
||||
"defaultHabit2Text": "ジャンクフードを食べる(鉛筆をクリックして編集)",
|
||||
"defaultHabit3Text": "階段・エレベーター(鉛筆をクリックして編集)",
|
||||
"defaultHabit4Text": "Habiticaにタスクを追加しましょう",
|
||||
"defaultHabit4Notes": "習慣、日課、To Doのどれでも",
|
||||
"defaultTodo1Text": "Habiticaに参加する(チェックして完了しましょう!)",
|
||||
"defaultTodo1Text": "Habiticaに参加する(チェックして完了しましょう!)",
|
||||
"defaultTodoNotes": "このTo Doを完了にする、または編集、削除できます。",
|
||||
"defaultReward1Text": "15分間の休憩",
|
||||
"defaultReward2Text": "自分へのごほうび",
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
"webFaqAnswer27": "タスクの色は、その値を視覚的に表現しています。全てのタスクは中立の黄色で始まり、青は良い状態を示し、赤は悪い状態を示します。各タスクの値は以下のように決まります:\n\n習慣はプラスボタンまたはマイナスボタンをタップすることによって、青または赤に変化します。良い習慣および悪い習慣は、完了しないと時間の経過とともに徐々に黄色に変化します。両方向の習慣は、ユーザーの入力に基づいてのみ色が変わります。\n\n日課は完了の頻度に基づいて色が変わり、完了すると青く、未完了だと赤くなります。\n\nTo Doは完了されないまま経過する時間とともに徐々に赤くなります。\n\nタスクが赤くなればなるほど、そのタスクを完了すると獲得できるゴールドと経験値が増えるので、一番難しいタスクにも挑戦してくださいね!",
|
||||
"webFaqAnswer29": "体力回復の薬をごほうび欄から25ゴールドで購入することで、15 HPを回復できます。さらに、レベルアップすると常にHPが完全に回復します!",
|
||||
"webFaqAnswer31": "タスクを完了してHPが不当に減少した場合、他のプラットフォームで行われた変更がサーバーに同期される際に遅延が発生した可能性があります。たとえば、モバイルアプリでゴールドを使用したり、マナを使ったり、HPが減少した後にウェブサイトでタスクを完了すると、サーバーはすべてが同期されていることを確認しているだけです。",
|
||||
"webFaqAnswer32": "全てのプレイヤーは、レベル10に達するまで戦士としてスタートします。レベル10に達すると、新しいクラスを選択するか戦士として続行するかの選択が与えられます。\n\nそれぞれのクラスには異なる装備とスキルがあります。クラスを選択したくない場合は、「選ばない」を選択できます。「選ばない」を選択すると、後で設定からいつでもクラスシステムを有効にできます。\n\nレベル10以降にクラスを変更したい場合は、「転生のオーブ」を使用することで可能です。転生のオーブはレベル50で6ジェム、レベル100で無料で市場で購入できるようになります。\n\nまた、設定からいつでも3ジェムでクラスを変更することもできます。この場合、転生のオーブのようにレベルがリセットされることはありませんが、レベルアップで貯めたスキルポイントを新しいクラスに合わせて再割り当てすることができます。",
|
||||
"webFaqAnswer32": "Habiticaには戦士、魔道士、盗賊、そして治療師の4つのクラスがあります。全てのプレイヤーは、レベル10に達するまで戦士としてスタートします。レベル10に達すると、新しいクラスを選択するか戦士として続行するかの選択が与えられます。\n\nそれぞれのクラスには異なる装備とスキルがあります。クラスを選択したくない場合は、「選ばない」を選択できます。「選ばない」を選択すると、後で設定からいつでもクラスシステムを有効にできます。",
|
||||
"webFaqAnswer30": "HPがゼロになると、レベルが1下がり、すべてのゴールドと再購入可能な装備の一部が失われます。",
|
||||
"webFaqAnswer34": "ペットは自分の色に合ったエサが好きです。ただし、普通のペットは例外で、すべての普通のペットが同じアイテムを好みます。各ペットが好む具体的なエサは以下の通りです:\n\n * 普通のペット:肉\n * 白いペット:牛乳\n * 砂漠のペット:じゃがいも\n * 赤いペット:いちご\n * 影のペット:チョコレート\n * 骨のペット:さかな\n * ゾンビのペット:腐った肉\n * わたあめピンクのペット:ピンクの綿菓子\n * わたあめブルーのペット:ブルーの綿菓子\n * 黄金のペット:ハチミツ",
|
||||
"webFaqAnswer36": "Habiticaのアバターの外見をカスタマイズする方法は無限です!アバターの体型、髪型と色、肌の色、メガネや移動補助具の追加などを変更するには、メニューから「アバターのカスタマイズ」を選択します。\n\nモバイルアプリでアバターをカスタマイズするには:\n * メニューから「アバターのカスタマイズ」を選択します\n\nウェブサイトでアバターをカスタマイズするには:\n * ナビゲーションのユーザーメニューから「アバターのカスタマイズ」を選択します",
|
||||
@@ -201,13 +201,5 @@
|
||||
"subscriptionDetail000": "12か月間有料会員になると神秘の砂時計12個を前の4個に追加でもらえます。",
|
||||
"subscriptionDetail002": "有料会員は、定期支払いの翌月まで神秘の砂時計を待つ必要がなくなります。",
|
||||
"subscriptionDetail01": "新しく12か月の有料会員購入時一回、12個追加で神秘の砂時計を手に入れることができます。",
|
||||
"subscriptionDetail010": "これはすべての有料会員が毎月1個ずつもらえる神秘の砂時計に追加してもらえます。",
|
||||
"webFaqAnswer67": "「クラス」とは、あなたが選べるキャラクタータイプであり、レベルアップすると、それぞれ固有の能力やスキルが使えるようになります。このスキルはタスクをするときや、パーティーでクエストをするときに役に立ちます。\n\nクラスによって、ごほうび、市場と季節の店で買える装備も違います。\n\n自分のプレイスタイルに合うクラスが選べるように、ここで各クラスの概要を説明します:\n#### **戦士**\n* 戦士はボスに大きなダメージを与え、クリティカルヒットのチャンスが高いので、タスクを完了するときに経験値やゴールドがたくさん獲得できます。\n* メインの属性は力で、ボスに与えられるダメージを高めます。\n* 二番目の属性は体質で、ボスから受けるダメージを減らします。\n* 戦士のスキルは、パーティーメンバーの力と体質を向上させます。\n* このクラスはボスと戦うことが好きだが、タスクをやり忘れてしまった時のために保護が欲しい人におすすめです。\n#### **治療師**\n* 治療師はディフェンスが強くて、自分またはパーティーメンバーを治療することができます。\n* メインの属性は体質で、治療を強めて、ボスから受けるダメージを減らします。\n* 二番目の属性は知能で、マナと経験値を増やします。\n* 治療師のスキルは、タスクの色を青くすることと、パーティーメンバーの体質を向上させることです。\n* このクラスはよくタスクをやり忘れて、自分やパーティーメンバーの体力を回復する能力が必要な人におすすめです。\n#### **魔導士**\n* 魔導士は速くレベルアップし、マナをたくさん取って、クエストのボスにダメージを与えます。\n* メインの属性は知能で、マナと経験値を増やします。\n* 二番目の属性は知覚で、ゴールドとアイテムがたくさん獲得できます。\n* 魔導士のスキルは、タスクの連続実行回数を保留したり、パーティーメンバーのマナや知能を向上させたりすることです。\n* このクラスはレベルアップでやる気が出て、ボスにダメージを与えたい人におすすめです。\n#### **盗賊**\n* 盗賊はタスクを完了するときに一番多くアイテムとゴールドが獲得でき、クリティカルヒットのチャンスが高いので、更に多くの経験値とゴールドが獲得できます。\n* メインの属性は知覚で、獲得できるゴールドとアイテムを増やします。\n* 二番目の属性は力で、ボスに与えられるダメージを増やします。\n* 盗賊のスキルは、しなかった日課から受けるダメージを避けて、ゴールドをたくさん取って、パーティーメンバーの知覚を向上させることです。\n* このクラスはごほうびがモチベーションになる人におすすめです。",
|
||||
"subscriptionDetail011": "現在、12ヶ月の継続有料プランに加入しているプレイヤーは、この変更が実施される日にボーナスを受け取ることができます。",
|
||||
"subscriptionDetail012": "このボーナスは、プレゼントされた有料プランには適用されません。",
|
||||
"subscriptionHeading1": "有料会員のジェムへの変更",
|
||||
"subscriptionDetail10": "有料会員が市場でゴールドを使って購入できるジェムの量は、最大50個に達するまで、特典がある月ごとに2個ずつ増えます。",
|
||||
"subscriptionDetail100": "新しく1ヶ月、3ヶ月、6ヶ月のサブスクリプションを始めると、1ヶ月あたり24ジェムが購入でき、購入できるジェムの量は特典がある月ごとに増加します。",
|
||||
"subscriptionDetail101": "現在、月間ジェム数が奇数である有料会員は、ジェム上限が偶数に切り上げられます。",
|
||||
"subscriptionDetail41": "有料プランの料金は変わりますか?"
|
||||
"subscriptionDetail010": "これはすべての有料会員が毎月1個ずつもらえる神秘の砂時計に追加してもらえます。"
|
||||
}
|
||||
|
||||
@@ -25,28 +25,28 @@
|
||||
"invalidEmail": "パスワードのリセットには有効なメールアドレスが必要です。",
|
||||
"login": "ログイン",
|
||||
"logout": "ログアウト",
|
||||
"marketing1Header": "一歩ずつ、より良い習慣を身につけよう!",
|
||||
"marketing1Header": "ゲームをプレーして、習慣を改善しましょう",
|
||||
"marketing1Lead1Title": "人生をゲーム化しよう",
|
||||
"marketing1Lead1": "Habiticaはタスクがたくさんたまっていて困っている人に向いているアプリです。アイテムをゲットしたり、モンスターと戦ったりするなど、基戦ったりームの仕組みで生活をもっと生産的にして、タスクを完了したときの達成感を高めます。タスクをたくさんするほど、ゲーム内の進歩も大きくなります。",
|
||||
"marketing1Lead2Title": "かっこいい装備を身に着けよう",
|
||||
"marketing1Lead2": "タスクをクリアして獲得したゴールドで剣やよろいなどを集めよう。何百種類もの剣や防具を集めることができるので、試してみたい組み合わせが尽きることはありません。ステータス、スタイル、またはその両方を最適化しよう! ",
|
||||
"marketing1Lead2Title": "すばらしい装備を手に入れよう",
|
||||
"marketing1Lead2": "習慣を改善して、アバターを成長させましょう。手に入れたカッコいい衣装をみんなに披露しましょう!",
|
||||
"marketing1Lead3Title": "頑張って賞金を取ろう",
|
||||
"marketing1Lead3": "楽しみなご褒美があるだけで、先延ばししているタスクでもやる気が出るかもしれません。現実世界でのご褒美がないときは、Habiticaがゲーム内で出してあげます!どのタスクでも完了するとご褒美がもらえますが、いろいろなサプライズが待っているので、継続して頑張りましょう! ",
|
||||
"marketing1Lead3": "ギャンブルこそがやる気につながる、そんな人たちには、「確率的報酬」とよんでいるシステムがあります。Habiticaには、積極的、消極的、確実、気まぐれ…すべてのやる気対策が入っています。",
|
||||
"marketing2Header": "友達と協力しよう",
|
||||
"marketing2Lead1Title": "仲間と一緒に生産性を高めよう",
|
||||
"marketing2Lead1": "他の人と協力し、競い合い、交流することで、モチベーションを高めましょう!Habiticaは、自己改善プログラムの最も効果的な部分である「社会的説明責任」を活用するように作られています。",
|
||||
"marketing2Lead1": "Habitica を一人でプレーすることもできますが、だれかと協力し、競争し、責任を感じあいはじめてこそ、Habitica の本領発揮です。自分を成長させるプログラムでいちばん効果的なのは、社会的な責任感です。責任感と競争を実現する環境として、ビデオゲーム以上のものがあるでしょうか?",
|
||||
"marketing2Lead2Title": "モンスターと戦おう",
|
||||
"marketing2Lead2": "パーティーの友達と一緒にクエストに挑戦しよう!モンスターと戦えるかは、あなたにかかっています。デンタルフロスを忘れてしまうと、参加者全員がダメージを受けます!",
|
||||
"marketing2Lead3Title": "チャレンジで競おう",
|
||||
"marketing2Lead3": "Habiticaのコミュニティが作った「チャレンジ」に参加して、自分の興味や目標に合うタスクに挑戦しましょう。賞品のジェムがもらえるように頑張ろう!",
|
||||
"marketing3Header": "Habiticaの他の使い方",
|
||||
"marketing3Lead1": "HabiticaをAndroidやiOSデバイスにインストールすれば、どこでもタスクをチェックできます。数々の賞を受賞しているアプリをチェックして、新しいアプローチで物事を実行しましょう。",
|
||||
"marketing3Lead2Title": "オープンソースコミュニティ",
|
||||
"marketing3Lead1": "**iPhone もしくは Android** アプリを使えば、外出先でも Habitica が操作できます。私たちは日課などのボタンをクリックするためにwebサイトにいちいちログインするのはめんどうだと気づきました。",
|
||||
"marketing3Lead2Title": "サードパーティーとの連動",
|
||||
"marketing3Lead2": "Habiticaはオープンソースのプロジェクトであり、メンバーからの寄付によりサポートされています。自分のニーズに合わせたり、世界中のプレイヤーに使いやすくしたりするために、皆さんもHabiticaに貢献できます。[詳しくはこちらをご覧ください](https://habitica.fandom.com/ja/wiki/Habitica%E3%81%B8%E3%81%AE%E8%B2%A2%E7%8C%AE)。",
|
||||
"marketing4Header": "家事の枠を超える",
|
||||
"marketing4Lead1": "教育は、ちょっとしたゲーム化に最適な分野のひとつです!ゲーム感覚を取り入れることで、毎日の単調な授業から解放されましょう。Habiticaは、宿題を記録したり、教室でチャレンジを作ったり、生徒たちに成果を披露させたりする楽しい方法です。",
|
||||
"marketing4Header": "組織での利用",
|
||||
"marketing4Lead1": "教育は、ゲーム化のもっとも適した分野の一つです。近年、電話機とゲームが、どれだけ学生・生徒たちとぴったりくっついていることか--みなさんご存知でしょう。力を解き放つのです!学生・生徒たちを友達同士の競争のスタートラインに並べましょう。よい行いにはレアな賞をあげましょう。学生・生徒の成績と態度が急上昇するのを見ていましょう。",
|
||||
"marketing4Lead1Title": "教育におけるゲーミフィケーション",
|
||||
"marketing4Lead2": "健康のためのライフスタイルを構築するのは、簡単なことではありません。Habiticaは、柔軟なスケジューリングと強度で、あなたの健康に関する目標のすべての側面を追跡するお手伝いをします。楽しみながら、より健康的な生活を目指しましょう!",
|
||||
"marketing4Lead2": "健康管理のコストは増大の一途をたどっており、その影響も大きくなっています。コストを削減し、健康を促進するために無数の計画が立てられています。Habitica も健康的なライフスタイルへの、しっかりとした道を築きあげるものであると私たちは信じています。",
|
||||
"marketing4Lead2Title": "健康維持・向上のゲーミフィケーション",
|
||||
"marketing4Lead3-1": "タスクを楽しんでやってみよう!",
|
||||
"marketing4Lead3-2": "教育、健康管理、その他に Habitica でとりくんでいるグループのことに興味がありますか?",
|
||||
@@ -182,7 +182,5 @@
|
||||
"translateHabitica": "Habiticaの翻訳",
|
||||
"incorrectResetPhrase": "アカウントをリセットするには、すべて大文字で <%= magicWord %> と入力してください。",
|
||||
"marketing3Lead1Title": "アンドロイド&iOS用アプリ",
|
||||
"marketing4Lead3Button": "Habiticaを始める",
|
||||
"emailBlockedRegistration": "このEメールは登録ブロックされています。間違いだと思われる場合は、admin@habitica.com までご連絡ください。",
|
||||
"missingClientHeader": "x-client ヘッダが見つかりません。"
|
||||
"marketing4Lead3Button": "Habiticaを始める"
|
||||
}
|
||||
|
||||
@@ -3085,7 +3085,7 @@
|
||||
"armorArmoireSoftWhiteSuitText": "やわらかい白スーツ",
|
||||
"backMystery202402Text": "パラダイスピンクのハート",
|
||||
"weaponMystery202408Text": "神秘の神盾",
|
||||
"gearItemsCompleted": "あなたは <%= klass %> の装備全てを持っています!新しい装備は季節の大祭の際にリリースされます。",
|
||||
"gearItemsCompleted": "すべての<%= klass %>の装備を集めました! 新しいギアは季節のイベントでリリースされます。",
|
||||
"moreArmoireGearAvailable": "それまでは、<%= armoireCount %> 個の装備をラッキー宝箱で見つけることができます!",
|
||||
"moreArmoireGearComing": "ラッキー宝箱も毎月新入荷します!",
|
||||
"weaponSpecialSummer2024RogueNotes": "敵の攻撃を相手に返します!力が<%= str %>上がります。2024年夏の限定装備。",
|
||||
@@ -3111,28 +3111,5 @@
|
||||
"weaponSpecialSpring2025RogueText": "水晶ポイントの殻竿",
|
||||
"weaponSpecialSpring2025RogueNotes": "1回振るだけで、目標を達成するまでの障害物が乗り越えられる。力が<%= str %>上がります。2025年春の限定装備。",
|
||||
"weaponSpecialSpring2025MageText": "カマキリの杖",
|
||||
"armorSpecialSummer2024WarriorText": "ジンベイザメのしっぽ",
|
||||
"weaponArmoireSpookyCandyBucketText": "不気味なキャンディーバケツ",
|
||||
"armorSpecialFall2024MageText": "冥界の魔術師のよろい",
|
||||
"armorSpecialFall2024RogueNotes": "このよろいを着用して暗闇の中へ溶け込んで、アジリティと隠密性を高めよう。知覚が<%= per %>上がります。2024年秋の限定装備。",
|
||||
"armorSpecialFall2024HealerText": "スペースインベーダーのよろい",
|
||||
"armorSpecialWinter2025RogueNotes": "冷たい雪に埋もれたように見えるかもしれないが、本当はこのコスチュームを着ると温まって、うれしくなる。知覚が<%= per %>上がります。2024-2025年冬の限定装備。",
|
||||
"weaponArmoireSpookyCandyBucketNotes": "トリック・オア・トリート!そんな素晴らしい仮装をしていると、お菓子がたくさんもらえそう!詰め放題のバケツを持っていてよかった。帰るまでに食べるのを我慢できるかな?知能が<%= int %>上がります。ラッキー宝箱:恐怖の夜のセット(アイテム2/2)",
|
||||
"weaponArmoireStormKnightAxeText": "嵐の騎士の斧",
|
||||
"weaponArmoireBeekeepersSmokerText": "ミツバチ用燻煙器",
|
||||
"armorSpecialSummer2024RogueText": "ウミウシのしっぽ",
|
||||
"armorSpecialSummer2024RogueNotes": "貝殻はないかもしれないが、代わりに翼のような立派なヒレを持っている!知覚が<%= per %>上がります。2024年夏の限定装備。",
|
||||
"armorSpecialFall2024RogueText": "黒ネコのよろい",
|
||||
"armorSpecialWinter2025RogueText": "雪のコスチューム",
|
||||
"armorSpecialWinter2025HealerText": "ストリングライトのローブ",
|
||||
"armorSpecialWinter2025MageText": "オーロラのマント",
|
||||
"armorSpecialSpring2025RogueText": "水晶ポイントのマント",
|
||||
"armorSpecialSpring2025HealerText": "プルメリアのローブ",
|
||||
"armorSpecialSpring2025MageText": "カマキリの制服",
|
||||
"armorSpecialSummer2025WarriorText": "ホタテ貝のよろい",
|
||||
"armorSpecialSummer2025RogueNotes": "このスーツは自由に色が変えられるだけでなく、墨を吐き出す可能性もある。敵を紛らわすか、目をくらませるかはあなた次第!知覚が<%= per %>上がります。2025年夏の限定装備。",
|
||||
"armorSpecialSummer2025HealerText": "クリオネのスーツ",
|
||||
"armorSpecialSummer2025MageNotes": "このスーツは鮮やかな色をしているだけでなく、着用すると水の中を美しく泳げるようになります。泳ぐか踊るかはあなた次第!知能が<%= int %>上がります。2025年夏の限定装備。",
|
||||
"armorSpecialSpring2025HealerNotes": "この美しいローブはやわらかくてひらひらするプルメリアの花びらからできています。体質が<%= con %>上がります。2025年春の限定装備。",
|
||||
"armorSpecialSummer2025RogueText": "イカのスーツ"
|
||||
"armorSpecialSummer2024WarriorText": "ジンベイザメのしっぽ"
|
||||
}
|
||||
|
||||
@@ -107,7 +107,7 @@
|
||||
"greeting0": "こんにちは!",
|
||||
"greeting1": "ちょっとあいさつしてみただけ (^_^)",
|
||||
"greeting2": "(遠くから手をブンブン振る)",
|
||||
"greeting3": "ヤッホー!",
|
||||
"greeting3": "そこのあなた!",
|
||||
"greetingCardAchievementTitle": "Kawaii",
|
||||
"greetingCardAchievementText": "やあ!よう!こんにちは!<%= count %> 通のあいさつカードをやりとりしました。",
|
||||
"thankyouCard": "ありがとうのカード",
|
||||
|
||||
@@ -293,7 +293,7 @@
|
||||
"teamBasedTasksList": "共有タスクのボード",
|
||||
"teamBasedTasksListDesc": "グループメンバー全員が同じタスクボードで作業することで、グループ内の状況を把握することができます。共有タスクボードからタスクを完了させることも、個人のタスクにコピーして外出先で完了させることもできます。",
|
||||
"groupManagementControls": "柔軟な説明責任",
|
||||
"groupManagementControlsDesc": "タスクを何人かのメンバーにあてて、任務を分担したり、誰が先に完了できるかチャレンジにしたりできます。グループメンバーはタスクのステータスでお互いの進捗状況が見れます。",
|
||||
"groupManagementControlsDesc": "タスクが本当に完了されたかを確認するためにタスク承認機能を使いましょう。グループメンバーへ任務を共有するためのグループマネージャーを追加し、全てのチームメンバーのためのプライベートなグループチャットを楽しみましょう。",
|
||||
"inGameBenefits": "メリットがたくさん!",
|
||||
"inGameBenefitsDesc": "グループメンバーは、限定のジャッカロープの乗騎だけでなく、毎月の特別な装備セットや、ゴールドでジェムを買う機能など、有料プランの特典を全て受けられます。",
|
||||
"letsMakeAccount": "まずはアカウントを作成しましょう",
|
||||
@@ -425,8 +425,5 @@
|
||||
"upgradeYourCrew": "チームをアップグレードする準備はできていますか?",
|
||||
"messageCopiedToClipboard": "コピーしました。",
|
||||
"createGroupToday": "グループを作成しよう!",
|
||||
"createGroupTitle": "グループを作成する",
|
||||
"interestedLearningMore": "もっと知りたい?",
|
||||
"readyToUpgrade": "アップグレードしますか?",
|
||||
"checkGroupPlanFAQ": "グループプランの機能をよりよく使う方法については<a href='/static/faq#what-is-group-plan'>よくある質問</a>をご覧ください。"
|
||||
"createGroupTitle": "グループを作成する"
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"step1": "ステップ 1:タスクを入力しよう",
|
||||
"webStep1Text": "Habiticaは現実世界の目標がないと成り立たないので、いくつかタスクを入力してみましょう。後から思いついたときは追加できます。 緑の「作成」ボタンを押してタスクを追加できます。\n* **[To Do](https://habitica.fandom.com/ja/wiki/To_Do)の作成:** 一度きり、もしくはめったに繰り返さないタスクはTo Do欄に一つずつ入力しましょう。タスクをクリックするとチェックリストや期限日などを追加できます。\n* **[日課](https://habitica.fandom.com/ja/wiki/日課)の作成:** 毎日、曜日ごと、月ごと、年ごとにやる必要がある行動は日課の欄に入力しましょう。タスクをクリックして、期限日や開始日を設定できます。「3日ごと」など、くり返しの間隔も設定できます。\n* **[習慣](https://habitica.fandom.com/ja/wiki/習慣)の作成:** 身に付けたい習慣は習慣の欄に入力しましょう。習慣を編集して、良い習慣:heavy_plus_sign:、または悪い習慣:heavy_minus_sign:のどちらかに変更できます。\n* **[ごほうび](https://habitica.fandom.com/ja/wiki/ごほうび)の作成:**ゲーム内でゲットできるごほうびに加えて、やる気を出すために楽しいアクティビティや好きなお菓子をごほうび欄に追加しましょう。休息をとること、そしてたまには自分を甘やかすことが大切です!\n* どのようなタスクを入力すればいいかアイデアがほしい人は、[習慣の例](https://habitica.fandom.com/ja/wiki/習慣の例)、[日課の例](https://habitica.fandom.com/ja/wiki/日課の例)、[To Doの例](https://habitica.fandom.com/ja/wiki/To_Doの例)、[自分好みのごほうびの例](https://habitica.fandom.com/ja/wiki/自分好みのごほうびの例)のWikiページを参考にしてみてください。",
|
||||
"step2": "ステップ 2:現実世界の行動でゲームのポイントを得ましょう",
|
||||
"webStep2Text": "さあ、リストの中の目標に向けてはじめましょう!タスクを完了してHabiticaでチェックを入れるごとに、レベルアップにつながる[経験値](https://habitica.fandom.com/ja/wiki/経験値)と、ごほうびを購入できる[ゴールド](https://habitica.fandom.com/ja/wiki/ゴールド)を獲得します。悪い習慣が出てしまったり、日課を欠かしてしまったりすると、[体力](https://habitica.fandom.com/ja/wiki/体力)が減ります。このように、目標までの進歩がHabiticaでの経験値と体力のバーに表れます。ゲーム内のキャラクターが成長するにつれて、現実世界での生活にも改善が見られることでしょう。",
|
||||
"webStep2Text": "さあ、リストの中の目標に取り組みはじめましょう! あなたがタスクを完了してHabiticaでチェックを入れるごとに、あなたはレベルアップに必要な[経験値](https://habitica.fandom.com/ja/wiki/経験値)と、ごほうびを購入できる[ゴールド](https://habitica.fandom.com/ja/wiki/ゴールド)を獲得します。あなたが悪い習慣を行ってしまったり、日課をやりそびれてしまったりすると、あなたは[体力](https://habitica.fandom.com/ja/wiki/体力)を失います。このように、Habiticaでの経験と体力のバーはあなたが目標に向かって進むうえでの楽しい指標になります。あなたのキャラクターがゲーム中で成長するにつれて、現実世界でのあなたの生活にも改善がみられることでしょう。",
|
||||
"step3": "ステップ 3:カスタマイズして、Habiticaの冒険に出かけよう",
|
||||
"webStep3Text": "基本の操作を覚えたら、以下のすばらしい機能でHabiticaをよりいっそう楽しめます:\n* タスクを[タグ](https://habitica.fandom.com/ja/wiki/タグ)で整理しましょう(タグを追加するにはタスクを編集してください)。\n* 右上のユーザーアイコンを押して、あなたの[アバター](https://habitica.fandom.com/ja/wiki/アバター)をカスタマイズしましょう。\n* ごほうび欄や [ショップ](<%= shopUrl %>)から[装備](https://habitica.fandom.com/ja/wiki/装備)を買って、[所持品 > 装備](<%= equipUrl %>)で変更しましょう。\n* [パーティーを探す](https://habitica.com/looking-for-party)でほかのユーザーと交流しましょう。\n* [たまご](https://habitica.fandom.com/ja/wiki/たまご)と[たまごがえしの薬](https://habitica.fandom.com/ja/wiki/たまごがえしの薬)を集めて、[ペット](https://habitica.fandom.com/ja/wiki/ペット)をかえしましょう。[えさ](https://habitica.fandom.com/ja/wiki/えさ)をやって[乗騎](https://habitica.fandom.com/ja/wiki/乗騎)に育てましょう。\n* レベル10になったら:任意の[クラス](https://habitica.fandom.com/ja/wiki/クラス・システム)を選んで、クラス固有の[スキル](https://habitica.fandom.com/ja/wiki/スキル)を使用しましょう(レベル11~14)。\n* (ナビゲーションバーの[パーティー](<%= partyUrl %>)を押して)友達とパーティーを組むことで、責任感を持ち、クエストの巻物を手に入れましょう。\n* [クエスト](https://habitica.fandom.com/ja/wiki/クエスト)でモンスターを倒したり、指定された物を集めたりしましょう(レベル15でクエストの巻物をもらえます)。",
|
||||
"overviewQuestionsRevised": "質問がありますか?<a href='/static/faq'>FAQ</a>をご覧ください!あなたの質問がそこに記載されていない場合は、このフォームを使用してさらに助けを求めることができます: "
|
||||
|
||||
@@ -240,8 +240,8 @@
|
||||
"questDilatoryDistress2Completion": "悪夢のようなドクロの群れを退けました。しかし、アドヴァ姫のもとに近づいたとは思えません。あなたたちは王家捜索隊の @Kiwibot が何かいいアイデアを持っているかもしれないと思い話しかけました。「都市を守っているシャコは、アドヴァ姫が逃げたところをきっと見ているでしょう」と @Kiwibot は言います。「彼等を闇のクレバスまで追いかけて行ってみましょう」",
|
||||
"questDilatoryDistress2Boss": "ウミドクロの群れ",
|
||||
"questDilatoryDistress2RageTitle": "群れの復活",
|
||||
"questDilatoryDistress2RageDescription": "群れの復活:日課を完了しないとこのバーが増えていきます。いっぱいになると、ウミドクロの群れは残りの体力を30%回復します!",
|
||||
"questDilatoryDistress2RageEffect": "`ウミドクロの群れは、『群れの復活』を使いました!`\n\nウミドクロは勝利に活気づき、クレバスから仲間がやってきて、群れを強化しました!",
|
||||
"questDilatoryDistress2RageDescription": "群れの復活:このゲージは日課を完了できなかった場合に溜まります。ゲージが最大になると、水の頭蓋骨の群れは残りの体力を30%回復します!",
|
||||
"questDilatoryDistress2RageEffect": "`ウミドクロの群れが、群れの復活を使った!`\n\nウミドクロは勝利に活気づき、クレバスから仲間がやってきて、群れを強化しました!",
|
||||
"questDilatoryDistress2DropSkeletonPotion": "骨のたまごがえしの薬",
|
||||
"questDilatoryDistress2DropCottonCandyBluePotion": "わたあめブルーのたまごがえしの薬",
|
||||
"questDilatoryDistress2DropHeadgear": "炎のサンゴのサークレット(頭装備)",
|
||||
@@ -342,7 +342,7 @@
|
||||
"questAxolotlDropAxolotlEgg": "ウーパールーパー ( たまご )",
|
||||
"questAxolotlUnlockText": "市場でウーパールーパーのたまごを買えるようになります",
|
||||
"questAxolotlRageTitle": "アホロートル再生",
|
||||
"questAxolotlRageDescription": "日課を完了しないとこのバーが増えていきます。いっぱいになると魔のウーパールーパーは体力を30%回復してしまいます!",
|
||||
"questAxolotlRageDescription": "あなたが日課を終えないとこのバーが埋まります。満杯になると魔のウーパールーパーは残りの体力の30%分回復します!",
|
||||
"questAxolotlRageEffect": "`魔のウーパールーパーが、『アホロートル再生』の呪文を使った!`\n\n`色とりどりの泡がモンスターの姿をさえぎったかと思うと、それが晴れるとモンスターの傷の一部が消えています!`",
|
||||
"questTurtleText": "カメの案内",
|
||||
"questTurtleNotes": "助けてください! この巨大ウミガメは、海岸の巣への戻り方がわからなくなってしまいました。この母ウミガメは、毎年そこに戻ってきてたまごを生んでいましたが、今年、ミスミ湾は赤くなった日課や未達成のTo Doでできた毒性のタスクがガラクタとなって埋まっています。@JessicaChaseがいうには「ウミガメがパニックを起こして暴れている!」 とのこと。<br><br>@UncommonCriminalはうなずいて、「あのウミガメの帰巣本能がぼやけてしまって混乱してるんだ」。<br><br>@Scarabsiは、あなたの腕をつかんで「彼女の行く手を邪魔しているタスクのガラクタをかたづけるのを手伝ってくれませんか? このままでは危険です。あのウミガメを救わないと!」",
|
||||
@@ -374,8 +374,8 @@
|
||||
"questTaskwoodsTerror1Completion": "ジョイフル・リーパーと名高い火占い師の @Beffymaroo の助けがあって、なんとか群れを追い払うことができました。Beffymaroo は、連帯の記念に火占い師のターバンをくれました。森の奥へと進みます。",
|
||||
"questTaskwoodsTerror1Boss": "炎ドクロの群れ",
|
||||
"questTaskwoodsTerror1RageTitle": "群れの復活",
|
||||
"questTaskwoodsTerror1RageDescription": "群れの復活:日課を完了しないとこのバーが増えていきます。いっぱいになると、炎ドクロの群れは体力を30%回復してしまいます!",
|
||||
"questTaskwoodsTerror1RageEffect": "`炎ドクロの群れは、『群れの復活』を使いました!`\n\nヤツらは勝利に勢いづき、熱風とともにもっとたくさんのドクロがあなたのまわりに渦巻いています!",
|
||||
"questTaskwoodsTerror1RageDescription": "群れの復活: あなた方が日課をやり遂げないと、このバーがたまっていきます。いっぱいになると、炎ドクロの群れは、残っている体力の 30% を復活させます!",
|
||||
"questTaskwoodsTerror1RageEffect": "`炎ドクロの群れは、『群れの復活』を唱えた!`\n\nヤツらは勝利に勢いづき、熱風とともにもっとたくさんのガイコツがあなたのまわりに渦巻いています!",
|
||||
"questTaskwoodsTerror1DropSkeletonPotion": "骨のたまごがえしの薬",
|
||||
"questTaskwoodsTerror1DropRedPotion": "赤のたまごがえしの薬",
|
||||
"questTaskwoodsTerror1DropHeadgear": "火占い師のターバン ( 帽子・ヘルメット )",
|
||||
@@ -435,9 +435,9 @@
|
||||
"questStoikalmCalamity1Notes": "@Kiwibotから短い手紙が届き、霜に覆われたその巻物は触れた指先と同じくらい心臓を凍えさせました。「オダヤカニ草原に来てみたら -- 地面から爆発する怪物が -- 救援を!!」あなたはパーティーを結成して北へ向かいます。しかし危険な山岳地帯を抜けてから間もなく、足元の雪が爆発し、ぞっとするような笑いを浮かべたドクロに取り囲まれてしまいました!<br><br>その時、突如として槍が脇をすり抜け、あなたを雪の中から不意打ちせんとしているドクロを貫きました。砕けたドクロから無造作に槍を引き戻すのに合わせて長い三つ編みがなびき、精巧な鎧をまとった長身の女性がマンモスの背に乗って躍り込んできます。マンモス乗りたちのリーダー、レディ・グラシエイトの助けを借りて、今こそ戦うときです!",
|
||||
"questStoikalmCalamity1Completion": "あなたがドクロの群れに最後の一撃を食らわせると、それらは魔法の煙の中に掻き消えてしまいました。「忌々しい連中はいなくなったようだ」レディ・グラシエイトは告げます。「しかしもっと厄介な問題が残っている。私についてこい」彼女はあなたに冷たい外気から守ってくれる外套を投げてよこします。そうしてあなたたちは彼女に従い、その場を後にしたのでした。",
|
||||
"questStoikalmCalamity1Boss": "ツチドクロの群れ",
|
||||
"questStoikalmCalamity1RageTitle": "群れの復活",
|
||||
"questStoikalmCalamity1RageDescription": "群れの復活:日課を完了しないとこのバーが増えていきます。いっぱいになると、ツチドクロの群れは体力を30%回復してしまいます!",
|
||||
"questStoikalmCalamity1RageEffect": "`ツチドクロの群れは『群れの復活』を使いました!`\n\n更なるドクロが地底から現れ、寒さに歯をガチガチ鳴らしています!",
|
||||
"questStoikalmCalamity1RageTitle": "群れの再生",
|
||||
"questStoikalmCalamity1RageDescription": "群れの再生:あなたが日課を完了させないとこのバーが増加します。一杯になると、ツチドクロの群れの体力が30%回復してしまいます!",
|
||||
"questStoikalmCalamity1RageEffect": "`ツチドクロの群れは群れの再生を使った!`\n\n更なるドクロが地底から現れ、寒さに歯をガチガチ鳴らした!",
|
||||
"questStoikalmCalamity1DropSkeletonPotion": "骨のたまごがえしの薬",
|
||||
"questStoikalmCalamity1DropDesertPotion": "砂漠のたまごがえしの薬",
|
||||
"questStoikalmCalamity1DropArmor": "マンモス乗りの鎧",
|
||||
@@ -477,8 +477,8 @@
|
||||
"questMayhemMistiflying1Completion": "最後の一匹のドクロが、きらきらと輝く虹色のローブを口にくわえたまま地に落ちます。しかし暴風はまだ止みません。ここでは何か別のものが暗躍しているようです。そして、あのぐうたらなエイプリル・フールはどこへ行ってしまったのでしょう? あなたはローブを拾い上げ、マドワシティーめがけて飛び込んでいきます。",
|
||||
"questMayhemMistiflying1Boss": "風ドクロの群れ",
|
||||
"questMayhemMistiflying1RageTitle": "群れの復活",
|
||||
"questMayhemMistiflying1RageDescription": "群れの復活:日課を完了しないとこのバーが増えていきます。いっぱいになると、風ドクロの群れは体力を30%回復してしまいます!",
|
||||
"questMayhemMistiflying1RageEffect": "`風ドクロの群れは『群れの復活』を使いました!`\n\n勝利に勢いづき、さらにたくさんのドクロが雲の中から飛び出してきます!",
|
||||
"questMayhemMistiflying1RageDescription": "群れの復活:このゲージは日課を完了できなかった場合に溜まります。いっぱいになると、風ドクロの群れは残りの体力の30%を回復してしまいます!",
|
||||
"questMayhemMistiflying1RageEffect": "`風ドクロの群れが、群れの復活を使った!`\n\n勝利に勢いづき、さらにたくさんのドクロが雲の中から飛び出してきます!",
|
||||
"questMayhemMistiflying1DropSkeletonPotion": "骨のたまごがえしの薬",
|
||||
"questMayhemMistiflying1DropWhitePotion": "白いたまごがえしの薬",
|
||||
"questMayhemMistiflying1DropArmor": "ゆかいな虹色の配達人ローブ(鎧)",
|
||||
@@ -530,12 +530,12 @@
|
||||
"questLostMasterclasser2Boss": "メ・クラマシー",
|
||||
"questLostMasterclasser2DropEyewear": "エーテルの仮面(アイウエア)",
|
||||
"questLostMasterclasser3Text": "クラス・マスターの謎 第3部:砂塵都市",
|
||||
"questLostMasterclasser3Notes": "ムダボネ砂漠の灼熱の砂上に夜のとばりが降り、案内役の@AnnDeLune、@Kiwibotと@Katy133たちはあなたを先導して歩きます。何本かの風化した柱が砂丘の影から突きだし、あなたが近づくにつれて、無人に思えていた砂原に奇妙なせわしない音が響き渡りました。<br><br>「不可視の獣ですよ!」そう言うエイプリル・フールは妙にやる気を見せています。「ははっ、もうお分かりですよね!これはもうステルスを極めた真の盗賊の出番に間違いありません」<br><br>「この悪党め、我らのそばに潜伏してはいるのだろうが」レディ・グラシエイトは乗騎を降りて槍を構えます。「もし正面から行くなら、相手を怒らせないようにしてもらいたいものだな。火山の二の舞はごめんだぞ」<br><br>彼はレディに微笑みかけました。「ですが、あの救出劇での貴女は最高に輝いていらっしゃった」<br><br>驚いたことにレディ・グラシエイトはそのお世辞に真っ赤になり、廃墟を調べに足早に立ち去ってしまいました。<br><br>「古代都市の廃墟に見えますね」と@AnnDeLune。「ですが……」<br><br>彼女が言い終わるより速く、空中に轟く転移の門が出現しました。この辺りでは転移魔法は使えなかったはずでは?泡を食って逃げ去る不可視の獣たちの足音が雷鳴のように響く中、空を埋め尽くし叫びをあげるドクロたちの突撃にあなたたちは身構えます。",
|
||||
"questLostMasterclasser3Notes": "ムダボネ砂漠の灼熱の砂上に夜のとばりが降り、案内役の@AnnDeLunと@Kiwibot、@Katy133たちはあなたを先導して歩きます。何本かの風化した柱が砂丘の影から突きだし、あなたが近づくにつれて、無人に思えていた砂原に奇妙なせわしない音が響き渡りました。<br><br>「不可視の獣ですよ!」そう言うエイプリル・フールは妙にやる気を見せています。「ははっ、もうお分かりですよね! これはもうステルスを極めた真の盗賊の出番に間違いありません」<br><br>「この悪党め、我らのそばに潜伏してはいるのだろうが」レディ・グラシエイトは乗騎を降りて槍を構えます。「もし正面から行くなら、相手を怒らせないようにしてもらいたいものだな。火山の二の舞はごめんだぞ」<br><br>彼はレディに微笑みかけました。「ですが、あの救出劇での貴女は最高に輝いていらっしゃった」<br><br>驚いたことにレディ・グラシエイトはそのお世辞に真っ赤になり、廃墟を調べに足早に立ち去ってしまいました。<br><br>「古代都市の廃墟に見えますね」と@AnnDeLune。「ですが……」<br><br>彼女が言い終わるより速く、空中に轟く転移の門が出現しました。この辺りでは転移魔法は使えなかったはずでは? 泡を食って逃げ去る不可視の獣たちの足音が雷鳴のように響く中、空を埋め尽くし叫びをあげるドクロたちの突撃にあなたたちは身構えます。",
|
||||
"questLostMasterclasser3Completion": "エイプリル・フールに砂を吹きかけられ、不意を突かれた最後のドクロはレディ・グラシエイトに背後を取られて見事に打ち砕かれました。あなたがホッと一息ついて見上げたとき、閉じゆく転移の門の向こう側で何者かの影が動いたのが目に付きました。ふとひらめいて、あなたが以前の憑りつきアイテムであるアミュレットを取りだすと、確かにそれは目に見えない誰かに引き寄せられていきます。レディ・グラシエイトとエイプリル・フールの制止の声も聞かず、あなたは閉じる寸前の門へその身を躍らせると、黒々とした虚無の領域へとまっさかさまに落ちていきました。",
|
||||
"questLostMasterclasser3Boss": "虚無ドクロの群れ",
|
||||
"questLostMasterclasser3RageTitle": "群れの復活",
|
||||
"questLostMasterclasser3RageDescription": "群れの復活:日課を完了しないとこのバーが増えていきます。いっぱいになると、虚無ドクロの群れは体力を30%回復してしまいます!",
|
||||
"questLostMasterclasser3RageEffect": "`虚無ドクロの群れは『群れの復活』を使いました!`\n\n勝利に勢いづき、空から更なるドクロが叫びながら急降下してきて、群れを強化しました!",
|
||||
"questLostMasterclasser3RageTitle": "群れの再生",
|
||||
"questLostMasterclasser3RageDescription": "群れの再生:あなたが日課を完了させないとこのバーが増加します。一杯になると、虚無ドクロの群れの体力が30%回復してしまいます!",
|
||||
"questLostMasterclasser3RageEffect": "`虚無ドクロの群れは『群れの再生』を使った!`\n\n勝利に勢いづき、天空より更なるドクロが叫びながら急降下してきて、群れを強化しました!",
|
||||
"questLostMasterclasser3DropBodyAccessory": "エーテルのアミュレット(胴のアクセサリー)",
|
||||
"questLostMasterclasser3DropBasePotion": "普通のたまごがえしの薬",
|
||||
"questLostMasterclasser3DropGoldenPotion": "金のたまごがえしの薬",
|
||||
@@ -806,10 +806,5 @@
|
||||
"questOpalText": "不思議なオパールの伝説",
|
||||
"questOpalCollectOpalGems": "オパールの宝石",
|
||||
"questOpalDropOpalPotion": "オパールのたまごがえしの薬",
|
||||
"questGiraffeCompletion": "キ・リンの物の整理を助けてあげると、なんだか楽しくなって元気が出る!<br><br>キリンはギターを手に取って、教則本を開いて、さっそく音を出してみる。「君のおかげでやる気が出たよ!助けてくれてありがとう!まだまだ先はあるけどね。これ、お礼にあげる!」",
|
||||
"questChameleonCompletion": "何回か元気よく回転すると、カメレオンはあなたのリクエストに応じて虹の七色全てに変色することができました。<br><br>「わぁ、」と彼は言います。「協力して、ゲームにしてみたら、集中力アップできたよ!これ、お礼にあげる。この子ら生まれたら、いろんな色に変色できるように教えてあげてね!」",
|
||||
"questDogBoss": "シバベロス",
|
||||
"questDogRageDescription": "日課を完了しないと、このバーが増えていきます。いっぱいになると、シバベロスはパーティーのマナを減らしてしまいます!",
|
||||
"questDogRageEffect": "シバベロスは一気におもちゃをたくさん投げて、魔法をブロックします!パーティーのマナが減ってしまいました!",
|
||||
"questCrabRageDescription": "日課を完了しないと、このバーが増えていきます。いっぱいになると、いじわるカニはパーティーのマナを減らしてしまいます!"
|
||||
"questGiraffeCompletion": "キ・リンの物の整理を助けてあげると、なんだか楽しくなって元気が出る!<br><br>キリンはギターを手に取って、教則本を開いて、さっそく音を出してみる。「君のおかげでやる気が出たよ!ありがとう!まだまだ先はあるけどね。これ、お礼にあげる!」"
|
||||
}
|
||||
|
||||
@@ -256,7 +256,5 @@
|
||||
"oneMonthGift": "1ヶ月プラン",
|
||||
"selectPayment": "お支払い方法を選択する",
|
||||
"mysterySet202504": "伝説の雪男セット",
|
||||
"mysterySet202506": "太陽の輝きセット",
|
||||
"subscriptionChangeAnnouncement": "<strong>有料プランの特典とそれの付与の方法は11/19から変わります。詳しくは</strong> <%= linkStart %>このページ</a>をご覧ください。",
|
||||
"monthlyGemsLabel": "毎月のジェム"
|
||||
"mysterySet202506": "太陽の輝きセット"
|
||||
}
|
||||
|
||||
@@ -27,9 +27,9 @@
|
||||
"seasonalShopClosedTitle": "<%= linkStart %>레슬리<%= linkEnd %>",
|
||||
"seasonalShopTitle": "<%= linkStart %>계절 마법사<%= linkEnd %>",
|
||||
"seasonalShopClosedText": "The Seasonal Shop is currently closed!! It’s only open during Habitica’s four Grand Galas.",
|
||||
"seasonalShopSummerText": "해피 썸머 스플래시!! 희귀 아이템을 구매하시겠습니까? 축제가 끝나기 전에 꼭 구입하세요!",
|
||||
"seasonalShopFallText": "행복한 가을 축제 되세요!! 희귀 아이템을 구매하시겠습니까? 축제가 끝나기 전에 꼭 구입하세요!",
|
||||
"seasonalShopWinterText": "해피 윈터 원더랜드!! 희귀 아이템을 구매하시겠습니까? 축제가 끝나기 전에 꼭 구입하세요!",
|
||||
"seasonalShopSummerText": "Happy Summer Splash!! Would you like to buy some rare items? They’ll only be available until July 31st!",
|
||||
"seasonalShopFallText": "Happy Fall Festival!! Would you like to buy some rare items? They’ll only be available until October 31st!",
|
||||
"seasonalShopWinterText": "Happy Winter Wonderland!! Would you like to buy some rare items? They’ll only be available until January 31st!",
|
||||
"seasonalShopSpringText": "Happy Spring Fling!! Would you like to buy some rare items? They’ll only be available until April 30th!",
|
||||
"seasonalShopFallTextBroken": "오.... 계절 상점에 오신 것을 환영합니다... 저희는 가을 계절 에디션 상품같은 걸 구비해 두고 있지요... 여기 있는 모든 물건은 매년 가을 축제 기간 동안만 구매하실 수 있어요. 우린 10월 31일까지만 여니까.... 지금 쟁여두지 않으면 당신은 기다리고...기다리고... 또 기다려야 할 거예요...<strong>*한숨*</strong>",
|
||||
"seasonalShopBrokenText": "My pavilion!!!!!!! My decorations!!!! Oh, the Dysheartener's destroyed everything :( Please help defeat it in the Tavern so I can rebuild!",
|
||||
@@ -41,7 +41,7 @@
|
||||
"northMageSet": "북쪽의 마법사 (마법사)",
|
||||
"icicleDrakeSet": "얼음오리 (도적)",
|
||||
"soothingSkaterSet": "Soothing Skater (Healer)",
|
||||
"gingerbreadSet": "진저브레드 (전사)",
|
||||
"gingerbreadSet": "쿠키 전사 (전사)",
|
||||
"snowDaySet": "눈오는 날(전사)",
|
||||
"snowboardingSet": "스노우보딩 마법사(마법사)",
|
||||
"festiveFairySet": "Festive Fairy (Healer)",
|
||||
@@ -56,7 +56,7 @@
|
||||
"nye0": "새해 복 많이 받으세요! 올해도 많은 나쁜 습관들을 죽이길.",
|
||||
"nye1": "새해 복 많이 받으세요! 올해도 많은 보상을 쟁취하시길.",
|
||||
"nye2": "새해 복 많이 받으세요! 올해도 완벽한 날들 많이 이루시길.",
|
||||
"nye3": "새해 복 많이 받으세요! 당신이 해야 할 일이 짧고 달콤하게 유지되기를 바랍니다.",
|
||||
"nye3": "새해 복 많이 받으세요! 당신의 할 일 목록이 짧고 달콤하게 유지되길.",
|
||||
"nye4": "Happy New Year! May you not get attacked by a raging Hippogriff.",
|
||||
"mightyBunnySet": "강한 토끼 (전사)",
|
||||
"magicMouseSet": "마법의 쥐 (마법사)",
|
||||
@@ -130,7 +130,7 @@
|
||||
"winter2019PyrotechnicSet": "Pyrotechnic (Mage)",
|
||||
"winter2019WinterStarSet": "Winter Star (Healer)",
|
||||
"winter2019PoinsettiaSet": "Poinsettia (Rogue)",
|
||||
"winterPromoGiftHeader": "GIFT A SUBSCRIPTION AND GET ONE FREE!",
|
||||
"winterPromoGiftHeader": "GIFT A SUBSCRIPTION AND GET ONE FREE!",
|
||||
"winterPromoGiftDetails1": "Until January 15th only, when you gift somebody a subscription, you get the same subscription for yourself for free!",
|
||||
"winterPromoGiftDetails2": "Please note that if you or your gift recipient already have a recurring subscription, the gifted subscription will only start after that subscription is cancelled or has expired. Thanks so much for your support! <3",
|
||||
"discountBundle": "bundle",
|
||||
@@ -169,6 +169,5 @@
|
||||
"spring2019CloudRogueSet": "클라우드 (도적)",
|
||||
"fall2019OperaticSpecterSet": "오페라 유령 (도적)",
|
||||
"spring2020IrisHealerSet": "아이리스 (힐러)",
|
||||
"winter2020CarolOfTheMageSet": "마법사의 캐롤 (메이지)",
|
||||
"birthdaySet": "생일세트"
|
||||
"winter2020CarolOfTheMageSet": "마법사의 캐롤 (메이지)"
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
"sunsetFaqList10": "Spelers worden tevens aangemoedigd te mailen naar <a href='mailto:admin@habitica.com'>admin@habitica.com</a>met de vragen waarop ze geen antwoord konden vinden in bovenstaande links.",
|
||||
"sunsetFaqPara20": "Habitica's Gemeenschapsrichtlijnen zullen bijgewerkt worden op het moment dat de service Herberg en Gilden ophouden te bestaan. Ze zullen de klemtoon leggen op gemeenschapsregels die in relatie staan tot het spelersprofiel, Uitdagingen en berichten in private ruimtes. Onze Gebruikersvoorwaarden waren steeds van toepassing op zowel publieke als private ruimtes, en vereisen niet meteen een update hierdoor.",
|
||||
"contentQuestion0": "Wat veranderd er?",
|
||||
"commonQuestions": "Veelvoorkomende Vragen",
|
||||
"commonQuestions": "Veelvoorkomende vragen",
|
||||
"faqQuestion25": "Wat zijn de verschillende type taken?",
|
||||
"faqQuestion40": "Wat zijn Edelstenen en hoe kan ik ze krijgen?",
|
||||
"faqQuestion48": "Kan ik Habitica spelen met anderen?",
|
||||
@@ -90,17 +90,5 @@
|
||||
"faqQuestion43": "Hoe kan ik Quests starten?",
|
||||
"webFaqAnswer43": "Om een Quest te starten, moet je lid zijn van een groep. Groepen kunnen solo-avonturen zijn waarin je Quests alleen aangaat, of je kunt andere Habitica-spelers uitnodigen om Quests sneller aan te pakken!\n\nKies een Quest-scroll uit je inventaris door op de knop “Quest starten” te klikken in je groep. Voltooi je taken zoals je normaal doet om voortgang te boeken in de Quest! Je bouwt schade op tegen een monster als je een Baas-Quest doet, of je maakt kans om voorwerpen te vinden als je een Verzamel-Quest doet. Alle opgebouwde voortgang wordt de volgende dag toegepast.\n\nWanneer je genoeg schade hebt aangericht of alle voorwerpen hebt verzameld, is de Quest voltooid en ontvang je je beloningen!",
|
||||
"faqQuestion44": "Hoe kan ik Challenge-taken verwijderen?",
|
||||
"webFaqAnswer44": "Je moet de Challenge verlaten of wachten tot de Challenge wordt afgesloten om de bijbehorende taken te kunnen verwijderen. Een rood megafoonpictogram betekent dat de Challenge is afgesloten, terwijl een grijze megafoon betekent dat de Challenge nog loopt.\n\nOm Challenge-taken te verwijderen in de Android-app:\n1. Tik op een taak die bij de Challenge hoort.\n2. Tik rechtsboven op het scherm op “Verwijderen”.\n3. Kies om de Challenge-taken van je takenlijst te verwijderen.\n\nOm Challenge-taken te verwijderen in de iOS-app:\n1. Zoek de Challenge-taak die je wilt verwijderen en kijk naar het megafoonpictogram.\n2. Als het megafoonpictogram rood is, tik op de taak en selecteer onderaan “Verwijderen”.\n3. Als het megafoonpictogram grijs is, moet je de Challenge opzoeken en deze verlaten om de taak te verwijderen.\n\nOm Challenge-taken te verwijderen op de website:\n1. Zoek de Challenge-taak die je wilt verwijderen en kijk naar het megafoonpictogram.\n2. Als het megafoonpictogram rood is, klik erop en kies om de taken van je takenlijst te verwijderen.\n3. Als het megafoonpictogram grijs is, moet je de Challenge opzoeken en deze verlaten om de taak te verwijderen.",
|
||||
"faqQuestion45": "Mijn personage is veranderd in een sneeuwpop, zeester, bloem of geest. Hoe kan ik terug veranderen?",
|
||||
"webFaqAnswer45": "Één van jouw groepsleden heeft een Seizoens Winkel transformatie voorwerp op jou gebruikt! Jouw personage zal de volgende dag weer normaal worden. Als je de transformatie eerder wilt verwijderen, kan je een tegengif (Zout, Zand, Bloemblaadjes-vrij of Ondoorzichtig drankje) kopen bij Beloningen.",
|
||||
"faqQuestion46": "Hoe meld ik een bug?",
|
||||
"webFaqAnswer46": "Als je denkt dat je een bug bent tegengekomen, laat het ons dan weten!\n\nOm een bug in de mobiele apps te melden:\n *In het menu, selecteer Ondersteuning en tik op ''Hulp krijgen'' en scroll omlaag naar ''Meld een bug''\n\nOm een bug op de website te melden:\n *In het menu, selecteer ''Meld een bug''",
|
||||
"faqQuestion47": "Kan ik gegevens zien om te bekijken hoe goed ik mijn taken en gewoontes heb gedaan?",
|
||||
"webFaqAnswer47": "Op dit moment heeft Habitica geen visuele weergave van je taak gegevens in de loop van de tijd. Echter kan je op de Habitica website je taak gegevens exporteren via het tabblad ''Site Gegevens'' in Instellingen.",
|
||||
"webFaqAnswer48": "Ja, met Groepen! Je kunt je eigen Groep starten of je aansluiten bij een bestaande Groep. Samen spelen met andere Habitica spelers is een geweldige manier om Queestes aan te gaan, versterking te ontvangen door de vaardigheden van Groepsleden en je motivatie een boost te geven door extra verantwoordelijkheid.",
|
||||
"faqQuestion49": "Hoe vind ik een Groep als ik niet bij een Groep ben aangesloten?",
|
||||
"webFaqAnswer49": "Als je Habitica samen met anderen wilt beleven maar geen andere spelers kent, is het zoeken naar een groep je beste optie! Als je al andere spelers kent die in een groep zitten, kun je je @gebruikersnaam met hen delen om uitgenodigd te worden. Je kunt ook een nieuwe groep maken en hen uitnodigen met hun @gebruikersnaam of e-mailadres.\n\nOm een groep te maken of te zoeken, selecteer je “Groep” in het navigatiemenu en kies je vervolgens de optie die voor jou werkt.",
|
||||
"faqQuestion50": "Hoe werkt het zoeken naar een groep?",
|
||||
"webFaqAnswer50": "Nadat je “Zoek naar een groep” hebt geselecteerd, word je toegevoegd aan een lijst met spelers die zich bij een groep willen aansluiten. Groepsleiders kunnen deze lijst bekijken en uitnodigingen versturen. Zodra je een uitnodiging ontvangt, kun je deze via je meldingen accepteren om lid te worden van de groep van jouw keuze!\n\nJe kunt meerdere uitnodigingen voor verschillende groepen krijgen. Je kunt echter maar lid zijn van één groep tegelijk.",
|
||||
"faqQuestion51": "Hoe lang kan ik naar een groep zoeken nadat ik me op de lijst heb gezet?"
|
||||
"webFaqAnswer44": "Je moet de Challenge verlaten of wachten tot de Challenge wordt afgesloten om de bijbehorende taken te kunnen verwijderen. Een rood megafoonpictogram betekent dat de Challenge is afgesloten, terwijl een grijze megafoon betekent dat de Challenge nog loopt.\n\nOm Challenge-taken te verwijderen in de Android-app:\n1. Tik op een taak die bij de Challenge hoort.\n2. Tik rechtsboven op het scherm op “Verwijderen”.\n3. Kies om de Challenge-taken van je takenlijst te verwijderen.\n\nOm Challenge-taken te verwijderen in de iOS-app:\n1. Zoek de Challenge-taak die je wilt verwijderen en kijk naar het megafoonpictogram.\n2. Als het megafoonpictogram rood is, tik op de taak en selecteer onderaan “Verwijderen”.\n3. Als het megafoonpictogram grijs is, moet je de Challenge opzoeken en deze verlaten om de taak te verwijderen.\n\nOm Challenge-taken te verwijderen op de website:\n1. Zoek de Challenge-taak die je wilt verwijderen en kijk naar het megafoonpictogram.\n2. Als het megafoonpictogram rood is, klik erop en kies om de taken van je takenlijst te verwijderen.\n3. Als het megafoonpictogram grijs is, moet je de Challenge opzoeken en deze verlaten om de taak te verwijderen."
|
||||
}
|
||||
|
||||
@@ -2601,48 +2601,5 @@
|
||||
"armorSpecialSummer2022WarriorNotes": "Maak je klaar voor een waterige strijd terwijl je jezelf omringt met deze wervelende, spiralerende kolom van lucht en mist. Verhoogt Constitutie met <%= con %>. Beperkte Editie 2022 Zomeruitrusting.",
|
||||
"armorSpecialSummer2022MageNotes": "Wanneer u dit pantser draagt, glijdt u gemakkelijk door uw werk zoals de mantarog door water glijdt. Verhoogt Intelligentie met <%= int %>. Beperkte Editie 2022 Zomeruitrusting.",
|
||||
"gearItemsCompleted": "Je bezit alle <%= klass %> uitrusting! Nieuwe uitrustingen worden uitgebracht tijdens de seizoensgebonden Gala's.",
|
||||
"moreArmoireGearAvailable": "Tot dan kan je <%= armoireCount %> stukken uitrusting in de Betoverde Kast vinden!",
|
||||
"headAccessoryMystery202307Notes": "Deze machtige Kroon roept cyclonen en stormachtig weer op! Verleent geen voordeel. Juli 2023 Item voor Abonnees.",
|
||||
"headAccessoryMystery202505Text": "Hoogvliegende zwaluwstaartantennes",
|
||||
"headAccessoryMystery202410Text": "Snoepmaïsoorjes",
|
||||
"eyewearMystery202312Text": "Winterse Blauwe Ogen",
|
||||
"headAccessoryMystery202212Notes": "Versterk je warmte en vriendschap tot nieuwe hoogten met deze sierlijke gouden tiara. Verleent geen voordeel. December 2022 Item voor Abonnees.",
|
||||
"headAccessoryMystery202310Text": "Kroon van spookachtige lichten",
|
||||
"eyewearArmoireRoseColoredGlassesText": "Roze bril",
|
||||
"headAccessoryMystery202305Text": "Eventide-hoorns",
|
||||
"headAccessoryMystery202405Text": "Vergulde drakenhoorns",
|
||||
"headAccessoryMystery202302Notes": "Het purr-fecte accessoire om je betoverende glimlach te accentueren. Verleent geen voordeel. Februari 2023 Item voor Abonnees.",
|
||||
"eyewearMystery202503Notes": "Deze doordringende blik zal elke vechter die je durft uit te dagen met angst vervullen! Geeft geen voordeel. Maart 2025 voorwerp voor Abonnees.",
|
||||
"eyewearMystery202503Text": "Jade Juggernaut Ogen",
|
||||
"eyewearMystery202406Text": "Spookpiratenmasker",
|
||||
"headAccessoryMystery202212Text": "Gletsjer Tiara",
|
||||
"headAccessoryMystery202205Text": "Schemervleugelige drakenhoorns",
|
||||
"headAccessoryMystery202203Text": "Onverschrokken Libelle Kroon",
|
||||
"headAccessoryMystery202203Notes": "Heb je een extra snelheidsboost nodig? De kleine decoratieve vleugeltjes op deze Kroon zijn krachtiger dan ze lijken! Verleent geen voordeel. Maart 2022 Item voor Abonnees.",
|
||||
"headAccessoryMystery202307Text": "Kraken's Kroon",
|
||||
"eyewearMystery202201Text": "Middernachtelijk feestvierdersmasker",
|
||||
"eyewearArmoireComedyMaskText": "Komedie Masker",
|
||||
"eyewearArmoireTragedyMaskText": "Tragedie Masker",
|
||||
"eyewearMystery202108Text": "Vurige Ogen",
|
||||
"eyewearMystery202208Text": "Sprankelende Ogen",
|
||||
"eyewearMystery202303Text": "Dromerige Ogen",
|
||||
"eyewearSpecialAnniversaryText": "Habitica Helden Masker",
|
||||
"headAccessorySpecialHeroicCircletText": "Heroïsche Kroon",
|
||||
"bodyArmoireKarateOrangeBeltText": "Oranje band",
|
||||
"headAccessoryMystery202305Notes": "Deze hoorns gloeien door het weerkaatste maanlicht. Verleent geen voordeel. Mei 2023 Item voor Abonnees.",
|
||||
"headAccessoryMystery202205Notes": "Deze schitterende hoorns zijn zo helder als een zonsondergang in de woestijn. Verleent geen voordeel. Mei 2022 Item voor Abonnees.",
|
||||
"eyewearMystery202202Text": "Turquoise Ogen met Blush",
|
||||
"eyewearMystery202204BText": "Virtueel Gezicht",
|
||||
"headAccessoryMystery202302Text": "Bedrieglijke tabbyoren",
|
||||
"bodyArmoireKarateGreenBeltText": "Groene band",
|
||||
"bodyArmoireKaratePurpleBeltText": "Paarse band",
|
||||
"bodyArmoireKarateBlueBeltText": "Blauwe band",
|
||||
"bodyArmoireKarateBrownBeltText": "Bruine band",
|
||||
"bodyArmoireKarateRedBeltText": "Rode band",
|
||||
"headAccessorySpecialHeroicCircletNotes": "Zwaar is het hoofd dat de kroon draagt, maar deze Kroon is zo licht als je genereuze geest. Verhoogt alle statistieken met <%= attrs %>.",
|
||||
"bodyArmoireKarateBlackBeltText": "Zwarte band",
|
||||
"eyewearArmoireJewelersEyeLoupeText": "Juweliersloep",
|
||||
"eyewearArmoireJewelersEyeLoupeNotes": "Deze oogloep vergroot wat je aan het doen bent, zodat je absoluut elk detail kunt zien. Verhoogt de waarneming met <%= per %>. Betoverd Kabinet: juweliersset (item 2 van 4).",
|
||||
"eyewearMystery202308Text": "Slaperige Ogen",
|
||||
"headAccessoryMystery202309Text": "Gigantische antennes van de kometenmot"
|
||||
"moreArmoireGearAvailable": "Tot dan kan je <%= armoireCount %> stukken uitrusting in de Betoverde Kast vinden!"
|
||||
}
|
||||
|
||||
@@ -10,9 +10,9 @@
|
||||
"showTour": "Rondleiding starten",
|
||||
"showBailey": "Bailey laten zien",
|
||||
"showBaileyPop": "Breng Bailey de Stadsomroeper uit haar schuilplaats zodat je nieuws uit het verleden kunt nalezen.",
|
||||
"fixVal": "Personage waarden bijstellen",
|
||||
"fixValPop": "Handmatig veranderen van waarden zoals Gezondheidspunten, Niveau en Goud.",
|
||||
"invalidLevel": "Ongeldige waarde: Niveau moet 1 of hoger zijn.",
|
||||
"fixVal": "Personagewaarden bijstellen",
|
||||
"fixValPop": "Handmatig veranderen van waarden zoals gezondheidspunten, niveau en goud.",
|
||||
"invalidLevel": "Ongeldige waarde: Niveau moet 1 of groter zijn.",
|
||||
"enableClass": "Klassensysteem aanzetten",
|
||||
"enableClassPop": "Aanvankelijk had je geen klasse gekozen. Wil je er nu een kiezen?",
|
||||
"resetAccPop": "Opnieuw starten, en alle niveaus, goud, uitrusting, geschiedenis en taken verliezen.",
|
||||
@@ -195,12 +195,12 @@
|
||||
"passwordSuccess": "Wachtwoord succesvol aangepast",
|
||||
"transaction_admin_update_balance": "Door beheerder gegeven",
|
||||
"giftSubscriptionRateText": "<strong>$<%= price %> USD</strong> voor <strong><%= months %> maanden</strong>",
|
||||
"generalSettings": "Algemene Instellingen",
|
||||
"taskSettings": "Taak Instellingen",
|
||||
"generalSettings": "Algemene instellingen",
|
||||
"taskSettings": "Taak instellingen",
|
||||
"confirmCancelChanges": "Weet je het zeker? Je wijzigingen worden niet opgeslagen.",
|
||||
"loginMethods": "Inlog Methodes",
|
||||
"loginMethods": "Inlog manieren",
|
||||
"character": "Karakter",
|
||||
"siteData": "Website Informatie",
|
||||
"siteData": "Website informatie",
|
||||
"account": "Account",
|
||||
"siteLanguage": "Websitetaal"
|
||||
}
|
||||
|
||||
@@ -330,7 +330,7 @@
|
||||
"backgroundMagicalMuseumText": "Magiczne Muzeum",
|
||||
"backgroundMagicalMuseumNotes": "Zwiedź Magiczne Muzeum.",
|
||||
"backgroundRoseGardenText": "Różany Ogród",
|
||||
"backgroundRoseGardenNotes": "Zmitręż czas w wonnym Różanym Ogrodzie.",
|
||||
"backgroundRoseGardenNotes": "Zmitręż czas w wonny Różanym Ogrodzie.",
|
||||
"backgrounds032018": "Zestaw 46: wypuszczony Marzec 2018",
|
||||
"backgroundGorgeousGreenhouseText": "Przepiękna Szklarnia",
|
||||
"backgroundGorgeousGreenhouseNotes": "Przejdź się pomiędzy florą trzymaną w Przepięknej Szklarni.",
|
||||
|
||||
@@ -78,6 +78,5 @@
|
||||
"faqQuestion39": "Gdzie mogę zdobyć więcej elementów Wyposażenia?",
|
||||
"faqQuestion40": "Czym są Klejnoty i jak je zdobyć?",
|
||||
"faqQuestion41": "Czym są Mistyczne Klepsydry i jak je zdobyć?",
|
||||
"webFaqAnswer39": "Jeśli zastanawiasz się jak uzyskać więcej elementów Wyposażenia, możesz zostać Abonentem Habitica, spróbować szczęścia z Zaczarowaną Skrzynią lub zaszaleć podczas jednej z Wielkich Gal Habitica.\n\nAbonenci Habitica comiesięcznie otrzymują wyjątkowy, ekskluzywny zestaw wyposażenia oraz Mistyczne Klepsydry, służące do zakupu zestawów Wyposażenia z przeszłości z Sklepu Podróżników w Czasie.\n\nZaczarowana Skrzynia w Twoich Nagrodach zawiera ponad 350 elementów Wyposażenia! Za 100 sztuk Złota otrzymasz szansę uzyskania wyjątkowego Wyposażenia, Jedzenia służącego do wychodowania Wierzchowca z Chowańca, bądź Doświadczenia potrzebnego do zdobywania kolejnych poziomów!\n\nPodczas czterech sezonowych Wielkich Gal, zupełnie nowe Wyposażenie dla klas staje się dostępne do kupienia za Złoto, a poprzednie zestawy Galowe można kupić za Klejnoty.",
|
||||
"subscriptionDetail44": "Jestem aktywnym subskrybentem. Ile Diamentów będę mieć do dyspozycji po wprowadzonych zmianach?"
|
||||
"webFaqAnswer39": "Jeśli zastanawiasz się jak uzyskać więcej elementów Wyposażenia, możesz zostać Abonentem Habitica, spróbować szczęścia z Zaczarowaną Skrzynią lub zaszaleć podczas jednej z Wielkich Gal Habitica.\n\nAbonenci Habitica comiesięcznie otrzymują wyjątkowy, ekskluzywny zestaw wyposażenia oraz Mistyczne Klepsydry, służące do zakupu zestawów Wyposażenia z przeszłości z Sklepu Podróżników w Czasie.\n\nZaczarowana Skrzynia w Twoich Nagrodach zawiera ponad 350 elementów Wyposażenia! Za 100 sztuk Złota otrzymasz szansę uzyskania wyjątkowego Wyposażenia, Jedzenia służącego do wychodowania Wierzchowca z Chowańca, bądź Doświadczenia potrzebnego do zdobywania kolejnych poziomów!\n\nPodczas czterech sezonowych Wielkich Gal, zupełnie nowe Wyposażenie dla klas staje się dostępne do kupienia za Złoto, a poprzednie zestawy Galowe można kupić za Klejnoty."
|
||||
}
|
||||
|
||||
@@ -25,32 +25,32 @@
|
||||
"invalidEmail": "Do zresetowania hasła potrzebny jest ważny adres e-mail.",
|
||||
"login": "Zaloguj się",
|
||||
"logout": "Wyloguj",
|
||||
"marketing1Header": "Zbuduj lepsze nawyki, poziom po poziomie!",
|
||||
"marketing1Lead1Title": "Zmień swoje życie w grę",
|
||||
"marketing1Lead1": "Habitica to idealna aplikacja dla każdego, kto ma trudności z listami rzeczy do zrobienia. Używamy znanych mechanik gry, takich jak nagradzanie Złotem, Doświadczeniem i przedmiotami, aby pomóc Ci poczuć się produktywnym i zwiększyć poczucie spełnienia po wykonaniu zadań. Im lepiej radzisz sobie z zadaniami, tym większe postępy robisz w grze.",
|
||||
"marketing1Header": "Popraw swoje nawyki grając w grę",
|
||||
"marketing1Lead1Title": "Twoje życie, jako gra RPG",
|
||||
"marketing1Lead1": "Habitica to gra komputerowa, mająca na celu pomóc Ci w doskonaleniu nawyków życiowych. \"Gamifikuje\" twoje życie, przedstawiając wszystkie zadania (pozytywne Nawyki, Codzienne obowiązki i sprawy Do Zrobienia) jako małe potwory, które musisz pokonać. Im lepiej ci to idzie, tym większe robisz postępy w grze. Jeśli popełnisz błąd w życiu, rozwój twojej postaci zacznie się cofać.",
|
||||
"marketing1Lead2Title": "Zdobądź wyczesane wyposażenie",
|
||||
"marketing1Lead2": "Kupuj miecze, zbroje i wiele więcej za Złoto zdobyte podczas wykonywania zadań. Dzięki setkom elementów do zebrania w grze, nigdy nie zabraknie Ci kombinacji do wypróbowania. Optymalizuj pod kątem statystyk, stylu lub jednego i drugiego! ",
|
||||
"marketing1Lead3Title": "Zostań nagrodzony za swój wysiłek",
|
||||
"marketing1Lead2": "Poprawiaj swoje nawyki, aby stworzyć swojego awatara. Pochwal się wspaniałym wyposażenie, jakie zdobyłeś!",
|
||||
"marketing1Lead3Title": "Znajdź losowe nagrody",
|
||||
"marketing1Lead3": "Dla niektórych to hazard, czyli nagrody stochastyczne, jest systemem ich motywującym. Habitica godzi wszystkie wzmocnienia i kary: pozytywne, negatywne, przewidywalne i losowe.",
|
||||
"marketing2Header": "Połącz siły z przyjaciółmi",
|
||||
"marketing2Lead1Title": "Produktywność społeczna",
|
||||
"marketing2Header": "Konkuruj z przyjaciółmi",
|
||||
"marketing2Lead1Title": "Produktywność Społeczna",
|
||||
"marketing2Lead1": "Choć w Habitica można grać w pojedynkę, prawdziwa zabawa zaczyna się kiedy starasz się współpracować i rywalizować z innymi graczami, którzy rozliczają cię z wykonanych zadań. Najefektywniejszą częścią każdego programu samodoskonalenia jest społeczna odpowiedzialność, a cóż może być lepszym miejscem dla odpowiedzialności i rywalizacji niż gra komputerowa?",
|
||||
"marketing2Lead2Title": "Walcz z potworami podczas Przygód",
|
||||
"marketing2Lead2Title": "Walcz z potworami",
|
||||
"marketing2Lead2": "Czym byłby RPG bez walki? Wraz z drużyną walcz przeciw potworom. Potwory to \"tryb super odpowiedzialności\" - dzień, w którym przegapiasz trening jest dniem, kiedy potwór rani *wszystkich!*",
|
||||
"marketing2Lead3Title": "Rzucajcie sobie wyzwania",
|
||||
"marketing2Lead3": "Wyzwania pozwalają rywalizować z przyjaciółmi i nieznajomymi. Osoba, która najlepiej sobie radziła wygrywa pod koniec wyzwania specjalne nagrody.",
|
||||
"marketing3Header": "Więcej sposobów na korzystanie z Habitica",
|
||||
"marketing3Header": "Aplikacje i rozszerzenia",
|
||||
"marketing3Lead1": "Aplikacje na **iPhone i Androida** pozwalają ci zadbać o wszystko w podróży. Rozumiemy, że logowanie się na stronę by poklikać guziczki może być kłopotliwe.",
|
||||
"marketing3Lead2Title": "Społeczność Open-Source",
|
||||
"marketing3Lead2Title": "Integracje",
|
||||
"marketing3Lead2": "Dodatki **innych firm** wiążą Habitikę z różnymi aspektami Twojego życia. Nasze API zapewnia łatwą integrację dla takich dodatków jak [rozszerzenie dla Chrome](https://chrome.google.com/webstore/detail/habitica/pidkmpibnnnhneohdgjclfdjpijggmjj?hl=en-US), które odbiera Ci punkty za przebywanie na bezproduktywnych stronach internetowych, a dodaje punkty za strony produktywne. [Tutaj dowiesz się więcej](https://habitica.fandom.com/wiki/Extensions,_Add-Ons,_and_Customizations).",
|
||||
"marketing4Header": "Nie tylko obowiązki domowe",
|
||||
"marketing4Header": "Użycie przez organizacje",
|
||||
"marketing4Lead1": "Edukacja jest jedną z najlepszych sfer dla gamifikacji. Wiadomo, że obecnie uczniowie i ich telefony i gry są praktycznie nierozłączni; wykorzystaj to! Niech twoi uczniowie zmierzą się w koleżeńskich pojedynkach. Wyróżniaj dobre zachowanie rzadkimi nagrodami. Obserwuj jak ich oceny i zachowanie się poprawiają.",
|
||||
"marketing4Lead1Title": "Gamifikacja w edukacji",
|
||||
"marketing4Lead2": "Koszty opieki zdrowotnej rosną i coś musi się w końcu zmienić. Tworzone są setki programów mających na celu obniżenie kosztów i poprawę kondycji społeczeństwa. Wierzymy, że Habitica może znacząco pomóc w utrzymaniu zdrowego stylu życia.",
|
||||
"marketing4Lead2Title": "Gamifikacja w zachowaniu dobrego zdrowia i samopoczucia",
|
||||
"marketing4Lead3-1": "Chcesz, by Twoje życie stało się grą?",
|
||||
"marketing4Lead3-2": "Chciałbyś stworzyć grupę w sferze edukacji, ochrony zdrowia lub innej?",
|
||||
"marketing4Lead3Title": "Rozpocznij swoją przygodę!",
|
||||
"marketing4Lead3Title": "Gamifikuj wszystko",
|
||||
"mobileAndroid": "Aplikacja na Androida",
|
||||
"mobileIOS": "Aplikacja na iOS",
|
||||
"oldNews": "Aktualności",
|
||||
@@ -180,8 +180,5 @@
|
||||
"socialAlreadyExists": "Ten login społeczny jest już powiązany z innym kontem.",
|
||||
"emailUsernamePlaceholder": "np. zajączekzwyczajów lub gryf @example.com",
|
||||
"translateHabitica": "Przetłumacz Habitica",
|
||||
"incorrectResetPhrase": "Wpisz <%= magiczne słowo %> wielkimi literami, aby zresetować twoje konto.",
|
||||
"emailBlockedRegistration": "Ten adres e-mail nie może zostać zarejestrowany. Jeżeli sądzisz, że jest to błąd, skontaktuj się z nami przez admin@habitica.com.",
|
||||
"marketing3Lead1Title": "Aplikacje na Androida i iOs",
|
||||
"marketing4Lead3Button": "Zacznij już dzisiaj"
|
||||
"incorrectResetPhrase": "Wpisz <%= magiczne słowo %> wielkimi literami, aby zresetować twoje konto."
|
||||
}
|
||||
|
||||
@@ -1773,9 +1773,5 @@
|
||||
"eyewearArmoireJewelersEyeLoupeText": "Lupa Jubilerska",
|
||||
"moreArmoireGearAvailable": "Do tego czasu, zostało do znalezienia <%= armoireCount %> części wyposażenia w Zaczarowanej Skrzyni!",
|
||||
"gearItemsCompleted": "Jesteś w posiadaniu całego ekwipunku klasy <%= class %>! Nowy ekwipunek jest wydawany podczas Gal sezonowych.",
|
||||
"moreArmoireGearComing": "Zaczarowana Skrzynia również comiesięcznie otrzymuje nowy asortyment!",
|
||||
"weaponSpecialFall2019MageNotes": "Niezależnie od tego, czy chodzi o wykuwanie piorunów, wznoszenie fortyfikacji, czy po prostu wzbudzanie przerażenia w sercach śmiertelników, ta laska daje moc gigantów do czynienia cudów. Zwiększa inteligencję o <%= int %> i percepcję o <%= per %>. Limitowana edycja 2019 Jesienne Wyposażenie.",
|
||||
"weaponSpecialFall2019HealerText": "Przerażające Filakterium",
|
||||
"weaponSpecialFall2019HealerNotes": "To filakterium może wzywać duchy dawno pokonanych zadań i czerpać z ich uzdrawiającej mocy. Zwiększa Inteligencję o <%= int %>. Edycja limitowana 2019 Jesienne Wyposażenie.",
|
||||
"weaponSpecialFall2019MageText": "Jednooka Laska"
|
||||
"moreArmoireGearComing": "Zaczarowana Skrzynia również comiesięcznie otrzymuje nowy asortyment!"
|
||||
}
|
||||
|
||||
@@ -135,7 +135,7 @@
|
||||
"winterPromoGiftDetails2": "Zauważ, że w przypadku gdy Ty, lub osoba dla której wykupujesz subskrypcję posiada już podobny abonament, subskrypcja wykupiona jako prezent rozpocznie się dopiero po anulowaniu subskrypcji lub jej wygaśnięciu. Dziękujemy bardzo za wsparcie! <3",
|
||||
"discountBundle": "pakiet",
|
||||
"g1g1Announcement": "Rozpoczynamy akcję: Sprezentuj subskrypcję, a dostaniesz również dla siebie za darmo!",
|
||||
"g1g1Details": "Sprezentuj subskrypcję przyjacielowi, a otrzymasz taką samą dla siebie za darmo!",
|
||||
"g1g1Details": "Sprezentuj subskrypcję przyjacielowi bezpośrednio z jego profilu, a otrzymasz podobną dla siebie za darmo!",
|
||||
"spring2019OrchidWarriorSet": "Orchidea (Wojownik)",
|
||||
"spring2019AmberMageSet": "Bursztyn (Mag)",
|
||||
"spring2019RobinHealerSet": "Robin (Uzdrowiciel)",
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
"messageMissingEggPotion": "Brakuje Ci takiego jaja lub eliksiru",
|
||||
"messageInvalidEggPotionCombo": "Nie możesz użyć Magicznego Eliksiru Wyklucia na jaju z misji. Spróbuj innego jaja.",
|
||||
"messageAlreadyPet": "Już masz takiego chowańca. Spróbuj innej kombinacji!",
|
||||
"messageHatched": "Z Twojego jaja wykluł się chowaniec! Odwiedź zakładkę Chowańce i Wierzchowce, by wziąć go ze sobą.",
|
||||
"messageHatched": "Z Twojego jaja wykluł się chowaniec! Odwiedź stajnię, by wziąć go ze sobą.",
|
||||
"messageNotEnoughGold": "Nie masz wystarczająco dużo złota",
|
||||
"messageTwoHandedEquip": "Odłożono <%= offHandedText %>, ponieważ <%= twoHandedText %> zajmuje obie ręce.",
|
||||
"messageTwoHandedUnequip": "Używanie <%= twoHandedText %> zajmuje obie ręce, więc trzeba to odłożyć, gdy chcesz zabrać <%= offHandedText %>.",
|
||||
|
||||
@@ -88,9 +88,5 @@
|
||||
"membersParticipating": "<%= accepted %> / <%= invited %> członków bierze udział",
|
||||
"yourPartyIsNotOnQuest": "Twoja drużyna nie jest na Misji",
|
||||
"selectQuest": "Wybierz misję",
|
||||
"sureLeaveInactive": "Czy na pewno chcesz opuścić misję? Nie będziesz w stanie brać udziału.",
|
||||
"ownerOnly": "Wyłącznie dla właściciela",
|
||||
"noQuestToStartTitle": "Nie możesz znaleźć Misji, od której mógłbyś zacząć?",
|
||||
"questItemsPending": "<%= amount %> przedmiotów w toku",
|
||||
"questOwner": "Właściciel Misji"
|
||||
"sureLeaveInactive": "Czy na pewno chcesz opuścić misję? Nie będziesz w stanie brać udziału."
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user