Compare commits
59 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3e0e448250 | ||
|
|
3377f8a916 | ||
|
|
22f83d09c4 | ||
|
|
496534ab4b | ||
|
|
7325d2020e | ||
|
|
1a0d39e566 | ||
|
|
dc62ab7577 | ||
|
|
c4e5633e48 | ||
|
|
d977656e96 | ||
|
|
b0a980d56e | ||
|
|
3d75c99f8d | ||
|
|
0aa16d7021 | ||
|
|
4fa3046104 | ||
|
|
fda4be01b8 | ||
|
|
67436fbef1 | ||
|
|
ffb92a5faa | ||
|
|
4a44d60fac | ||
|
|
122cc510d3 | ||
|
|
29a7a07d14 | ||
|
|
18783aefe3 | ||
|
|
a5e242759c | ||
|
|
e01c6cc9a6 | ||
|
|
8a75383c43 | ||
|
|
47ebee9ae8 | ||
|
|
4e97355110 | ||
|
|
7045b5c214 | ||
|
|
e41dccf6d5 | ||
|
|
d0815ea9ee | ||
|
|
d0bd62bf02 | ||
|
|
39d7581c6c | ||
|
|
fdf146dd01 | ||
|
|
023b3ffd88 | ||
|
|
f01e552637 | ||
|
|
9f11820a02 | ||
|
|
ca6c7b8e5f | ||
|
|
02f8ba1638 | ||
|
|
8eb7c67f12 | ||
|
|
1f895fda44 | ||
|
|
e87c180e9b | ||
|
|
dbf9fd54be | ||
|
|
c9b99d1ecf | ||
|
|
fd8120c80d | ||
|
|
053e75562f | ||
|
|
1d50027f51 | ||
|
|
7fe74fd06a | ||
|
|
173a8561b6 | ||
|
|
f21bef707b | ||
|
|
9cf2ccf7c4 | ||
|
|
77d75c4669 | ||
|
|
1c17b95146 | ||
|
|
d89fc209d1 | ||
|
|
844c8bb3e6 | ||
|
|
569fb11db8 | ||
|
|
7671347d3a | ||
|
|
dc3a02bc2e | ||
|
|
1d8c126687 | ||
|
|
7ee49a43f2 | ||
|
|
900bc8dfc1 | ||
|
|
ec260016d3 |
@@ -47,10 +47,5 @@ gulp.task('build:prod', [
|
||||
'build:server',
|
||||
'prepare:staticNewStuff',
|
||||
'build:client',
|
||||
], (done) => {
|
||||
runSequence(
|
||||
'grunt-build:prod',
|
||||
'apidoc',
|
||||
done
|
||||
);
|
||||
});
|
||||
'apidoc',
|
||||
]);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
var updateStore = require('../website/common/script/libs/updateStore');
|
||||
import { selectGearToPin } from '../website/common/script/ops/pinnedGearUtils';
|
||||
|
||||
var getItemInfo = require('../website/common/script/libs/getItemInfo');
|
||||
|
||||
var migrationName = '20170928_redesign_launch.js';
|
||||
@@ -69,7 +70,7 @@ function updateUser (user) {
|
||||
|
||||
var set = {'migration': migrationName};
|
||||
|
||||
var oldRewardsList = updateStore(user);
|
||||
var oldRewardsList = selectGearToPin(user);
|
||||
var newPinnedItems = [
|
||||
{
|
||||
type: 'armoire',
|
||||
|
||||
97
package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "habitica",
|
||||
"version": "4.0.5",
|
||||
"version": "4.2.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@@ -68,6 +68,15 @@
|
||||
"@types/mime": "1.3.1"
|
||||
}
|
||||
},
|
||||
"JSONStream": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.1.tgz",
|
||||
"integrity": "sha1-cH92HgHa6eFvG8+TcDt4xwlmV5o=",
|
||||
"requires": {
|
||||
"jsonparse": "1.3.1",
|
||||
"through": "2.3.8"
|
||||
}
|
||||
},
|
||||
"abbrev": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.0.tgz",
|
||||
@@ -782,6 +791,14 @@
|
||||
"is-buffer": "1.1.5"
|
||||
}
|
||||
},
|
||||
"axios-progress-bar": {
|
||||
"version": "0.1.7",
|
||||
"resolved": "https://registry.npmjs.org/axios-progress-bar/-/axios-progress-bar-0.1.7.tgz",
|
||||
"integrity": "sha512-xStxJUtcQUH0ulLni5qc8YwvNMTUjO7rmUTsvnxS1bD2GJKEemozP6sJ5OeoC6jjd6MS5FZyx5BlkU/lD8JLUw==",
|
||||
"requires": {
|
||||
"nprogress": "0.2.0"
|
||||
}
|
||||
},
|
||||
"babel-code-frame": {
|
||||
"version": "6.26.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz",
|
||||
@@ -2325,9 +2342,9 @@
|
||||
"resolved": "https://registry.npmjs.org/browser-pack/-/browser-pack-6.0.2.tgz",
|
||||
"integrity": "sha1-+GzWzvT1MAyOY+B6TVEvZfv/RTE=",
|
||||
"requires": {
|
||||
"JSONStream": "1.3.1",
|
||||
"combine-source-map": "0.7.2",
|
||||
"defined": "1.0.0",
|
||||
"JSONStream": "1.3.1",
|
||||
"through2": "2.0.3",
|
||||
"umd": "3.0.1"
|
||||
}
|
||||
@@ -2357,6 +2374,7 @@
|
||||
"resolved": "https://registry.npmjs.org/browserify/-/browserify-12.0.2.tgz",
|
||||
"integrity": "sha1-V/IeXm4wj/WYfE2v1EhAsrmPehk=",
|
||||
"requires": {
|
||||
"JSONStream": "1.3.1",
|
||||
"assert": "1.3.0",
|
||||
"browser-pack": "6.0.2",
|
||||
"browser-resolve": "1.11.2",
|
||||
@@ -2378,7 +2396,6 @@
|
||||
"inherits": "2.0.3",
|
||||
"insert-module-globals": "7.0.1",
|
||||
"isarray": "0.0.1",
|
||||
"JSONStream": "1.3.1",
|
||||
"labeled-stream-splicer": "2.0.0",
|
||||
"module-deps": "4.1.1",
|
||||
"os-browserify": "0.1.2",
|
||||
@@ -7389,13 +7406,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "1.0.1",
|
||||
"bundled": true,
|
||||
"requires": {
|
||||
"safe-buffer": "5.0.1"
|
||||
}
|
||||
},
|
||||
"string-width": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
@@ -7405,6 +7415,13 @@
|
||||
"strip-ansi": "3.0.1"
|
||||
}
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "1.0.1",
|
||||
"bundled": true,
|
||||
"requires": {
|
||||
"safe-buffer": "5.0.1"
|
||||
}
|
||||
},
|
||||
"stringstream": {
|
||||
"version": "0.0.5",
|
||||
"bundled": true,
|
||||
@@ -10353,10 +10370,10 @@
|
||||
"resolved": "https://registry.npmjs.org/insert-module-globals/-/insert-module-globals-7.0.1.tgz",
|
||||
"integrity": "sha1-wDv04BywhtW15azorQr+eInWOMM=",
|
||||
"requires": {
|
||||
"JSONStream": "1.3.1",
|
||||
"combine-source-map": "0.7.2",
|
||||
"concat-stream": "1.5.2",
|
||||
"is-buffer": "1.1.5",
|
||||
"JSONStream": "1.3.1",
|
||||
"lexical-scope": "1.2.0",
|
||||
"process": "0.11.10",
|
||||
"through2": "2.0.3",
|
||||
@@ -11349,15 +11366,6 @@
|
||||
"resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz",
|
||||
"integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk="
|
||||
},
|
||||
"JSONStream": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.1.tgz",
|
||||
"integrity": "sha1-cH92HgHa6eFvG8+TcDt4xwlmV5o=",
|
||||
"requires": {
|
||||
"jsonparse": "1.3.1",
|
||||
"through": "2.3.8"
|
||||
}
|
||||
},
|
||||
"jsprim": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
|
||||
@@ -13257,6 +13265,7 @@
|
||||
"resolved": "https://registry.npmjs.org/module-deps/-/module-deps-4.1.1.tgz",
|
||||
"integrity": "sha1-IyFYM/HaE/1gbMuAh7RIUty4If0=",
|
||||
"requires": {
|
||||
"JSONStream": "1.3.1",
|
||||
"browser-resolve": "1.11.2",
|
||||
"cached-path-relative": "1.0.1",
|
||||
"concat-stream": "1.5.2",
|
||||
@@ -13264,7 +13273,6 @@
|
||||
"detective": "4.5.0",
|
||||
"duplexer2": "0.1.4",
|
||||
"inherits": "2.0.3",
|
||||
"JSONStream": "1.3.1",
|
||||
"parents": "1.0.1",
|
||||
"readable-stream": "2.0.6",
|
||||
"resolve": "1.4.0",
|
||||
@@ -14640,6 +14648,11 @@
|
||||
"set-blocking": "2.0.0"
|
||||
}
|
||||
},
|
||||
"nprogress": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz",
|
||||
"integrity": "sha1-y480xTIT2JVyP8urkH6UIq28r7E="
|
||||
},
|
||||
"nth-check": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.1.tgz",
|
||||
@@ -17234,22 +17247,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"require_optional": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz",
|
||||
"integrity": "sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g==",
|
||||
"requires": {
|
||||
"resolve-from": "2.0.0",
|
||||
"semver": "5.4.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"semver": {
|
||||
"version": "5.4.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz",
|
||||
"integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"require-again": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/require-again/-/require-again-2.0.0.tgz",
|
||||
@@ -17289,6 +17286,22 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"require_optional": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz",
|
||||
"integrity": "sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g==",
|
||||
"requires": {
|
||||
"resolve-from": "2.0.0",
|
||||
"semver": "5.4.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"semver": {
|
||||
"version": "5.4.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz",
|
||||
"integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"requires-port": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
|
||||
@@ -18655,11 +18668,6 @@
|
||||
"resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz",
|
||||
"integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM="
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "0.10.31",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
|
||||
"integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
|
||||
},
|
||||
"string-length": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/string-length/-/string-length-0.1.2.tgz",
|
||||
@@ -18693,6 +18701,11 @@
|
||||
"strip-ansi": "3.0.1"
|
||||
}
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "0.10.31",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
|
||||
"integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
|
||||
},
|
||||
"stringify-object": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-1.0.1.tgz",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "habitica",
|
||||
"description": "A habit tracker app which treats your goals like a Role Playing Game.",
|
||||
"version": "4.0.5",
|
||||
"version": "4.2.0",
|
||||
"main": "./website/server/index.js",
|
||||
"dependencies": {
|
||||
"@slack/client": "^3.8.1",
|
||||
@@ -14,6 +14,7 @@
|
||||
"autoprefixer": "^6.4.0",
|
||||
"aws-sdk": "^2.0.25",
|
||||
"axios": "^0.16.0",
|
||||
"axios-progress-bar": "^0.1.7",
|
||||
"babel-core": "^6.0.0",
|
||||
"babel-eslint": "^7.2.3",
|
||||
"babel-loader": "^6.0.0",
|
||||
|
||||
@@ -56,6 +56,7 @@ describe('POST /user/buy/:key', () => {
|
||||
message: t('messageHealthAlreadyMax'),
|
||||
});
|
||||
});
|
||||
|
||||
it('buys a piece of gear', async () => {
|
||||
let key = 'armor_warrior_1';
|
||||
|
||||
@@ -64,4 +65,21 @@ describe('POST /user/buy/:key', () => {
|
||||
|
||||
expect(user.items.gear.owned.armor_warrior_1).to.eql(true);
|
||||
});
|
||||
|
||||
it('buys a special spell', async () => {
|
||||
let key = 'spookySparkles';
|
||||
let item = content.special[key];
|
||||
|
||||
await user.update({'stats.gp': 250});
|
||||
let res = await user.post(`/user/buy/${key}`);
|
||||
await user.sync();
|
||||
|
||||
expect(res.data).to.eql({
|
||||
items: JSON.parse(JSON.stringify(user.items)), // otherwise dates can't be compared
|
||||
stats: user.stats,
|
||||
});
|
||||
expect(res.message).to.equal(t('messageBought', {
|
||||
itemText: item.text(),
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -105,7 +105,12 @@ const baseConfig = {
|
||||
{
|
||||
test: /\.svg$/,
|
||||
use: [
|
||||
{ loader: 'svg-url-loader' },
|
||||
{
|
||||
loader: 'svg-url-loader',
|
||||
options: {
|
||||
limit: 10000,
|
||||
},
|
||||
},
|
||||
{ loader: 'svgo-loader' },
|
||||
],
|
||||
include: [path.resolve(projectRoot, 'website/client/assets/svg/for-css')],
|
||||
|
||||
@@ -1,24 +1,12 @@
|
||||
.promo_chat_avatars {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: 0px 0px;
|
||||
width: 617px;
|
||||
height: 405px;
|
||||
}
|
||||
.promo_login_screen {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: 0px -406px;
|
||||
background-position: 0px 0px;
|
||||
width: 524px;
|
||||
height: 274px;
|
||||
}
|
||||
.promo_seasonal_shop_fall_2017 {
|
||||
.promo_spooky_sparkles {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -618px 0px;
|
||||
width: 752px;
|
||||
height: 248px;
|
||||
}
|
||||
.promo_veteran_pets_2017 {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -618px -249px;
|
||||
width: 363px;
|
||||
height: 141px;
|
||||
background-position: 0px -275px;
|
||||
width: 140px;
|
||||
height: 294px;
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 77 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 8.0 KiB |
|
Before Width: | Height: | Size: 11 KiB |
@@ -56,6 +56,10 @@
|
||||
</style>
|
||||
|
||||
<style>
|
||||
.modal {
|
||||
overflow-y: scroll !important;
|
||||
}
|
||||
|
||||
.modal-backdrop.show {
|
||||
opacity: 1 !important;
|
||||
background-color: rgba(67, 40, 116, 0.9) !important;
|
||||
@@ -64,6 +68,8 @@
|
||||
|
||||
<script>
|
||||
import axios from 'axios';
|
||||
import { loadProgressBar } from 'axios-progress-bar';
|
||||
|
||||
import AppMenu from './components/appMenu';
|
||||
import AppHeader from './components/appHeader';
|
||||
import AppFooter from './components/appFooter';
|
||||
@@ -125,6 +131,7 @@ export default {
|
||||
this.$refs.sound.load();
|
||||
});
|
||||
|
||||
// @TODO: I'm not sure these should be at the app level. Can we move these back into shop/inventory or maybe they need a lateral move?
|
||||
this.$root.$on('buyModal::showItem', (item) => {
|
||||
this.selectedItemToBuy = item;
|
||||
this.$root.$emit('show::modal', 'buy-modal');
|
||||
@@ -136,14 +143,32 @@ export default {
|
||||
});
|
||||
|
||||
// @TODO split up this file, it's too big
|
||||
|
||||
loadProgressBar();
|
||||
|
||||
// Set up Error interceptors
|
||||
axios.interceptors.response.use((response) => {
|
||||
if (this.user) {
|
||||
if (this.user && response.data && response.data.notifications) {
|
||||
this.$set(this.user, 'notifications', response.data.notifications);
|
||||
}
|
||||
return response;
|
||||
}, (error) => {
|
||||
if (error.response.status >= 400) {
|
||||
// Check for conditions to reset the user auth
|
||||
const invalidUserMessage = [this.$t('invalidCredentials'), 'Missing authentication headers.'];
|
||||
if (invalidUserMessage.indexOf(error.response.data.message) !== -1) {
|
||||
this.$store.dispatch('auth:logout');
|
||||
}
|
||||
|
||||
// Don't show errors from getting user details. These users have delete their account,
|
||||
// but their chat message still exists.
|
||||
let configExists = Boolean(error.response) && Boolean(error.response.config);
|
||||
if (configExists && error.response.config.method === 'get' && error.response.config.url.indexOf('/api/v3/members/') !== -1) {
|
||||
// @TODO: We resolve the promise because we need our caching to cache this user as tried
|
||||
// Chat paging should help this, but maybe we can also find another solution..
|
||||
return Promise.resolve(error);
|
||||
}
|
||||
|
||||
this.$store.state.notificationStore.push({
|
||||
title: 'Habitica',
|
||||
text: error.response.data.message,
|
||||
@@ -301,10 +326,12 @@ export default {
|
||||
}
|
||||
}
|
||||
},
|
||||
memberSelected (member) {
|
||||
async memberSelected (member) {
|
||||
this.$store.dispatch('user:castSpell', {key: this.selectedSpellToBuy.key, targetId: member.id});
|
||||
this.selectedSpellToBuy = null;
|
||||
|
||||
this.$store.dispatch('party:getMembers', {forceLoad: true});
|
||||
|
||||
this.$root.$emit('hide::modal', 'select-member-modal');
|
||||
},
|
||||
hideLoadingScreen () {
|
||||
|
||||
@@ -1,24 +1,12 @@
|
||||
.promo_chat_avatars {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: 0px 0px;
|
||||
width: 617px;
|
||||
height: 405px;
|
||||
}
|
||||
.promo_login_screen {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: 0px -406px;
|
||||
background-position: 0px 0px;
|
||||
width: 524px;
|
||||
height: 274px;
|
||||
}
|
||||
.promo_seasonal_shop_fall_2017 {
|
||||
.promo_spooky_sparkles {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -618px 0px;
|
||||
width: 752px;
|
||||
height: 248px;
|
||||
}
|
||||
.promo_veteran_pets_2017 {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -618px -249px;
|
||||
width: 363px;
|
||||
height: 141px;
|
||||
background-position: 0px -275px;
|
||||
width: 140px;
|
||||
height: 294px;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
@import './loading-screen';
|
||||
|
||||
// Global styles
|
||||
@import './misc';
|
||||
@import './typography';
|
||||
@import './markdown';
|
||||
@import './form';
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
.items > div {
|
||||
display: inline-block;
|
||||
margin-right: 23px;
|
||||
margin-right: 24px;
|
||||
}
|
||||
|
||||
.items > div:last-of-type {
|
||||
|
||||
17
website/client/assets/scss/misc.scss
Normal file
@@ -0,0 +1,17 @@
|
||||
.expand-toggle:after {
|
||||
display: inline-block;
|
||||
width: 0;
|
||||
height: 0;
|
||||
content: "";
|
||||
border-bottom: 4px solid transparent;
|
||||
border-top: 4px solid transparent;
|
||||
border-right: 4px solid transparent;
|
||||
border-left: 4px solid;
|
||||
}
|
||||
|
||||
.expand-toggle.open:after {
|
||||
border-top: 4px solid;
|
||||
border-right: 4px solid transparent;
|
||||
border-left: 4px solid transparent;
|
||||
border-bottom: 0;
|
||||
}
|
||||
3
website/client/assets/svg/checklist.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="8" viewBox="0 0 12 8">
|
||||
<path fill="#878190" fill-rule="evenodd" d="M3 6h9v2H3V6zm0-3h7v2H3V3zm0-3h5v2H3V0zM0 6h2v2H0V6zm0-3h2v2H0V3zm0-3h2v2H0V0z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 219 B |
@@ -6,35 +6,7 @@
|
||||
:hide-footer='true',
|
||||
)
|
||||
.modal-body
|
||||
.media
|
||||
.align-self-center.right-margin(:class='baileyClass')
|
||||
.media-body
|
||||
h1.align-self-center(v-markdown='$t("newStuff")')
|
||||
h2 9/28/2017 - HABITICA'S WEBSITE LEVELS UP!
|
||||
hr
|
||||
p Welcome to the new Habitica! We're so excited to share it with you at last. This project, which has been a labor of love since last December, is the single biggest update that Habitica has ever released (with over 150 pages of designs, an entire rewrite of all of our front-end code, countless rounds of testing and iteration, and many, many meetings). Just refresh your page to load the new website!
|
||||
.promo_login_screen.center-block
|
||||
p(v-markdown="'You can find a full list of changes [here](http://habitica.wikia.com/wiki/Habitica_Redesign_Fact_Sheet), as well as explanations for why we made each, but here are a few quick tips to help you get oriented:'")
|
||||
.grassy-meadow-backdrop
|
||||
.daniel_front
|
||||
ul
|
||||
li There's a ton of new art around the site! Peek at the NPCs and Guild chats to admire some of the changes.
|
||||
li Click directly on your tasks to bring up the edit modal!
|
||||
li The navigation bar contains several changes to be more intuitive for new users, so we recommend taking some time to open the drop-down menus and familiarize yourself with the new locations. Notably, the User menu has moved to an icon in the upper-right corner.
|
||||
li You can now pin any purchasable item in the game to your Rewards. You can pin Backgrounds, too! Just hover over the shop icon and click the pin. When you head back to the tasks page, you'll see it in your Rewards column!
|
||||
li There are lots of new filtering options, especially for Guilds and Challenges!
|
||||
li There are visual upgrades for every aspect of the site, from the front page to the Seasonal Shop. We hope that you like them!
|
||||
li Some of these upgrades have made their way to our <a href='https://itunes.apple.com/us/app/habitica/id994882113?ls=1&mt=8' target='_blank'>iOS</a> and <a href='https://play.google.com/store/apps/details?id=com.habitrpg.android.habitica' target='_blank'>Android</a> apps! Be sure to download the latest updates for the best performance.
|
||||
.seasonal-shop-backdrop
|
||||
.sorceress_front
|
||||
p Have general questions about how the new site works? Come ask in the <a href='https://habitica.com/groups/guild/5481ccf3-5d2d-48a9-a871-70a7380cee5a'>Habitica Help Guild</a>, and we'll be glad to assist! Likewise, if you encounter a persistent bug that isn't fixed by refreshing your page, you can report it in the <a href='https://habitica.com/groups/guild/a29da26b-37de-4a71-b0c6-48e72a900dac'>Report a Bug Guild</a> and we will investigate as soon as possible.
|
||||
p If you have thoughts about the new design, we look forward to hearing them. On <strong>October 12th</strong> we will be opening a Trello card for public comments on the redesign. This delay will give us time to focus our attention on answering general questions and fixing any bugs that might arise. For this reason, we ask that you hold back on sharing your feedback about the new designs until that Trello card is live and linked in a Bailey announcement. Thanks for understanding!
|
||||
.promo_veteran_pets_2017.center-block
|
||||
p This is a major time of change for Habitica, so to thank you for your patience, we've given everyone a Veteran Pet! Newer users have received a Veteran Wolf, and older users have received (depending on which pets they already had) a Veteran Tiger, a Veteran Lion, or a Veteran Bear. Head to the new <a href='https://habitica.com/inventory/stable'>Stable</a> page and filter to the Special Pets section to see the latest addition to your menagerie!"')
|
||||
p We are so excited to continue to build Habitica with you. Now go check it out!
|
||||
p Thank you for playing, and good luck with your tasks <3
|
||||
.small by Apollo, piyorii, TheHollidayInn, Paglias, Negue, Sabe, Alys, viirus, Lemoness, redphoenix, beffymaroo, and all our awesome testers!
|
||||
br
|
||||
new-stuff
|
||||
.modal-footer
|
||||
a.btn.btn-info(href='http://habitica.wikia.com/wiki/Whats_New', target='_blank') {{ this.$t('newsArchive') }}
|
||||
button.btn.btn-default(@click='close()') {{ this.$t('cool') }}
|
||||
@@ -44,86 +16,36 @@
|
||||
<style lang='scss' scoped>
|
||||
@import '~client/assets/scss/static.scss';
|
||||
|
||||
.center-block {
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.grassy-meadow-backdrop {
|
||||
background-image: url('~assets/images/npc/fall/tavern_background.png');
|
||||
background-repeat: repeat-x;
|
||||
width: 100%;
|
||||
height: 246px;
|
||||
}
|
||||
|
||||
.daniel_front {
|
||||
background-image: url('~assets/images/npc/fall/tavern_npc.png');
|
||||
height: 246px;
|
||||
width: 471px;
|
||||
background-repeat: no-repeat;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.seasonal-shop-backdrop {
|
||||
background: url('~assets/images/npc/fall/seasonal_shop_opened_background.png');
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
|
||||
.sorceress_front {
|
||||
background-image: url('~assets/images/npc/fall/seasonal_shop_opened_npc.png');
|
||||
height: 246px;
|
||||
width: 471px;
|
||||
background-repeat: no-repeat;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
padding-top: 2em;
|
||||
}
|
||||
|
||||
.left-margin {
|
||||
margin-left: 1em;
|
||||
}
|
||||
|
||||
.right-margin {
|
||||
margin-right: 1em;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import bModal from 'bootstrap-vue/lib/components/modal';
|
||||
import { mapState } from 'client/libs/store';
|
||||
import markdown from 'client/directives/markdown';
|
||||
import bModal from 'bootstrap-vue/lib/components/modal';
|
||||
import { mapState } from 'client/libs/store';
|
||||
import markdown from 'client/directives/markdown';
|
||||
import newStuff from 'client/components/static/newStuff';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
bModal,
|
||||
},
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
},
|
||||
data () {
|
||||
let worldDmg = {
|
||||
bailey: false,
|
||||
};
|
||||
|
||||
return {
|
||||
baileyClass: {
|
||||
'npc_bailey_broken': worldDmg.bailey, // eslint-disable-line
|
||||
'npc_bailey': !worldDmg.bailey, // eslint-disable-line
|
||||
export default {
|
||||
components: {
|
||||
bModal,
|
||||
newStuff,
|
||||
},
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
},
|
||||
directives: {
|
||||
markdown,
|
||||
},
|
||||
methods: {
|
||||
close () {
|
||||
this.$root.$emit('hide::modal', 'new-stuff');
|
||||
},
|
||||
dismissAlert () {
|
||||
this.$store.dispatch('user:set', {'flags.newStuff': false});
|
||||
this.close();
|
||||
},
|
||||
};
|
||||
},
|
||||
directives: {
|
||||
markdown,
|
||||
},
|
||||
methods: {
|
||||
close () {
|
||||
this.$root.$emit('hide::modal', 'new-stuff');
|
||||
},
|
||||
dismissAlert () {
|
||||
this.$store.dispatch('user:set', {'flags.newStuff': false});
|
||||
this.close();
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
<template lang="pug">
|
||||
b-modal#quest-completed(v-if='user.party.quest.completed', :title="quests[user.party.quest.completed].text() + '' + $t('completed')",
|
||||
size='lg', :hide-footer="true")
|
||||
b-modal#quest-completed(v-if='user.party.quest.completed', :title="title",
|
||||
size='md', :hide-footer="true")
|
||||
.modal-body.text-center
|
||||
div(:class='`quest_${user.party.quest.completed}`')
|
||||
p(v-html='quests[user.party.quest.completed].completion()')
|
||||
.quest(:class='`quest_${user.party.quest.completed}`')
|
||||
p(v-html='this.questData.completion()')
|
||||
.quest-rewards(key='user.party.quest.completed', header-participant="$t('youReceived')", header-quest-owner="$t('questOwnerReceived')")
|
||||
.modal-footer
|
||||
button.btn.btn-primary(@click='setQuestCompleted()') {{ $t('ok') }}
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.quest {
|
||||
margin: 0 auto;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import bModal from 'bootstrap-vue/lib/components/modal';
|
||||
import quests from 'common/script/content/quests';
|
||||
@@ -29,6 +35,12 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
questData () {
|
||||
return this.quests.quests[this.user.party.quest.completed];
|
||||
},
|
||||
title () {
|
||||
return `${this.questData.text()} ${this.$t('completed')}`;
|
||||
},
|
||||
barStyle () {
|
||||
return {
|
||||
width: `${percent(this.user.stats.hp, maxHealth)}%`,
|
||||
|
||||
@@ -49,7 +49,6 @@ import bModal from 'bootstrap-vue/lib/components/modal';
|
||||
|
||||
import quests from 'common/script/content/quests';
|
||||
import { mapState } from 'client/libs/store';
|
||||
import revive from '../../../common/script/ops/revive';
|
||||
import percent from '../../../common/script/libs/percent';
|
||||
import {maxHealth} from '../../../common/script/index';
|
||||
|
||||
@@ -73,11 +72,7 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
close () {
|
||||
this.$root.$emit('hide::modal', 'death');
|
||||
},
|
||||
revive () {
|
||||
// @TODO: Post
|
||||
revive(this.user);
|
||||
this.$root.$emit('hide::modal', 'quest-invitation');
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -4,12 +4,12 @@
|
||||
modify-inventory(v-if="isUserLoaded")
|
||||
footer.container-fluid
|
||||
.row
|
||||
.col-2
|
||||
.col-12.col-md-2
|
||||
h3
|
||||
a(href='https://itunes.apple.com/us/app/habitica/id994882113?ls=1&mt=8', target='_blank') {{ $t('mobileIOS') }}
|
||||
h3
|
||||
a(href='https://play.google.com/store/apps/details?id=com.habitrpg.android.habitica', target='_blank') {{ $t('mobileAndroid') }}
|
||||
.col-2
|
||||
.col-12.col-md-2
|
||||
h3 Company
|
||||
ul
|
||||
li
|
||||
@@ -28,7 +28,7 @@
|
||||
a(href='/static/press-kit') {{ $t('presskit') }}
|
||||
li
|
||||
a(href='/static/contact') {{ $t('contactUs') }}
|
||||
.col-2
|
||||
.col-12.col-md-2
|
||||
h3 Community
|
||||
ul
|
||||
li
|
||||
@@ -47,7 +47,7 @@
|
||||
a(href='https://www.facebook.com/Habitica', target='_blank') {{ $t('communityFacebook') }}
|
||||
li
|
||||
a(href='https://www.reddit.com/r/habitrpg/', target='_blank') {{ $t('communityReddit') }}
|
||||
.col-6
|
||||
.col-12.col-md-6
|
||||
.row
|
||||
.col-6
|
||||
h3 Developers
|
||||
@@ -55,7 +55,7 @@
|
||||
li
|
||||
a(href='/apidoc', target='_blank') {{ $t('APIv3') }}
|
||||
li
|
||||
a(href='http://data.habitrpg.com/?uuid=', target='_blank') {{ $t('dataDisplayTool') }}
|
||||
a(href='https://oldgods.net/habitrpg/habitrpg_user_data_display.html', target='_blank') {{ $t('dataDisplayTool') }}
|
||||
li
|
||||
a(href='http://habitica.wikia.com/wiki/Guidance_for_Blacksmiths', target='_blank') {{ $t('guidanceForBlacksmiths') }}
|
||||
li
|
||||
@@ -82,7 +82,7 @@
|
||||
.row
|
||||
.col-4
|
||||
| © 2017 Habitica. All rights reserved.
|
||||
// .debug.float-left(v-if="!IS_PRODUCTION && isUserLoaded")
|
||||
.debug.float-left(v-if="!IS_PRODUCTION && isUserLoaded")
|
||||
button.btn.btn-primary(@click="debugMenuShown = !debugMenuShown") Toggle Debug Menu
|
||||
.debug-group(v-if="debugMenuShown")
|
||||
a.btn.btn-default(@click="setHealthLow()") Health = 1
|
||||
|
||||
@@ -29,11 +29,27 @@ div
|
||||
br
|
||||
// TODO link to party creation or party page if partying solo
|
||||
button.btn.btn-primary(@click='openPartyModal()') {{ partyMembers && partyMembers.length > 1 ? $t('startAParty') : $t('inviteFriends') }}
|
||||
a.useMobileApp(v-if="isAndroidMobile()", v-once, href="https://play.google.com/store/apps/details?id=com.habitrpg.android.habitica") {{ $t('useMobileApps') }}
|
||||
a.useMobileApp(v-if="isIOSMobile()", v-once, href="https://itunes.apple.com/us/app/habitica-gamified-task-manager/id994882113?mt=8") {{ $t('useMobileApps') }}
|
||||
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '~client/assets/scss/colors.scss';
|
||||
|
||||
.useMobileApp {
|
||||
background: red;
|
||||
color: white;
|
||||
z-index: 10;
|
||||
width: 100%;
|
||||
margin: 10px 5px 0 0;
|
||||
height: 64px;
|
||||
text-align: center;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#app-header {
|
||||
padding-left: 14px;
|
||||
margin-top: 56px;
|
||||
@@ -117,6 +133,7 @@ export default {
|
||||
user: 'user:data',
|
||||
partyMembers: 'party:members',
|
||||
}),
|
||||
|
||||
showHeader () {
|
||||
if (this.$store.state.hideHeader) return false;
|
||||
return true;
|
||||
@@ -129,6 +146,12 @@ export default {
|
||||
...mapActions({
|
||||
getPartyMembers: 'party:getMembers',
|
||||
}),
|
||||
isAndroidMobile () {
|
||||
return navigator.userAgent.match(/Android/i);
|
||||
},
|
||||
isIOSMobile () {
|
||||
return navigator.userAgent.match(/iPhone|iPad|iPod/i);
|
||||
},
|
||||
expandMember (memberId) {
|
||||
if (this.expandedMember === memberId) {
|
||||
this.expandedMember = null;
|
||||
|
||||
@@ -3,9 +3,10 @@ div
|
||||
inbox-modal
|
||||
creator-intro
|
||||
profile
|
||||
nav.navbar.navbar-inverse.fixed-top.navbar-toggleable-lg
|
||||
nav.navbar.navbar-inverse.fixed-top.navbar-toggleable-md
|
||||
.navbar-header
|
||||
.logo.svg-icon(v-html="icons.logo")
|
||||
.logo.svg-icon.hidden-lg-down(v-html="icons.logo")
|
||||
.svg-icon.gryphon.hidden-xl-up
|
||||
b-collapse#nav_collapse.collapse.navbar-collapse(is-nav)
|
||||
ul.navbar-nav.mr-auto
|
||||
router-link.nav-item(tag="li", :to="{name: 'tasks'}", exact)
|
||||
@@ -52,7 +53,7 @@ div
|
||||
a.dropdown-item(href="https://trello.com/c/odmhIqyW/440-read-first-table-of-contents", target='_blank') {{ $t('requestAF') }}
|
||||
a.dropdown-item(href="http://habitica.wikia.com/wiki/Contributing_to_Habitica", target='_blank') {{ $t('contributing') }}
|
||||
a.dropdown-item(href="http://habitica.wikia.com/wiki/Habitica_Wiki", target='_blank') {{ $t('wiki') }}
|
||||
.item-with-icon(v-if="userHourglasses != 0")
|
||||
.item-with-icon(v-if="userHourglasses > 0")
|
||||
.svg-icon(v-html="icons.hourglasses")
|
||||
span {{ userHourglasses }}
|
||||
.item-with-icon
|
||||
@@ -93,58 +94,34 @@ div
|
||||
@import '~client/assets/scss/colors.scss';
|
||||
@import '~client/assets/scss/utils.scss';
|
||||
|
||||
/* Less than Desktops and laptops ----------- */
|
||||
@media only screen and (max-width : 1224px) {
|
||||
#nav_collapse {
|
||||
background: $purple-100;
|
||||
margin-top: 1em;
|
||||
margin-left: 70%;
|
||||
padding-bottom: 1em;
|
||||
|
||||
a {
|
||||
padding: .5em !important;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
position: absolute !important;
|
||||
left: -10em;
|
||||
top: -.5em;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width : 1224px) and (min-width: 1200px) {
|
||||
#nav_collapse {
|
||||
margin-top: 37em !important;
|
||||
|
||||
a {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.navbar-collapse.collapse {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.navbar-collapse.collapse.show {
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
.navbar-toggler, .navbar-nav {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.navbar-toggleable-lg .navbar-collapse {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1280px) {
|
||||
.nav-link {
|
||||
padding: .8em 1em !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1200px) {
|
||||
.navbar-header {
|
||||
margin-right: 0px;
|
||||
}
|
||||
|
||||
.gryphon {
|
||||
background-image: url('~assets/images/melior@3x.png');
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
background-size: cover;
|
||||
color: $white;
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 990px) {
|
||||
#nav_collapse {
|
||||
margin-top: 1.3em;
|
||||
background-color: $purple-200;
|
||||
}
|
||||
}
|
||||
|
||||
nav.navbar {
|
||||
background: $purple-100 url(~assets/svg/for-css/bits.svg) right no-repeat;
|
||||
padding-left: 25px;
|
||||
@@ -354,7 +331,6 @@ export default {
|
||||
hourglasses: svgHourglasses,
|
||||
logo,
|
||||
}),
|
||||
groupPlans: [],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@@ -364,6 +340,7 @@ export default {
|
||||
...mapState({
|
||||
user: 'user.data',
|
||||
userHourglasses: 'user.data.purchased.plan.consecutive.trinkets',
|
||||
groupPlans: 'groupPlans',
|
||||
}),
|
||||
},
|
||||
mounted () {
|
||||
@@ -390,7 +367,7 @@ export default {
|
||||
this.$root.$emit('show::modal', 'profile');
|
||||
},
|
||||
async getUserGroupPlans () {
|
||||
this.groupPlans = await this.$store.dispatch('guilds:getGroupPlans');
|
||||
this.$store.state.groupPlans = await this.$store.dispatch('guilds:getGroupPlans');
|
||||
},
|
||||
openPartyModal () {
|
||||
this.$root.$emit('show::modal', 'create-party-modal');
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
h1(v-markdown='challenge.name')
|
||||
div
|
||||
strong(v-once) {{$t('createdBy')}}:
|
||||
span {{challenge.leader.profile.name}}
|
||||
span(v-if='challenge.leader && challenge.leader.profile') {{challenge.leader.profile.name}}
|
||||
// @TODO: make challenge.author a variable inside the createdBy string (helps with RTL languages)
|
||||
// @TODO: Implement in V2 strong.margin-left(v-once)
|
||||
.svg-icon.calendar-icon(v-html="icons.calendarIcon")
|
||||
@@ -35,7 +35,7 @@
|
||||
b-dropdown.create-dropdown(text="Select a Participant")
|
||||
b-dropdown-item(v-for="member in members", :key="member._id", @click="openMemberProgressModal(member._id)")
|
||||
| {{ member.profile.name }}
|
||||
span(v-if='isLeader')
|
||||
span(v-if='isLeader || isAdmin')
|
||||
b-dropdown.create-dropdown(:text="$t('addTaskToChallenge')", :variant="'success'")
|
||||
b-dropdown-item(v-for="type in columns", :key="type", @click="createTask(type)")
|
||||
| {{$t(type)}}
|
||||
@@ -51,7 +51,7 @@
|
||||
)
|
||||
|
||||
.row
|
||||
task-column.col-6(
|
||||
task-column.col-12.col-sm-6(
|
||||
v-for="column in columns",
|
||||
:type="column",
|
||||
:key="column",
|
||||
@@ -64,13 +64,13 @@
|
||||
button.btn.btn-success(v-once, @click='joinChallenge()') {{$t('joinChallenge')}}
|
||||
div(v-if='isMember')
|
||||
button.btn.btn-danger(v-once, @click='leaveChallenge()') {{$t('leaveChallenge')}}
|
||||
div(v-if='isLeader')
|
||||
div(v-if='isLeader || isAdmin')
|
||||
button.btn.btn-secondary(v-once, @click='edit()') {{$t('editChallenge')}}
|
||||
div(v-if='isLeader')
|
||||
div(v-if='isLeader || isAdmin')
|
||||
button.btn.btn-danger(v-once, @click='closeChallenge()') {{$t('endChallenge')}}
|
||||
div(v-if='isLeader')
|
||||
div(v-if='isLeader || isAdmin')
|
||||
button.btn.btn-secondary(v-once, @click='exportChallengeCsv()') {{$t('exportChallengeCsv')}}
|
||||
div(v-if='isLeader')
|
||||
div(v-if='isLeader || isAdmin')
|
||||
button.btn.btn-secondary(v-once, @click='cloneChallenge()') {{$t('clone')}}
|
||||
.description-section
|
||||
h2 {{$t('challengeSummary')}}
|
||||
@@ -246,6 +246,9 @@ export default {
|
||||
if (!this.challenge.leader) return false;
|
||||
return this.user._id === this.challenge.leader._id;
|
||||
},
|
||||
isAdmin () {
|
||||
return Boolean(this.user.contributor.admin);
|
||||
},
|
||||
canJoin () {
|
||||
return !this.isMember;
|
||||
},
|
||||
@@ -403,6 +406,7 @@ export default {
|
||||
cloneChallenge () {
|
||||
this.cloning = true;
|
||||
this.$store.state.challengeOptions.tasksToClone = this.tasksByType;
|
||||
this.$store.state.challengeOptions.workingChallenge = Object.assign({}, this.$store.state.challengeOptions.workingChallenge, this.challenge);
|
||||
this.$root.$emit('show::modal', 'challenge-modal');
|
||||
},
|
||||
},
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
.card
|
||||
.row
|
||||
router-link.col-12(:to="{ name: 'challenge', params: { challengeId: challenge._id } }")
|
||||
h3 {{challenge.name}}
|
||||
h3(v-markdown='challenge.name')
|
||||
.row
|
||||
.col-6
|
||||
div.details
|
||||
@@ -163,6 +163,7 @@ import habitIcon from 'assets/svg/habit.svg';
|
||||
import todoIcon from 'assets/svg/todo.svg';
|
||||
import dailyIcon from 'assets/svg/daily.svg';
|
||||
import rewardIcon from 'assets/svg/reward.svg';
|
||||
import markdownDirective from 'client/directives/markdown';
|
||||
|
||||
export default {
|
||||
props: ['challenge'],
|
||||
@@ -179,5 +180,8 @@ export default {
|
||||
}),
|
||||
};
|
||||
},
|
||||
directives: {
|
||||
markdown: markdownDirective,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
.form-group
|
||||
label
|
||||
strong(v-once) {{$t('challengeDescription')}} *
|
||||
a.float-right {{ $t('markdownFormattingHelp') }}
|
||||
a.float-right(v-markdown='$t("markdownFormattingHelp")')
|
||||
textarea.description-textarea.form-control(:placeholder="$t('challengeDescriptionPlaceholder')", v-model="workingChallenge.description")
|
||||
.form-group(v-if='creating')
|
||||
label
|
||||
@@ -136,6 +136,8 @@ import bDropdown from 'bootstrap-vue/lib/components/dropdown';
|
||||
import bDropdownItem from 'bootstrap-vue/lib/components/dropdown-item';
|
||||
import bFormInput from 'bootstrap-vue/lib/components/form-input';
|
||||
|
||||
import markdownDirective from 'client/directives/markdown';
|
||||
|
||||
import { TAVERN_ID, MIN_SHORTNAME_SIZE_FOR_CHALLENGES, MAX_SUMMARY_SIZE_FOR_CHALLENGES } from '../../../common/script/constants';
|
||||
import { mapState } from 'client/libs/store';
|
||||
|
||||
@@ -147,6 +149,9 @@ export default {
|
||||
bDropdownItem,
|
||||
bFormInput,
|
||||
},
|
||||
directives: {
|
||||
markdown: markdownDirective,
|
||||
},
|
||||
data () {
|
||||
let categoryOptions = [
|
||||
{
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
.svg-icon.positive-icon(v-html="icons.positiveIcon")
|
||||
span(v-once) {{$t('createChallenge')}}
|
||||
.row
|
||||
.col-6(v-for='challenge in filteredChallenges', v-if='!memberOf(challenge)')
|
||||
.col-12.col-md-6(v-for='challenge in filteredChallenges', v-if='!memberOf(challenge)')
|
||||
challenge-item(:challenge='challenge')
|
||||
</template>
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
p(v-once) {{$t('challengeDescription2')}}
|
||||
|
||||
.row
|
||||
.col-6(v-for='challenge in filteredChallenges')
|
||||
.col-12.col-md-6(v-for='challenge in filteredChallenges')
|
||||
challenge-item(:challenge='challenge')
|
||||
</template>
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template lang="pug">
|
||||
.col-2.standard-sidebar
|
||||
.col-2.standard-sidebar.hidden-xs-down
|
||||
.form-group
|
||||
input.form-control.search(type="text", :placeholder="$t('search')", v-model='searchTerm')
|
||||
|
||||
|
||||
@@ -5,14 +5,14 @@
|
||||
copy-as-todo-modal(:copying-message='copyingMessage', :group-name='groupName', :group-id='groupId')
|
||||
report-flag-modal
|
||||
|
||||
div(v-for="(msg, index) in chat", v-if='chat && canViewFlag(msg)')
|
||||
div(v-for="(msg, index) in messages", v-if='chat && canViewFlag(msg)')
|
||||
// @TODO: is there a different way to do these conditionals? This creates an infinite loop
|
||||
//.hr(v-if='displayDivider(msg)')
|
||||
.hr-middle(v-once) {{ msg.timestamp }}
|
||||
.row(v-if='user._id !== msg.uuid')
|
||||
div(:class='inbox ? "col-4" : "col-2"')
|
||||
avatar(
|
||||
v-if='cachedProfileData[msg.uuid]',
|
||||
v-if='cachedProfileData[msg.uuid] && !cachedProfileData[msg.uuid].rejected',
|
||||
:member="cachedProfileData[msg.uuid]",
|
||||
:avatarOnly="true",
|
||||
:hideClassBadge='true',
|
||||
@@ -36,13 +36,16 @@
|
||||
.svg-icon(v-html="icons.like")
|
||||
span(v-if='!msg.likes[user._id]') {{ $t('like') }}
|
||||
span(v-if='msg.likes[user._id]') {{ $t('liked') }}
|
||||
span.action( @click='copyAsTodo(msg)')
|
||||
.svg-icon(v-html="icons.copy")
|
||||
| {{$t('copyAsTodo')}}
|
||||
span.action(v-if='user.contributor.admin || (msg.uuid !== user._id && user.flags.communityGuidelinesAccepted)', @click='report(msg)')
|
||||
// @TODO make copyAsTodo work in Tavern, guilds, party (inbox can be done later)
|
||||
span.action(v-if='!inbox', @click='copyAsTodo(msg)')
|
||||
.svg-icon(v-html="icons.copy")
|
||||
| {{$t('copyAsTodo')}}
|
||||
// @TODO make copyAsTodo work in the inbox
|
||||
span.action(v-if='!inbox && user.flags.communityGuidelinesAccepted', @click='report(msg)')
|
||||
.svg-icon(v-html="icons.report")
|
||||
| {{$t('report')}}
|
||||
span.action(v-if='msg.uuid === user._id || inbox', @click='remove(msg, index)')
|
||||
// @TODO make flagging/reporting work in the inbox. NOTE: it must work even if the communityGuidelines are not accepted and it MUST work for messages that you have SENT as well as received. -- Alys
|
||||
span.action(v-if='msg.uuid === user._id || inbox || user.contributor.admin', @click='remove(msg, index)')
|
||||
.svg-icon(v-html="icons.delete")
|
||||
| {{$t('delete')}}
|
||||
span.action.float-right.liked(v-if='likeCount(msg) > 0')
|
||||
@@ -70,12 +73,16 @@
|
||||
.svg-icon(v-html="icons.like")
|
||||
span(v-if='!msg.likes[user._id]') {{ $t('like') }}
|
||||
span(v-if='msg.likes[user._id]') {{ $t('liked') }}
|
||||
span.action( @click='copyAsTodo(msg)')
|
||||
.svg-icon(v-html="icons.copy")
|
||||
| {{$t('copyAsTodo')}}
|
||||
// @TODO make copyAsTodo work in Tavern, guilds, party (inbox can be done later)
|
||||
span.action(v-if='!inbox', @click='copyAsTodo(msg)')
|
||||
.svg-icon(v-html="icons.copy")
|
||||
| {{$t('copyAsTodo')}}
|
||||
// @TODO make copyAsTodo work in the inbox
|
||||
span.action(v-if='user.flags.communityGuidelinesAccepted', @click='report(msg)')
|
||||
span.action(v-if='!inbox && user.flags.communityGuidelinesAccepted', @click='report(msg)')
|
||||
.svg-icon(v-html="icons.report")
|
||||
| {{$t('report')}}
|
||||
// @TODO make flagging/reporting work in the inbox. NOTE: it must work even if the communityGuidelines are not accepted and it MUST work for messages that you have SENT as well as received. -- Alys
|
||||
span.action(v-if='msg.uuid === user._id', @click='remove(msg, index)')
|
||||
.svg-icon(v-html="icons.delete")
|
||||
| {{$t('delete')}}
|
||||
@@ -84,7 +91,7 @@
|
||||
| + {{ likeCount(msg) }}
|
||||
div(:class='inbox ? "col-4" : "col-2"')
|
||||
avatar(
|
||||
v-if='cachedProfileData[msg.uuid]',
|
||||
v-if='cachedProfileData[msg.uuid] && !cachedProfileData[msg.uuid].rejected',
|
||||
:member="cachedProfileData[msg.uuid]",
|
||||
:avatarOnly="true",
|
||||
:hideClassBadge='true',
|
||||
@@ -238,7 +245,7 @@ import axios from 'axios';
|
||||
import moment from 'moment';
|
||||
import cloneDeep from 'lodash/cloneDeep';
|
||||
import { mapState } from 'client/libs/store';
|
||||
import throttle from 'lodash/throttle';
|
||||
import debounce from 'lodash/debounce';
|
||||
import markdownDirective from 'client/directives/markdown';
|
||||
import Avatar from '../avatar';
|
||||
import styleHelper from 'client/mixins/styleHelper';
|
||||
@@ -277,12 +284,10 @@ export default {
|
||||
this.loadProfileCache();
|
||||
},
|
||||
created () {
|
||||
window.addEventListener('scroll', throttle(() => {
|
||||
this.loadProfileCache(window.scrollY / 1000);
|
||||
}, 1000));
|
||||
window.addEventListener('scroll', this.handleScroll);
|
||||
},
|
||||
destroyed () {
|
||||
// window.removeEventListener('scroll', this.handleScroll);
|
||||
window.removeEventListener('scroll', this.handleScroll);
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
@@ -308,6 +313,7 @@ export default {
|
||||
cachedProfileData: {},
|
||||
currentProfileLoadedCount: 0,
|
||||
currentProfileLoadedEnd: 10,
|
||||
loading: false,
|
||||
};
|
||||
},
|
||||
filters: {
|
||||
@@ -321,17 +327,22 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
// @TODO: We need a different lazy load mechnism.
|
||||
// But honestly, adding a paging route to chat would solve this
|
||||
messages () {
|
||||
return this.chat;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
messages () {
|
||||
// @TODO: MAybe we should watch insert and remove?
|
||||
messages (oldValue, newValue) {
|
||||
if (newValue.length === oldValue.length) return;
|
||||
this.loadProfileCache();
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
handleScroll () {
|
||||
this.loadProfileCache(window.scrollY / 1000);
|
||||
},
|
||||
isUserMentioned (message) {
|
||||
let user = this.user;
|
||||
|
||||
@@ -358,7 +369,10 @@ export default {
|
||||
if (!message.flagCount || message.flagCount < 2) return true;
|
||||
return this.user.contributor.admin;
|
||||
},
|
||||
async loadProfileCache (screenPosition) {
|
||||
loadProfileCache: debounce(function loadProfileCache (screenPosition) {
|
||||
this._loadProfileCache(screenPosition);
|
||||
}, 1000),
|
||||
async _loadProfileCache (screenPosition) {
|
||||
let promises = [];
|
||||
|
||||
// @TODO: write an explination
|
||||
@@ -368,11 +382,10 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
// @TODO: Not sure we need this hash
|
||||
let aboutToCache = {};
|
||||
this.messages.forEach(message => {
|
||||
let uuid = message.uuid;
|
||||
if (uuid && !this.cachedProfileData[uuid] && !aboutToCache[uuid]) {
|
||||
if (Boolean(uuid) && !this.cachedProfileData[uuid] && !aboutToCache[uuid]) {
|
||||
if (uuid === 'system' || this.currentProfileLoadedCount === this.currentProfileLoadedEnd) return;
|
||||
aboutToCache[uuid] = {};
|
||||
promises.push(axios.get(`/api/v3/members/${uuid}`));
|
||||
@@ -382,9 +395,21 @@ export default {
|
||||
|
||||
let results = await Promise.all(promises);
|
||||
results.forEach(result => {
|
||||
// We could not load the user. Maybe they were deleted. So, let's cache empty so we don't try again
|
||||
if (!result || !result.data || result.status >= 400) {
|
||||
return;
|
||||
}
|
||||
|
||||
let userData = result.data.data;
|
||||
this.$set(this.cachedProfileData, userData._id, userData);
|
||||
});
|
||||
|
||||
// Merge in any attempts that were rejected so we don't attempt again
|
||||
for (let uuid in aboutToCache) {
|
||||
if (!this.cachedProfileData[uuid]) {
|
||||
this.$set(this.cachedProfileData, uuid, {rejected: true});
|
||||
}
|
||||
}
|
||||
},
|
||||
displayDivider (message) {
|
||||
if (this.currentDayDividerDisplay !== moment(message.timestamp).day()) {
|
||||
@@ -447,10 +472,15 @@ export default {
|
||||
});
|
||||
},
|
||||
showMemberModal (memberId) {
|
||||
// @TODO move to action or anyway move from here because it's super duplicate
|
||||
this.$store.state.profileUser = this.cachedProfileData[memberId];
|
||||
this.$store.state.profileOptions.startingPage = 'profile';
|
||||
this.$root.$emit('show::modal', 'profile');
|
||||
const profile = this.cachedProfileData[memberId];
|
||||
|
||||
// Open the modal only if the data is available
|
||||
if (profile && !profile.rejected) {
|
||||
// @TODO move to action or anyway move from here because it's super duplicate
|
||||
this.$store.state.profileUser = profile;
|
||||
this.$store.state.profileOptions.startingPage = 'profile';
|
||||
this.$root.$emit('show::modal', 'profile');
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1350,7 +1350,15 @@ export default {
|
||||
cost = fullSet ? 1.25 : 0.5; // (Hair, skin, etc) 5G per set, 2G per individual
|
||||
}
|
||||
|
||||
let loginIncentives = ['background.blue', 'background.green', 'background.red', 'background.purple', 'background.yellow', 'background.violet'];
|
||||
let loginIncentives = [
|
||||
'background.blue',
|
||||
'background.green',
|
||||
'background.red',
|
||||
'background.purple',
|
||||
'background.yellow',
|
||||
'background.violet',
|
||||
];
|
||||
|
||||
if (loginIncentives.indexOf(path) === -1) {
|
||||
if (fullSet) {
|
||||
if (confirm(this.$t('purchaseFor', {cost: cost * 4})) !== true) return;
|
||||
|
||||
74
website/client/components/group-plans/billing.vue
Normal file
@@ -0,0 +1,74 @@
|
||||
<template lang="pug">
|
||||
.row.standard-page(v-if='groupIsSubscribed && isLeader')
|
||||
.col-12.col-md-6.offset-md-3
|
||||
table.table.alert.alert-info
|
||||
tr(v-if='group.purchased.plan.dateTerminated')
|
||||
td.alert.alert-warning
|
||||
span.noninteractive-button.btn-danger {{ $t('canceledGroupPlan') }}
|
||||
i.glyphicon.glyphicon-time {{ $t('groupPlanCanceled') }}
|
||||
strong {{dateTerminated}}
|
||||
tr(v-if='!group.purchased.plan.dateTerminated')
|
||||
td
|
||||
h3 {{ $t('paymentDetails') }}
|
||||
p(v-if='group.purchased.plan.planId') {{ $t('groupSubscriptionPrice') }}
|
||||
tr(v-if='group.purchased.plan.extraMonths')
|
||||
td
|
||||
span.glyphicon.glyphicon-credit-card
|
||||
| {{ $t('purchasedGroupPlanPlanExtraMonths', purchasedGroupPlanPlanExtraMonths) }}
|
||||
tr(v-if='group.purchased.plan.consecutive.count || group.purchased.plan.consecutive.offset')
|
||||
td
|
||||
span.glyphicon.glyphicon-forward
|
||||
| {{ $t('consecutiveSubscription') }}
|
||||
ul.list-unstyled
|
||||
li {{ $t('consecutiveMonths') }} {{group.purchased.plan.consecutive.count + group.purchased.plan.consecutive.offset}}
|
||||
li {{ $t('gemCapExtra') }} {{group.purchased.plan.consecutive.gemCapExtra}}
|
||||
li {{ $t('mysticHourglasses') }} {{group.purchased.plan.consecutive.trinkets}}
|
||||
.col-12.col-md-6.offset-md-3
|
||||
.btn.btn-primary(v-if='!group.purchased.plan.dateTerminated && group.purchased.plan.paymentMethod === "Stripe"',
|
||||
@click='showStripeEdit({groupId: group.id})') {{ $t('subUpdateCard') }}
|
||||
.btn.btn-sm.btn-danger(v-if='!group.purchased.plan.dateTerminated',
|
||||
@click='cancelSubscription({group: group})') {{ $t('cancelGroupSub') }}
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import moment from 'moment';
|
||||
import { mapState } from 'client/libs/store';
|
||||
import paymentsMixin from 'client/mixins/payments';
|
||||
|
||||
export default {
|
||||
mixins: [paymentsMixin],
|
||||
props: ['groupId'],
|
||||
data () {
|
||||
return {
|
||||
group: {},
|
||||
};
|
||||
},
|
||||
mounted () {
|
||||
this.loadGroup();
|
||||
},
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
isLeader () {
|
||||
return this.user._id === this.group.leader._id;
|
||||
},
|
||||
groupIsSubscribed () {
|
||||
return this.group.purchased && this.group.purchased.plan && this.group.purchased.plan.customerId;
|
||||
},
|
||||
dateTerminated () {
|
||||
if (!this.user.preferences || !this.user.preferences.dateFormat) return moment(this.group.purchased.plan.dateTerminated);
|
||||
return moment(this.group.purchased.plan.dateTerminated).format(this.user.preferences.dateFormat.toUpperCase());
|
||||
},
|
||||
purchasedGroupPlanPlanExtraMonths () {
|
||||
return {
|
||||
months: parseFloat(this.group.purchased.plan.extraMonths).toFixed(2),
|
||||
};
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async loadGroup () {
|
||||
let group = await this.$store.dispatch('guilds:getGroup', {groupId: this.groupId});
|
||||
this.group = Object.assign({}, group);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -2,9 +2,14 @@
|
||||
.row
|
||||
secondary-menu.col-12
|
||||
router-link.nav-link(:to="{name: 'groupPlanDetailTaskInformation', params: {groupId}}",
|
||||
exact, :class="{'active': $route.name === 'groupPlanDetailTaskInformation'}") Task Board
|
||||
exact, :class="{'active': $route.name === 'groupPlanDetailTaskInformation'}") {{ $t('groupTaskBoard') }}
|
||||
router-link.nav-link(:to="{name: 'groupPlanDetailInformation', params: {groupId}}",
|
||||
exact, :class="{'active': $route.name === 'groupPlanDetailInformation'}") Group Information
|
||||
exact, :class="{'active': $route.name === 'groupPlanDetailInformation'}") {{ $t('groupInformation') }}
|
||||
router-link.nav-link(
|
||||
v-if='isLeader',
|
||||
:to="{name: 'groupPlanBilling', params: {groupId}}",
|
||||
exact,
|
||||
:class="{'active': $route.name === 'groupPlanBilling'}") {{ $t('groupBilling') }}
|
||||
|
||||
.col-12
|
||||
router-view
|
||||
@@ -12,11 +17,26 @@
|
||||
|
||||
<script>
|
||||
import SecondaryMenu from 'client/components/secondaryMenu';
|
||||
import { mapState } from 'client/libs/store';
|
||||
|
||||
export default {
|
||||
props: ['groupId'],
|
||||
components: {
|
||||
SecondaryMenu,
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
user: 'user.data',
|
||||
groupPlans: 'groupPlans',
|
||||
}),
|
||||
isLeader () {
|
||||
let groupFound = this.groupPlans.find(group => {
|
||||
return group._id === this.groupId;
|
||||
});
|
||||
|
||||
if (!groupFound) return false;
|
||||
return groupFound.leader === this.user._id;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -72,7 +72,7 @@
|
||||
v-on:taskEdited='taskEdited',
|
||||
)
|
||||
.row
|
||||
task-column.col-3(
|
||||
task-column.col-12.col-sm-6.col-3(
|
||||
v-for="column in columns",
|
||||
:type="column",
|
||||
:key="column",
|
||||
@@ -349,15 +349,21 @@ export default {
|
||||
groupId: this.searchId,
|
||||
});
|
||||
|
||||
let groupedApprovals = this.loadApprovals();
|
||||
|
||||
tasks.forEach((task) => {
|
||||
if (groupedApprovals.length > 0) task.approvals = groupedApprovals[task._id];
|
||||
this.tasksByType[task.type].push(task);
|
||||
});
|
||||
},
|
||||
async loadApprovals () {
|
||||
if (this.group.leader._id !== this.user._id) return [];
|
||||
|
||||
let approvalRequests = await this.$store.dispatch('tasks:getGroupApprovals', {
|
||||
groupId: this.searchId,
|
||||
});
|
||||
let groupedApprovals = groupBy(approvalRequests, 'group.taskId');
|
||||
|
||||
tasks.forEach((task) => {
|
||||
task.approvals = groupedApprovals[task._id];
|
||||
this.tasksByType[task.type].push(task);
|
||||
});
|
||||
return groupBy(approvalRequests, 'group.taskId');
|
||||
},
|
||||
editTask (task) {
|
||||
this.taskFormPurpose = 'edit';
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
group-form-modal(v-if='isParty')
|
||||
invite-modal(:group='this.group')
|
||||
start-quest-modal(:group='this.group')
|
||||
.col-8.standard-page
|
||||
quest-details-modal(:group='this.group')
|
||||
group-gems-modal
|
||||
.col-12.col-sm-8.standard-page
|
||||
.row
|
||||
.col-6.title-details
|
||||
h1 {{group.name}}
|
||||
@@ -17,9 +19,9 @@
|
||||
.svg-icon.shield(v-html="icons.silverGuildBadgeIcon", v-if='group.memberCount > 100 && group.memberCount < 999')
|
||||
.svg-icon.shield(v-html="icons.bronzeGuildBadgeIcon", v-if='group.memberCount < 100')
|
||||
span.number {{ group.memberCount | abbrNum }}
|
||||
div(v-once) {{ $t('members') }}
|
||||
div(v-once) {{ $t('memberList') }}
|
||||
.col-4(v-if='!isParty')
|
||||
.item-with-icon
|
||||
.item-with-icon(@click='showGroupGems()')
|
||||
.svg-icon.gem(v-html="icons.gem")
|
||||
span.number {{group.balance * 4}}
|
||||
div(v-once) {{ $t('guildBank') }}
|
||||
@@ -30,11 +32,13 @@
|
||||
.row.new-message-row
|
||||
textarea(:placeholder="!isParty ? $t('chatPlaceholder') : $t('partyChatPlaceholder')", v-model='newMessage', @keydown='updateCarretPosition')
|
||||
autocomplete(:text='newMessage', v-on:select="selectedAutocomplete", :coords='coords', :chat='group.chat')
|
||||
button.btn.btn-secondary.send-chat.float-left(v-once, @click='sendMessage()') {{ $t('send') }}
|
||||
|
||||
.row
|
||||
.col-6
|
||||
button.btn.btn-secondary.float-left.fetch(v-once, @click='fetchRecentMessages()') {{ $t('fetchRecentMessages') }}
|
||||
button.btn.btn-secondary.float-left(v-once, @click='reverseChat()') {{ $t('reverseChat') }}
|
||||
.col-6
|
||||
button.btn.btn-secondary.send-chat.float-right(v-once, @click='sendMessage()') {{ $t('send') }}
|
||||
|
||||
.row.community-guidelines(v-if='!communityGuidelinesAccepted')
|
||||
div.col-8(v-once, v-html="$t('communityGuidelinesIntro')")
|
||||
@@ -44,8 +48,7 @@
|
||||
.row
|
||||
.col-12.hr
|
||||
chat-message(:chat.sync='group.chat', :group-id='group._id', group-name='group.name')
|
||||
|
||||
.col-4.sidebar
|
||||
.col-12.col-sm-4.sidebar
|
||||
.row(:class='{"guild-background": !isParty}')
|
||||
.col-6
|
||||
.col-6
|
||||
@@ -53,15 +56,16 @@
|
||||
button.btn.btn-success(class='btn-success', v-if='isLeader && !group.purchased.active', @click='upgradeGroup()')
|
||||
| {{ $t('upgrade') }}
|
||||
.button-container
|
||||
button.btn.btn-primary(b-btn, @click="updateGuild", v-once, v-if='isLeader') {{ $t('edit') }}
|
||||
button.btn.btn-primary(b-btn, @click="updateGuild", v-once, v-if='isLeader || isAdmin') {{ $t('edit') }}
|
||||
.button-container
|
||||
button.btn.btn-success(class='btn-success', v-if='!isMember', @click='join()') {{ $t('join') }}
|
||||
.button-container
|
||||
button.btn.btn-primary(v-once, @click='showInviteModal()') {{$t('invite')}}
|
||||
// @TODO: hide the invitation button if there's an active group plan and the player is not the leader
|
||||
.button-container
|
||||
// @TODO: V2 button.btn.btn-primary(v-once, v-if='!isLeader') {{$t('messageGuildLeader')}}
|
||||
// @TODO: V2 button.btn.btn-primary(v-once, v-if='!isLeader') {{$t('messageGuildLeader')}} // Suggest making the button visible to the leader too - useful for them to test how the feature works or to send a note to themself. -- Alys
|
||||
.button-container
|
||||
// @TODO: V2 button.btn.btn-primary(v-once, v-if='isMember && !isParty') {{$t('donateGems')}}
|
||||
// @TODO: V2 button.btn.btn-primary(v-once, v-if='isMember && !isParty') {{$t('donateGems')}} // Suggest removing the isMember restriction - it's okay if non-members donate to a public guild. Also probably allow it for parties if parties can buy imagery. -- Alys
|
||||
|
||||
.section-header(v-if='isParty')
|
||||
.row
|
||||
@@ -78,11 +82,15 @@
|
||||
.svg-icon(v-html="icons.questIcon")
|
||||
h4(v-once) {{ $t('youAreNotOnQuest') }}
|
||||
p(v-once) {{ $t('questDescription') }}
|
||||
button.btn.btn-secondary(v-once, @click="openStartQuestModal()", v-if='isLeader') {{ $t('startAQuest') }}
|
||||
button.btn.btn-secondary(v-once, @click="openStartQuestModal()") {{ $t('startAQuest') }}
|
||||
.row.quest-active-section(v-if='isParty && onPendingQuest && !onActiveQuest')
|
||||
h2 Pending quest
|
||||
button.btn.btn-secondary(v-once, @click="questForceStart()") {{ $t('begin') }}
|
||||
button.btn.btn-secondary(v-once, @click="questCancel()") {{ $t('cancel') }}
|
||||
.col-2
|
||||
.quest(:class='`inventory_quest_scroll_${questData.key}`')
|
||||
.col-6.titles
|
||||
strong {{ questData.text() }}
|
||||
p {{acceptedCount}} / {{group.memberCount}}
|
||||
.col-4
|
||||
button.btn.btn-secondary(@click="openQuestDetails()") {{ $t('details') }}
|
||||
.row.quest-active-section(v-if='isParty && !onPendingQuest && onActiveQuest')
|
||||
.col-12.text-center
|
||||
.quest-boss(:class="'quest_' + questData.key")
|
||||
@@ -122,7 +130,7 @@
|
||||
.col-6
|
||||
span.float-left
|
||||
| Rage {{questData.boss.rage.value}}
|
||||
button.btn.btn-secondary(v-once, @click="questAbort()", v-if='isLeader') {{ $t('abort') }}
|
||||
button.btn.btn-secondary(v-once, @click="questAbort()", v-if='canEditQuest') {{ $t('abort') }}
|
||||
|
||||
.section-header(v-if='!isParty')
|
||||
.row
|
||||
@@ -296,13 +304,6 @@
|
||||
.new-message-row {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.send-chat {
|
||||
z-index: 10;
|
||||
position: absolute;
|
||||
right: 1em;
|
||||
bottom: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
.toggle-up .svg-icon, .toggle-down .svg-icon {
|
||||
@@ -353,6 +354,10 @@
|
||||
}
|
||||
|
||||
.quest-active-section {
|
||||
.titles {
|
||||
padding-top: .5em;
|
||||
}
|
||||
|
||||
.quest-box {
|
||||
background-image: url('~client/assets/svg/for-css/quest-border.svg');
|
||||
background-size: 100% 100%;
|
||||
@@ -431,6 +436,9 @@
|
||||
</style>
|
||||
|
||||
<script>
|
||||
// @TODO: Break this down into components
|
||||
|
||||
import debounce from 'lodash/debounce';
|
||||
import extend from 'lodash/extend';
|
||||
import groupUtilities from 'client/mixins/groupsUtilities';
|
||||
import styleHelper from 'client/mixins/styleHelper';
|
||||
@@ -438,6 +446,7 @@ import { mapState } from 'client/libs/store';
|
||||
import * as Analytics from 'client/libs/analytics';
|
||||
import membersModal from './membersModal';
|
||||
import startQuestModal from './startQuestModal';
|
||||
import questDetailsModal from './questDetailsModal';
|
||||
import quests from 'common/script/content/quests';
|
||||
import percent from 'common/script/libs/percent';
|
||||
import groupFormModal from './groupFormModal';
|
||||
@@ -445,6 +454,7 @@ import inviteModal from './inviteModal';
|
||||
import chatMessage from '../chat/chatMessages';
|
||||
import autocomplete from '../chat/autoComplete';
|
||||
import groupChallenges from '../challenges/groupChallenges';
|
||||
import groupGemsModal from 'client/components/groups/groupGemsModal';
|
||||
import markdownDirective from 'client/directives/markdown';
|
||||
|
||||
import bCollapse from 'bootstrap-vue/lib/components/collapse';
|
||||
@@ -481,6 +491,8 @@ export default {
|
||||
inviteModal,
|
||||
groupChallenges,
|
||||
autocomplete,
|
||||
questDetailsModal,
|
||||
groupGemsModal,
|
||||
},
|
||||
directives: {
|
||||
bToggle,
|
||||
@@ -522,6 +534,17 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
acceptedCount () {
|
||||
let count = 0;
|
||||
|
||||
if (!this.group || !this.group.quest) return count;
|
||||
|
||||
for (let uuid in this.group.quest.members) {
|
||||
if (this.group.quest.members[uuid]) count += 1;
|
||||
}
|
||||
|
||||
return count;
|
||||
},
|
||||
communityGuidelinesAccepted () {
|
||||
return this.user.flags.communityGuidelinesAccepted;
|
||||
},
|
||||
@@ -540,12 +563,17 @@ export default {
|
||||
isLeader () {
|
||||
return this.user._id === this.group.leader._id;
|
||||
},
|
||||
isAdmin () {
|
||||
return Boolean(this.user.contributor.admin);
|
||||
},
|
||||
isMember () {
|
||||
return this.isMemberOfGroup(this.user, this.group);
|
||||
},
|
||||
canEditQuest () {
|
||||
let isQuestLeader = this.group.quest && this.group.quest.leader === this.user._id;
|
||||
return isQuestLeader;
|
||||
if (!this.group.quest) return false;
|
||||
let isQuestLeader = this.group.quest.leader === this.user._id;
|
||||
let isPartyLeader = this.group.leader._id === this.user._id;
|
||||
return isQuestLeader || isPartyLeader;
|
||||
},
|
||||
isMemberOfPendingQuest () {
|
||||
let userid = this.user._id;
|
||||
@@ -655,7 +683,10 @@ export default {
|
||||
};
|
||||
document.body.removeChild(div);
|
||||
},
|
||||
updateCarretPosition (eventUpdate) {
|
||||
updateCarretPosition: debounce(function updateCarretPosition (eventUpdate) {
|
||||
this._updateCarretPosition(eventUpdate);
|
||||
}, 250),
|
||||
_updateCarretPosition (eventUpdate) {
|
||||
if (eventUpdate.metaKey && eventUpdate.keyCode === 13) {
|
||||
this.sendMessage();
|
||||
return;
|
||||
@@ -685,6 +716,9 @@ export default {
|
||||
fetchRecentMessages () {
|
||||
this.fetchGuild();
|
||||
},
|
||||
reverseChat () {
|
||||
this.group.chat.reverse();
|
||||
},
|
||||
updateGuild () {
|
||||
this.$store.state.editingGroup = this.group;
|
||||
this.$root.$emit('show::modal', 'guild-form');
|
||||
@@ -717,6 +751,9 @@ export default {
|
||||
openStartQuestModal () {
|
||||
this.$root.$emit('show::modal', 'start-quest-modal');
|
||||
},
|
||||
openQuestDetails () {
|
||||
this.$root.$emit('show::modal', 'quest-details');
|
||||
},
|
||||
checkForAchievements () {
|
||||
// Checks if user's party has reached 2 players for the first time.
|
||||
if (!this.user.achievements.partyUp && this.group.memberCount >= 2) {
|
||||
@@ -811,11 +848,6 @@ export default {
|
||||
this.$store.state.profileOptions.startingPage = 'profile';
|
||||
this.$root.$emit('show::modal', 'profile');
|
||||
},
|
||||
async questCancel () {
|
||||
if (!confirm(this.$t('sureCancel'))) return;
|
||||
let quest = await this.$store.dispatch('quests:sendAction', {groupId: this.group._id, action: 'quests/cancel'});
|
||||
this.group.quest = quest;
|
||||
},
|
||||
async questAbort () {
|
||||
if (!confirm(this.$t('sureAbort'))) return;
|
||||
if (!confirm(this.$t('doubleSureAbort'))) return;
|
||||
@@ -831,15 +863,14 @@ export default {
|
||||
let quest = await this.$store.dispatch('quests:sendAction', {groupId: this.group._id, action: 'quests/accept'});
|
||||
this.group.quest = quest;
|
||||
},
|
||||
async questForceStart () {
|
||||
let quest = await this.$store.dispatch('quests:sendAction', {groupId: this.group._id, action: 'quests/force-start'});
|
||||
this.group.quest = quest;
|
||||
},
|
||||
// @TODO: Move to notificaitons component?
|
||||
async questReject () {
|
||||
let quest = await this.$store.dispatch('quests:sendAction', {groupId: this.group._id, action: 'quests/reject'});
|
||||
this.group.quest = quest;
|
||||
},
|
||||
showGroupGems () {
|
||||
this.$root.$emit('show::modal', 'group-gems-modal');
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
label
|
||||
strong(v-once) {{$t('guildOrPartyLeader')}} *
|
||||
select.form-control(v-model="workingGroup.newLeader")
|
||||
option(v-for='member in members', :value="member._id") {{ member.profile.name }}
|
||||
option(v-for='potentialLeader in potentialLeaders', :value="potentialLeader._id") {{ potentialLeader.name }}
|
||||
|
||||
.form-group(v-if='!this.workingGroup.id')
|
||||
label
|
||||
@@ -63,7 +63,7 @@
|
||||
.form-group
|
||||
label
|
||||
strong(v-once) {{$t('groupDescription')}} *
|
||||
a.float-right {{ $t('markdownFormattingHelp') }}
|
||||
a.float-right(v-markdown='$t("markdownFormattingHelp")')
|
||||
textarea.form-control.description-textarea(type="text", textarea, :placeholder="isParty ? $t('partyDescriptionPlaceholder') : $t('guildDescriptionPlaceholder')", v-model="workingGroup.description")
|
||||
|
||||
.form-group(v-if='creatingParty && !workingGroup.id')
|
||||
@@ -177,6 +177,7 @@ import bTooltip from 'bootstrap-vue/lib/components/tooltip';
|
||||
|
||||
import { mapState } from 'client/libs/store';
|
||||
import toggleSwitch from 'client/components/ui/toggleSwitch';
|
||||
import markdownDirective from 'client/directives/markdown';
|
||||
import gemIcon from 'assets/svg/gem.svg';
|
||||
import informationIcon from 'assets/svg/information.svg';
|
||||
|
||||
@@ -198,6 +199,9 @@ export default {
|
||||
bTooltip,
|
||||
toggleSwitch,
|
||||
},
|
||||
directives: {
|
||||
markdown: markdownDirective,
|
||||
},
|
||||
data () {
|
||||
let data = {
|
||||
workingGroup: {
|
||||
@@ -316,6 +320,17 @@ export default {
|
||||
isParty () {
|
||||
return this.workingGroup.type === 'party';
|
||||
},
|
||||
potentialLeaders () {
|
||||
let leaders = [{ _id: this.user._id, name: this.user.profile.name }];
|
||||
// @TODO consider pushing all recent posters to the top of the list if they are guild members - more likely to be the ones the leader wants to see (and then ignore them in the while below)
|
||||
let i = 0;
|
||||
while (this.members[i]) {
|
||||
let memb = this.members[i];
|
||||
i++;
|
||||
if (memb._id !== this.user._id) leaders.push({_id: memb._id, name: memb.profile.name});
|
||||
}
|
||||
return leaders;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
editingGroup () {
|
||||
|
||||
32
website/client/components/groups/groupGemsModal.vue
Normal file
@@ -0,0 +1,32 @@
|
||||
<template lang="pug">
|
||||
b-modal#group-gems-modal(:title="$t('groupGems')", size='md', :hide-footer="true")
|
||||
.modal-body
|
||||
.row
|
||||
.col-6.offset-3
|
||||
h3 {{ $t('groupGemsDesc') }}
|
||||
.modal-footer
|
||||
.col-12.text-center
|
||||
button.btn.btn-primary(@click='close()') {{$t('close')}}
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.modal-body {
|
||||
margin-top: 1em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import bModal from 'bootstrap-vue/lib/components/modal';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
bModal,
|
||||
},
|
||||
methods: {
|
||||
close () {
|
||||
this.$root.$emit('hide::modal', 'group-gems-modal');
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -1,6 +1,7 @@
|
||||
<template lang="pug">
|
||||
// @TODO: Move to group plans folder
|
||||
div
|
||||
amazon-payments-modal(:amazon-payments='amazonPayments')
|
||||
amazon-payments-modal(:amazon-payments-prop='amazonPayments')
|
||||
div
|
||||
.header
|
||||
h1.text-center Need more for your Group?
|
||||
@@ -390,21 +391,23 @@ export default {
|
||||
this.changePage(this.PAGES.PAY);
|
||||
},
|
||||
pay (paymentMethod) {
|
||||
this.paymentMethod = paymentMethod;
|
||||
let subscriptionKey = 'group_monthly'; // @TODO: Get from content API?
|
||||
let paymentData = {
|
||||
subscription: subscriptionKey,
|
||||
coupon: null,
|
||||
};
|
||||
|
||||
if (this.upgradingGroup && this.upgradingGroup._id) {
|
||||
paymentData.groupId = this.upgradingGroup._id;
|
||||
} else {
|
||||
paymentData.groupToCreate = this.newGroup;
|
||||
}
|
||||
|
||||
this.paymentMethod = paymentMethod;
|
||||
if (this.paymentMethod === this.PAYMENTS.STRIPE) {
|
||||
this.showStripe({
|
||||
subscription: subscriptionKey,
|
||||
coupon: null,
|
||||
groupToCreate: this.newGroup,
|
||||
});
|
||||
this.showStripe(paymentData);
|
||||
} else if (this.paymentMethod === this.PAYMENTS.AMAZON) {
|
||||
this.amazonPaymentsInit({
|
||||
type: 'subscription',
|
||||
subscription: subscriptionKey,
|
||||
coupon: null,
|
||||
groupToCreate: this.newGroup,
|
||||
});
|
||||
this.amazonPaymentsInit(paymentData);
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
217
website/client/components/groups/questDetailsModal.vue
Normal file
@@ -0,0 +1,217 @@
|
||||
<template lang="pug">
|
||||
b-modal#quest-details(title="Empty", size='md', :hide-footer="true", :hide-header="true")
|
||||
.left-panel.content
|
||||
h3.text-center {{ $t('participantsTitle') }}
|
||||
.row
|
||||
.col-10.offset-1.text-center
|
||||
span.description(v-once) {{ $t('participantDesc') }}
|
||||
.row
|
||||
.col-12.member(v-for='member in members')
|
||||
strong(:class="{'declined-name': member.accepted === false}") {{member.name}}
|
||||
.accepted.float-right(v-if='member.accepted === true') {{ $t('accepted') }}
|
||||
.declined.float-right(v-if='member.accepted === false') {{ $t('declined') }}
|
||||
.pending.float-right(v-if='member.accepted === null') {{ $t('pending') }}
|
||||
div(v-if='questData')
|
||||
questDialogContent(:item="questData")
|
||||
div.text-center.actions(v-if='canEditQuest')
|
||||
div
|
||||
button.btn.btn-secondary(v-once, @click="questForceStart()") {{ $t('begin') }}
|
||||
// @TODO don't allow the party leader to start the quest until the leader has accepted or rejected the invitation (users get confused and think "begin" means "join quest")
|
||||
div
|
||||
.cancel(v-once, @click="questCancel()") {{ $t('cancel') }}
|
||||
.side-panel(v-if='questData')
|
||||
questDialogDrops(:item="questData")
|
||||
</template>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
@import '~client/assets/scss/colors.scss';
|
||||
|
||||
header {
|
||||
background-color: $white !important;
|
||||
border: none !important;
|
||||
|
||||
h5 {
|
||||
text-indent: -99999px;
|
||||
}
|
||||
}
|
||||
|
||||
.quest-details {
|
||||
margin: 0 auto;
|
||||
text-align: left;
|
||||
width: 180px;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
.left-panel {
|
||||
background: #4e4a57;
|
||||
color: $white;
|
||||
position: absolute;
|
||||
height: 460px;
|
||||
width: 320px;
|
||||
top: 2.5em;
|
||||
left: -22em;
|
||||
z-index: -1;
|
||||
padding: 2em;
|
||||
overflow: scroll;
|
||||
|
||||
h3 {
|
||||
color: $white;
|
||||
}
|
||||
|
||||
.selected .quest-wrapper {
|
||||
border: solid 1.5px #9a62ff;
|
||||
}
|
||||
|
||||
.quest-wrapper:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.quest-col .quest-wrapper {
|
||||
background: $white;
|
||||
padding: .2em;
|
||||
margin-bottom: 1em;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.description {
|
||||
text-align: center;
|
||||
color: #a5a1ac;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.side-panel {
|
||||
position: absolute;
|
||||
right: -350px;
|
||||
top: 25px;
|
||||
border-radius: 8px;
|
||||
background-color: $gray-600;
|
||||
box-shadow: 0 2px 16px 0 rgba(26, 24, 29, 0.32);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
width: 364px;
|
||||
z-index: -1;
|
||||
height: 93%;
|
||||
}
|
||||
|
||||
.member {
|
||||
padding: 1em .5em;
|
||||
border-top: 1px solid #686274;
|
||||
|
||||
.declined-name {
|
||||
color: #878190;
|
||||
}
|
||||
|
||||
.accepted {
|
||||
color: #1ed3a0;
|
||||
}
|
||||
|
||||
.declined {
|
||||
color: #f19595;
|
||||
}
|
||||
|
||||
.pending {
|
||||
color: #c3c0c7;
|
||||
}
|
||||
}
|
||||
|
||||
.actions {
|
||||
padding-top: 2em;
|
||||
padding-bottom: 2em;
|
||||
|
||||
.cancel {
|
||||
color: #f74e52;
|
||||
margin-top: 3em;
|
||||
}
|
||||
|
||||
.cancel:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'client/libs/store';
|
||||
import bModal from 'bootstrap-vue/lib/components/modal';
|
||||
|
||||
import quests from 'common/script/content/quests';
|
||||
|
||||
import copyIcon from 'assets/svg/copy.svg';
|
||||
import greyBadgeIcon from 'assets/svg/grey-badge.svg';
|
||||
import qrCodeIcon from 'assets/svg/qrCode.svg';
|
||||
import facebookIcon from 'assets/svg/facebook.svg';
|
||||
import twitterIcon from 'assets/svg/twitter.svg';
|
||||
import starIcon from 'assets/svg/star.svg';
|
||||
import goldIcon from 'assets/svg/gold.svg';
|
||||
import difficultyStarIcon from 'assets/svg/difficulty-star.svg';
|
||||
import questDialogDrops from '../shops/quests/questDialogDrops';
|
||||
import questDialogContent from '../shops/quests/questDialogContent';
|
||||
|
||||
export default {
|
||||
props: ['group'],
|
||||
components: {
|
||||
bModal,
|
||||
questDialogDrops,
|
||||
questDialogContent,
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
loading: false,
|
||||
selectedQuest: {},
|
||||
icons: Object.freeze({
|
||||
copy: copyIcon,
|
||||
greyBadge: greyBadgeIcon,
|
||||
qrCode: qrCodeIcon,
|
||||
facebook: facebookIcon,
|
||||
twitter: twitterIcon,
|
||||
starIcon,
|
||||
goldIcon,
|
||||
difficultyStarIcon,
|
||||
}),
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
user: 'user.data',
|
||||
partyMembers: 'party.members.data',
|
||||
}),
|
||||
questData () {
|
||||
return quests.quests[this.group.quest.key];
|
||||
},
|
||||
members () {
|
||||
return this.partyMembers.map(member => {
|
||||
return {
|
||||
name: member.profile.name,
|
||||
accepted: this.group.quest.members[member._id],
|
||||
};
|
||||
});
|
||||
},
|
||||
canEditQuest () {
|
||||
if (!this.group.quest) return false;
|
||||
let isQuestLeader = this.group.quest.leader === this.user._id;
|
||||
let isPartyLeader = this.group.leader._id === this.user._id;
|
||||
return isQuestLeader || isPartyLeader;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async questForceStart () {
|
||||
let quest = await this.$store.dispatch('quests:sendAction', {groupId: this.group._id, action: 'quests/force-start'});
|
||||
this.group.quest = quest;
|
||||
this.close();
|
||||
},
|
||||
async questCancel () {
|
||||
if (!confirm(this.$t('sureCancel'))) return;
|
||||
let quest = await this.$store.dispatch('quests:sendAction', {groupId: this.group._id, action: 'quests/cancel'});
|
||||
this.group.quest = quest;
|
||||
this.close();
|
||||
},
|
||||
close () {
|
||||
this.$root.$emit('hide::modal', 'quest-details');
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -1,5 +1,5 @@
|
||||
<template lang="pug">
|
||||
.standard-sidebar
|
||||
.standard-sidebar.hidden-xs-down
|
||||
.form-group
|
||||
input.form-control.search(type="text", :placeholder="$t('search')", v-model='searchTerm')
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template lang="pug">
|
||||
.row
|
||||
.clearfix.col-8.standard-page
|
||||
.col-12.col-sm-8.clearfix.standard-page
|
||||
.row
|
||||
.col-6.title-details
|
||||
h1(v-once) {{ $t('welcomeToTavern') }}
|
||||
@@ -15,10 +15,10 @@
|
||||
|
||||
.row
|
||||
.col-6
|
||||
button.btn.btn-secondary.send-chat.float-left(v-once, @click='sendMessage()') {{ $t('send') }}
|
||||
button.btn.btn-secondary.float-left.fetch(v-once, @click='fetchRecentMessages()') {{ $t('fetchRecentMessages') }}
|
||||
button.btn.btn-secondary.float-left(v-once, @click='reverseChat()') {{ $t('reverseChat') }}
|
||||
.col-6
|
||||
button.btn.btn-secondary.float-right.fetch(v-once, @click='fetchRecentMessages()') {{ $t('fetchRecentMessages') }}
|
||||
button.btn.btn-secondary.float-right(v-once, @click='reverseChat()') {{ $t('reverseChat') }}
|
||||
button.btn.btn-secondary.send-chat.float-right(v-once, @click='sendMessage()') {{ $t('send') }}
|
||||
|
||||
.row.community-guidelines(v-if='!communityGuidelinesAccepted')
|
||||
div.col-8(v-once, v-html="$t('communityGuidelinesIntro')")
|
||||
@@ -29,7 +29,7 @@
|
||||
.hr.col-12
|
||||
chat-message(:chat.sync='group.chat', :group-id='group._id', group-name='group.name')
|
||||
|
||||
.col-md-4.sidebar
|
||||
.col-12.col-sm-4.sidebar
|
||||
.section
|
||||
.grassy-meadow-backdrop
|
||||
.daniel_front
|
||||
@@ -350,6 +350,7 @@
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import debounce from 'lodash/debounce';
|
||||
import { mapState } from 'client/libs/store';
|
||||
|
||||
import { TAVERN_ID } from '../../../common/script/constants';
|
||||
@@ -527,7 +528,10 @@ export default {
|
||||
};
|
||||
document.body.removeChild(div);
|
||||
},
|
||||
updateCarretPosition (eventUpdate) {
|
||||
updateCarretPosition: debounce(function updateCarretPosition (eventUpdate) {
|
||||
this._updateCarretPosition(eventUpdate);
|
||||
}, 250),
|
||||
_updateCarretPosition (eventUpdate) {
|
||||
if (eventUpdate.metaKey && eventUpdate.keyCode === 13) {
|
||||
this.sendMessage();
|
||||
return;
|
||||
|
||||
@@ -38,6 +38,8 @@
|
||||
drawer(
|
||||
:title="$t('equipment')",
|
||||
:errorMessage="(costume && !user.preferences.costume) ? $t('costumeDisabled') : null",
|
||||
:openStatus='openStatus',
|
||||
v-on:toggled='drawerToggled'
|
||||
)
|
||||
div(slot="drawer-header")
|
||||
.drawer-tab-container
|
||||
@@ -137,6 +139,8 @@
|
||||
|
||||
<script>
|
||||
import { mapState } from 'client/libs/store';
|
||||
import { CONSTANTS, setLocalSetting, getLocalSetting } from 'client/libs/userlocalManager';
|
||||
|
||||
import each from 'lodash/each';
|
||||
import map from 'lodash/map';
|
||||
import throttle from 'lodash/throttle';
|
||||
@@ -220,6 +224,12 @@ export default {
|
||||
this.searchTextThrottled = this.searchText;
|
||||
}, 250),
|
||||
},
|
||||
mounted () {
|
||||
const drawerState = getLocalSetting(CONSTANTS.keyConstants.EQUIPMENT_DRAWER_STATE);
|
||||
if (drawerState === CONSTANTS.valueConstants.DRAWER_CLOSED) {
|
||||
this.$store.state.equipmentDrawerOpen = false;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
openEquipDialog (item) {
|
||||
this.gearToEquip = item;
|
||||
@@ -241,6 +251,16 @@ export default {
|
||||
sortItems (items, sortBy) {
|
||||
return _reverse(_sortBy(items, sortGearTypeMap[sortBy]));
|
||||
},
|
||||
drawerToggled (newState) {
|
||||
this.$store.state.equipmentDrawerOpen = newState;
|
||||
|
||||
if (newState) {
|
||||
setLocalSetting(CONSTANTS.keyConstants.EQUIPMENT_DRAWER_STATE, CONSTANTS.valueConstants.DRAWER_OPEN);
|
||||
return;
|
||||
}
|
||||
|
||||
setLocalSetting(CONSTANTS.keyConstants.EQUIPMENT_DRAWER_STATE, CONSTANTS.valueConstants.DRAWER_CLOSED);
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
@@ -251,6 +271,9 @@ export default {
|
||||
costumeItems: 'user.data.items.gear.costume',
|
||||
flatGear: 'content.gear.flat',
|
||||
}),
|
||||
openStatus () {
|
||||
return this.$store.state.equipmentDrawerOpen ? 1 : 0;
|
||||
},
|
||||
drawerPreference () {
|
||||
return this.costume === true ? 'costume' : 'autoEquip';
|
||||
},
|
||||
|
||||
@@ -127,10 +127,7 @@
|
||||
:count="context.item.quantity"
|
||||
)
|
||||
|
||||
hatchedPetDialog(
|
||||
:pet="hatchedPet",
|
||||
@closed="closeHatchedPetDialog()"
|
||||
)
|
||||
hatchedPetDialog()
|
||||
|
||||
div.hatchingPotionInfo(ref="draggingPotionInfo")
|
||||
div(v-if="currentDraggingPotion != null")
|
||||
@@ -251,7 +248,6 @@ export default {
|
||||
|
||||
currentDraggingPotion: null,
|
||||
potionClickMode: false,
|
||||
hatchedPet: null,
|
||||
cardOptions: {
|
||||
cardType: '',
|
||||
messageOptions: 0,
|
||||
@@ -345,7 +341,7 @@ export default {
|
||||
},
|
||||
hatchPet (potion, egg) {
|
||||
this.$store.dispatch('common:hatch', {egg: egg.key, hatchingPotion: potion.key});
|
||||
this.hatchedPet = createAnimal(egg, potion, 'pet', this.content, this.user.items);
|
||||
this.$root.$emit('hatchedPet::open', createAnimal(egg, potion, 'pet', this.content, this.user.items));
|
||||
},
|
||||
onDragEnd () {
|
||||
this.currentDraggingPotion = null;
|
||||
@@ -405,9 +401,6 @@ export default {
|
||||
this.potionClickMode = false;
|
||||
}
|
||||
},
|
||||
closeHatchedPetDialog () {
|
||||
this.hatchedPet = null;
|
||||
},
|
||||
|
||||
async itemClicked (groupKey, item) {
|
||||
if (item.type && item.type === 'card') {
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
<template lang="pug">
|
||||
|
||||
b-modal#hatchedPet-modal(
|
||||
:visible="true",
|
||||
v-if="pet != null",
|
||||
:hide-header="true"
|
||||
)
|
||||
div.content
|
||||
div.dialog-header.title You hatched a new pet!
|
||||
div.content(v-if="pet != null")
|
||||
div.dialog-header.title(v-once) {{ $t('hatchedPetGeneric') }}
|
||||
|
||||
|
||||
div.inner-content
|
||||
@@ -45,7 +43,7 @@
|
||||
}
|
||||
|
||||
.inner-content {
|
||||
margin: 33px auto auto;
|
||||
margin: 24px auto auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
@@ -77,16 +75,33 @@
|
||||
components: {
|
||||
bModal,
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
pet: null,
|
||||
};
|
||||
},
|
||||
created () {
|
||||
|
||||
},
|
||||
mounted () {
|
||||
this.$root.$on('hatchedPet::open', this.openDialog);
|
||||
},
|
||||
destroyed () {
|
||||
this.$root.$off('hatchedPet::open', this.openDialog);
|
||||
},
|
||||
methods: {
|
||||
openDialog (item) {
|
||||
this.pet = item;
|
||||
this.$root.$emit('show::modal', 'hatchedPet-modal');
|
||||
},
|
||||
|
||||
close () {
|
||||
this.$emit('closed', this.item);
|
||||
this.$root.$emit('hide::modal', 'hatchedPet-modal');
|
||||
this.pet = null;
|
||||
},
|
||||
},
|
||||
props: {
|
||||
pet: {
|
||||
type: Object,
|
||||
},
|
||||
hideText: {
|
||||
type: Boolean,
|
||||
},
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<template lang="pug">
|
||||
// @TODO: breakdown to componentes and use some SOLID
|
||||
.row.stable(v-mousePosition="30", @mouseMoved="mouseMoved($event)")
|
||||
.standard-sidebar
|
||||
.standard-sidebar.col-3.hidden-xs-down
|
||||
div
|
||||
#npmMattStable.npc_matt
|
||||
b-popover(
|
||||
@@ -53,7 +54,7 @@
|
||||
@change="updateHideMissing"
|
||||
)
|
||||
|
||||
.standard-page
|
||||
.standard-page.col-12.col-sm-9
|
||||
.clearfix
|
||||
h1.float-left.mb-0.page-header(v-once) {{ $t('stable') }}
|
||||
|
||||
@@ -73,55 +74,49 @@
|
||||
span.badge.badge-pill.badge-default {{countOwnedAnimals(petGroups[0], 'pet')}}
|
||||
|
||||
div(
|
||||
v-for="petGroup in petGroups",
|
||||
v-for="(petGroup, index) in petGroups",
|
||||
v-if="viewOptions[petGroup.key].selected",
|
||||
:key="petGroup.key"
|
||||
)
|
||||
h4(v-if="viewOptions[petGroup.key].animalCount != 0") {{ petGroup.label }}
|
||||
h4(v-if="viewOptions[petGroup.key].animalCount !== 0") {{ petGroup.label }}
|
||||
|
||||
itemRows(
|
||||
:items="pets(petGroup, hideMissing, selectedSortBy, searchTextThrottled)",
|
||||
:itemWidth=94,
|
||||
:itemMargin=24,
|
||||
:type="petGroup.key",
|
||||
)
|
||||
template(slot="item", scope="context")
|
||||
div(
|
||||
v-drag.drop.food="context.item.key",
|
||||
@itemDragOver="onDragOver($event, context.item)",
|
||||
@itemDropped="onDrop($event, context.item)",
|
||||
@itemDragLeave="onDragLeave()",
|
||||
:class="{'last': context.item.isLastInRow}"
|
||||
.pet-row.d-flex(
|
||||
v-for="(group, key, index) in pets(petGroup, hideMissing, selectedSortBy, searchTextThrottled)",
|
||||
v-if='index === 0 || showMore === petGroup.key')
|
||||
.pet-group(
|
||||
v-for='item in group'
|
||||
v-drag.drop.food="item.key",
|
||||
@itemDragOver="onDragOver($event, item)",
|
||||
@itemDropped="onDrop($event, item)",
|
||||
@itemDragLeave="onDragLeave()",
|
||||
:class="{'last': item.isLastInRow}"
|
||||
)
|
||||
petItem(
|
||||
:item="item",
|
||||
:itemContentClass="getPetItemClass(item)",
|
||||
:popoverPosition="'top'",
|
||||
:progress="item.progress",
|
||||
:emptyItem="!item.isOwned()",
|
||||
:showPopover="currentDraggingFood == null",
|
||||
:highlightBorder="highlightPet == item.key",
|
||||
@click="petClicked(item)"
|
||||
)
|
||||
petItem(
|
||||
:item="context.item",
|
||||
:itemContentClass="getPetItemClass(context.item)",
|
||||
:popoverPosition="'top'",
|
||||
:progress="context.item.progress",
|
||||
:emptyItem="!context.item.isOwned()",
|
||||
:showPopover="currentDraggingFood == null",
|
||||
:highlightBorder="highlightPet == context.item.key",
|
||||
@click="petClicked(context.item)"
|
||||
)
|
||||
span(slot="popoverContent")
|
||||
div.hatchablePopover(v-if="context.item.isHatchable()")
|
||||
h4.popover-content-title {{ context.item.name }}
|
||||
div.popover-content-text(v-html="$t('haveHatchablePet', { potion: context.item.potionName, egg: context.item.eggName })")
|
||||
div.potionEggGroup
|
||||
div.potionEggBackground
|
||||
div(:class="'Pet_HatchingPotion_'+context.item.potionKey")
|
||||
div.potionEggBackground
|
||||
div(:class="'Pet_Egg_'+context.item.eggKey")
|
||||
span(slot="popoverContent")
|
||||
div.hatchablePopover(v-if="item.isHatchable()")
|
||||
h4.popover-content-title {{ item.name }}
|
||||
div.popover-content-text(v-html="$t('haveHatchablePet', { potion: item.potionName, egg: item.eggName })")
|
||||
div.potionEggGroup
|
||||
div.potionEggBackground
|
||||
div(:class="'Pet_HatchingPotion_'+item.potionKey")
|
||||
div.potionEggBackground
|
||||
div(:class="'Pet_Egg_'+item.eggKey")
|
||||
div(v-else)
|
||||
h4.popover-content-title {{ item.name }}
|
||||
template(slot="itemBadge", scope="context")
|
||||
starBadge(:selected="item.key === currentPet", :show="item.isOwned()", @click="selectPet(item)")
|
||||
|
||||
div(v-else)
|
||||
h4.popover-content-title {{ context.item.name }}
|
||||
|
||||
template(slot="itemBadge", scope="context")
|
||||
starBadge(
|
||||
:selected="context.item.key === currentPet",
|
||||
:show="context.item.isOwned()",
|
||||
@click="selectPet(context.item)",
|
||||
)
|
||||
.btn.btn-flat.btn-show-more(@click="setShowMore(petGroup.key)", v-if='petGroup.key !== "specialPets"')
|
||||
| {{ showMore === petGroup.key ? $t('showLess') : $t('showMore') }}
|
||||
|
||||
h2
|
||||
| {{ $t('mounts') }}
|
||||
@@ -135,31 +130,30 @@
|
||||
)
|
||||
h4(v-if="viewOptions[mountGroup.key].animalCount != 0") {{ mountGroup.label }}
|
||||
|
||||
itemRows(
|
||||
:items="mounts(mountGroup, hideMissing, selectedSortBy, searchTextThrottled)",
|
||||
:itemWidth=94,
|
||||
:itemMargin=24,
|
||||
:type="mountGroup.key",
|
||||
)
|
||||
template(slot="item", scope="context")
|
||||
.pet-row.d-flex(v-for="(group, key, index) in mounts(mountGroup, hideMissing, selectedSortBy, searchTextThrottled)"
|
||||
v-if='index === 0 || showMore === mountGroup.key')
|
||||
.pet-group(v-for='item in group')
|
||||
mountItem(
|
||||
:item="context.item",
|
||||
:itemContentClass="context.item.isOwned() ? ('Mount_Icon_' + context.item.key) : 'PixelPaw GreyedOut'",
|
||||
:key="context.item.key",
|
||||
:item="item",
|
||||
:itemContentClass="item.isOwned() ? ('Mount_Icon_' + item.key) : 'PixelPaw GreyedOut'",
|
||||
:key="item.key",
|
||||
:popoverPosition="'top'",
|
||||
:emptyItem="!context.item.isOwned()",
|
||||
:emptyItem="!item.isOwned()",
|
||||
:showPopover="true",
|
||||
@click="selectMount(context.item)"
|
||||
@click="selectMount(item)"
|
||||
)
|
||||
span(slot="popoverContent")
|
||||
h4.popover-content-title {{ context.item.name }}
|
||||
h4.popover-content-title {{ item.name }}
|
||||
template(slot="itemBadge", scope="context")
|
||||
starBadge(
|
||||
:selected="context.item.key === currentMount",
|
||||
:show="context.item.isOwned()",
|
||||
@click="selectMount(context.item)",
|
||||
:selected="item.key === currentMount",
|
||||
:show="item.isOwned()",
|
||||
@click="selectMount(item)",
|
||||
)
|
||||
|
||||
.btn.btn-flat.btn-show-more(@click="setShowMore(mountGroup.key)", v-if='mountGroup.key !== "specialMounts"')
|
||||
| {{ showMore === mountGroup.key ? $t('showLess') : $t('showMore') }}
|
||||
|
||||
drawer(
|
||||
:title="$t('quickInventory')",
|
||||
:errorMessage="(!hasDrawerTabItems(selectedDrawerTab)) ? ((selectedDrawerTab === 0) ? $t('noFoodAvailable') : $t('noSaddlesAvailable')) : null"
|
||||
@@ -216,10 +210,8 @@
|
||||
div.content-text(v-once) {{ $t('welcomeStableText') }}
|
||||
|
||||
b-modal#hatching-modal(
|
||||
:visible="hatchablePet != null",
|
||||
@change="resetHatchablePet($event)"
|
||||
)
|
||||
|
||||
div.content(v-if="hatchablePet")
|
||||
div.potionEggGroup
|
||||
div.potionEggBackground
|
||||
@@ -237,9 +229,7 @@
|
||||
button.btn.btn-secondary.btn-flat(@click="closeHatchPetDialog()") {{ $t('cancel') }}
|
||||
|
||||
hatchedPetDialog(
|
||||
:pet="hatchedPet",
|
||||
:hideText="true",
|
||||
@closed="closeHatchedPetDialog()"
|
||||
)
|
||||
|
||||
div.foodInfo(ref="dragginFoodInfo")
|
||||
@@ -253,11 +243,25 @@
|
||||
div.food-icon(:class="'Pet_Food_'+currentDraggingFood.key")
|
||||
div.popover
|
||||
div.popover-content {{ $t('clickOnPetToFeed', {foodName: currentDraggingFood.text() }) }}
|
||||
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
<style lang='scss' scoped>
|
||||
.group {
|
||||
height: 130px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.pet-row {
|
||||
max-width: 100%;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.item {
|
||||
margin-right: .5em;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss">
|
||||
@import '~client/assets/scss/colors.scss';
|
||||
@import '~client/assets/scss/modal.scss';
|
||||
|
||||
@@ -330,11 +334,6 @@
|
||||
padding-right:0;
|
||||
}
|
||||
|
||||
.drawer-container {
|
||||
// 3% padding + 252px sidebar width
|
||||
left: calc(3% + 252px) !important;
|
||||
}
|
||||
|
||||
.svg-icon.inline.icon-16 {
|
||||
vertical-align: bottom;
|
||||
}
|
||||
@@ -385,7 +384,6 @@
|
||||
}
|
||||
|
||||
.title {
|
||||
height: 24px;
|
||||
margin-top: 24px;
|
||||
font-family: Roboto;
|
||||
font-size: 20px;
|
||||
@@ -500,6 +498,7 @@
|
||||
import _filter from 'lodash/filter';
|
||||
import _flatMap from 'lodash/flatMap';
|
||||
import _throttle from 'lodash/throttle';
|
||||
import groupBy from 'lodash/groupBy';
|
||||
|
||||
import Item from '../item';
|
||||
import ItemRows from 'client/components/ui/itemRows';
|
||||
@@ -580,11 +579,11 @@
|
||||
highlightPet: '',
|
||||
|
||||
hatchablePet: null,
|
||||
hatchedPet: null,
|
||||
foodClickMode: false,
|
||||
currentDraggingFood: null,
|
||||
|
||||
selectedDrawerTab: 0,
|
||||
showMore: '',
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
@@ -712,7 +711,13 @@
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
|
||||
setShowMore (key) {
|
||||
if (this.showMore === key) {
|
||||
this.showMore = '';
|
||||
return;
|
||||
}
|
||||
this.showMore = key;
|
||||
},
|
||||
getAnimalList (animalGroup, type) {
|
||||
let key = animalGroup.key;
|
||||
|
||||
@@ -833,11 +838,39 @@
|
||||
},
|
||||
|
||||
pets (animalGroup, hideMissing, sortBy, searchText) {
|
||||
return this.listAnimals(animalGroup, 'pet', hideMissing, sortBy, searchText);
|
||||
let pets = this.listAnimals(animalGroup, 'pet', hideMissing, sortBy, searchText);
|
||||
|
||||
// Don't group special
|
||||
if (animalGroup.key === 'specialPets') {
|
||||
return {none: pets};
|
||||
}
|
||||
|
||||
let groupKey = 'eggKey';
|
||||
if (sortBy === 'sortByColor') {
|
||||
groupKey = 'potionKey';
|
||||
} else if (sortBy === 'AZ') {
|
||||
groupKey = '';
|
||||
}
|
||||
|
||||
return groupBy(pets, groupKey);
|
||||
},
|
||||
|
||||
mounts (animalGroup, hideMissing, sortBy, searchText) {
|
||||
return this.listAnimals(animalGroup, 'mount', hideMissing, sortBy, searchText);
|
||||
let mounts = this.listAnimals(animalGroup, 'mount', hideMissing, sortBy, searchText);
|
||||
|
||||
// Don't group special
|
||||
if (animalGroup.key === 'specialMounts') {
|
||||
return {none: mounts};
|
||||
}
|
||||
|
||||
let groupKey = 'eggKey';
|
||||
if (sortBy === 'sortByColor') {
|
||||
groupKey = 'potionKey';
|
||||
} else if (sortBy === 'AZ') {
|
||||
groupKey = '';
|
||||
}
|
||||
|
||||
return groupBy(mounts, groupKey);
|
||||
},
|
||||
|
||||
getPetItemClass (pet) {
|
||||
@@ -875,7 +908,8 @@
|
||||
|
||||
hatchPet (pet) {
|
||||
this.$store.dispatch('common:hatch', {egg: pet.eggKey, hatchingPotion: pet.potionKey});
|
||||
this.hatchedPet = pet;
|
||||
|
||||
this.$root.$emit('hatchedPet::open', pet);
|
||||
this.closeHatchPetDialog();
|
||||
},
|
||||
|
||||
@@ -931,6 +965,8 @@
|
||||
}
|
||||
// opens the hatch dialog
|
||||
this.hatchablePet = pet;
|
||||
|
||||
this.$root.$emit('show::modal', 'hatching-modal');
|
||||
}
|
||||
},
|
||||
|
||||
@@ -945,9 +981,6 @@
|
||||
closeHatchPetDialog () {
|
||||
this.$root.$emit('hide::modal', 'hatching-modal');
|
||||
},
|
||||
closeHatchedPetDialog () {
|
||||
this.hatchedPet = null;
|
||||
},
|
||||
|
||||
resetHatchablePet ($event) {
|
||||
if (!$event) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template lang="pug">
|
||||
div
|
||||
.item-wrapper(:id="itemId")
|
||||
.item-wrapper(@click="click()", :id="itemId")
|
||||
.item(
|
||||
:class="{'item-empty': emptyItem}",
|
||||
)
|
||||
@@ -48,5 +48,10 @@ div
|
||||
itemId: uuid.v4(),
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
click () {
|
||||
this.$emit('click', {});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
<template lang="pug">
|
||||
div.item-with-icon.item-notifications.dropdown
|
||||
span.message-count.top-count(v-if='notificationsCount > 0') {{ notificationsCount }}
|
||||
.svg-icon.notifications(v-html="icons.notifications")
|
||||
// span.glyphicon(:class='iconClasses()')
|
||||
// span.notification-counter(v-if='getNotificationsCount()') {{getNotificationsCount()}}
|
||||
.dropdown-menu.dropdown-menu-right.user-dropdown
|
||||
h4.dropdown-item.dropdown-separated(v-if='!hasNoNotifications()') {{ $t('notifications') }}
|
||||
h4.dropdown-item.toolbar-notifs-no-messages(v-if='hasNoNotifications()') {{ $t('noNotifications') }}
|
||||
@@ -37,11 +36,11 @@ div.item-with-icon.item-notifications.dropdown
|
||||
@click='go("/user/profile")')
|
||||
span.glyphicon.glyphicon-plus-sign
|
||||
span {{ $t('haveUnallocated', {points: user.stats.points}) }}
|
||||
a.dropdown-item(v-for='(message, key) in user.newMessages', v-if='message.value')
|
||||
span(@click='navigateToGroup(key)')
|
||||
a.dropdown-item(v-for='message in userNewMessages')
|
||||
span(@click='navigateToGroup(message.key)')
|
||||
span.glyphicon.glyphicon-comment
|
||||
span {{message.name}}
|
||||
span.clear-button(@click='clearMessages(key)', :popover="$t('clear')",
|
||||
span.clear-button(@click='clearMessages(message.key)', :popover="$t('clear')",
|
||||
popover-placement='right', popover-trigger='mouseenter', popover-append-to-body='true') Clear
|
||||
a.dropdown-item(v-for='(notification, index) in user.groupNotifications', @click='viewGroupApprovalNotification(notification, index, true)')
|
||||
span(:class="groupApprovalNotificationIcon(notification)")
|
||||
@@ -58,6 +57,25 @@ div.item-with-icon.item-notifications.dropdown
|
||||
<style lang='scss' scoped>
|
||||
@import '~client/assets/scss/colors.scss';
|
||||
|
||||
.message-count {
|
||||
background-color: #46a7d9;
|
||||
border-radius: 50%;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
float: right;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.message-count.top-count {
|
||||
position: absolute;
|
||||
right: -.5em;
|
||||
top: .5em;
|
||||
padding: .2em;
|
||||
}
|
||||
|
||||
.clear-button {
|
||||
margin-left: .5em;
|
||||
}
|
||||
@@ -156,6 +174,43 @@ export default {
|
||||
return {name: ''};
|
||||
// return this.user.party;
|
||||
},
|
||||
userNewMessages () {
|
||||
// @TODO: For some reason data becomes corrupted. We should fix this on the server
|
||||
let userNewMessages = [];
|
||||
for (let key in this.user.newMessages) {
|
||||
let message = this.user.newMessages[key];
|
||||
if (message && message.name && message.value) {
|
||||
message.key = key;
|
||||
userNewMessages.push(message);
|
||||
}
|
||||
}
|
||||
return userNewMessages;
|
||||
},
|
||||
notificationsCount () {
|
||||
let count = 0;
|
||||
|
||||
if (this.user.invitations.parties) {
|
||||
count += this.user.invitations.parties.length;
|
||||
}
|
||||
|
||||
if (this.user.purchased.plan && this.user.purchased.plan.mysteryItems.length) {
|
||||
count++;
|
||||
}
|
||||
|
||||
if (this.user.invitations.guilds) {
|
||||
count += this.user.invitations.guilds.length;
|
||||
}
|
||||
|
||||
if (this.user.flags.classSelected && !this.user.preferences.disableClasses && this.user.stats.points) {
|
||||
count += this.user.stats.points > 0 ? 1 : 0;
|
||||
}
|
||||
|
||||
if (this.userNewMessages) {
|
||||
count += Object.keys(this.userNewMessages).length;
|
||||
}
|
||||
|
||||
return count;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
// @TODO: I hate this function, we can do better with a hashmap
|
||||
@@ -225,31 +280,6 @@ export default {
|
||||
clearCards () {
|
||||
this.$store.dispatch('chat:clearCards');
|
||||
},
|
||||
getNotificationsCount () {
|
||||
let count = 0;
|
||||
|
||||
if (this.user.invitations.parties) {
|
||||
count += this.user.invitations.parties.length;
|
||||
}
|
||||
|
||||
if (this.user.purchased.plan && this.user.purchased.plan.mysteryItems.length) {
|
||||
count++;
|
||||
}
|
||||
|
||||
if (this.user.invitations.guilds) {
|
||||
count += this.user.invitations.guilds.length;
|
||||
}
|
||||
|
||||
if (this.user.flags.classSelected && !this.user.preferences.disableClasses && this.user.stats.points) {
|
||||
count += this.user.stats.points > 0 ? 1 : 0;
|
||||
}
|
||||
|
||||
if (this.user.newMessages) {
|
||||
count += Object.keys(this.user.newMessages).length;
|
||||
}
|
||||
|
||||
return count;
|
||||
},
|
||||
iconClasses () {
|
||||
return this.selectNotificationValue(
|
||||
'glyphicon-gift',
|
||||
|
||||
@@ -158,6 +158,7 @@ export default {
|
||||
|
||||
return {
|
||||
yesterDailies: [],
|
||||
levelBeforeYesterdailies: 0,
|
||||
notificationData: {},
|
||||
unlockLevels,
|
||||
lastShownNotifications,
|
||||
@@ -256,12 +257,8 @@ export default {
|
||||
this.mp(mana);
|
||||
},
|
||||
userLvl (after, before) {
|
||||
if (after <= before) return;
|
||||
this.lvl();
|
||||
this.playSound('Level_Up');
|
||||
if (this.user._tmp && this.user._tmp.drop && this.user._tmp.drop.type === 'Quest') return;
|
||||
if (this.unlockLevels[`${after}`]) return;
|
||||
if (!this.user.preferences.suppressModals.levelUp) this.$root.$emit('show::modal', 'level-up');
|
||||
if (after <= before || this.isRunningYesterdailies) return;
|
||||
this.showLevelUpNotifications(after);
|
||||
},
|
||||
userClassSelect (after) {
|
||||
if (!after) return;
|
||||
@@ -294,6 +291,7 @@ export default {
|
||||
this.$store.dispatch('user:fetch'),
|
||||
this.$store.dispatch('tasks:fetchUserTasks'),
|
||||
]).then(() => {
|
||||
// List of prompts for user on changes. Sounds like we may need a refactor here, but it is clean for now
|
||||
if (this.user.flags.newStuff) {
|
||||
this.$root.$emit('show::modal', 'new-stuff');
|
||||
}
|
||||
@@ -303,6 +301,19 @@ export default {
|
||||
this.$root.$emit('show::modal', 'avatar-modal');
|
||||
}
|
||||
|
||||
if (this.user.stats.hp <= 0) {
|
||||
this.playSound('Death');
|
||||
this.$root.$emit('show::modal', 'death');
|
||||
}
|
||||
|
||||
if (this.questCompleted) {
|
||||
this.$root.$emit('show::modal', 'quest-completed');
|
||||
}
|
||||
|
||||
if (this.userClassSelect) {
|
||||
this.$root.$emit('show::modal', 'choose-class');
|
||||
}
|
||||
|
||||
// @TODO: This is a timeout to ensure dom is loaded
|
||||
window.setTimeout(() => {
|
||||
this.initTour();
|
||||
@@ -320,6 +331,13 @@ export default {
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
showLevelUpNotifications (newlevel) {
|
||||
this.lvl();
|
||||
this.playSound('Level_Up');
|
||||
if (this.user._tmp && this.user._tmp.drop && this.user._tmp.drop.type === 'Quest') return;
|
||||
if (this.unlockLevels[`${newlevel}`]) return;
|
||||
if (!this.user.preferences.suppressModals.levelUp) this.$root.$emit('show::modal', 'level-up');
|
||||
},
|
||||
playSound (sound) {
|
||||
this.$root.$emit('playSound', sound);
|
||||
},
|
||||
@@ -376,6 +394,7 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
this.levelBeforeYesterdailies = this.user.stats.lvl;
|
||||
this.$root.$emit('show::modal', 'yesterdaily');
|
||||
},
|
||||
async runYesterDailiesAction () {
|
||||
@@ -391,6 +410,10 @@ export default {
|
||||
this.$store.dispatch('tasks:fetchUserTasks', {forceLoad: true}),
|
||||
]);
|
||||
|
||||
if (this.levelBeforeYesterdailies < this.user.stats.lvl) {
|
||||
this.showLevelUpNotifications(this.user.stats.lvl);
|
||||
}
|
||||
|
||||
this.handleUserNotifications(this.user.notifications);
|
||||
this.scheduleNextCron();
|
||||
},
|
||||
|
||||
@@ -1,15 +1,24 @@
|
||||
<template lang="pug">
|
||||
b-modal#amazon-payment(title="Amazon", :hide-footer="true", size='lg')
|
||||
b-modal#amazon-payment(title="Amazon", :hide-footer="true", size='md')
|
||||
h2.text-center Continue with Amazon
|
||||
#AmazonPayButton
|
||||
#AmazonPayWallet(v-if="amazonPayments.loggedIn", style="width: 400px; height: 228px;")
|
||||
#AmazonPayRecurring(v-if="amazonPayments.loggedIn && amazonPayments.type === 'subscription'",
|
||||
| {{amazonPayments}}
|
||||
| {{amazonLoggedIn}}
|
||||
#AmazonPayWallet(v-if="amazonLoggedIn", style="width: 400px; height: 228px;")
|
||||
#AmazonPayRecurring(v-if="amazonLoggedIn && amazonPayments.type === 'subscription'",
|
||||
style="width: 400px; height: 140px;")
|
||||
.modal-footer
|
||||
.btn.btn-primary(:disabled="amazonPaymentsCanCheckout() || !amazonButtonEnabled",
|
||||
@click="amazonCheckOut()") {{ $t('checkout') }}
|
||||
.text-center
|
||||
.btn.btn-primary(v-if="amazonPaymentsCanCheckout",
|
||||
@click="amazonCheckOut()") {{ $t('checkout') }}
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
#AmazonPayButton {
|
||||
margin: 0 auto;
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
#AmazonPayRecurring {
|
||||
height: 200px;
|
||||
width: 500px;
|
||||
@@ -27,7 +36,7 @@ export default {
|
||||
components: {
|
||||
bModal,
|
||||
},
|
||||
props: ['amazonPayments'],
|
||||
props: ['amazonPaymentsProp'],
|
||||
data () {
|
||||
return {
|
||||
OffAmazonPayments: {},
|
||||
@@ -36,11 +45,32 @@ export default {
|
||||
amazonPaymentsbillingAgreementId: '',
|
||||
amazonPaymentspaymentSelected: false,
|
||||
amazonPaymentsrecurringConsent: 'false',
|
||||
amazonLoggedIn: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
...mapState(['isAmazonReady']),
|
||||
// @TODO: Eh, idk if we should move data props here or move these props to data. But we shouldn't have both
|
||||
amazonPayments () {
|
||||
let amazonPayments = {
|
||||
type: 'single',
|
||||
loggedIn: this.amazonLoggedIn,
|
||||
};
|
||||
amazonPayments = Object.assign({}, amazonPayments, this.amazonPaymentsProp);
|
||||
return amazonPayments;
|
||||
},
|
||||
amazonPaymentsCanCheckout () {
|
||||
if (this.amazonPayments.type === 'single') {
|
||||
return this.amazonPaymentspaymentSelected === true;
|
||||
} else if (this.amazonPayments.type === 'subscription') {
|
||||
return this.amazonPaymentspaymentSelected === true &&
|
||||
// Mah.. one is a boolean the other a string...
|
||||
this.amazonPaymentsrecurringConsent === 'true';
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
},
|
||||
mounted () {
|
||||
if (this.isAmazonReady) return this.setupAmazon();
|
||||
@@ -69,9 +99,10 @@ export default {
|
||||
|
||||
onSignIn: async (contract) => {
|
||||
this.amazonPaymentsbillingAgreementId = contract.getAmazonBillingAgreementId();
|
||||
this.amazonLoggedIn = true;
|
||||
this.$set(this.amazonPayments, 'loggedIn', true);
|
||||
|
||||
if (this.amazonPayments.type === 'subscription') {
|
||||
this.amazonPayments.loggedIn = true;
|
||||
this.amazonPaymentsinitWidgets();
|
||||
} else {
|
||||
let url = '/amazon/createOrderReferenceId';
|
||||
@@ -79,11 +110,15 @@ export default {
|
||||
billingAgreementId: this.amazonPaymentsbillingAgreementId,
|
||||
});
|
||||
|
||||
// @TODO: Success
|
||||
this.amazonPayments.loggedIn = true;
|
||||
this.amazonPaymentsorderReferenceId = response.data.orderReferenceId;
|
||||
this.amazonPaymentsinitWidgets();
|
||||
// @TODO: error
|
||||
if (response.status <= 400) {
|
||||
this.amazonPayments.orderReferenceId = response.data.data.orderReferenceId;
|
||||
|
||||
// @TODO: Clarify the deifference of these functions by renaming
|
||||
this.amazonPaymentsinitWidgets();
|
||||
this.amazonInitWidgets();
|
||||
return;
|
||||
}
|
||||
|
||||
alert(response.message);
|
||||
}
|
||||
},
|
||||
@@ -106,17 +141,6 @@ export default {
|
||||
onError: this.amazonOnError,
|
||||
});
|
||||
},
|
||||
amazonPaymentsCanCheckout () {
|
||||
if (this.amazonPayments.type === 'single') {
|
||||
return this.amazonPaymentspaymentSelected === true;
|
||||
} else if (this.amazonPayments.type === 'subscription') {
|
||||
return this.amazonPaymentspaymentSelected === true &&
|
||||
// Mah.. one is a boolean the other a string...
|
||||
this.amazonPaymentsrecurringConsent === 'true';
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
amazonInitWidgets () {
|
||||
let walletParams = {
|
||||
sellerId: AMAZON_PAYMENTS.SELLER_ID, // @TODO: Import
|
||||
@@ -131,6 +155,7 @@ export default {
|
||||
onError: this.amazonOnError,
|
||||
};
|
||||
|
||||
// @TODO: Check if this is duplicated below
|
||||
if (this.amazonPayments.type === 'subscription') {
|
||||
walletParams.agreementType = 'BillingAgreement';
|
||||
|
||||
@@ -158,7 +183,7 @@ export default {
|
||||
}).bind('AmazonPayRecurring');
|
||||
};
|
||||
} else {
|
||||
walletParams.amazonOrderReferenceId = this.amazonPaymentsorderReferenceId;
|
||||
walletParams.amazonOrderReferenceId = this.amazonPayments.orderReferenceId;
|
||||
}
|
||||
|
||||
new this.OffAmazonPayments.Widgets.Wallet(walletParams).bind('AmazonPayWallet');
|
||||
@@ -166,18 +191,22 @@ export default {
|
||||
async amazonCheckOut () {
|
||||
this.amazonButtonEnabled = false;
|
||||
|
||||
// @TODO: Create factory functions
|
||||
// @TODO: A gift should not read the same as buying gems for yourself.
|
||||
if (this.amazonPayments.type === 'single') {
|
||||
let url = '/amazon/checkout';
|
||||
let response = await axios.post(url, {
|
||||
orderReferenceId: this.amazonPaymentsorderReferenceId,
|
||||
orderReferenceId: this.amazonPayments.orderReferenceId,
|
||||
gift: this.amazonPaymentsgift,
|
||||
});
|
||||
|
||||
// Success
|
||||
this.amazonPaymentsreset();
|
||||
window.location.reload(true);
|
||||
if (response.status < 400) {
|
||||
this.reset();
|
||||
// @TODO: What are we syncing?
|
||||
window.location.reload(true);
|
||||
return;
|
||||
}
|
||||
|
||||
// Failure
|
||||
alert(response.message);
|
||||
this.amazonPaymentsreset();
|
||||
} else if (this.amazonPayments.type === 'subscription') {
|
||||
@@ -211,8 +240,13 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.amazonPayments.groupId) {
|
||||
this.$router.push(`/group-plans/${this.amazonPayments.groupId}/task-information`);
|
||||
return;
|
||||
}
|
||||
|
||||
window.location.reload(true);
|
||||
this.amazonPaymentsreset();
|
||||
this.reset();
|
||||
}
|
||||
},
|
||||
amazonPaymentsinitWidgets () {
|
||||
@@ -263,16 +297,15 @@ export default {
|
||||
},
|
||||
amazonOnError (error) {
|
||||
alert(error.getErrorMessage());
|
||||
// @TODO: this.amazonPaymentsreset();
|
||||
this.reset();
|
||||
},
|
||||
reset () {
|
||||
this.amazonPaymentsmodal.close(); // @TODO: this.$root.$emit('hide::modal', 'guild-form');
|
||||
this.amazonPaymentsmodal = null;
|
||||
this.amazonPayments.type = null;
|
||||
this.amazonPayments.loggedIn = false;
|
||||
this.amazonLoggedIn = false;
|
||||
this.amazonPaymentsgift = null;
|
||||
this.amazonPaymentsbillingAgreementId = null;
|
||||
this.amazonPaymentsorderReferenceId = null;
|
||||
this.amazonPayments.orderReferenceId = null;
|
||||
this.amazonPaymentspaymentSelected = false;
|
||||
this.amazonPaymentsrecurringConsent = false;
|
||||
this.amazonPaymentssubscription = null;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
.col-md-8.align-self-center
|
||||
p=text
|
||||
div(v-if='user')
|
||||
amazon-payments-modal(:amazon-payments='amazonPayments')
|
||||
amazon-payments-modal(:amazon-payments-prop='amazonPayments')
|
||||
b-modal(:hide-footer='true', :hide-header='true', :id='"buy-gems"', size='lg')
|
||||
.container-fluid.purple-gradient
|
||||
.gemfall
|
||||
@@ -21,6 +21,12 @@
|
||||
.nav-item(@click='selectedPage = "subscribe"', :class="{active: selectedPage === 'subscribe'}") {{ $t('subscribe') }}
|
||||
.nav-item(@click='selectedPage = "gems"', :class="{active: selectedPage === 'gems'}") {{ $t('buyGems') }}
|
||||
div(v-show='selectedPage === "gems"')
|
||||
div(v-if='hasSubscription')
|
||||
.row.text-center
|
||||
h2.mx-auto.text-leadin {{ $t('subscriptionAlreadySubscribedLeadIn') }}
|
||||
.row.text-center
|
||||
.col-6.offset-3
|
||||
p {{ $t("gemsPurchaseNote") }}
|
||||
.row.text-center
|
||||
h2.mx-auto.text-leadin {{ $t('gemBenefitLeadin') }}
|
||||
.row
|
||||
@@ -39,14 +45,14 @@
|
||||
.gem-text {{ $t('gems') }}
|
||||
.divider
|
||||
button.btn.btn-primary(@click='gemAmount = 4') {{gemAmount === 4 ? $t('selected') : '$1.00'}}
|
||||
.card.text-center.col-3(:class="{active: gemAmount === 21 }")
|
||||
.card.text-center.col-3(:class="{active: gemAmount === 20 }")
|
||||
.card-img-top
|
||||
.mx-auto(v-html='icons.twentyOneGems', style='"height: 55px; width: 47.5px; margin-top: 1.85em;"')
|
||||
.card-body
|
||||
.gem-count 20
|
||||
.gem-text {{ $t('gems') }}
|
||||
.divider
|
||||
button.btn.btn-primary(@click='gemAmount === 21 ? gemAmount = 0 : gemAmount = 21') {{gemAmount === 21 ? $t('selected') : '$5.00'}}
|
||||
button.btn.btn-primary(@click='gemAmount === 20 ? gemAmount = 0 : gemAmount = 20') {{gemAmount === 20 ? $t('selected') : '$5.00'}}
|
||||
//.card.text-center(:class="{active: gemAmount === 42}")
|
||||
.card-img-top
|
||||
.mx-auto(v-html='icons.fortyTwoGems', style='"height: 49.5px; width: 51px; margin-top: 1.9em;"')
|
||||
|
||||
@@ -141,6 +141,7 @@ export default {
|
||||
gemAmount: this.gift.gems.amount,
|
||||
});
|
||||
this.text(this.$t('sentGems'));
|
||||
this.close();
|
||||
},
|
||||
close () {
|
||||
this.$root.$emit('hide::modal', 'send-gems');
|
||||
|
||||
@@ -36,9 +36,9 @@
|
||||
h6(v-once) {{ $t('class') + ': ' }}
|
||||
// @TODO: what is classText
|
||||
span(v-if='classText') {{ classText }}
|
||||
button.btn.btn-danger.btn-xs(@click='changeClass(null)', v-once) {{ $t('changeClass') }}
|
||||
small.cost 3
|
||||
span.Pet_Currency_Gem1x.inline-gems
|
||||
button.btn.btn-danger.btn-xs(@click='changeClassForUser(true)', v-once) {{ $t('changeClass') }}
|
||||
small.cost 3 {{ $t('gems') }}
|
||||
// @TODO add icon span.Pet_Currency_Gem1x.inline-gems
|
||||
hr
|
||||
|
||||
div
|
||||
@@ -82,7 +82,7 @@
|
||||
|
||||
button.btn.btn-primary(@click='showBailey()', popover-trigger='mouseenter', popover-placement='right', :popover="$t('showBaileyPop')") {{ $t('showBailey') }}
|
||||
button.btn.btn-primary(@click='openRestoreModal()', popover-trigger='mouseenter', popover-placement='right', :popover="$t('fixValPop')") {{ $t('fixVal') }}
|
||||
button.btn.btn-primary(v-if='user.preferences.disableClasses == true', @click='changeClass({})',
|
||||
button.btn.btn-primary(v-if='user.preferences.disableClasses == true', @click='changeClassForUser(false)',
|
||||
popover-trigger='mouseenter', popover-placement='right', :popover="$t('enableClassPop')") {{ $t('enableClass') }}
|
||||
|
||||
hr
|
||||
@@ -169,14 +169,15 @@
|
||||
.form-group
|
||||
input.form-control(type='password', :placeholder="$t('confirmPass')", v-model='passwordUpdates.confirmPassword')
|
||||
button.btn.btn-primary(type='submit', @click='changeUser("password", passwordUpdates)') {{ $t('submit') }}
|
||||
hr
|
||||
|
||||
div
|
||||
h5 {{ $t('dangerZone') }}
|
||||
div
|
||||
h5 {{ $t('dangerZone') }}
|
||||
div
|
||||
button.btn.btn-danger(@click='openResetModal()',
|
||||
popover-trigger='mouseenter', popover-placement='right', v-b-popover.hover.auto="$t('resetAccPop')") {{ $t('resetAccount') }}
|
||||
button.btn.btn-danger(@click='openDeleteModal()',
|
||||
popover-trigger='mouseenter', v-b-popover.hover.auto="$t('deleteAccPop')") {{ $t('deleteAccount') }}
|
||||
button.btn.btn-danger(@click='openResetModal()',
|
||||
popover-trigger='mouseenter', popover-placement='right', v-b-popover.hover.auto="$t('resetAccPop')") {{ $t('resetAccount') }}
|
||||
button.btn.btn-danger(@click='openDeleteModal()',
|
||||
popover-trigger='mouseenter', v-b-popover.hover.auto="$t('deleteAccPop')") {{ $t('deleteAccount') }}
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
@@ -378,8 +379,8 @@ export default {
|
||||
|
||||
this.$router.go('/tasks');
|
||||
},
|
||||
async changeClass () {
|
||||
if (!confirm('Are you sure you want to change your class for 3 gems?')) return;
|
||||
async changeClassForUser (confirmationNeeded) {
|
||||
if (confirmationNeeded && !confirm(this.$t('changeClassConfirmCost'))) return;
|
||||
try {
|
||||
changeClass(this.user);
|
||||
await axios.post('/api/v3/user/change-class');
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template lang="pug">
|
||||
.standard-page
|
||||
amazon-payments-modal(:amazon-payments='amazonPayments')
|
||||
amazon-payments-modal(:amazon-payments-prop='amazonPayments')
|
||||
|
||||
h1 {{ $t('subscription') }}
|
||||
.row
|
||||
@@ -112,7 +112,6 @@ import filter from 'lodash/filter';
|
||||
import sortBy from 'lodash/sortBy';
|
||||
import min from 'lodash/min';
|
||||
import { mapState } from 'client/libs/store';
|
||||
import encodeParams from 'client/libs/encodeParams';
|
||||
|
||||
import subscriptionBlocks from '../../../common/script/content/subscriptionBlocks';
|
||||
import planGemLimits from '../../../common/script/libs/planGemLimits';
|
||||
@@ -261,42 +260,6 @@ export default {
|
||||
subs.basic_6mo.discount = true;
|
||||
subs.google_6mo.discount = false;
|
||||
},
|
||||
async cancelSubscription (config) {
|
||||
if (config && config.group && !confirm(this.$t('confirmCancelGroupPlan'))) return;
|
||||
if (!confirm(this.$t('sureCancelSub'))) return;
|
||||
|
||||
let group;
|
||||
if (config && config.group) {
|
||||
group = config.group;
|
||||
}
|
||||
|
||||
let paymentMethod = this.user.purchased.plan.paymentMethod;
|
||||
if (group) {
|
||||
paymentMethod = group.purchased.plan.paymentMethod;
|
||||
}
|
||||
|
||||
if (paymentMethod === 'Amazon Payments') {
|
||||
paymentMethod = 'amazon';
|
||||
} else {
|
||||
paymentMethod = paymentMethod.toLowerCase();
|
||||
}
|
||||
|
||||
let queryParams = {
|
||||
_id: this.user._id,
|
||||
apiToken: this.credentials.API_TOKEN,
|
||||
noRedirect: true,
|
||||
};
|
||||
|
||||
if (group) {
|
||||
queryParams.groupId = group._id;
|
||||
}
|
||||
|
||||
let cancelUrl = `/${paymentMethod}/subscribe/cancel?${encodeParams(queryParams)}`;
|
||||
await axios.get(cancelUrl);
|
||||
// Success
|
||||
alert(this.$t('paypalCanceled'));
|
||||
this.$router.push('/');
|
||||
},
|
||||
getCancelSubInfo () {
|
||||
// @TODO: String 'cancelSubInfoGroup Plan' not found. ?
|
||||
return this.$t(`cancelSubInfo${this.user.purchased.plan.paymentMethod}`);
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
span.svg-icon.inline.icon-10(aria-hidden="true", v-html="icons.close", @click="hideDialog()")
|
||||
|
||||
div.content(v-if="item != null")
|
||||
|
||||
div.inner-content
|
||||
slot(name="item", :item="item")
|
||||
div(v-if="showAvatar")
|
||||
@@ -46,6 +45,11 @@
|
||||
span.svg-icon.inline.icon-32(aria-hidden="true", v-html="icons[getPriceClass()]")
|
||||
span.value(:class="getPriceClass()") {{ item.value }}
|
||||
|
||||
.gems-left(v-if='item.key === "gem"')
|
||||
strong(v-if='gemsLeft > 0') {{ gemsLeft }} {{ $t('gemsRemaining') }}
|
||||
strong(v-if='gemsLeft === 0') {{ $t('maxBuyGems') }}
|
||||
|
||||
|
||||
button.btn.btn-primary(
|
||||
@click="purchaseGems()",
|
||||
v-if="getPriceClass() === 'gems' && !this.enoughCurrency(getPriceClass(), item.value)"
|
||||
@@ -54,6 +58,7 @@
|
||||
button.btn.btn-primary(
|
||||
@click="buyItem()",
|
||||
v-else,
|
||||
:disabled='item.key === "gem" && gemsLeft === 0',
|
||||
:class="{'notEnough': !preventHealthPotion || !this.enoughCurrency(getPriceClass(), item.value)}"
|
||||
) {{ $t('buyNow') }}
|
||||
|
||||
@@ -203,12 +208,18 @@
|
||||
.bordered {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.gems-left {
|
||||
margin-top: .5em;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import bModal from 'bootstrap-vue/lib/components/modal';
|
||||
import * as Analytics from 'client/libs/analytics';
|
||||
import spellsMixin from 'client/mixins/spells';
|
||||
import planGemLimits from 'common/script/libs/planGemLimits';
|
||||
|
||||
import svgClose from 'assets/svg/close.svg';
|
||||
import svgGold from 'assets/svg/gold.svg';
|
||||
@@ -220,6 +231,7 @@
|
||||
import BalanceInfo from './balanceInfo.vue';
|
||||
import currencyMixin from './_currencyMixin';
|
||||
import notifications from 'client/mixins/notifications';
|
||||
import buyMixin from 'client/mixins/buy';
|
||||
|
||||
import { mapState } from 'client/libs/store';
|
||||
|
||||
@@ -233,7 +245,7 @@
|
||||
import moment from 'moment';
|
||||
|
||||
export default {
|
||||
mixins: [currencyMixin, notifications],
|
||||
mixins: [currencyMixin, notifications, spellsMixin, buyMixin],
|
||||
components: {
|
||||
bModal,
|
||||
BalanceInfo,
|
||||
@@ -290,6 +302,10 @@
|
||||
limitedString () {
|
||||
return this.$t('limitedOffer', {date: moment(seasonalShopConfig.dateRange.end).format('LL')});
|
||||
},
|
||||
gemsLeft () {
|
||||
if (!this.user.purchased.plan) return 0;
|
||||
return planGemLimits.convCap + this.user.purchased.plan.consecutive.gemCapExtra - this.user.purchased.plan.gemsBought;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
item: function itemChanged () {
|
||||
@@ -301,17 +317,11 @@
|
||||
this.$emit('change', $event);
|
||||
},
|
||||
buyItem () {
|
||||
if (this.genericPurchase) {
|
||||
this.$store.dispatch('shops:genericPurchase', {
|
||||
pinType: this.item.pinType,
|
||||
type: this.item.purchaseType,
|
||||
key: this.item.key,
|
||||
currency: this.item.currency,
|
||||
});
|
||||
|
||||
if (this.item.cast) {
|
||||
this.castStart(this.item);
|
||||
} else if (this.genericPurchase) {
|
||||
this.makeGenericPurchase(this.item);
|
||||
this.purchased(this.item.text);
|
||||
this.$root.$emit('buyModal::boughtItem', this.item);
|
||||
this.$root.$emit('playSound', 'Reward');
|
||||
}
|
||||
|
||||
this.$emit('buyPressed', this.item);
|
||||
|
||||
@@ -383,6 +383,8 @@
|
||||
const sortGearTypes = ['sortByType', 'sortByPrice', 'sortByCon', 'sortByPer', 'sortByStr', 'sortByInt'];
|
||||
|
||||
import notifications from 'client/mixins/notifications';
|
||||
import buyMixin from 'client/mixins/buy';
|
||||
import currencyMixin from '../_currencyMixin';
|
||||
|
||||
const sortGearTypeMap = {
|
||||
sortByType: 'type',
|
||||
@@ -393,7 +395,7 @@
|
||||
};
|
||||
|
||||
export default {
|
||||
mixins: [notifications],
|
||||
mixins: [notifications, buyMixin, currencyMixin],
|
||||
components: {
|
||||
ShopItem,
|
||||
Item,
|
||||
@@ -694,6 +696,11 @@ export default {
|
||||
}
|
||||
},
|
||||
itemSelected (item) {
|
||||
if (item.purchaseType !== 'gear' && this.$store.state.recentlyPurchased[item.key]) {
|
||||
this.makeGenericPurchase(item);
|
||||
return;
|
||||
}
|
||||
|
||||
this.$root.$emit('buyModal::showItem', item);
|
||||
},
|
||||
featuredItemSelected (item) {
|
||||
|
||||
@@ -43,11 +43,9 @@
|
||||
:currencyNeeded="priceType",
|
||||
:amountNeeded="item.value"
|
||||
).float-right
|
||||
|
||||
|
||||
</template>
|
||||
<style lang="scss">
|
||||
|
||||
<style lang="scss">
|
||||
@import '~client/assets/scss/colors.scss';
|
||||
@import '~client/assets/scss/modal.scss';
|
||||
|
||||
@@ -173,7 +171,6 @@
|
||||
</style>
|
||||
|
||||
<script>
|
||||
|
||||
import {mapState} from 'client/libs/store';
|
||||
|
||||
import bModal from 'bootstrap-vue/lib/components/modal';
|
||||
@@ -188,12 +185,13 @@
|
||||
import currencyMixin from '../_currencyMixin';
|
||||
import QuestInfo from './questInfo.vue';
|
||||
import notifications from 'client/mixins/notifications';
|
||||
import buyMixin from 'client/mixins/buy';
|
||||
|
||||
import questDialogDrops from './questDialogDrops';
|
||||
import questDialogContent from './questDialogContent';
|
||||
|
||||
export default {
|
||||
mixins: [currencyMixin, notifications],
|
||||
mixins: [currencyMixin, notifications, buyMixin],
|
||||
components: {
|
||||
bModal,
|
||||
BalanceInfo,
|
||||
@@ -243,15 +241,8 @@
|
||||
this.$emit('change', $event);
|
||||
},
|
||||
buyItem () {
|
||||
this.$store.dispatch('shops:genericPurchase', {
|
||||
pinType: this.item.pinType,
|
||||
type: this.item.purchaseType,
|
||||
key: this.item.key,
|
||||
currency: this.item.currency,
|
||||
});
|
||||
this.makeGenericPurchase(this.item, 'buyQuestModal');
|
||||
this.purchased(this.item.text);
|
||||
this.$root.$emit('playSound', 'Reward');
|
||||
this.$emit('buyPressed', this.item);
|
||||
this.hideDialog();
|
||||
},
|
||||
togglePinned () {
|
||||
|
||||
@@ -328,6 +328,8 @@
|
||||
import ItemRows from 'client/components/ui/itemRows';
|
||||
import toggleSwitch from 'client/components/ui/toggleSwitch';
|
||||
import Avatar from 'client/components/avatar';
|
||||
import buyMixin from 'client/mixins/buy';
|
||||
import currencyMixin from '../_currencyMixin';
|
||||
|
||||
import BuyModal from './buyQuestModal.vue';
|
||||
import QuestInfo from './questInfo.vue';
|
||||
@@ -348,6 +350,7 @@
|
||||
import _map from 'lodash/map';
|
||||
|
||||
export default {
|
||||
mixins: [buyMixin, currencyMixin],
|
||||
components: {
|
||||
ShopItem,
|
||||
Item,
|
||||
@@ -470,6 +473,11 @@ export default {
|
||||
selectItem (item) {
|
||||
this.selectedItemToBuy = item;
|
||||
|
||||
if (this.$store.state.recentlyPurchased[item.key]) {
|
||||
this.makeGenericPurchase(item);
|
||||
return;
|
||||
}
|
||||
|
||||
this.$root.$emit('show::modal', 'buy-quest-modal');
|
||||
},
|
||||
},
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
span.text(v-once, v-html="seasonal.notes")
|
||||
span.rectangle
|
||||
div.content(v-else-if="seasonal.featured.items.length !== 0")
|
||||
div.featured-label.with-border
|
||||
div.featured-label.with-border(v-if='!featuredGearBought')
|
||||
span.rectangle
|
||||
span.text(v-once) {{ $t('featuredset', { name: seasonal.featured.text }) }}
|
||||
span.rectangle
|
||||
@@ -274,6 +274,8 @@
|
||||
import ItemRows from 'client/components/ui/itemRows';
|
||||
import toggleSwitch from 'client/components/ui/toggleSwitch';
|
||||
import Avatar from 'client/components/avatar';
|
||||
import buyMixin from 'client/mixins/buy';
|
||||
import currencyMixin from '../_currencyMixin';
|
||||
|
||||
import bPopover from 'bootstrap-vue/lib/components/popover';
|
||||
import bDropdown from 'bootstrap-vue/lib/components/dropdown';
|
||||
@@ -302,6 +304,7 @@
|
||||
import shops from 'common/script/libs/shops';
|
||||
|
||||
export default {
|
||||
mixins: [buyMixin, currencyMixin],
|
||||
components: {
|
||||
ShopItem,
|
||||
Item,
|
||||
@@ -349,6 +352,7 @@
|
||||
selectedSortItemsBy: 'AZ',
|
||||
|
||||
hidePinned: false,
|
||||
featuredGearBought: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@@ -363,7 +367,21 @@
|
||||
},
|
||||
|
||||
seasonal () {
|
||||
return shops.getSeasonalShop(this.user);
|
||||
let seasonal = shops.getSeasonalShop(this.user);
|
||||
|
||||
let itemsNotOwned = seasonal.featured.items.filter(item => {
|
||||
return !this.user.items.gear.owned[item.key];
|
||||
});
|
||||
seasonal.featured.items = itemsNotOwned;
|
||||
|
||||
// If we are out of gear, show the spells
|
||||
// @TODO: add dates to check instead?
|
||||
if (seasonal.featured.items.length === 0) {
|
||||
this.featuredGearBought = true;
|
||||
seasonal.featured.items = seasonal.featured.items.concat(seasonal.categories[0].items);
|
||||
}
|
||||
|
||||
return seasonal;
|
||||
},
|
||||
seasonalCategories () {
|
||||
return this.seasonal.categories;
|
||||
@@ -470,9 +488,14 @@
|
||||
}
|
||||
},
|
||||
itemSelected (item) {
|
||||
if (!item.locked) {
|
||||
this.$root.$emit('buyModal::showItem', item);
|
||||
if (item.locked) return;
|
||||
|
||||
if (this.$store.state.recentlyPurchased[item.key]) {
|
||||
this.makeGenericPurchase(item);
|
||||
return;
|
||||
}
|
||||
|
||||
this.$root.$emit('buyModal::showItem', item);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -4,7 +4,6 @@ div
|
||||
.item(:class="getItemClasses()")
|
||||
slot(name="itemBadge", :item="item", :emptyItem="emptyItem")
|
||||
|
||||
|
||||
span.badge.badge-pill.badge-item.badge-clock(
|
||||
v-if="item.event && showEventBadge",
|
||||
)
|
||||
@@ -19,7 +18,6 @@ div
|
||||
slot(name="itemImage", :item="item")
|
||||
span.svg-icon.inline.icon-48(v-if="item.key == 'gem'", v-html="icons.gems")
|
||||
|
||||
|
||||
div.price
|
||||
span.svg-icon.inline.icon-16(v-html="icons[currencyClass]")
|
||||
|
||||
|
||||
@@ -123,6 +123,7 @@ export default {
|
||||
};
|
||||
},
|
||||
created () {
|
||||
// @TODO the notifications always close even if timeout is false
|
||||
let timeout = this.notification.hasOwnProperty('timeout') ? this.notification.timeout : true;
|
||||
if (timeout) {
|
||||
let delay = this.notification.delay || 1000;
|
||||
|
||||
@@ -71,9 +71,6 @@
|
||||
p.span
|
||||
span {{ $t('marketing4Lead3-2') }}
|
||||
a.btn.btn-primary(href='/static/plans',target='_blank') {{ $t('contactUs') }}
|
||||
p.span
|
||||
span {{ $t('marketing4Lead3-3') }}
|
||||
a.btn.btn-primary(href='/static/videos') {{ $t('watchVideos') }}
|
||||
</template>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
|
||||
94
website/client/components/static/newStuff.vue
Normal file
@@ -0,0 +1,94 @@
|
||||
<template lang='pug'>
|
||||
div
|
||||
.media
|
||||
.align-self-center.right-margin(:class='baileyClass')
|
||||
.media-body
|
||||
h1.align-self-center(v-markdown='$t("newStuff")')
|
||||
h2 10/2/2017 - SPOOKY SPARKLES, MOBILE UPDATES, AND BUG FIXES
|
||||
hr
|
||||
.media
|
||||
.media-body
|
||||
h3 Spooky Sparkles and Seasonal Shop
|
||||
p(v-markdown='"There\'s a new Gold-purchasable item in the Seasonal Shop: [Spooky Sparkles](/shops/seasonal)! Buy some and then cast it on your friends. I wonder what it will do?"')
|
||||
p If you have Spooky Sparkles cast on you, you will receive the "Alarming Friends" badge! Don't worry, any mysterious effects will wear off the next day.... or you can cancel them early by buying an Opaque Potion!
|
||||
p While you're at it, be sure to check out all the other items in the Seasonal Shop! There are lots of equipment items from the previous Fall Festivals. The Seasonal Shop will only be open until October 31st, so stock up now.
|
||||
.small by Lemoness and SabreCat
|
||||
.promo_spooky_sparkles
|
||||
h3 Updates to iOS and Android Apps!
|
||||
p(v-markdown='"There are exciting new updates to our [Android](https://play.google.com/store/apps/details?id=com.habitrpg.android.habitica&hl=en) and [iOS](https://itunes.apple.com/us/app/habitica-gamified-task-manager/id994882113?mt=8) apps! In both, we\'ve updated the design of the Shops and your Rewards column. Plus, now you can pin items you want from Shops to your Rewards column! We hope this makes upgrading your avatar and earning Rewards from your tasks even more motivating."')
|
||||
p Android users can also now reset their accounts via the app!
|
||||
p In the iOS app, we've also added improved VoiceOver support for better accessibility. We welcome your feedback on this feature via the in-app "Send Feedback" option.
|
||||
p We hope you enjoy the updates! Be sure to download them now for a better Habitica experience!
|
||||
p If you like the improvements that we’ve been making to our apps, please consider reviewing these new versions. It really helps us out!
|
||||
.small by viirus and piyorii
|
||||
.promo_login_screen.center-block
|
||||
h3 New Post-Update Bug Fixes!
|
||||
p Hello Habiticans! <3 We’ve released a big batch of bug fixes! Many thanks for your patience, and for taking the time to report these bugs. Some changes:
|
||||
ul
|
||||
li Due to concerns about accessibility and usability of the Tasks page, we’ve removed the scrollbars on the different Tasks columns.
|
||||
li Due to concerns about the skills bar being too large, we have released a new, more compact design! It should also now stay minimized once you’ve minimized it before, so that it won’t block your tasks all the time.
|
||||
li Now you no longer need to click “Apply Filters” – they take automatic effect when you check them and uncheck them! The dropdown also closes when you move your mouse away, to further reduce the clicking required.
|
||||
li Clicking “Scheduled” now correctly sorts your To-Dos by date
|
||||
li Creating a task while a filter is selected now defaults to being tagged with that filter!
|
||||
li Your Stables are now correctly configured so that the rows of pets and mounts don’t blend together on smaller screens!
|
||||
li You can now click directly on a Mount to equip it!
|
||||
li We’ve fixed a few bugs that were preventing some people from subscribing or buying gems, but some are remaining. Please be sure to report these in Report a Bug (linked below) and we will help you out ASAP!
|
||||
li You should now be able to remove tasks when you leave a Challenge!
|
||||
li You can edit Challenge Tags again!
|
||||
li We’ve fixed the confusing placement of the “Send” buttons in chat
|
||||
li We’ve fixed some style issues that were causing the Quests to occasionally look strange
|
||||
li When you edit a Guild, it will default to you as the leader
|
||||
li The "Members" label is now called the "Member list" to help clarify that you can click it to see all the members of a Party, Guild, or Challenge!
|
||||
li If you have a pending quest, you can now see which members have accepted before you start your quest.
|
||||
li When you complete a quest, the final reward popups are appearing again!
|
||||
li You can now edit Parties normally!
|
||||
li You can now correctly allocate your Stat points without needing to refresh the page!
|
||||
li If you opted out of choosing a class, it will no longer give you an unnecessary warning about costing 3 Gems.
|
||||
li You can no longer accidentally block yourself from your profile.
|
||||
li Mystic Hourglasses now only show in the header if you have more than one. (Thanks for subscribing!)
|
||||
li Markdown works in Challenge titles again!
|
||||
li You can now Clone Challenges again!
|
||||
li The Bailey news loads on the mobile apps again!
|
||||
li The red User ID notifications in the corner should be gone!
|
||||
li(v-markdown='"Chat performance should be better. (We’re still investigating to make it even better, so please let us know in the [Report a Bug Guild](https://habitica.com/groups/guild/a29da26b-37de-4a71-b0c6-48e72a900dac) if chat lag is still happening to you!)"')
|
||||
p(v-markdown='"Refresh the page to enjoy these fixes! If you’re still having trouble with them after refreshing the page, please let us know in the [Report a Bug Guild](https://habitica.com/groups/guild/a29da26b-37de-4a71-b0c6-48e72a900dac) and we will investigate :) If you have feedback about the update that is not related to bugs or immediate accessibility issues, we’re opening a public Trello card for these comments on October 5!"')
|
||||
p Thanks for being such a great community! It means a lot to us <3
|
||||
br
|
||||
</template>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
@import '~client/assets/scss/static.scss';
|
||||
.center-block {
|
||||
margin: 0 auto 1em auto;
|
||||
}
|
||||
|
||||
.right-margin {
|
||||
margin-right: 1em;
|
||||
}
|
||||
|
||||
.small {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import markdown from 'client/directives/markdown';
|
||||
|
||||
export default {
|
||||
data () {
|
||||
let worldDmg = {
|
||||
bailey: false,
|
||||
};
|
||||
|
||||
return {
|
||||
baileyClass: {
|
||||
'npc_bailey_broken': worldDmg.bailey, // eslint-disable-line
|
||||
'npc_bailey': !worldDmg.bailey, // eslint-disable-line
|
||||
},
|
||||
};
|
||||
},
|
||||
directives: {
|
||||
markdown,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -1,14 +1,14 @@
|
||||
<template lang='pug'>
|
||||
div
|
||||
static-header(:class='{"home-header": $route.name === "home"}')
|
||||
static-header(v-if='showContentWrap', :class='{"home-header": $route.name === "home"}')
|
||||
|
||||
.static-wrapper
|
||||
router-view
|
||||
|
||||
#purple-footer
|
||||
#purple-footer(v-if='showContentWrap')
|
||||
app-footer
|
||||
|
||||
#bottom-wrap.purple-4
|
||||
#bottom-wrap.purple-4(v-if='showContentWrap')
|
||||
#bottom-background
|
||||
.seamless_mountains_demo_repeat
|
||||
.midground_foreground_extended2
|
||||
@@ -149,5 +149,10 @@
|
||||
AppFooter,
|
||||
StaticHeader,
|
||||
},
|
||||
computed: {
|
||||
showContentWrap () {
|
||||
return this.$route.name !== 'news';
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template lang='pug'>
|
||||
b-modal#broken-task-modal(title="Broken Challenge", size='sm', :hide-footer="true", v-if='brokenChallengeTask.challenge')
|
||||
b-modal#broken-task-modal(title="Broken Challenge", size='sm', :hide-footer="true", v-if='brokenChallengeTask && brokenChallengeTask.challenge')
|
||||
.modal-body
|
||||
div(v-if='brokenChallengeTask.challenge.broken === "TASK_DELETED" || brokenChallengeTask.challenge.broken === "CHALLENGE_TASK_NOT_FOUND"')
|
||||
h2 {{ $t('brokenTask') }}
|
||||
@@ -38,10 +38,14 @@ import notifications from 'client/mixins/notifications';
|
||||
|
||||
export default {
|
||||
mixins: [notifications],
|
||||
props: ['brokenChallengeTask'],
|
||||
components: {
|
||||
bModal,
|
||||
},
|
||||
computed: {
|
||||
brokenChallengeTask () {
|
||||
return this.$store.state.brokenChallengeTask;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...mapActions({
|
||||
destroyTask: 'tasks:destroy',
|
||||
@@ -70,6 +74,7 @@ export default {
|
||||
this.destroyTask(this.brokenChallengeTask);
|
||||
},
|
||||
close () {
|
||||
this.$store.state.brokenChallengeTask = {};
|
||||
this.$root.$emit('hide::modal', 'broken-task-modal');
|
||||
},
|
||||
},
|
||||
|
||||
@@ -46,6 +46,11 @@
|
||||
min-height: 556px;
|
||||
}
|
||||
|
||||
.task-wrapper {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.task-wrapper + .reward-items {
|
||||
margin-top: 16px;
|
||||
}
|
||||
@@ -104,7 +109,7 @@
|
||||
.column-background {
|
||||
position: absolute;
|
||||
bottom: 32px;
|
||||
z-index: 7;
|
||||
z-index: 1;
|
||||
|
||||
&.initial-description {
|
||||
top: 30%;
|
||||
@@ -159,19 +164,26 @@
|
||||
|
||||
<script>
|
||||
import Task from './task';
|
||||
import sortBy from 'lodash/sortBy';
|
||||
import throttle from 'lodash/throttle';
|
||||
import bModal from 'bootstrap-vue/lib/components/modal';
|
||||
|
||||
import sortable from 'client/directives/sortable.directive';
|
||||
import buyMixin from 'client/mixins/buy';
|
||||
import { mapState, mapActions } from 'client/libs/store';
|
||||
import shopItem from '../shops/shopItem';
|
||||
|
||||
import { shouldDo } from 'common/script/cron';
|
||||
import inAppRewards from 'common/script/libs/inAppRewards';
|
||||
import spells from 'common/script/content/spells';
|
||||
|
||||
import habitIcon from 'assets/svg/habit.svg';
|
||||
import dailyIcon from 'assets/svg/daily.svg';
|
||||
import todoIcon from 'assets/svg/todo.svg';
|
||||
import rewardIcon from 'assets/svg/reward.svg';
|
||||
import bModal from 'bootstrap-vue/lib/components/modal';
|
||||
import shopItem from '../shops/shopItem';
|
||||
import throttle from 'lodash/throttle';
|
||||
import sortable from 'client/directives/sortable.directive';
|
||||
|
||||
export default {
|
||||
mixins: [buyMixin],
|
||||
components: {
|
||||
Task,
|
||||
bModal,
|
||||
@@ -203,7 +215,7 @@ export default {
|
||||
label: 'todos',
|
||||
filters: [
|
||||
{label: 'remaining', filter: t => !t.completed, default: true}, // active
|
||||
{label: 'scheduled', filter: t => !t.completed && t.date},
|
||||
{label: 'scheduled', filter: t => !t.completed && t.date, sort: t => t.date},
|
||||
{label: 'complete2', filter: t => t.completed},
|
||||
],
|
||||
},
|
||||
@@ -251,8 +263,30 @@ export default {
|
||||
},
|
||||
inAppRewards () {
|
||||
let watchRefresh = this.forceRefresh; // eslint-disable-line
|
||||
let rewards = inAppRewards(this.user);
|
||||
|
||||
return inAppRewards(this.user);
|
||||
|
||||
// Add season rewards if user is affected
|
||||
// @TODO: Add buff coniditional
|
||||
const seasonalSkills = {
|
||||
snowball: 'salt',
|
||||
spookySparkles: 'opaquePotion',
|
||||
shinySeed: 'petalFreePotion',
|
||||
seafoam: 'sand',
|
||||
};
|
||||
|
||||
for (let key in seasonalSkills) {
|
||||
if (this.user.stats.buffs[key]) {
|
||||
let debuff = seasonalSkills[key];
|
||||
let item = Object.assign({}, spells.special[debuff]);
|
||||
item.text = item.text();
|
||||
item.notes = item.notes();
|
||||
item.class = `shop_${key}`;
|
||||
rewards.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
return rewards;
|
||||
},
|
||||
hasRewardsList () {
|
||||
return this.isUser === true && this.type === 'reward' && this.activeFilters[this.type].label !== 'custom';
|
||||
@@ -316,6 +350,10 @@ export default {
|
||||
this.loadCompletedTodos();
|
||||
}
|
||||
this.activeFilters[type] = filter;
|
||||
|
||||
if (filter.sort) {
|
||||
this.tasks[`${type}s`] = sortBy(this.tasks[`${type}s`], filter.sort);
|
||||
}
|
||||
},
|
||||
setColumnBackgroundVisibility () {
|
||||
this.$nextTick(() => {
|
||||
@@ -373,6 +411,14 @@ export default {
|
||||
}
|
||||
},
|
||||
openBuyDialog (rewardItem) {
|
||||
// Buy armoire and health potions immediately
|
||||
let itemsToPurchaseImmediately = ['potion', 'armoire'];
|
||||
if (itemsToPurchaseImmediately.indexOf(rewardItem.key) !== -1) {
|
||||
this.makeGenericPurchase(rewardItem);
|
||||
this.$emit('buyPressed', rewardItem);
|
||||
return;
|
||||
}
|
||||
|
||||
if (rewardItem.purchaseType !== 'gear' || !rewardItem.locked) {
|
||||
this.$emit('openBuyDialog', rewardItem);
|
||||
}
|
||||
|
||||
@@ -21,16 +21,13 @@ div(v-if='user.stats.lvl > 10')
|
||||
@click='castStart(skill)',
|
||||
v-for='(skill, key) in spells[user.stats.class]',
|
||||
v-if='user.stats.lvl >= skill.lvl',
|
||||
popover-trigger='mouseenter',
|
||||
popover-placement='top',
|
||||
:popover='skillNotes(skill)')
|
||||
v-b-popover.hover.auto='skill.notes()')
|
||||
.spell.col-12.row
|
||||
.col-8.details
|
||||
a(:class='{"disabled": spellDisabled(key)}')
|
||||
p.title {{skill.text()}}
|
||||
p.notes {{skill.notes()}}
|
||||
div.img(:class='`shop_${skill.key} shop-sprite item-img`')
|
||||
span.title {{skill.text()}}
|
||||
.col-4.mana
|
||||
.img(:class='`shop_${skill.key} shop-sprite item-img`')
|
||||
.mana-text
|
||||
.svg-icon(v-html="icons.mana")
|
||||
div {{skill.mana}}
|
||||
@@ -52,23 +49,33 @@ div(v-if='user.stats.lvl > 10')
|
||||
|
||||
.spell:hover {
|
||||
cursor: pointer;
|
||||
border: solid 2px #50b5e9;
|
||||
}
|
||||
|
||||
.spell {
|
||||
background: #ffffff;
|
||||
border: solid 2px transparent;
|
||||
margin-bottom: 1em;
|
||||
box-shadow: 0 2px 2px 0 rgba(26, 24, 29, 0.16), 0 1px 4px 0 rgba(26, 24, 29, 0.12);
|
||||
border-radius: 2px;
|
||||
border-radius: 1000px;
|
||||
color: #4e4a57;
|
||||
padding-right: 0;
|
||||
padding-left: 0;
|
||||
overflow: hidden;
|
||||
|
||||
.details {
|
||||
text-align: left;
|
||||
padding-top: .5em;
|
||||
|
||||
p {
|
||||
margin-bottom: .5em;
|
||||
.img {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
span {
|
||||
display: inline-block;
|
||||
width: 50%;
|
||||
padding-bottom: .7em;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
.notes {
|
||||
@@ -83,6 +90,7 @@ div(v-if='user.stats.lvl > 10')
|
||||
|
||||
.mana-text {
|
||||
margin-bottom: .2em;
|
||||
padding-top: 1.1em;
|
||||
|
||||
div {
|
||||
display: inline-block;
|
||||
@@ -140,26 +148,28 @@ div(v-if='user.stats.lvl > 10')
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import axios from 'axios';
|
||||
import isArray from 'lodash/isArray';
|
||||
import bPopover from 'bootstrap-vue/lib/directives/popover';
|
||||
|
||||
import spells from '../../../common/script/content/spells';
|
||||
|
||||
import { mapState } from 'client/libs/store';
|
||||
import notifications from 'client/mixins/notifications';
|
||||
import spellsMixin from 'client/mixins/spells';
|
||||
import Drawer from 'client/components/ui/drawer';
|
||||
import MouseMoveDirective from 'client/directives/mouseposition.directive';
|
||||
|
||||
import mana from 'assets/svg/mana.svg';
|
||||
import quests from 'common/script/content/quests';
|
||||
import { CONSTANTS, setLocalSetting, getLocalSetting } from 'client/libs/userlocalManager';
|
||||
|
||||
export default {
|
||||
mixins: [notifications],
|
||||
mixins: [notifications, spellsMixin],
|
||||
components: {
|
||||
Drawer,
|
||||
},
|
||||
directives: {
|
||||
mousePosition: MouseMoveDirective,
|
||||
bPopover,
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
@@ -183,6 +193,12 @@ export default {
|
||||
if (keyEvent.keyCode !== 27) return;
|
||||
this.castCancel();
|
||||
});
|
||||
|
||||
// @TODO: should we abstract the drawer state/local store to a library and mixing combo? We use a similar pattern in equipment
|
||||
const spellDrawerState = getLocalSetting(CONSTANTS.keyConstants.SPELL_DRAWER_STATE);
|
||||
if (spellDrawerState === CONSTANTS.valueConstants.DRAWER_CLOSED) {
|
||||
this.$store.state.spellOptions.spellDrawOpen = false;
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
@@ -191,8 +207,15 @@ export default {
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
drawerToggled (openChanged) {
|
||||
this.$store.state.spellOptions.spellDrawOpen = openChanged;
|
||||
drawerToggled (newState) {
|
||||
this.$store.state.spellOptions.spellDrawOpen = newState;
|
||||
|
||||
if (newState) {
|
||||
setLocalSetting(CONSTANTS.keyConstants.SPELL_DRAWER_STATE, CONSTANTS.valueConstants.DRAWER_OPEN);
|
||||
return;
|
||||
}
|
||||
|
||||
setLocalSetting(CONSTANTS.keyConstants.SPELL_DRAWER_STATE, CONSTANTS.valueConstants.DRAWER_CLOSED);
|
||||
},
|
||||
spellDisabled (skill) {
|
||||
if (skill === 'frost' && this.user.stats.buffs.streaks) {
|
||||
@@ -221,122 +244,6 @@ export default {
|
||||
|
||||
return notes;
|
||||
},
|
||||
async castStart (spell) {
|
||||
if (this.$store.state.spellOptions.castingSpell) {
|
||||
this.castCancel();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.user.stats.mp < spell.mana) return this.text(this.$t('notEnoughMana'));
|
||||
|
||||
if (spell.immediateUse && this.user.stats.gp < spell.value) {
|
||||
return this.text('Not enough gold.');
|
||||
}
|
||||
|
||||
this.potionClickMode = true;
|
||||
this.applyingAction = true;
|
||||
this.$store.state.spellOptions.castingSpell = true;
|
||||
this.spell = spell;
|
||||
|
||||
if (spell.target === 'self') {
|
||||
this.castEnd(null, spell.target);
|
||||
} else if (spell.target === 'party') {
|
||||
if (!this.user.party._id) {
|
||||
let party = [this.user];
|
||||
this.castEnd(party, spell.target);
|
||||
return;
|
||||
}
|
||||
|
||||
let party = this.$store.state.party.members;
|
||||
party = isArray(party) ? party : [];
|
||||
party = party.concat(this.user);
|
||||
this.castEnd(party, spell.target);
|
||||
} else if (spell.target === 'tasks') {
|
||||
let userTasks = this.$store.state.tasks.data;
|
||||
// exclude rewards
|
||||
let tasks = userTasks.habits
|
||||
.concat(userTasks.dailys)
|
||||
.concat(userTasks.todos);
|
||||
// exclude challenge and group plan tasks
|
||||
tasks = tasks.filter((task) => {
|
||||
if (task.challenge && task.challenge.id && !task.challenge.broken) return false;
|
||||
if (task.group && task.group.id && !task.group.broken) return false;
|
||||
return true;
|
||||
});
|
||||
this.castEnd(tasks, spell.target);
|
||||
}
|
||||
},
|
||||
async castEnd (target, type) {
|
||||
if (!this.$store.state.spellOptions.castingSpell) return;
|
||||
let beforeQuestProgress = this.questProgress();
|
||||
|
||||
if (!this.applyingAction) return 'No applying action';
|
||||
|
||||
if (this.spell.target !== type) return this.text(this.$t('invalidTarget'));
|
||||
if (target && target.type && target.type === 'reward') return this.text(this.$t('invalidTarget'));
|
||||
if (target && target.challenge && target.challenge.id) return this.text(this.$t('invalidTarget'));
|
||||
if (target && target.group && target.group.id) return this.text(this.$t('invalidTarget'));
|
||||
|
||||
// @TODO: just call castCancel?
|
||||
this.$store.state.spellOptions.castingSpell = false;
|
||||
this.potionClickMode = false;
|
||||
|
||||
this.spell.cast(this.user, target);
|
||||
// User.save(); // @TODO:
|
||||
|
||||
let spell = this.spell;
|
||||
let targetId = target ? target._id : null;
|
||||
this.spell = null;
|
||||
this.applyingAction = false;
|
||||
|
||||
let spellUrl = `/api/v3/user/class/cast/${spell.key}`;
|
||||
if (targetId) spellUrl += `?targetId=${targetId}`;
|
||||
|
||||
await axios.post(spellUrl);
|
||||
let msg = this.$t('youCast', {
|
||||
spell: spell.text(),
|
||||
});
|
||||
|
||||
switch (type) {
|
||||
case 'task':
|
||||
msg = this.$t('youCastTarget', {
|
||||
spell: spell.text(),
|
||||
target: target.text,
|
||||
});
|
||||
break;
|
||||
case 'user':
|
||||
msg = this.$t('youCastTarget', {
|
||||
spell: spell.text(),
|
||||
target: target.profile.name,
|
||||
});
|
||||
break;
|
||||
case 'party':
|
||||
msg = this.$t('youCastParty', {
|
||||
spell: spell.text(),
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
this.markdown(msg); // @TODO: mardown directive?
|
||||
// @TODO:
|
||||
let questProgress = this.questProgress() - beforeQuestProgress;
|
||||
if (questProgress > 0) {
|
||||
let userQuest = this.quests[this.user.party.quest.key];
|
||||
if (userQuest.boss) {
|
||||
this.quest('questDamage', questProgress.toFixed(1));
|
||||
} else if (userQuest.collection && userQuest.collect) {
|
||||
this.quest('questCollection', questProgress);
|
||||
}
|
||||
}
|
||||
// @TOOD: User.sync();
|
||||
},
|
||||
castCancel () {
|
||||
this.potionClickMode = false;
|
||||
this.applyingAction = false;
|
||||
this.spell = null;
|
||||
document.querySelector('body').style.cursor = 'initial';
|
||||
this.$store.state.spellOptions.castingSpell = false;
|
||||
},
|
||||
questProgress () {
|
||||
let user = this.user;
|
||||
if (!user.party.quest) return 0;
|
||||
|
||||
107
website/client/components/tasks/tagsPopup.vue
Normal file
@@ -0,0 +1,107 @@
|
||||
<template lang="pug">
|
||||
.tags-popup
|
||||
.tags-category.d-flex
|
||||
.tags-header
|
||||
strong(v-once) {{ $t('tags') }}
|
||||
.tags-list.container
|
||||
.row
|
||||
.col-4(v-for="tag in tags")
|
||||
label.custom-control.custom-checkbox
|
||||
input.custom-control-input(type="checkbox", :value="tag.id", v-model="selectedTags")
|
||||
span.custom-control-indicator
|
||||
span.custom-control-description(:title='tag.name') {{tag.name}}
|
||||
.tags-footer
|
||||
span.clear-tags(@click="clearTags()") {{$t("clearTags")}}
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '~client/assets/scss/colors.scss';
|
||||
|
||||
.tags-popup {
|
||||
padding-left: 24px;
|
||||
padding-right: 24px;
|
||||
max-width: 593px;
|
||||
z-index: 9999;
|
||||
background: $white;
|
||||
border-radius: 2px;
|
||||
box-shadow: 0 2px 2px 0 rgba($black, 0.16), 0 1px 4px 0 rgba($black, 0.12);
|
||||
font-size: 14px;
|
||||
line-height: 1.43;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
.tags-category {
|
||||
border-bottom: 1px solid $gray-600;
|
||||
padding-bottom: 24px;
|
||||
padding-top: 24px;
|
||||
}
|
||||
|
||||
.tags-header {
|
||||
flex-basis: 96px;
|
||||
flex-shrink: 0;
|
||||
|
||||
a {
|
||||
font-size: 12px;
|
||||
line-height: 1.33;
|
||||
color: $blue-10;
|
||||
margin-top: 4px;
|
||||
|
||||
&:focus, &:hover, &:active {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tags-list {
|
||||
.custom-control-description {
|
||||
color: $gray-50 !important;
|
||||
font-weight: normal;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: pre;
|
||||
width: 8em;
|
||||
}
|
||||
}
|
||||
|
||||
.tags-footer {
|
||||
border-top: 1px solid $gray-600;
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
.clear-tags {
|
||||
cursor: pointer;
|
||||
margin: 1.1em 0;
|
||||
color: $red-50;
|
||||
font-size: 14px;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: ['tags', 'value'],
|
||||
data () {
|
||||
return {
|
||||
selectedTags: [],
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
selectedTags () {
|
||||
this.$emit('input', this.selectedTags);
|
||||
},
|
||||
},
|
||||
mounted () {
|
||||
this.selectedTags = this.value;
|
||||
},
|
||||
methods: {
|
||||
clearTags () {
|
||||
this.selectedTags = [];
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -1,6 +1,5 @@
|
||||
<template lang="pug">
|
||||
.task-wrapper
|
||||
broken-task-modal(:brokenChallengeTask='brokenChallengeTask')
|
||||
.task(@click='castEnd($event, task)')
|
||||
approval-header(:task='task', v-if='this.task.group.id', :group='group')
|
||||
.d-flex(:class="{'task-not-scoreable': isUser !== true}")
|
||||
@@ -17,8 +16,17 @@
|
||||
.task-clickable-area(@click="edit($event, task)")
|
||||
h3.task-title(:class="{ 'has-notes': task.notes }", v-markdown="task.text")
|
||||
.task-notes.small-text(v-markdown="task.notes")
|
||||
.checklist(v-if="task.checklist && task.checklist.length > 0")
|
||||
.checklist(v-if="canViewchecklist")
|
||||
.d-inline-flex
|
||||
.collapse-checklist.d-flex.align-items-center.expand-toggle(
|
||||
v-if="isUser",
|
||||
@click="collapseChecklist(task)",
|
||||
:class="{open: !task.collapseChecklist}",
|
||||
)
|
||||
.svg-icon(v-html="icons.checklist")
|
||||
span {{ checklistProgress }}
|
||||
label.custom-control.custom-checkbox.checklist-item(
|
||||
v-if='!castingSpell && !task.collapseChecklist',
|
||||
v-for="item in task.checklist", :class="{'checklist-item-done': item.completed}",
|
||||
)
|
||||
input.custom-control-input(type="checkbox", :checked="item.completed", @change="toggleChecklistItem(item)")
|
||||
@@ -119,6 +127,26 @@
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.collapse-checklist {
|
||||
padding: 2px 6px;
|
||||
margin-bottom: 9px;
|
||||
border-radius: 1px;
|
||||
background-color: $gray-600;
|
||||
font-size: 10px;
|
||||
line-height: 1.2;
|
||||
text-align: center;
|
||||
color: $gray-200;
|
||||
|
||||
span {
|
||||
margin: 0px 4px;
|
||||
}
|
||||
|
||||
.svg-icon {
|
||||
width: 12px;
|
||||
height: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.checklist-item {
|
||||
color: $gray-50;
|
||||
font-size: 14px;
|
||||
@@ -308,10 +336,10 @@ import calendarIcon from 'assets/svg/calendar.svg';
|
||||
import challengeIcon from 'assets/svg/challenge.svg';
|
||||
import tagsIcon from 'assets/svg/tags.svg';
|
||||
import checkIcon from 'assets/svg/check.svg';
|
||||
import checklistIcon from 'assets/svg/checklist.svg';
|
||||
import bPopover from 'bootstrap-vue/lib/components/popover';
|
||||
import markdownDirective from 'client/directives/markdown';
|
||||
import notifications from 'client/mixins/notifications';
|
||||
import brokenTaskModal from './brokenTaskModal';
|
||||
import approvalHeader from './approvalHeader';
|
||||
import approvalFooter from './approvalFooter';
|
||||
|
||||
@@ -321,7 +349,6 @@ export default {
|
||||
bPopover,
|
||||
approvalFooter,
|
||||
approvalHeader,
|
||||
brokenTaskModal,
|
||||
},
|
||||
directives: {
|
||||
markdown: markdownDirective,
|
||||
@@ -338,16 +365,31 @@ export default {
|
||||
challenge: challengeIcon,
|
||||
tags: tagsIcon,
|
||||
check: checkIcon,
|
||||
checklist: checklistIcon,
|
||||
}),
|
||||
brokenChallengeTask: {},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
...mapState({
|
||||
user: 'user.data',
|
||||
castingSpell: 'spellOptions.castingSpell',
|
||||
}),
|
||||
...mapGetters({
|
||||
getTagsFor: 'tasks:getTagsFor',
|
||||
getTaskClasses: 'tasks:getTaskClasses',
|
||||
}),
|
||||
canViewchecklist () {
|
||||
let hasChecklist = this.task.checklist && this.task.checklist.length > 0;
|
||||
let userIsTaskUser = this.task.userId ? this.task.userId === this.user._id : true;
|
||||
return hasChecklist && userIsTaskUser;
|
||||
},
|
||||
checklistProgress () {
|
||||
const totalItems = this.task.checklist.length;
|
||||
const completedItems = this.task.checklist.reduce((total, item) => {
|
||||
return item.completed ? total + 1 : total;
|
||||
}, 0);
|
||||
return `${completedItems}/${totalItems}`;
|
||||
},
|
||||
leftControl () {
|
||||
const task = this.task;
|
||||
if (task.type === 'reward') return false;
|
||||
@@ -387,9 +429,13 @@ export default {
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...mapActions({scoreChecklistItem: 'tasks:scoreChecklistItem'}),
|
||||
...mapActions({
|
||||
scoreChecklistItem: 'tasks:scoreChecklistItem',
|
||||
collapseChecklist: 'tasks:collapseChecklist',
|
||||
}),
|
||||
toggleChecklistItem (item) {
|
||||
item.completed = !item.completed;
|
||||
if (this.castingSpell) return;
|
||||
item.completed = !item.completed; // @TODO this should go into the action?
|
||||
this.scoreChecklistItem({taskId: this.task._id, itemId: item.id});
|
||||
},
|
||||
edit (e, task) {
|
||||
@@ -406,6 +452,8 @@ export default {
|
||||
this.$root.$emit('castEnd', task, 'task', e);
|
||||
},
|
||||
async score (direction) {
|
||||
if (this.castingSpell) return;
|
||||
|
||||
// TODO move to an action
|
||||
const Content = this.$store.state.content;
|
||||
const user = this.user;
|
||||
@@ -505,7 +553,7 @@ export default {
|
||||
}
|
||||
},
|
||||
handleBrokenTask (task) {
|
||||
this.brokenChallengeTask = task;
|
||||
this.$store.state.brokenChallengeTask = task;
|
||||
this.$root.$emit('show::modal', 'broken-task-modal');
|
||||
},
|
||||
},
|
||||
|
||||
@@ -110,29 +110,22 @@
|
||||
span.custom-control-indicator
|
||||
span.custom-control-description {{ $t('dayOfWeek') }}
|
||||
|
||||
.option(v-if="isUserTask")
|
||||
label(v-once) {{ $t('tags') }}
|
||||
.category-wrap(@click="showTagsSelect = !showTagsSelect")
|
||||
span.category-select(v-if='task.tags && task.tags.length === 0') {{$t('none')}}
|
||||
span.category-select(v-else)
|
||||
.category-label(v-for='tagName in getTagsFor(task)') {{tagName}}
|
||||
.category-box(v-if="showTagsSelect")
|
||||
.container
|
||||
.row
|
||||
.form-check.col-6(
|
||||
v-for="tag in user.tags",
|
||||
:key="tag.id",
|
||||
)
|
||||
label.custom-control.custom-checkbox
|
||||
input.custom-control-input(type="checkbox", :value="tag.id", v-model="task.tags")
|
||||
span.custom-control-indicator
|
||||
span.custom-control-description(v-once) {{ tag.name }}
|
||||
.row
|
||||
button.btn.btn-primary(@click="showTagsSelect = !showTagsSelect") {{$t('close')}}
|
||||
.tags-select.option(v-if="isUserTask")
|
||||
.tags-inline
|
||||
label(v-once) {{ $t('tags') }}
|
||||
.category-wrap(@click="showTagsSelect = !showTagsSelect", v-bind:class="{ active: showTagsSelect }")
|
||||
span.category-select(v-if='task.tags && task.tags.length === 0')
|
||||
.tags-none {{$t('none')}}
|
||||
.dropdown-toggle
|
||||
span.category-select(v-else)
|
||||
.category-label(v-for='tagName in truncatedSelectedTags', :title="tagName") {{ tagName }}
|
||||
.tags-more(v-if='remainingSelectedTags.length > 0') +{{ $t('more', { count: remainingSelectedTags.length }) }}
|
||||
.dropdown-toggle
|
||||
tags-popup(v-if="showTagsSelect", :tags="user.tags", v-model="task.tags")
|
||||
|
||||
.option(v-if="task.type === 'habit'")
|
||||
label(v-once) {{ $t('resetStreak') }}
|
||||
b-dropdown(:text="$t(task.frequency)")
|
||||
b-dropdown.streak-dropdown(:text="$t(task.frequency)")
|
||||
b-dropdown-item(v-for="frequency in ['daily', 'weekly', 'monthly']", :key="frequency", @click="task.frequency = frequency", :class="{active: task.frequency === frequency}")
|
||||
| {{ $t(frequency) }}
|
||||
|
||||
@@ -328,6 +321,88 @@
|
||||
}
|
||||
}
|
||||
|
||||
.tags-select {
|
||||
position: relative;
|
||||
|
||||
.tags-inline {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
|
||||
label {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.category-wrap {
|
||||
cursor: inherit;
|
||||
position: relative;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 2px;
|
||||
margin-left: 4em;
|
||||
|
||||
&.active {
|
||||
border-color: $purple-500;
|
||||
|
||||
.category-select {
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
.category-select {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
padding: .6em;
|
||||
padding-right: 2.8em;
|
||||
width: 100%;
|
||||
|
||||
.tags-none {
|
||||
margin: .26em 0 .26em .6em;
|
||||
|
||||
& + .dropdown-toggle {
|
||||
right: 1.3em;
|
||||
}
|
||||
}
|
||||
|
||||
.tags-more {
|
||||
color: #a5a1ac;
|
||||
flex: 0 1 auto;
|
||||
font-size: 12px;
|
||||
text-align: left;
|
||||
position: relative;
|
||||
left: .5em;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.dropdown-toggle {
|
||||
position: absolute;
|
||||
right: 1em;
|
||||
top: .8em;
|
||||
}
|
||||
|
||||
.category-label {
|
||||
min-width: 68px;
|
||||
overflow: hidden;
|
||||
padding: .5em 1em;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
width: 68px;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tags-popup {
|
||||
position: absolute;
|
||||
top: 3.5em;
|
||||
left: 6.2em;
|
||||
}
|
||||
}
|
||||
|
||||
.streak-dropdown {
|
||||
margin-left: .5em;
|
||||
}
|
||||
|
||||
.checklist-group {
|
||||
border-top: 1px solid $gray-500;
|
||||
}
|
||||
@@ -418,6 +493,7 @@
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import TagsPopup from './tagsPopup';
|
||||
import bModal from 'bootstrap-vue/lib/components/modal';
|
||||
import { mapGetters, mapActions, mapState } from 'client/libs/store';
|
||||
import bDropdown from 'bootstrap-vue/lib/components/dropdown';
|
||||
@@ -441,6 +517,7 @@ import goldIcon from 'assets/svg/gold.svg';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
TagsPopup,
|
||||
bModal,
|
||||
bDropdown,
|
||||
bDropdownItem,
|
||||
@@ -453,6 +530,7 @@ export default {
|
||||
props: ['task', 'purpose', 'challengeId', 'groupId'], // purpose is either create or edit, task is the task created or edited
|
||||
data () {
|
||||
return {
|
||||
maxTags: 3,
|
||||
showTagsSelect: false,
|
||||
showAssignedSelect: false,
|
||||
newChecklistItem: null,
|
||||
@@ -574,6 +652,15 @@ export default {
|
||||
}
|
||||
},
|
||||
},
|
||||
selectedTags () {
|
||||
return this.getTagsFor(this.task);
|
||||
},
|
||||
truncatedSelectedTags () {
|
||||
return this.selectedTags.slice(0, this.maxTags);
|
||||
},
|
||||
remainingSelectedTags () {
|
||||
return this.selectedTags.slice(this.maxTags);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...mapActions({saveTask: 'tasks:save', destroyTask: 'tasks:destroy', createTask: 'tasks:create'}),
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<template lang="pug">
|
||||
.row.user-tasks-page
|
||||
broken-task-modal
|
||||
task-modal(
|
||||
:task="editingTask || creatingTask",
|
||||
:purpose="creatingTask !== null ? 'create' : 'edit'",
|
||||
@@ -11,25 +12,25 @@
|
||||
.col-4.offset-4
|
||||
.input-group
|
||||
input.form-control.input-search(type="text", :placeholder="$t('search')", v-model="searchText")
|
||||
.filter-panel(v-if="isFilterPanelOpen")
|
||||
.filter-panel(v-if="isFilterPanelOpen", v-on:mouseleave="checkMouseOver")
|
||||
.tags-category.d-flex(
|
||||
v-for="tagsType in tagsByType",
|
||||
v-for="tagsType in tagsByType",
|
||||
v-if="tagsType.tags.length > 0 || tagsType.key === 'tags'",
|
||||
:key="tagsType.key"
|
||||
)
|
||||
.tags-header
|
||||
strong(v-once) {{ $t(tagsType.key) }}
|
||||
a.d-block(v-if="tagsType.key === 'tags' && !editingTags", @click="editTags()") {{ $t('editTags2') }}
|
||||
a.d-block(v-if="tagsType.key !== 'groups' && !editingTags", @click="editTags(tagsType.key)") {{ $t('editTags2') }}
|
||||
.tags-list.container
|
||||
.row(:class="{'no-gutters': !editingTags}")
|
||||
template(v-if="editingTags && tagsType.key === 'tags'")
|
||||
.col-6(v-for="(tag, tagIndex) in tagsSnap")
|
||||
template(v-if="editingTags && tagsType.key !== 'groups'")
|
||||
.col-6(v-for="(tag, tagIndex) in tagsSnap[tagsType.key]")
|
||||
.inline-edit-input-group.tag-edit-item.input-group
|
||||
input.tag-edit-input.inline-edit-input.form-control(type="text", v-model="tag.name")
|
||||
span.input-group-btn(@click="removeTag(tagIndex)")
|
||||
span.input-group-btn(@click="removeTag(tagIndex, tagsType.key)")
|
||||
.svg-icon.destroy-icon(v-html="icons.destroy")
|
||||
.col-6
|
||||
input.new-tag-item.edit-tag-item.inline-edit-input.form-control(type="text", :placeholder="$t('newTag')", @keydown.enter="addTag($event)", v-model="newTag")
|
||||
.col-6(v-if="tagsType.key === 'tags'")
|
||||
input.new-tag-item.edit-tag-item.inline-edit-input.form-control(type="text", :placeholder="$t('newTag')", @keydown.enter="addTag($event, tagsType.key)", v-model="newTag")
|
||||
template(v-else)
|
||||
.col-6(v-for="(tag, tagIndex) in tagsType.tags")
|
||||
label.custom-control.custom-checkbox
|
||||
@@ -50,7 +51,6 @@
|
||||
.float-left
|
||||
a.btn-filters-danger(@click="resetFilters()", v-once) {{ $t('resetFilters') }}
|
||||
.float-right
|
||||
a.mr-3.btn-filters-primary(@click="applyFilters()", v-once) {{ $t('applyFilters') }}
|
||||
a.btn-filters-secondary(@click="closeFilterPanel()", v-once) {{ $t('cancel') }}
|
||||
span.input-group-btn
|
||||
button.btn.btn-secondary.filter-button(
|
||||
@@ -290,6 +290,7 @@ import throttle from 'lodash/throttle';
|
||||
import cloneDeep from 'lodash/cloneDeep';
|
||||
import { mapState, mapActions } from 'client/libs/store';
|
||||
import taskDefaults from 'common/script/libs/taskDefaults';
|
||||
import brokenTaskModal from './brokenTaskModal';
|
||||
|
||||
import Item from 'client/components/inventory/item.vue';
|
||||
|
||||
@@ -301,6 +302,7 @@ export default {
|
||||
bDropdownItem,
|
||||
Item,
|
||||
spells,
|
||||
brokenTaskModal,
|
||||
},
|
||||
directives: {
|
||||
markdown,
|
||||
@@ -322,7 +324,10 @@ export default {
|
||||
}),
|
||||
selectedTags: [],
|
||||
temporarilySelectedTags: [],
|
||||
tagsSnap: null, // tags snapshot when being edited
|
||||
tagsSnap: {
|
||||
tags: [],
|
||||
challenges: [],
|
||||
}, // tags snapshot when being edited
|
||||
editingTags: false,
|
||||
newTag: null,
|
||||
editingTask: null,
|
||||
@@ -368,26 +373,38 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
...mapActions({setUser: 'user:set'}),
|
||||
checkMouseOver: throttle(function throttleSearch () {
|
||||
if (this.editingTags) return;
|
||||
this.closeFilterPanel();
|
||||
}, 250),
|
||||
editTags () {
|
||||
// clone the arrays being edited so that we can revert if needed
|
||||
this.tagsSnap = this.tagsByType.user.tags.slice();
|
||||
this.tagsSnap.tags = this.tagsByType.user.tags.slice();
|
||||
this.tagsSnap.challenges = this.tagsByType.challenges.tags.slice();
|
||||
this.editingTags = true;
|
||||
},
|
||||
addTag () {
|
||||
this.tagsSnap.push({id: uuid.v4(), name: this.newTag});
|
||||
addTag (eventObj, key) {
|
||||
this.tagsSnap[key].push({id: uuid.v4(), name: this.newTag});
|
||||
this.newTag = null;
|
||||
},
|
||||
removeTag (index) {
|
||||
this.tagsSnap.splice(index, 1);
|
||||
removeTag (index, key) {
|
||||
this.$delete(this.tagsSnap[key], index);
|
||||
},
|
||||
saveTags () {
|
||||
if (this.newTag) this.addTag();
|
||||
this.setUser({tags: this.tagsSnap});
|
||||
|
||||
this.tagsByType.user.tags = this.tagsSnap.tags;
|
||||
this.tagsByType.challenges.tags = this.tagsSnap.challenges;
|
||||
|
||||
this.setUser({tags: this.tagsSnap.tags.concat(this.tagsSnap.challenges)});
|
||||
this.cancelTagsEditing();
|
||||
},
|
||||
cancelTagsEditing () {
|
||||
this.editingTags = false;
|
||||
this.tagsSnap = null;
|
||||
this.tagsSnap = {
|
||||
tags: [],
|
||||
challenges: [],
|
||||
};
|
||||
this.newTag = null;
|
||||
},
|
||||
editTask (task) {
|
||||
@@ -399,6 +416,8 @@ export default {
|
||||
},
|
||||
createTask (type) {
|
||||
this.creatingTask = taskDefaults({type, text: ''});
|
||||
this.creatingTask.tags = this.selectedTags;
|
||||
|
||||
// Necessary otherwise the first time the modal is not rendered
|
||||
Vue.nextTick(() => {
|
||||
this.$root.$emit('show::modal', 'task-modal');
|
||||
@@ -430,7 +449,6 @@ export default {
|
||||
applyFilters () {
|
||||
const temporarilySelectedTags = this.temporarilySelectedTags;
|
||||
this.selectedTags = temporarilySelectedTags.slice();
|
||||
this.closeFilterPanel();
|
||||
},
|
||||
toggleTag (tag) {
|
||||
const temporarilySelectedTags = this.temporarilySelectedTags;
|
||||
@@ -440,6 +458,8 @@ export default {
|
||||
} else {
|
||||
temporarilySelectedTags.splice(tagI, 1);
|
||||
}
|
||||
|
||||
this.applyFilters();
|
||||
},
|
||||
isTagSelected (tag) {
|
||||
const tagId = tag.id;
|
||||
|
||||
@@ -26,9 +26,9 @@
|
||||
max-width: 80%;
|
||||
|
||||
@media screen and (min-width: 1241px) {
|
||||
max-width: 968px;
|
||||
max-width: 978px;
|
||||
// 16.67% is the width of the .col-2 sidebar
|
||||
left: calc((100% + 16.67% - 968px) / 2);
|
||||
left: calc((100% + 16.67% - 978px) / 2);
|
||||
right: 0%;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,10 @@
|
||||
.fill-height {
|
||||
height: 38px; // button + margin + padding
|
||||
}
|
||||
|
||||
.item-rows {
|
||||
margin-right: -24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
|
||||
@@ -5,7 +5,7 @@ div
|
||||
.profile-actions
|
||||
button.btn.btn-secondary(@click='sendMessage()')
|
||||
.svg-icon.message-icon(v-html="icons.message")
|
||||
button.btn.btn-secondary(v-if='userLoggedIn.inbox.blocks.indexOf(user._id) === -1', :tooltip="$t('unblock')",
|
||||
button.btn.btn-secondary(v-if='user._id !== this.userLoggedIn._id && userLoggedIn.inbox.blocks.indexOf(user._id) === -1', :tooltip="$t('unblock')",
|
||||
@click="blockUser()", tooltip-placement='right')
|
||||
span.glyphicon.glyphicon-plus
|
||||
| {{$t('block')}}
|
||||
@@ -111,7 +111,7 @@ div
|
||||
.achievement-icon.achievement-alien
|
||||
h2.text-center {{$t('challengesWon')}}
|
||||
div(v-for='chal in user.achievements.challenges')
|
||||
span {{chal}}
|
||||
span(v-markdown='chal')
|
||||
hr
|
||||
.col-6(v-if='user.achievements.quests')
|
||||
.achievement-icon.achievement-karaoke
|
||||
@@ -795,7 +795,7 @@ export default {
|
||||
return display;
|
||||
},
|
||||
allocate (stat) {
|
||||
allocate(this.user, stat);
|
||||
allocate(this.user, {query: { stat }});
|
||||
axios.post(`/api/v3/user/allocate?stat=${stat}`);
|
||||
},
|
||||
allocateNow () {
|
||||
|
||||
52
website/client/libs/notifications.js
Normal file
@@ -0,0 +1,52 @@
|
||||
export function getDropClass ({type, key}) {
|
||||
let dropClass = '';
|
||||
|
||||
if (type) {
|
||||
switch (type) {
|
||||
case 'Egg':
|
||||
dropClass = `Pet_Egg_${key}`;
|
||||
break;
|
||||
case 'HatchingPotion':
|
||||
dropClass = `Pet_HatchingPotion_${key}`;
|
||||
break;
|
||||
case 'Food':
|
||||
case 'food':
|
||||
dropClass = `Pet_Food_${key}`;
|
||||
break;
|
||||
case 'armor':
|
||||
case 'back':
|
||||
case 'body':
|
||||
case 'eyewear':
|
||||
case 'head':
|
||||
case 'headAccessory':
|
||||
case 'shield':
|
||||
case 'weapon':
|
||||
case 'gear':
|
||||
dropClass = `shop_${key}`;
|
||||
break;
|
||||
default:
|
||||
dropClass = 'glyphicon glyphicon-gift';
|
||||
}
|
||||
}
|
||||
|
||||
return dropClass;
|
||||
}
|
||||
|
||||
export function getSign (number) {
|
||||
let sign = '+';
|
||||
|
||||
if (number && number < 0) {
|
||||
sign = '-';
|
||||
}
|
||||
|
||||
return sign;
|
||||
}
|
||||
|
||||
export function round (number, nDigits) {
|
||||
return Math.abs(number.toFixed(nDigits || 1));
|
||||
}
|
||||
|
||||
export function getXPMessage (val) {
|
||||
if (val < -50) return; // don't show when they level up (resetting their exp)
|
||||
return `${getSign(val)} ${round(val)}`;
|
||||
}
|
||||
@@ -29,4 +29,4 @@ export function setup () {
|
||||
stripeScript.async = true;
|
||||
stripeScript.src = '//checkout.stripe.com/v2/checkout.js';
|
||||
firstScript.parentNode.insertBefore(stripeScript, firstScript);
|
||||
}
|
||||
}
|
||||
|
||||
25
website/client/libs/userlocalManager.js
Normal file
@@ -0,0 +1,25 @@
|
||||
// @TODO: This might become too generic. If so, check the refactor docs
|
||||
const CONSTANTS = {
|
||||
keyConstants: {
|
||||
SPELL_DRAWER_STATE: 'spell-drawer-state',
|
||||
EQUIPMENT_DRAWER_STATE: 'equipment-drawer-state',
|
||||
},
|
||||
valueConstants: {
|
||||
DRAWER_CLOSED: 'drawer-closed',
|
||||
DRAWER_OPEN: 'drawer-open',
|
||||
},
|
||||
};
|
||||
|
||||
function setLocalSetting (key, value) {
|
||||
localStorage.setItem(key, value);
|
||||
}
|
||||
|
||||
function getLocalSetting (key) {
|
||||
return localStorage.getItem(key);
|
||||
}
|
||||
|
||||
export {
|
||||
CONSTANTS,
|
||||
getLocalSetting,
|
||||
setLocalSetting,
|
||||
};
|
||||
25
website/client/mixins/buy.js
Normal file
@@ -0,0 +1,25 @@
|
||||
export default {
|
||||
methods: {
|
||||
makeGenericPurchase (item, type = 'buyModal') {
|
||||
this.$store.dispatch('shops:genericPurchase', {
|
||||
pinType: item.pinType,
|
||||
type: item.purchaseType,
|
||||
key: item.key,
|
||||
currency: item.currency,
|
||||
});
|
||||
|
||||
if (item.purchaseType !== 'gear') {
|
||||
this.$store.state.recentlyPurchased[item.key] = true;
|
||||
}
|
||||
|
||||
this.$root.$emit('playSound', 'Reward');
|
||||
|
||||
if (type !== 'buyModal') {
|
||||
this.$emit('buyPressed', this.item);
|
||||
return;
|
||||
}
|
||||
|
||||
this.$root.$emit('buyModal::boughtItem', item);
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -1,5 +1,6 @@
|
||||
import habiticaMarkdown from 'habitica-markdown';
|
||||
import { mapState } from 'client/libs/store';
|
||||
import { getDropClass, getXPMessage, getSign, round } from 'client/libs/notifications';
|
||||
|
||||
export default {
|
||||
computed: {
|
||||
@@ -14,40 +15,14 @@ export default {
|
||||
this.notify(message, 'crit', 'glyphicon glyphicon-certificate');
|
||||
},
|
||||
drop (val, item) {
|
||||
let dropClass = '';
|
||||
if (item) {
|
||||
switch (item.type) {
|
||||
case 'Egg':
|
||||
dropClass = `Pet_Egg_${item.key}`;
|
||||
break;
|
||||
case 'HatchingPotion':
|
||||
dropClass = `Pet_HatchingPotion_${item.key}`;
|
||||
break;
|
||||
case 'Food':
|
||||
dropClass = `Pet_Food_${item.key}`;
|
||||
break;
|
||||
case 'armor':
|
||||
case 'back':
|
||||
case 'body':
|
||||
case 'eyewear':
|
||||
case 'head':
|
||||
case 'headAccessory':
|
||||
case 'shield':
|
||||
case 'weapon':
|
||||
dropClass = `shop_${item.key}`;
|
||||
break;
|
||||
default:
|
||||
dropClass = 'glyphicon glyphicon-gift';
|
||||
}
|
||||
}
|
||||
let dropClass = getDropClass({key: item.key, type: item.type});
|
||||
this.notify(val, 'drop', dropClass);
|
||||
},
|
||||
quest (type, val) {
|
||||
this.notify(this.$t(type, { val }), 'success');
|
||||
},
|
||||
exp (val) {
|
||||
if (val < -50) return; // don't show when they level up (resetting their exp)
|
||||
let message = `${this.sign(val)} ${this.round(val)}`;
|
||||
let message = getXPMessage(val);
|
||||
this.notify(message, 'xp', 'glyphicon glyphicon-star', this.sign(val));
|
||||
},
|
||||
error (error) {
|
||||
@@ -84,14 +59,10 @@ export default {
|
||||
this.notify(val, 'info', null, null, onClick);
|
||||
},
|
||||
sign (number) {
|
||||
let sign = '+';
|
||||
if (number && number < 0) {
|
||||
sign = '-';
|
||||
}
|
||||
return sign;
|
||||
return getSign(number);
|
||||
},
|
||||
round (number, nDigits) {
|
||||
return Math.abs(number.toFixed(nDigits || 1));
|
||||
return round(number, nDigits);
|
||||
},
|
||||
notify (html, type, icon, sign) {
|
||||
this.notifications.push({
|
||||
|
||||
@@ -3,6 +3,7 @@ import axios from 'axios';
|
||||
const STRIPE_PUB_KEY = process.env.STRIPE_PUB_KEY; // eslint-disable-line
|
||||
import subscriptionBlocks from '../../common/script/content/subscriptionBlocks';
|
||||
import { mapState } from 'client/libs/store';
|
||||
import encodeParams from 'client/libs/encodeParams';
|
||||
import notificationsMixin from 'client/mixins/notifications';
|
||||
|
||||
export default {
|
||||
@@ -100,6 +101,11 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.groupId) {
|
||||
this.$router.push(`/group-plans/${data.groupId}/task-information`);
|
||||
return;
|
||||
}
|
||||
|
||||
window.location.reload(true);
|
||||
},
|
||||
});
|
||||
@@ -165,5 +171,41 @@ export default {
|
||||
|
||||
this.$root.$emit('show::modal', 'amazon-payment');
|
||||
},
|
||||
async cancelSubscription (config) {
|
||||
if (config && config.group && !confirm(this.$t('confirmCancelGroupPlan'))) return;
|
||||
if (!confirm(this.$t('sureCancelSub'))) return;
|
||||
|
||||
let group;
|
||||
if (config && config.group) {
|
||||
group = config.group;
|
||||
}
|
||||
|
||||
let paymentMethod = this.user.purchased.plan.paymentMethod;
|
||||
if (group) {
|
||||
paymentMethod = group.purchased.plan.paymentMethod;
|
||||
}
|
||||
|
||||
if (paymentMethod === 'Amazon Payments') {
|
||||
paymentMethod = 'amazon';
|
||||
} else {
|
||||
paymentMethod = paymentMethod.toLowerCase();
|
||||
}
|
||||
|
||||
let queryParams = {
|
||||
_id: this.user._id,
|
||||
apiToken: this.credentials.API_TOKEN,
|
||||
noRedirect: true,
|
||||
};
|
||||
|
||||
if (group) {
|
||||
queryParams.groupId = group._id;
|
||||
}
|
||||
|
||||
let cancelUrl = `/${paymentMethod}/subscribe/cancel?${encodeParams(queryParams)}`;
|
||||
await axios.get(cancelUrl);
|
||||
// Success
|
||||
alert(this.$t('paypalCanceled'));
|
||||
this.$router.push('/');
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
128
website/client/mixins/spells.js
Normal file
@@ -0,0 +1,128 @@
|
||||
import axios from 'axios';
|
||||
import isArray from 'lodash/isArray';
|
||||
|
||||
// @TODO: Let's separate some of the business logic out of Vue if possible
|
||||
export default {
|
||||
methods: {
|
||||
async castStart (spell) {
|
||||
if (this.$store.state.spellOptions.castingSpell) {
|
||||
this.castCancel();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.user.stats.mp < spell.mana) return this.text(this.$t('notEnoughMana'));
|
||||
|
||||
if (spell.immediateUse && this.user.stats.gp < spell.value) {
|
||||
return this.text('Not enough gold.');
|
||||
}
|
||||
|
||||
this.potionClickMode = true;
|
||||
this.applyingAction = true;
|
||||
this.$store.state.spellOptions.castingSpell = true;
|
||||
this.spell = spell;
|
||||
|
||||
if (spell.target === 'self') {
|
||||
this.castEnd(null, spell.target);
|
||||
} else if (spell.target === 'party') {
|
||||
if (!this.user.party._id) {
|
||||
let party = [this.user];
|
||||
this.castEnd(party, spell.target);
|
||||
return;
|
||||
}
|
||||
|
||||
let party = this.$store.state.party.members;
|
||||
party = isArray(party) ? party : [];
|
||||
party = party.concat(this.user);
|
||||
this.castEnd(party, spell.target);
|
||||
} else if (spell.target === 'tasks') {
|
||||
let userTasks = this.$store.state.tasks.data;
|
||||
// exclude rewards
|
||||
let tasks = userTasks.habits
|
||||
.concat(userTasks.dailys)
|
||||
.concat(userTasks.todos);
|
||||
// exclude challenge and group plan tasks
|
||||
tasks = tasks.filter((task) => {
|
||||
if (task.challenge && task.challenge.id && !task.challenge.broken) return false;
|
||||
if (task.group && task.group.id && !task.group.broken) return false;
|
||||
return true;
|
||||
});
|
||||
this.castEnd(tasks, spell.target);
|
||||
}
|
||||
},
|
||||
async castEnd (target, type) {
|
||||
if (!this.$store.state.spellOptions.castingSpell) return;
|
||||
let beforeQuestProgress;
|
||||
if (this.spell.target === 'party') beforeQuestProgress = this.questProgress();
|
||||
|
||||
if (!this.applyingAction) return 'No applying action';
|
||||
|
||||
if (this.spell.target !== type) return this.text(this.$t('invalidTarget'));
|
||||
if (target && target.type && target.type === 'reward') return this.text(this.$t('invalidTarget'));
|
||||
if (target && target.challenge && target.challenge.id) return this.text(this.$t('invalidTarget'));
|
||||
if (target && target.group && target.group.id) return this.text(this.$t('invalidTarget'));
|
||||
|
||||
// @TODO: just call castCancel?
|
||||
this.$store.state.spellOptions.castingSpell = false;
|
||||
this.potionClickMode = false;
|
||||
|
||||
this.spell.cast(this.user, target);
|
||||
// User.save(); // @TODO:
|
||||
|
||||
let spell = this.spell;
|
||||
let targetId = target ? target._id : null;
|
||||
this.spell = null;
|
||||
this.applyingAction = false;
|
||||
|
||||
let spellUrl = `/api/v3/user/class/cast/${spell.key}`;
|
||||
if (targetId) spellUrl += `?targetId=${targetId}`;
|
||||
|
||||
let spellText = typeof spell.text === 'function' ? spell.text() : spell.text;
|
||||
|
||||
await axios.post(spellUrl);
|
||||
let msg = this.$t('youCast', {
|
||||
spell: spellText,
|
||||
});
|
||||
|
||||
switch (type) {
|
||||
case 'task':
|
||||
msg = this.$t('youCastTarget', {
|
||||
spell: spellText,
|
||||
target: target.text,
|
||||
});
|
||||
break;
|
||||
case 'user':
|
||||
msg = this.$t('youCastTarget', {
|
||||
spell: spellText,
|
||||
target: target.profile.name,
|
||||
});
|
||||
break;
|
||||
case 'party':
|
||||
msg = this.$t('youCastParty', {
|
||||
spell: spellText,
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
this.markdown(msg); // @TODO: mardown directive?
|
||||
// @TODO:
|
||||
if (!beforeQuestProgress) return;
|
||||
let questProgress = this.questProgress() - beforeQuestProgress;
|
||||
if (questProgress > 0) {
|
||||
let userQuest = this.quests[this.user.party.quest.key];
|
||||
if (userQuest.boss) {
|
||||
this.quest('questDamage', questProgress.toFixed(1));
|
||||
} else if (userQuest.collection && userQuest.collect) {
|
||||
this.quest('questCollection', questProgress);
|
||||
}
|
||||
}
|
||||
// @TOOD: User.sync();
|
||||
},
|
||||
castCancel () {
|
||||
this.potionClickMode = false;
|
||||
this.applyingAction = false;
|
||||
this.spell = null;
|
||||
document.querySelector('body').style.cursor = 'initial';
|
||||
this.$store.state.spellOptions.castingSpell = false;
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -9,16 +9,18 @@ import * as Analytics from 'client/libs/analytics';
|
||||
import ParentPage from './components/parentPage';
|
||||
|
||||
// Static Pages
|
||||
const StaticWrapper = () => import(/* webpackChunkName: "static" */'./components/static/staticWrapper');
|
||||
const StaticWrapper = () => import(/* webpackChunkName: "entry" */'./components/static/staticWrapper');
|
||||
const HomePage = () => import(/* webpackChunkName: "entry" */'./components/static/home');
|
||||
|
||||
const AppPage = () => import(/* webpackChunkName: "static" */'./components/static/app');
|
||||
const ClearBrowserDataPage = () => import(/* webpackChunkName: "static" */'./components/static/clearBrowserData');
|
||||
const CommunityGuidelinesPage = () => import(/* webpackChunkName: "static" */'./components/static/communityGuidelines');
|
||||
const ContactPage = () => import(/* webpackChunkName: "static" */'./components/static/contact');
|
||||
const FAQPage = () => import(/* webpackChunkName: "static" */'./components/static/faq');
|
||||
const FeaturesPage = () => import(/* webpackChunkName: "static" */'./components/static/features');
|
||||
const HomePage = () => import(/* webpackChunkName: "static" */'./components/static/home');
|
||||
const GroupPlansPage = () => import(/* webpackChunkName: "static" */'./components/static/groupPlans');
|
||||
const MerchPage = () => import(/* webpackChunkName: "static" */'./components/static/merch');
|
||||
const NewsPage = () => import(/* webpackChunkName: "static" */'./components/static/newStuff');
|
||||
const OverviewPage = () => import(/* webpackChunkName: "static" */'./components/static/overview');
|
||||
const PressKitPage = () => import(/* webpackChunkName: "static" */'./components/static/pressKit');
|
||||
const PrivacyPage = () => import(/* webpackChunkName: "static" */'./components/static/privacy');
|
||||
@@ -71,6 +73,7 @@ const GroupPlansAppPage = () => import(/* webpackChunkName: "guilds" */ './compo
|
||||
// Group Plans
|
||||
const GroupPlanIndex = () => import(/* webpackChunkName: "group-plans" */ './components/group-plans/index');
|
||||
const GroupPlanTaskInformation = () => import(/* webpackChunkName: "group-plans" */ './components/group-plans/taskInformation');
|
||||
const GroupPlanBilling = () => import(/* webpackChunkName: "group-plans" */ './components/group-plans/billing');
|
||||
|
||||
// Challenges
|
||||
const ChallengeIndex = () => import(/* webpackChunkName: "challenges" */ './components/challenges/index');
|
||||
@@ -143,6 +146,12 @@ const router = new VueRouter({
|
||||
component: GroupPage,
|
||||
props: true,
|
||||
},
|
||||
{
|
||||
name: 'groupPlanBilling',
|
||||
path: '/group-plans/:groupId/billing',
|
||||
component: GroupPlanBilling,
|
||||
props: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -250,6 +259,7 @@ const router = new VueRouter({
|
||||
{ name: 'groupPlans', path: 'group-plans', component: GroupPlansPage, meta: {requiresLogin: false}},
|
||||
{ name: 'home', path: 'home', component: HomePage, meta: {requiresLogin: false} },
|
||||
{ name: 'merch', path: 'merch', component: MerchPage, meta: {requiresLogin: false}},
|
||||
{ name: 'news', path: 'new-stuff', component: NewsPage, meta: {requiresLogin: false}},
|
||||
{ name: 'overview', path: 'overview', component: OverviewPage, meta: {requiresLogin: false}},
|
||||
{ name: 'plans', path: 'plans', component: GroupPlansPage, meta: {requiresLogin: false}},
|
||||
{ name: 'pressKit', path: 'press-kit', component: PressKitPage, meta: {requiresLogin: false}},
|
||||
|
||||
@@ -8,6 +8,7 @@ import sellOp from 'common/script/ops/sell';
|
||||
import unlockOp from 'common/script/ops/unlock';
|
||||
import buyArmoire from 'common/script/ops/buyArmoire';
|
||||
import rerollOp from 'common/script/ops/reroll';
|
||||
import { getDropClass } from 'client/libs/notifications';
|
||||
|
||||
export function buyItem (store, params) {
|
||||
const user = store.state.user.data;
|
||||
@@ -69,24 +70,33 @@ export function unlock (store, params) {
|
||||
};
|
||||
}
|
||||
|
||||
export function genericPurchase (store, params) {
|
||||
export async function genericPurchase (store, params) {
|
||||
switch (params.pinType) {
|
||||
case 'mystery_set':
|
||||
return purchaseMysterySet(store, params);
|
||||
case 'armoire': // eslint-disable-line
|
||||
let buyResult = buyArmoire(store.state.user.data);
|
||||
|
||||
// @TODO: We might need to abstract notifications to library rather than mixin
|
||||
if (buyResult[1]) {
|
||||
// We need the server result because armoir has random item in the result
|
||||
let result = await axios.post('/api/v3/user/buy-armoire');
|
||||
buyResult = result.data.data;
|
||||
|
||||
if (buyResult) {
|
||||
const resData = buyResult;
|
||||
const item = resData.armoire;
|
||||
|
||||
const isExperience = item.type === 'experience';
|
||||
|
||||
// @TODO: We might need to abstract notifications to library rather than mixin
|
||||
store.state.notificationStore.push({
|
||||
title: '',
|
||||
text: buyResult[1],
|
||||
type: 'drop',
|
||||
text: isExperience ? item.value : item.dropText,
|
||||
type: isExperience ? 'xp' : 'drop',
|
||||
icon: isExperience ? null : getDropClass({type: item.type, key: item.dropKey}),
|
||||
timeout: true,
|
||||
});
|
||||
}
|
||||
|
||||
axios.post('/api/v3/user/buy-armoire');
|
||||
return;
|
||||
case 'fortify': {
|
||||
let rerollResult = rerollOp(store.state.user.data);
|
||||
|
||||
@@ -124,6 +124,13 @@ export async function scoreChecklistItem (store, {taskId, itemId}) {
|
||||
await axios.post(`/api/v3/tasks/${taskId}/checklist/${itemId}/score`);
|
||||
}
|
||||
|
||||
export async function collapseChecklist (store, task) {
|
||||
task.collapseChecklist = !task.collapseChecklist;
|
||||
await axios.put(`/api/v3/tasks/${task._id}`, {
|
||||
collapseChecklist: task.collapseChecklist,
|
||||
});
|
||||
}
|
||||
|
||||
export async function destroy (store, task) {
|
||||
const list = store.state.tasks.data[`${task.type}s`];
|
||||
const taskIndex = list.findIndex(t => t._id === task._id);
|
||||
|
||||
@@ -26,7 +26,7 @@ export async function set (store, changes) {
|
||||
if (key === 'tags') {
|
||||
// Keep challenge and group tags
|
||||
const oldTags = user.tags.filter(t => {
|
||||
return t.group || t.challenge;
|
||||
return t.group;
|
||||
});
|
||||
|
||||
user.tags = changes[key].concat(oldTags);
|
||||
|
||||
@@ -22,12 +22,15 @@ let AUTH_SETTINGS = localStorage.getItem('habit-mobile-settings');
|
||||
|
||||
if (AUTH_SETTINGS) {
|
||||
AUTH_SETTINGS = JSON.parse(AUTH_SETTINGS);
|
||||
axios.defaults.headers.common['x-api-user'] = AUTH_SETTINGS.auth.apiId;
|
||||
axios.defaults.headers.common['x-api-key'] = AUTH_SETTINGS.auth.apiToken;
|
||||
|
||||
axios.defaults.headers.common['x-user-timezoneOffset'] = browserTimezoneOffset;
|
||||
if (AUTH_SETTINGS.auth && AUTH_SETTINGS.auth.apiId && AUTH_SETTINGS.auth.apiToken) {
|
||||
axios.defaults.headers.common['x-api-user'] = AUTH_SETTINGS.auth.apiId;
|
||||
axios.defaults.headers.common['x-api-key'] = AUTH_SETTINGS.auth.apiToken;
|
||||
|
||||
isUserLoggedIn = true;
|
||||
axios.defaults.headers.common['x-user-timezoneOffset'] = browserTimezoneOffset;
|
||||
|
||||
isUserLoggedIn = true;
|
||||
}
|
||||
}
|
||||
|
||||
const i18nData = window && window['habitica-i18n'];
|
||||
@@ -57,7 +60,7 @@ export default function () {
|
||||
isUserLoaded: false, // Means the user and the user's tasks are ready
|
||||
isAmazonReady: false, // Whether the Amazon Payments lib can be used
|
||||
user: asyncResourceFactory(),
|
||||
credentials: AUTH_SETTINGS ? {
|
||||
credentials: isUserLoggedIn ? {
|
||||
API_ID: AUTH_SETTINGS.auth.apiId,
|
||||
API_TOKEN: AUTH_SETTINGS.auth.apiToken,
|
||||
} : {},
|
||||
@@ -130,6 +133,10 @@ export default function () {
|
||||
notificationStore: [],
|
||||
modalStack: [],
|
||||
userIdToMessage: '',
|
||||
brokenChallengeTask: {},
|
||||
equipmentDrawerOpen: true,
|
||||
recentlyPurchased: {},
|
||||
groupPlans: [],
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"challenge": "Предизвикателство",
|
||||
"challengeDetails": "Challenges are community events in which players compete and earn prizes by completing a group of related tasks.",
|
||||
"challengeDetails": "Предизвикателствата са обществени събития, в които играчите се състезават и печелят награди като изпълняват няколко свързани по някакъв начин задачи.",
|
||||
"brokenChaLink": "Повредена връзка на предизвикателство",
|
||||
"brokenTask": "Повредена връзка на предизвикателство: тази задача е била част от предизвикателство, но е била премахната от него. Какво бихте искали да направите?",
|
||||
"keepIt": "Запазване",
|
||||
@@ -28,8 +28,8 @@
|
||||
"notParticipating": "Не участвам",
|
||||
"either": "Без значение",
|
||||
"createChallenge": "Създаване на предизвикателство",
|
||||
"createChallengeAddTasks": "Add Challenge Tasks",
|
||||
"addTaskToChallenge": "Add Task",
|
||||
"createChallengeAddTasks": "Добавяне на задачи в предизвикателството",
|
||||
"addTaskToChallenge": "Добавяне на задача",
|
||||
"discard": "Отказ",
|
||||
"challengeTitle": "Име на предизвикателството",
|
||||
"challengeTag": "Име на етикета",
|
||||
@@ -39,7 +39,7 @@
|
||||
"prizePop": "Ако предизвикателството Ви може да бъде „спечелено“, може да наградите победителя с диаманти. Максималната награда е броят на Вашите диаманти (плюс броя на диамантите на гилдията, ако това е предизвикателство на гилдия). Забележка: Наградата не може да бъде променена по-късно.",
|
||||
"prizePopTavern": "Ако предизвикателството Ви може да бъде „спечелено“, може да наградите победителя с диаманти. Максималната награда е броят на Вашите диаманти. Забележка: Наградата не може да бъде променена по-късно, а цената на предизвикателствата в кръчмата не може да бъде възстановена, ако предизвикателството бъде прекратено.",
|
||||
"publicChallenges": "Поне 1 диамант за <strong>обществените предизвикателства</strong> (предотвратява пускането на твърде много безсмислени предизвикателства, наистина).",
|
||||
"publicChallengesTitle": "Public Challenges",
|
||||
"publicChallengesTitle": "Обществени предизвикателства",
|
||||
"officialChallenge": "Официално предизвикателство на Хабитика",
|
||||
"by": "от",
|
||||
"participants": "Участници: <%= membercount %>",
|
||||
@@ -55,10 +55,10 @@
|
||||
"leaveCha": "Напускане на предизвикателството и…",
|
||||
"challengedOwnedFilterHeader": "Притежание",
|
||||
"challengedOwnedFilter": "Собствени",
|
||||
"owned": "Owned",
|
||||
"owned": "Собствени",
|
||||
"challengedNotOwnedFilter": "Чужди",
|
||||
"not_owned": "Not Owned",
|
||||
"not_participating": "Not Participating",
|
||||
"not_owned": "Чужди",
|
||||
"not_participating": "Не участвате",
|
||||
"challengedEitherOwnedFilter": "Без значение",
|
||||
"backToChallenges": "Назад към всички предизвикателства",
|
||||
"prizeValue": "Награда: <%= gemcount %> <%= gemicon %>",
|
||||
@@ -73,7 +73,7 @@
|
||||
"noChallengeOwnerPopover": "Това предизвикателство няма притежател, тъй като създателят му е изтрил профила си.",
|
||||
"challengeMemberNotFound": "Потребителят не е намерен сред участниците в предизвикателството",
|
||||
"onlyGroupLeaderChal": "Само водачът на групата може да създава предизвикателства",
|
||||
"tavChalsMinPrize": "Prize must be at least 1 Gem for Public Challenges.",
|
||||
"tavChalsMinPrize": "Наградата за обществено предизвикателство трябва да бъде поне 1 диамант.",
|
||||
"cantAfford": "Не можете да си позволите такава награда. Купете още диаманти или намалете стойността на наградата.",
|
||||
"challengeIdRequired": "„challengeId“ трябва да бъде правилно форматиран идентификатор UUID.",
|
||||
"winnerIdRequired": "„winnerId“ трябва да бъде правилно форматиран идентификатор UUID.",
|
||||
@@ -89,41 +89,41 @@
|
||||
"shortNameTooShort": "Името на етикета трябва да бъде с дължина поне 3 знака.",
|
||||
"joinedChallenge": "Присъединил(а) се към предизвикателство",
|
||||
"joinedChallengeText": "Този потребител се е подложил на изпитание, като се е присъединил към предизвикателство!",
|
||||
"myChallenges": "My Challenges",
|
||||
"findChallenges": "Discover Challenges",
|
||||
"noChallengeTitle": "You don't have any Challenges.",
|
||||
"challengeDescription1": "Challenges are community events in which players compete and earn prizes by completing a group of related tasks.",
|
||||
"challengeDescription2": "Find recommended Challenges based on your interests, browse Habitica's public Challenges, or create your own Challenges.",
|
||||
"createdBy": "Created By",
|
||||
"joinChallenge": "Join Challenge",
|
||||
"leaveChallenge": "Leave Challenge",
|
||||
"addTask": "Add Task",
|
||||
"editChallenge": "Edit Challenge",
|
||||
"challengeDescription": "Challenge Description",
|
||||
"selectChallengeWinnersDescription": "Select winners from the Challenge participants",
|
||||
"awardWinners": "Award Winners",
|
||||
"doYouWantedToDeleteChallenge": "Do you want to delete this Challenge?",
|
||||
"deleteChallenge": "Delete Challenge",
|
||||
"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!",
|
||||
"challengeDescriptionPlaceholder": "Use this section to go into more detail about everything that Challenge participants should know about your Challenge.",
|
||||
"challengeGuild": "Add to",
|
||||
"challengeMinimum": "Minimum 1 Gem for public Challenges (helps prevent spam, it really does).",
|
||||
"participantsTitle": "Participants",
|
||||
"shortName": "Short Name",
|
||||
"shortNamePlaceholder": "What short tag should be used to identify your Challenge?",
|
||||
"updateChallenge": "Update Challenge",
|
||||
"haveNoChallenges": "You don't have any Challenges",
|
||||
"myChallenges": "Моите предизвикателства",
|
||||
"findChallenges": "Разглеждане на предизвикателствата",
|
||||
"noChallengeTitle": "Нямате никакви предизвикателства.",
|
||||
"challengeDescription1": "Предизвикателствата са обществени събития, в които играчите се състезават и печелят награди като изпълняват няколко свързани по някакъв начин задачи.",
|
||||
"challengeDescription2": "Открийте препоръчани за Вас предизвикателства според интересите Ви, разгледайте обществените предизвикателства на Хабитика, или създайте свои собствени предизвикателства.",
|
||||
"createdBy": "Създадено от",
|
||||
"joinChallenge": "Присъединяване към предизвикателството",
|
||||
"leaveChallenge": "Напускане на предизвикателството",
|
||||
"addTask": "Добавяне на задача",
|
||||
"editChallenge": "Редактиране на предизвикателството",
|
||||
"challengeDescription": "Описание на предизвикателството",
|
||||
"selectChallengeWinnersDescription": "Изберете победителите от участниците в предизвикателството",
|
||||
"awardWinners": "Награждаване на победителите",
|
||||
"doYouWantedToDeleteChallenge": "Искате ли да изтриете това предизвикателство?",
|
||||
"deleteChallenge": "Изтриване на предизвикателството",
|
||||
"challengeNamePlaceholder": "Какво е името на предизвикателството Ви?",
|
||||
"challengeSummary": "Резюме",
|
||||
"challengeSummaryPlaceholder": "Напишете кратко описание, което да представи предизвикателството Ви на другите хабитиканци. Каква е основната цел на предизвикателството Ви и защо хората биха искали да се включат? Опитайте се да използвате в описанието ключови думи, така че хабитиканците да могат лесно да го открият при търсене!",
|
||||
"challengeDescriptionPlaceholder": "Използвайте този раздел, за да добавите повече подробности за всичко, което участниците трябва да знаят за предизвикателството Ви.",
|
||||
"challengeGuild": "Добавяне в",
|
||||
"challengeMinimum": "Поне 1 диамант за обществените предизвикателства (това предотвратява пускането на твърде много безсмислени предизвикателства, наистина).",
|
||||
"participantsTitle": "Участници",
|
||||
"shortName": "Кратко име",
|
||||
"shortNamePlaceholder": "Какъв кратък етикет трябва да бъде използван за разпознаване на предизвикателството Ви?",
|
||||
"updateChallenge": "Обновяване на предизвикателството",
|
||||
"haveNoChallenges": "Нямате никакви предизвикателства",
|
||||
"loadMore": "Зареждане на още",
|
||||
"exportChallengeCsv": "Export Challenge",
|
||||
"editingChallenge": "Editing Challenge",
|
||||
"nameRequired": "Name is required",
|
||||
"tagTooShort": "Tag name is too short",
|
||||
"summaryRequired": "Summary is required",
|
||||
"summaryTooLong": "Summary is too long",
|
||||
"descriptionRequired": "Description is required",
|
||||
"locationRequired": "Location of challenge is required ('Add to')",
|
||||
"categoiresRequired": "One or more categories must be selected",
|
||||
"viewProgressOf": "View Progress Of"
|
||||
"exportChallengeCsv": "Изнасяне на предизвикателоството",
|
||||
"editingChallenge": "Редактиране на предизвикателството",
|
||||
"nameRequired": "Името е задължително",
|
||||
"tagTooShort": "Името на етикета е твърде кратко",
|
||||
"summaryRequired": "Резюмето е задължително",
|
||||
"summaryTooLong": "Резюмето е твърде дълго",
|
||||
"descriptionRequired": "Описанието е задължително",
|
||||
"locationRequired": "Мястото на предизвикателството е задължително („Добавяне в“)",
|
||||
"categoiresRequired": "Задължително е да бъде избрана поне една категория",
|
||||
"viewProgressOf": "Преглед на напредъка на"
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
"communityGuidelinesWarning": "Имайте предвид, че екранното Ви име, профилната снимка и информацията за Вас трябва да отговорят на <a href='https://habitica.com/static/community-guidelines' target='_blank'>Обществените правила</a> (тоест без ругатни, съдържание за възрастни, обиди и т.н.). Ако имате въпроси относно това дали нещо е подходящо или не, можете да пишете на <%= hrefBlankCommunityManagerEmail %>!",
|
||||
"profile": "Профил",
|
||||
"avatar": "Персонализиране на героя",
|
||||
"editAvatar": "Edit Avatar",
|
||||
"editAvatar": "Редактиране на героя",
|
||||
"other": "Други",
|
||||
"fullName": "Пълно име",
|
||||
"displayName": "Екранно име",
|
||||
@@ -17,24 +17,24 @@
|
||||
"buffed": "Подсилен",
|
||||
"bodyBody": "Тяло",
|
||||
"bodySize": "Размер",
|
||||
"size": "Size",
|
||||
"size": "Размер",
|
||||
"bodySlim": "Слаб",
|
||||
"bodyBroad": "Широк",
|
||||
"unlockSet": "Отключване на комплекта — <%= cost %>",
|
||||
"locked": "Заключен",
|
||||
"shirts": "Ризи",
|
||||
"shirt": "Shirt",
|
||||
"shirt": "Риза",
|
||||
"specialShirts": "Специални ризи",
|
||||
"bodyHead": "Прически и цветове на косата",
|
||||
"bodySkin": "Кожа",
|
||||
"skin": "Skin",
|
||||
"skin": "Кожа",
|
||||
"color": "Цвят",
|
||||
"bodyHair": "Коса",
|
||||
"hair": "Hair",
|
||||
"bangs": "Bangs",
|
||||
"hair": "Коса",
|
||||
"bangs": "Бретон",
|
||||
"hairBangs": "Бретон",
|
||||
"ponytail": "Ponytail",
|
||||
"glasses": "Glasses",
|
||||
"ponytail": "Конска опашка",
|
||||
"glasses": "Очила",
|
||||
"hairBase": "Основа",
|
||||
"hairSet1": "Прически — комплект 1",
|
||||
"hairSet2": "Прически — комплект 2",
|
||||
@@ -44,7 +44,7 @@
|
||||
"mustache": "Мустак",
|
||||
"flower": "Цвете",
|
||||
"wheelchair": "Инвалидна количка",
|
||||
"extra": "Extra",
|
||||
"extra": "Допълнителни",
|
||||
"basicSkins": "Основни кожи",
|
||||
"rainbowSkins": "Кожи с цветовете на дъгата",
|
||||
"pastelSkins": "Пастелни кожи",
|
||||
@@ -68,12 +68,12 @@
|
||||
"costumeText": "Ако предпочитате вида на друго снаряжение пред това, което носите, отметнете кутийката „Използване на костюм“, за да облечете костюм, докато носите бойното си снаряжение отдолу.",
|
||||
"useCostume": "Използване на костюм",
|
||||
"useCostumeInfo1": "Натиснете „Използване на костюм“, за да екипирате героя си без да променяте показателите на своето бойно снаряжение! Това означава, че можете да екипирате най-добрите показатели вляво и да облечете героя си с екипировката вдясно.",
|
||||
"useCostumeInfo2": "Once you click \"Use Costume\" your avatar will look pretty basic... but don't worry! If you look on the left, you'll see that your Battle Gear is still equipped. Next, you can make things fancy! Anything you equip on the right won't affect your stats, but can make you look super awesome. Try out different combos, mixing sets, and coordinating your Costume with your pets, mounts, and backgrounds.<br><br>Got more questions? Check out the <a href=\"http://habitica.wikia.com/wiki/Equipment#Costumes\">Costume page</a> on the wiki. Find the perfect ensemble? Show it off in the <a href=\"/groups/guild/3884eeaa-2d6a-45e8-a279-ada6de9709e1\">Costume Carnival guild</a> or brag in the Tavern!",
|
||||
"costumePopoverText": "Select \"Use Costume\" to equip items to your avatar without affecting the stats from your Battle Gear! This means that you can dress up your avatar in whatever outfit you like while still having your best Battle Gear equipped.",
|
||||
"autoEquipPopoverText": "Select this option to automatically equip gear as soon as you purchase it.",
|
||||
"costumeDisabled": "You have disabled your costume.",
|
||||
"useCostumeInfo2": "След като натиснете „Използване на костюм“, героят Ви ще изглежда доста обикновен… но не се притеснявайте! Ако погледнете вляво, ще видите, че бойното Ви снаряжение е все още екипирано. След това можете да направите героя си по-интересен! Каквото и да екипирате вдясно, то няма да повлияе на показателите Ви, но може да направи героя Ви да изглежда страхотно. Опитайте различни комбинации, смесвайте комплекти и съчетайте костюма със своите любимци, превози и фонови изображения.<br><br>Имате още въпроси? Разгледайте <a href=\"http://habitica.wikia.com/wiki/Equipment#Costumes\">страницата с костюмите</a> в уикито. Направили сте перфектно съчетание? Изфукайте се в <a href=\"/groups/guild/3884eeaa-2d6a-45e8-a279-ada6de9709e1\">гилдията „Карнавал на костюмите“ (Costume Carnival)</a> или се покажете в кръчмата!",
|
||||
"costumePopoverText": "Изберете „Използване на костюм“, за да екипирате героя си без да променяте показателите на своето бойно снаряжение! Това означава, че облечете каквото пожелаете на героя си без да премахвате бойното си снаряжение.",
|
||||
"autoEquipPopoverText": "Изберете това, за да екипирате всеки предмет в момента, когато го купите.",
|
||||
"costumeDisabled": "Вие изключихте костюма си.",
|
||||
"gearAchievement": "Вие спечелихте постижението „Последното снаряжение“ заради това, че достигнахте максималното снаряжение за класа си! Получавате следните пълни комплекти:",
|
||||
"moreGearAchievements": "To attain more Ultimate Gear badges, change classes on <a href='/user/settings/site' target='_blank'>the Settings > Site page</a> and buy your new class's gear!",
|
||||
"moreGearAchievements": "За да получите още значки „Последното снаряжение“, променете класа си в <a href='/user/settings/site' target='_blank'>страницата „Настройки > Уеб сайт</a> и купете екипировката на новия си клас!",
|
||||
"armoireUnlocked": "За още екипировка, прегледайте <strong>Омагьосания гардероб!</strong> Щракнете наградата на Омагьосания гардероб и ще имате шанс да получите специална екипировка! Той може да Ви даде също опит или храна.",
|
||||
"ultimGearName": "Последното снаряжение — <%= ultClass %>",
|
||||
"ultimGearText": "Надградил екипировката си до най-високото ниво на комплектите за оръжия и брони за класа „<%= ultClass %>“.",
|
||||
@@ -121,10 +121,11 @@
|
||||
"healer": "Лечител",
|
||||
"rogue": "Мошеник",
|
||||
"mage": "Магьосник",
|
||||
"wizard": "Mage",
|
||||
"wizard": "Магьосник",
|
||||
"mystery": "Тайнствен",
|
||||
"changeClass": "Промяна на класа, възстановяване на показателни точки",
|
||||
"lvl10ChangeClass": "За да промените класа си, трябва да бъдете поне ниво 10.",
|
||||
"changeClassConfirmCost": "Наистина ли искате да смените класа си за 3 диаманта?",
|
||||
"invalidClass": "Неправилен клас. Моля, посочете един от следните: „warrior“, „rogue“, „wizard“ или „healer“.",
|
||||
"levelPopover": "Всяко ниво Ви дава една точка, която можете да разпределите на показател по свой избор. Можете да го направите ръчно или да оставите играта да реши вместо Вас, използвайки една възможностите за автоматично разпределяне.",
|
||||
"unallocated": "Неразпределени показателни точки",
|
||||
@@ -140,16 +141,16 @@
|
||||
"distributePoints": "Разпределяне на свободните точки",
|
||||
"distributePointsPop": "Разпределяне на всички свободни точки според избраната схема на разпределяне.",
|
||||
"warriorText": "Воините нанасят повече и по-силни „критични удари“, които на случаен принцип дават злато, опит и шанс за падане на предмет при изпълнение на задача. Те също така нанасят сериозни щети на чудовищата-главатари. Играйте като воин, ако Ви мотивират изненадващите награди, или ако искате да раздавате правосъдие в мисиите с главатари.",
|
||||
"wizardText": "Mages learn swiftly, gaining Experience and Levels faster than other classes. They also get a great deal of Mana for using special abilities. Play a Mage if you enjoy the tactical game aspects of Habitica, or if you are strongly motivated by leveling up and unlocking advanced features!",
|
||||
"wizardText": "Магьосниците се учат лесно, тъй като получават опит и повишават нивата си по-бързо от останалите класове. Те имат също и голям запас от мана за специалните си умения. Играйте с магьосник, ако Ви харесват тактическите елементи на Хабитика, или ако Ви мотивира повишаването на нива и отключването на специални функционалности!",
|
||||
"mageText": "Магьосниците се учат лесно, тъй като получават опит и повишават нивата си по-бързо от останалите класове. Те имат също и голям запас от мана за специалните си умения. Играйте с магьосник, ако Ви харесват тактическите елементи на Хабитика, или ако Ви мотивира повишаването на нива и отключването на специални функционалности!",
|
||||
"rogueText": "Мошениците обичат да трупат богатства, печелят повече злато от останалите и са майстори в намирането на случайни предмети. Отличителното им умение „Невидимост“ им позволява да избегнат последствията от пропуснати ежедневни задачи. Играйте като мошеник, ако Ви мотивират наградите и постиженията, и обичате плячката и значките!",
|
||||
"healerText": "Лечителите трудно могат да бъдат наранени, и разпростират защитата си върху останалите. Пропуснатите ежедневни задачи и лошите навици не ги смущават толкова много; те винаги могат да възстановят здравето си след провал. Играйте като лечител, ако обичате да помагате на останалите в групата си или ако искате да изиграете смъртта чрез усърдна работа!",
|
||||
"optOutOfClasses": "Отказване",
|
||||
"optOutOfPMs": "Отказване",
|
||||
"chooseClass": "Choose your Class",
|
||||
"chooseClassLearnMarkdown": "[Learn more about Habitica's class system](http://habitica.wikia.com/wiki/Class_System)",
|
||||
"chooseClass": "Изберете клас",
|
||||
"chooseClassLearnMarkdown": "[Научете повече относно класовата система на Хабитика](http://habitica.wikia.com/wiki/Class_System)",
|
||||
"optOutOfClassesText": "Не Ви се занимава с класове? Искате да изберете по-късно? Откажете се от тях — ще бъдете воин без специални умения. Можете да прочетете относно класовата система по-късно в уикито, както и да включите класовете по всяко време от Потребител -> Показатели.",
|
||||
"selectClass": "Select <%= heroClass %>",
|
||||
"selectClass": "Избиране на <%= heroClass %>",
|
||||
"select": "Избиране",
|
||||
"stealth": "Невидимост",
|
||||
"stealthNewDay": "Когато започне нов ден, ще избегнете щетите от толкова пропуснати ежедневни задачи.",
|
||||
@@ -158,29 +159,29 @@
|
||||
"respawn": "Възкръсване!",
|
||||
"youDied": "Вие умряхте!",
|
||||
"dieText": "Изгубихте ниво, всичкото си злато и случаен предмет от екипировката. Станете, хабитанецо, и опитайте отново! Обуздайте вредните навици, следете зорко изпълнението на ежедневните си задачи и дръжте смъртта на една ръка разстояние с Лековита отвара, ако залитнете!",
|
||||
"sureReset": "Сигурен ли сте? Това ще нулира класа на героя Ви и разпределените точки (ще си ги получите обратно за повторно разпределение), както и ще струва 3 диаманта.",
|
||||
"sureReset": "Наистина ли искате това? Това ще нулира класа на героя Ви и разпределените точки (ще си ги получите обратно за повторно разпределение), както и ще струва 3 диаманта.",
|
||||
"purchaseFor": "Купуване за <%= cost %> диамант(а)?",
|
||||
"notEnoughMana": "Няма достатъчно мана.",
|
||||
"invalidTarget": "You can't cast a skill on that.",
|
||||
"invalidTarget": "Не може да използвате умение върху това.",
|
||||
"youCast": "Направихте заклинанието <%= spell %>.",
|
||||
"youCastTarget": "Направихте заклинанието <%= spell %> върху <%= target %>.",
|
||||
"youCastParty": "Направихте заклинанието <%= spell %> върху групата.",
|
||||
"critBonus": "Критичен удар! Бонус:",
|
||||
"gainedGold": "You gained some Gold",
|
||||
"gainedMana": "You gained some Mana",
|
||||
"gainedHealth": "You gained some Health",
|
||||
"gainedExperience": "You gained some Experience",
|
||||
"lostGold": "You spent some Gold",
|
||||
"lostMana": "You used some Mana",
|
||||
"lostHealth": "You lost some Health",
|
||||
"lostExperience": "You lost some Experience",
|
||||
"gainedGold": "Получихте злато",
|
||||
"gainedMana": "Получихте мана",
|
||||
"gainedHealth": "Получихте здраве",
|
||||
"gainedExperience": "Получихте опит",
|
||||
"lostGold": "Похарчихте злато",
|
||||
"lostMana": "Използвахте мана",
|
||||
"lostHealth": "Загубихте здраве",
|
||||
"lostExperience": "Загубихте опит",
|
||||
"displayNameDescription1": "Това се появява в съобщенията, които пишете в кръчмата, гилдиите и чата на групата, както и се показва върху героя Ви. За да го промените, щракнете бутона „Редактиране“ по-горе. Ако искате да промените потребителското си име за влизане в системата, отидете в",
|
||||
"displayNameDescription2": "Настройки->Уеб сайт",
|
||||
"displayNameDescription3": "и погледнете в раздела за регистрация.",
|
||||
"unequipBattleGear": "Разекипиране на бойното снаряжение",
|
||||
"unequipCostume": "Разекипиране на костюма",
|
||||
"equip": "Equip",
|
||||
"unequip": "Unequip",
|
||||
"equip": "Екипиране",
|
||||
"unequip": "Разекипиране",
|
||||
"unequipPetMountBackground": "Разекипиране на любимец, превоз, фон",
|
||||
"animalSkins": "Животински кожи",
|
||||
"chooseClassHeading": "Изберете класа си! Или се откажете и го оставете за по-късно.",
|
||||
@@ -198,22 +199,22 @@
|
||||
"quickAllocationLevelPopover": "Всяко ниво Ви дава една точка, която можете да разпределите на показател по свой избор. Можете да го направите ръчно или да оставите играта да реши вместо Вас, използвайки една възможностите за автоматично разпределяне, които може да намерите в Потребител -> Показатели.",
|
||||
"invalidAttribute": "„<%= attr %>“ не е правилен показател.",
|
||||
"notEnoughAttrPoints": "Нямате достатъчно показателни точки.",
|
||||
"style": "Style",
|
||||
"facialhair": "Facial",
|
||||
"photo": "Photo",
|
||||
"info": "Info",
|
||||
"joined": "Joined",
|
||||
"totalLogins": "Total Check Ins",
|
||||
"latestCheckin": "Latest Check In",
|
||||
"editProfile": "Edit Profile",
|
||||
"challengesWon": "Challenges Won",
|
||||
"questsCompleted": "Quests Completed",
|
||||
"headGear": "Head Gear",
|
||||
"headAccess": "Head Access.",
|
||||
"backAccess": "Back Access.",
|
||||
"bodyAccess": "Body Access.",
|
||||
"mainHand": "Main-Hand",
|
||||
"offHand": "Off-Hand",
|
||||
"pointsAvailable": "Points Available",
|
||||
"pts": "pts"
|
||||
"style": "Стил",
|
||||
"facialhair": "Лице",
|
||||
"photo": "Снимка",
|
||||
"info": "Информация",
|
||||
"joined": "Присъединяване",
|
||||
"totalLogins": "Общ брой отчитания",
|
||||
"latestCheckin": "Последно отчитане",
|
||||
"editProfile": "Редактиране на профила",
|
||||
"challengesWon": "Спечелени предизвикателства",
|
||||
"questsCompleted": "Изпълнени мисии",
|
||||
"headGear": "Екипировка за глава",
|
||||
"headAccess": "Аксесоар за глава",
|
||||
"backAccess": "Аксесоар за гръб",
|
||||
"bodyAccess": "Аксесоар за тяло",
|
||||
"mainHand": "Основна ръка",
|
||||
"offHand": "Страничен",
|
||||
"pointsAvailable": "Налични точки",
|
||||
"pts": "точки"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"iAcceptCommunityGuidelines": "Съгласен съм да спазвам Обществените правила",
|
||||
"tavernCommunityGuidelinesPlaceholder": "Friendly reminder: this is an all-ages chat, so please keep content and language appropriate! Consult the Community Guidelines in the sidebar if you have questions.",
|
||||
"tavernCommunityGuidelinesPlaceholder": "Напомняне: в разговорите могат да участват хора от всякакви възрасти, така че внимавайте с езика! Ако имате въпроси, прегледайте Обществените правила – можете да ги откриете в страничната лента.",
|
||||
"commGuideHeadingWelcome": "Добре дошли в Хабитика!",
|
||||
"commGuidePara001": "Поздравления, приключенецо! Добре дошли в Хабитика, страната на продуктивността, здравословния начин на живот и на буйстващия понякога грифон. Тук сме създали приятно общество с услужливи хора, които се подкрепят по своя път на самоусъвършенстване.",
|
||||
"commGuidePara002": "За да може всеки да е в безопасност, щастлив и продуктивен в това общество, имаме някои правила. Създадохме ги внимателно, за да са с приятен тон и лесни за четене. Моля, отделете време, за да ги прочетете.",
|
||||
@@ -13,7 +13,7 @@
|
||||
"commGuideList01C": "<strong>Подкрепа.</strong> Хабитанците се радват на успехите на другите и се утешават в трудни времена. Ние си даваме сила един другиму, разчитаме си взаимно и се учим един от другиго. Когато сме в група, правим това със заклинанията си; а в чата — с мили и подкрепящи думи.",
|
||||
"commGuideList01D": "<strong>Уважение.</strong> Всички имаме различно минало, различни умения и различни мнения. Това е едно от нещата, които правят нашата общност толкова прекрасна! Хабитиканците уважават тези разлики и им се възхищават. Останете за малко и скоро ще имате най-разнообразни приятели.",
|
||||
"commGuideHeadingMeet": "Запознайте се с екипа и модераторите!",
|
||||
"commGuidePara006": "Habitica has some tireless knights-errant who join forces with the staff members to keep the community calm, contented, and free of trolls. Each has a specific domain, but will sometimes be called to serve in other social spheres. Staff and Mods will often precede official statements with the words \"Mod Talk\" or \"Mod Hat On\".",
|
||||
"commGuidePara006": "Хабитика има неуморни странстващи рицари, които помагат на екипа в опазването на реда и спокойствието в общността. Всеки има своя област на действие, но при нужда може да бъде привикан на служба в друга. Екипът и модераторите често започват официалните си изявления с думите „Модератор“ („Mod Talk“) или „Слагам модераторската шапка“ („Mod Hat On“).",
|
||||
"commGuidePara007": "Екипът има лилави етикети с корони. Тяхната титла е „Герой“.",
|
||||
"commGuidePara008": "Модераторите имат тъмносини етикети със звезди. Тяхната титла е „Пазител“. Единственото изключение е Бейли, която е компютърен персонаж и има черно-зелен етикет със звезда.",
|
||||
"commGuidePara009": "Настоящите членове на екипа са (от ляво надясно):",
|
||||
@@ -90,7 +90,7 @@
|
||||
"commGuideList04H": "Уверете се, че съдържанието на уикито се отнася за целия уеб сайт на Хабитика, а не само за конкретна гилдия или група (по-подходящото място за подобна информация са форумите).",
|
||||
"commGuidePara049": "Следните хора са настоящите администратори на уикито:",
|
||||
"commGuidePara049A": "Следните модератори могат да правят спешни поправки в случай, че има нужда от модератор, а горните администратори не са налични:",
|
||||
"commGuidePara018": "Wiki Administrators Emeritus are:",
|
||||
"commGuidePara018": "Почетни модератори на уикито:",
|
||||
"commGuideHeadingInfractionsEtc": "Нарушения, последствия и възстановяване",
|
||||
"commGuideHeadingInfractions": "Нарушения",
|
||||
"commGuidePara050": "Повечето от хабитиканците си помагат, уважават се и работят съвместно, за да бъде общността една приятна и приятелска среда. Но понякога, ако има синя луна, някой хабитиканец може да извърши нещо в разрез с гореописаните правила. Когато това се случи, модераторите могат да направят всичко, което сметнат за необходимо, за да подсигурят безопасността на Хабитика и добруването на обитателите ѝ.",
|
||||
@@ -184,5 +184,5 @@
|
||||
"commGuideLink07description": "за изпращане на пикселни графики.",
|
||||
"commGuideLink08": "Дъска за мисии в Trello",
|
||||
"commGuideLink08description": "за изпращане на текстове за мисии.",
|
||||
"lastUpdated": "Last updated:"
|
||||
"lastUpdated": "Последна промяна:"
|
||||
}
|
||||
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"playerTiersDesc": "The colored usernames you see in chat represent a person's contributor tier. The higher the tier, the more the person has contributed to habitica through art, code, the community, or more!",
|
||||
"tier1": "Tier 1 (Friend)",
|
||||
"tier2": "Tier 2 (Friend)",
|
||||
"tier3": "Tier 3 (Elite)",
|
||||
"tier4": "Tier 4 (Elite)",
|
||||
"tier5": "Tier 5 (Champion)",
|
||||
"tier6": "Tier 6 (Champion)",
|
||||
"tier7": "Tier 7 (Legendary)",
|
||||
"tierModerator": "Moderator (Guardian)",
|
||||
"tierStaff": "Staff (Heroic)",
|
||||
"tierNPC": "NPC",
|
||||
"playerTiersDesc": "Цветните потребителски имена, които виждате в разговорите, показват нивото на сътрудник на съответния човек. Колкото по-високо е нивото, толкова повече е допринесъл човекът към Хабитика, например с изображения, програмиране, подпомагане на общността и т.н.!",
|
||||
"tier1": "Ниво 1 (приятел)",
|
||||
"tier2": "Ниво 2 (приятел)",
|
||||
"tier3": "Ниво 3 (елит)",
|
||||
"tier4": "Ниво 4 (елит)",
|
||||
"tier5": "Ниво 5 (шампион)",
|
||||
"tier6": "Ниво 6 (шампион)",
|
||||
"tier7": "Ниво 7 (легенда)",
|
||||
"tierModerator": "Модератор (пазител)",
|
||||
"tierStaff": "Екип (герой)",
|
||||
"tierNPC": "Компютърен персонаж",
|
||||
"friend": "Приятел",
|
||||
"friendFirst": "Когато Вашият <strong>първи</strong> пакет предложения бъде приложен, ще получите значка на сътрудник на Хабитика. Името Ви в чата на кръчмата гордо ще показва, че Вие имате принос. В знак на признателност към работата Ви, ще получите също и <strong>3 диаманта</strong>.",
|
||||
"friendSecond": "Когато Вашият <strong>втори</strong> пакет предложения бъде приложен, <strong>Кристалната броня</strong> ще може да бъде закупена от раздела с наградите. В знак на признателност към продължаващата Ви работа ще получите също и <strong>3 диаманта.</strong>",
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"faqQuestion1": "Как да настроя задачите си?",
|
||||
"iosFaqAnswer1": "Добрите навици (тези с +) са задачи, които може да изпълнявате многократно всеки ден, като например яденето на зеленчуци. Лошите навици (тези с -) са задачи, които трябва да избягвате, като например гризането на нокти. Навиците с + и - имат добър и лош избор, като например използването на стълби срещу използването на асансьор. Добрите навици Ви носят опит и злато. Лошите навици отнемат от здравето Ви.\n\nЕжедневните задачи са такива, които трябва да изпълнявате всеки ден, като например миене на зъбите или проверка на е-пощата. Може да настройвате дните, в които една ежедневна задача трябва да бъде изпълнявана, като я докоснете и редактирате. Ако пропуснете да изпълните ежедневна задача, героят Ви ще получи щети след края на деня. Не добавяйте твърде много ежедневни задачи наведнъж!\n\nЗадачите представляват списък от неща, които трябва да направите. Завършването на задача Ви носи злато и опит. От задачите не можете да изгубите здраве. Можете да добавите крайна дата към задача като я докоснете и редактирате.",
|
||||
"androidFaqAnswer1": "Добрите навици (тези с +) са задачи, които може да изпълнявате многократно всеки ден, като например яденето на зеленчуци. Лошите навици (тези с -) са задачи, които трябва да избягвате, като например гризането на нокти. Навиците с + и - имат добър и лош избор, като например използването на стълби срещу използването на асансьор. Добрите навици Ви носят опит и злато. Лошите навици отнемат от здравето Ви.\n\nЕжедневните задачи са такива, които трябва да изпълнявате всеки ден, като например миене на зъбите или проверка на е-пощата. Може да настройвате дните, в които една ежедневна задача трябва да бъде изпълнявана, като я докоснете и редактирате. Ако пропуснете да изпълните ежедневна задача, героят Ви ще получи щети след края на деня. Не добавяйте твърде много ежедневни задачи наведнъж!\n\nЗадачите представляват списък от неща, които трябва да направите. Завършването на задача Ви носи злато и опит. От задачите не можете да изгубите здраве. Можете да добавите крайна дата към задача като я докоснете и редактирате.",
|
||||
"webFaqAnswer1": "* Good Habits (the ones with a :heavy_plus_sign:) are tasks that you can do many times a day, such as eating vegetables. Bad Habits (the ones with a :heavy_minus_sign:) are tasks that you should avoid, like biting nails. Habits with a :heavy_plus_sign: and a :heavy_minus_sign: have a good choice and a bad choice, like taking the stairs vs. taking the elevator. Good Habits award Experience and Gold. Bad Habits subtract Health.\n* Dailies are tasks that you have to do every day, like brushing your teeth or checking your email. You can adjust the days that a Daily is due by clicking the pencil item to edit it. If you skip a Daily that is due, your avatar will take damage overnight. Be careful not to add too many Dailies at once!\n* To-Dos are your To-Do list. Completing a To-Do earns you Gold and Experience. You never lose Health from To-Dos. You can add a due date to a To-Do by clicking the pencil icon to edit.",
|
||||
"webFaqAnswer1": "Добрите навици (тези с :heavy_plus_sign:) са задачи, които може да изпълнявате многократно всеки ден, като например яденето на зеленчуци. Лошите навици (тези с :heavy_minus_sign:) са задачи, които трябва да избягвате, като например гризането на нокти. Навиците с :heavy_plus_sign: и :heavy_minus_sign: имат добър и лош избор, като например използването на стълби срещу използването на асансьор. Добрите навици Ви носят опит и злато. Лошите навици отнемат от здравето Ви.\nЕжедневните задачи са такива, които трябва да изпълнявате всеки ден, като например миене на зъбите или проверка на е-пощата. Може да настройвате дните, в които една ежедневна задача трябва да бъде изпълнявана, като щракнете върху моливчето и я редактирате. Ако пропуснете да изпълните ежедневна задача, героят Ви ще получи щети след края на деня. Не добавяйте твърде много ежедневни задачи наведнъж!\nЗадачите представляват списък от неща, които трябва да направите. Завършването на задача Ви носи злато и опит. От задачите не можете да изгубите здраве. Можете да добавите крайна дата към задача като щракнете върху моливчето и я редактирате.",
|
||||
"faqQuestion2": "Мога ли да разгледам няколко примерни задачи?",
|
||||
"iosFaqAnswer2": "В уикито има четири списъка с примерни задачи за вдъхновение:\n<br><br>\n* [Примерни навици](http://habitica.wikia.com/wiki/Sample_Habits)\n* [Примерни ежедневни задачи](http://habitica.wikia.com/wiki/Sample_Dailies)\n* [Примерни задачи](http://habitica.wikia.com/wiki/Sample_To-Dos)\n* [Примерни персонализирани награди] (http://habitica.wikia.com/wiki/Sample_Custom_Rewards)",
|
||||
"androidFaqAnswer2": "В уикито има четири списъка с примерни задачи за вдъхновение:\n<br><br>\n* [Примерни навици](http://habitica.wikia.com/wiki/Sample_Habits)\n* [Примерни ежедневни задачи](http://habitica.wikia.com/wiki/Sample_Dailies)\n* [Примерни задачи](http://habitica.wikia.com/wiki/Sample_To-Dos)\n* [Примерни персонализирани награди] (http://habitica.wikia.com/wiki/Sample_Custom_Rewards)",
|
||||
@@ -17,42 +17,42 @@
|
||||
"androidFaqAnswer3": "Вашите задачи променят цвета си според това колко добре ги изпълнявате в момента! Всяка нова задача започва в неутрално жълто. Когато изпълнявате ежедневните си задачи или по-често вършите добрите си навици, задачите постепенно сменят цвета си към синьо. Ако пропуснете ежедневна задача или се поддадете на лош навик, задачата ще почервенее. Колкото по-червена е една задача, толкова по-голяма награда носи, но и толкова повече ще Ви нарани, ако е ежедневна или лош навик. Това ви помага да се мотивирате и да изпълните задачите, които ви създават проблеми.",
|
||||
"webFaqAnswer3": "Вашите задачи променят цвета си според това колко добре ги изпълнявате в момента! Всяка нова задача започва в неутрално жълто. Когато изпълнявате ежедневните си задачи или по-често вършите добрите си навици, задачите постепенно сменят цвета си към синьо. Ако пропуснете ежедневна задача или се поддадете на лош навик, задачата ще почервенее. Колкото по-червена е една задача, толкова по-голяма награда носи, но и толкова повече ще Ви нарани, ако е ежедневна или лош навик. Това ви помага да се мотивирате и да изпълните задачите, които ви създават проблеми.",
|
||||
"faqQuestion4": "Защо героят ми загуби здраве и как да го възстановя?",
|
||||
"iosFaqAnswer4": "There are several things that can cause you to take damage. First, if you left Dailies incomplete overnight and didn't check them off in the screen that popped up the next morning, those unfinished Dailies will damage you. Second, if you tap a bad Habit, it will damage you. Finally, if you are in a Boss Battle with your Party and one of your Party mates did not complete all their Dailies, the Boss will attack you.\n\n The main way to heal is to gain a level, which restores all your health. You can also buy a Health Potion with gold from the Rewards column. Plus, at level 10 or above, you can choose to become a Healer, and then you will learn healing skills. If you are in a Party with a Healer, they can heal you as well.",
|
||||
"androidFaqAnswer4": "There are several things that can cause you to take damage. First, if you left Dailies incomplete overnight and didn't check them off in the screen that popped up the next morning, those unfinished Dailies will damage you. Second, if you tap a bad Habit, it will damage you. Finally, if you are in a Boss Battle with your Party and one of your Party mates did not complete all their Dailies, the Boss will attack you.\n\n The main way to heal is to gain a level, which restores all your health. You can also buy a Health Potion with gold from the Rewards tab on the Tasks page. Plus, at level 10 or above, you can choose to become a Healer, and then you will learn healing skills. If you are in a Party with a Healer, they can heal you as well.",
|
||||
"webFaqAnswer4": "There are several things that can cause you to take damage. First, if you left Dailies incomplete overnight and didn't check them off in the screen that popped up the next morning, those unfinished Dailies will damage you. Second, if you click a bad Habit, it will damage you. Finally, if you are in a Boss Battle with your party and one of your party mates did not complete all their Dailies, the Boss will attack you. The main way to heal is to gain a level, which restores all your Health. You can also buy a Health Potion with Gold from the Rewards column. Plus, at level 10 or above, you can choose to become a Healer, and then you will learn healing skills. Other Healers can heal you as well if you are in a Party with them. Learn more by clicking \"Party\" in the navigation bar.",
|
||||
"iosFaqAnswer4": "Има няколко неща, които могат да Ви нанесат щети. Първо, ако не изпълните своите ежедневни задачи през деня, и ако не ги отметнете на екрана, който Ви се появи на следващата сутрин, тези незавършени ежедневни задачи ще Ви наранят. Второ, ако докоснете лош навик, ще поемете щети. И накрая, ако се биете срещу главатар заедно с групата си и някой от нея не е изпълнил всичките си ежедневни задачи, главатарят ще Ви нападне.\n\nОсновният начин да оздравеете е да качите ниво, тъй като така възстановявате изцяло здравето си. Може също така да си купите лековита отвара от колоната с награди, използвайки златото си. Освен това, след ниво 10 може да изберете да станете лечител и да придобиете умения за лечение. Ако в групата Ви има лечител, той също може да Ви излекува.",
|
||||
"androidFaqAnswer4": "Има няколко неща, които могат да Ви нанесат щети. Първо, ако не изпълните своите ежедневни задачи през деня, и ако не ги отметнете на екрана, който Ви се появи на следващата сутрин, тези незавършени ежедневни задачи ще Ви наранят. Второ, ако докоснете лош навик, ще поемете щети. И накрая, ако се биете срещу главатар заедно с групата си и някой от нея не е изпълнил всичките си ежедневни задачи, главатарят ще Ви нападне.\n\nОсновният начин да оздравеете е да качите ниво, тъй като така възстановявате изцяло здравето си. Може също така да си купите лековита отвара от раздела с наградите в страницата със задачи, използвайки златото си. Освен това, след ниво 10 може да изберете да станете лечител и да придобиете умения за лечение. Ако в групата Ви има лечител, той също може да Ви излекува.",
|
||||
"webFaqAnswer4": "Има няколко неща, които могат да Ви нанесат щети. Първо, ако не изпълните своите ежедневни задачи през деня, и ако не ги отметнете на екрана, който Ви се появи на следващата сутрин, тези незавършени ежедневни задачи ще Ви наранят. Второ, ако щракнете върху лош навик, ще поемете щети. И накрая, ако се биете срещу главатар заедно с групата си и някой от нея не е изпълнил всичките си ежедневни задачи, главатарят ще Ви нападне. Основният начин да оздравеете е да качите ниво, тъй като така възстановявате изцяло здравето си. Може също така да си купите лековита отвара от колоната с награди, използвайки златото си. Освен това, след ниво 10 може да изберете да станете лечител и да придобиете умения за лечение. Ако в групата Ви има лечител, той също може да Ви излекува. Научете повече като изберете „Група“ от лентата за навигация.",
|
||||
"faqQuestion5": "Как да играя Хабитика с приятелите си?",
|
||||
"iosFaqAnswer5": "Най-добрият начин е да си направите група! Групите могат заедно да изпълняват мисии, да се бият с чудовища и да използват уменията си, за да се подкрепят. Идете в Меню > Група и щракнете „Създаване на нова група“, ако все още нямате такава. След това докоснете списъка с членовете и докоснете „Покана“ в горния десен ъгъл, за да добавите приятелите си, като въведете техните потребителски идентификатори (низ от цифри и букви, които може да откриете в „Настройки > Подробности за профила“ в приложението, или в „Настройки > ППИ“ в уеб сайта). В уеб сайта можете да поканите приятелите си и чрез е-писмо; тази възможност ще бъде добавена и към приложението в някое бъдещо обновление.\n\nВ уеб сайта, вие и приятелите Ви можете също да се присъединявате към гилдии, които представляват обществени стаи за разговори. Гилдиите ще бъдат добавени към приложението в някое бъдещо обновление!",
|
||||
"androidFaqAnswer5": "The best way is to invite them to a Party with you! Parties can go on quests, battle monsters, and cast skills to support each other. Go to the [website](https://habitica.com/) to create one if you don't already have a Party. You can also join guilds together (Social > Guilds). Guilds are chat rooms focusing on a shared interest or the pursuit of a common goal, and can be public or private. You can join as many guilds as you'd like, but only one party.\n\n For more detailed info, check out the wiki pages on [Parties](http://habitrpg.wikia.com/wiki/Party) and [Guilds](http://habitrpg.wikia.com/wiki/Guilds).",
|
||||
"webFaqAnswer5": "The best way is to invite them to a Party with you by clicking \"Party\" in the navigation bar! Parties can go on quests, battle monsters, and cast skills to support each other. You can also join Guilds together (click on \"Guilds\" in the navigation bar). Guilds are chat rooms focusing on a shared interest or the pursuit of a common goal, and can be public or private. You can join as many Guilds as you'd like, but only one Party. For more detailed info, check out the wiki pages on [Parties](http://habitrpg.wikia.com/wiki/Party) and [Guilds](http://habitrpg.wikia.com/wiki/Guilds).",
|
||||
"androidFaqAnswer5": "Най-добрият начин е да си направите група! Групите могат заедно да изпълняват мисии, да се бият с чудовища и да използват уменията си, за да се подкрепят. Ако все още нямата група, идете в [уеб сайта(https://habitica.com/), за да си направите. Също така можете заедно да се присъединявате към гилдии (Общност > Гилдии). Гилдиите представляват стаи за разговори, концентрирани върху споделен интерес или преследването на обща цел и могат да бъдат обществени или частни. Можете да се присъедините към колкото желаете гилдии, но групата Ви може да бъде само една.\n\nЗа повече подробности, вижте страниците в уикито за [Групите](http://habitrpg.wikia.com/wiki/Party) и [Гилдиите](http://habitrpg.wikia.com/wiki/Guilds).",
|
||||
"webFaqAnswer5": "Най-добрият начин е да си направите група като щракнете върху „Група“ в лентата за навигация! Групите могат заедно да изпълняват мисии, да се бият с чудовища и да използват уменията си, за да се подкрепят. Можете също заедно да се присъединявате към гилдии („Общност > Гилдии“ в лентата за навигация). Гилдиите са стаи за разговори, организирани около общ интерес или преследването на обща цел, и могат да бъдат обществени или частни. Можете да се присъедините към колкото искате гилдии, но само към една група. За по-подробна информация, прегледайте страниците в уикито относно [Групите](http://habitrpg.wikia.com/wiki/Party) и [Гилдиите](http://habitrpg.wikia.com/wiki/Guilds).",
|
||||
"faqQuestion6": "Как да се сдобия с любимец или превоз?",
|
||||
"iosFaqAnswer6": "Когато достигнете ниво 3 се отключва системата за падане на предмети. Всеки път когато завършите задача, ще имате шанс да Ви се падне яйце, излюпваща отвара или храна. Тези неща ще се съхраняват в Меню > Предмети.\n\nЗа да се излюпи любимец Ви трябва яйце и излюпваща отвара. Докоснете яйцето, за да определите какво искате да се излюпи и изберете „Излюпване“. След това изберете излюпваща отвара, за да определите цвета! Идете в Меню > Любимци, за да екипирате новия си любимец като го докоснете.\n\nМожете да превърнете любимците си в превози като ги храните от Меню > Любимци. Докоснете любимец и изберете „Хранене“! Ще трябва да нахраните любимеца си много пъти, преди той да се превърне в превоз, но ако разберете каква е любимата му храна, той ще расте по-бързо. Опитайте чрез проба и грешка или [вижте информацията наготово тук](http://habitica.wikia.com/wiki/Food#Food_Preferences). След като вече имате превоз, идете в Меню > Превози и го докоснете, за да го екипирате.\n\nМоже да получите яйца за любимци от мисии, ако завършите някои конкретни мисии. (Вижте по-надолу, за да научите повече относно мисиите.)",
|
||||
"androidFaqAnswer6": "Когато достигнете ниво 3 се отключва системата за падане на предмети. Всеки път когато завършите задача, ще имате шанс да Ви се падне яйце, излюпваща отвара или храна. Тези неща ще се съхраняват в Меню > Предмети.\n\nЗа да се излюпи любимец Ви трябва яйце и излюпваща отвара. Докоснете яйцето, за да определите какво искате да се излюпи от него, и изберете „Излюпване с отвара“. След това изберете излюпваща отвара, за да определите цвета! За да екипирате новия си любимец, идете в Меню > Конюшня > Любимци, изберете вид, а след това желания любимец и изберете „Използване“ (героят Ви няма да се обнови, за да видите промяната).\n\nМожете да превърнете любимците си в превози, като ги храните от Меню > Конюшня [ > Любимци ]. Докоснете любимец и изберете „Хранене“! Ще трябва да нахраните любимеца си много пъти, преди той да се превърне в превоз, но ако разберете каква е любимата му храна, той ще расте по-бързо. Опитайте чрез проба и грешка, или [вижте информацията наготово тук](http://habitica.wikia.com/wiki/Food#Food_Preferences). За да екипирате превоза си, идете в Меню > Конюшня > Превози, изберете вид, а след това желания превози, и изберете „Използване“ (героят Ви няма да се обнови, за да видите промяната).\n\nМоже да получите яйца за любимци от мисии, ако завършите някои конкретни мисии. (Вижте по-надолу, за да научите повече относно мисиите.)",
|
||||
"webFaqAnswer6": "At level 3, you will unlock the Drop System. Every time you complete a task, you'll have a random chance at receiving an egg, a hatching potion, or a piece of food. They will be stored under Inventory > Items. To hatch a Pet, you'll need an egg and a hatching potion. Once you have both an egg and a potion, go to Inventory > Stable to hatch your pet by clicking on its image. Once you've hatched a pet, you can equip it by clicking on it. You can also grow your Pets into Mounts by feeding them under Inventory > Stable. Drag a piece of food from the action bar at the bottom of the screen and drop it on a pet to feed it! You'll have to feed a Pet many times before it becomes a Mount, but if you can figure out its favorite food, it will grow more quickly. Use trial and error, or [see the spoilers here](http://habitica.wikia.com/wiki/Food#Food_Preferences). Once you have a Mount, click on it to equip it to your avatar. You can also get eggs for Quest Pets by completing certain Quests. (See below to learn more about Quests.)",
|
||||
"webFaqAnswer6": "Когато достигнете ниво 3 се отключва системата за падане на предмети. Всеки път, когато завършите задача, ще имате шанс да Ви се падне яйце, излюпваща отвара или храна. Тези неща ще се съхраняват в „Инвентар > Предмети“. За да се излюпи любимец, Ви трябва яйце и излюпваща отвара. Когато се сдобиете с яйце и излюпваща отвара, идете в „Инвентар > Конюшня“, за да излюпите любимеца си, като щракнете върху изображението му. След като излюпите любимец, можете да го екипирате на героя си, като щракнете върху него. Можете да превърнете любимците си в превози като ги храните от „Инвентар > Конюшня“. Завлачете избраната храна от лентата с действия в дъното на екрана и я пуснете върху любимеца, за да го нахраните. Ще трябва да нахраните любимеца си много пъти, преди той да се превърне в превоз, но ако разберете каква е любимата му храна, той ще расте по-бързо. Опитайте чрез проба и грешка или [вижте информацията наготово тук](http://habitica.wikia.com/wiki/Food#Food_Preferences). След като вече имате превоз, щракнете върху него, за да го екипирате на героя си. Може да получите яйца за любимци от мисии, ако завършите някои конкретни мисии. (Вижте по-надолу, за да научите повече относно мисиите.)",
|
||||
"faqQuestion7": "Как да стана воин, магьосник, мошеник или лечител?",
|
||||
"iosFaqAnswer7": "Когато достигнете ниво 10, може да изберете да станете воин, магьосник, мошеник или лечител. (Всички играчи по подразбиране започват като воини.) Всеки клас има различна екипировка; различни умения, които могат да използват след ниво 11; и различни предимства. Воините могат лесно да нанасят щети на главатарите, както и да понесат повече щети от задачите си и като цяло правят групата си по-силна. Магьосниците също с лекота нанасят щети на главатарите, а също и качват нива по-бързо и възстановяват маната на групата си. Мошениците печелят най-много злато и намират най-много предмети, и могат да помогнат на останалите в групата да имат същия късмет. И накрая, лечителите могат да лекуват себе си и останалите в групата.\n\nАко не искате веднага да избирате клас, например ако все още събирате средства, с които да закупите цялата екипировка за текущия си клас, може да решите по-късно и когато сте готов(а), да го направите в Меню > Избор на клас.",
|
||||
"androidFaqAnswer7": "Когато достигнете ниво 10, може да изберете да станете воин, магьосник, мошеник или лечител. (Всички играчи по подразбиране започват като воини.) Всеки клас има различна екипировка; различни умения, които могат да използват след ниво 11; и различни предимства. Воините могат лесно да нанасят щети на главатарите, както и да понесат повече щети от задачите си и като цяло правят групата си по-силна. Магьосниците също с лекота нанасят щети на главатарите, а също и качват нива по-бързо и възстановяват маната на групата си. Мошениците печелят най-много злато и намират най-много предмети, и могат да помогнат на останалите в групата да имат същия късмет. И накрая, лечителите могат да лекуват себе си и останалите в групата.\n\nАко не искате веднага да избирате клас, например ако все още събирате средства, с които да закупите цялата екипировка за текущия си клас, може да се откажете от тази възможност, и когато сте готов(а), да го направите в Меню > Избор на клас.",
|
||||
"webFaqAnswer7": "At level 10, you can choose to become a Warrior, Mage, Rogue, or Healer. (All players start as Warriors by default.) Each Class has different equipment options, different Skills that they can cast after level 11, and different advantages. Warriors can easily damage Bosses, withstand more damage from their tasks, and help make their party tougher. Mages can also easily damage Bosses, as well as level up quickly and restore Mana for their party. Rogues earn the most Gold and find the most item drops, and they can help their party do the same. Finally, Healers can heal themselves and their party members. If you don't want to choose a Class immediately -- for example, if you are still working to buy all the gear of your current class -- you can click \"Opt Out\" and re-enable it later under Settings.",
|
||||
"webFaqAnswer7": "Когато достигнете ниво 10, може да изберете да станете воин, магьосник, мошеник или лечител. (Всички играчи по подразбиране започват като воини.) Всеки клас има различна екипировка; различни умения, които могат да използват след ниво 11; и различни предимства. Воините могат лесно да нанасят щети на главатарите, както и да понесат повече щети от задачите си и като цяло правят групата си по-силна. Магьосниците също с лекота нанасят щети на главатарите, а също и качват нива по-бързо и възстановяват маната на групата си. Мошениците печелят най-много злато и намират най-много предмети, и могат да помогнат на останалите в групата да имат същия късмет. И накрая, лечителите могат да лекуват себе си и останалите в групата. Ако не искате веднага да избирате клас, например ако все още събирате средства, с които да закупите цялата екипировка за текущия си клас, може да се откажете от тази възможност, и когато сте готов(а), да я включите отново от настройките.",
|
||||
"faqQuestion8": "Каква е синята лента, която се появява в горната част след ниво 10?",
|
||||
"iosFaqAnswer8": "Синята лента, която се появи след като достигнахте ниво 10 и избрахте клас, е лентата за маната. С качването на нива, ще отключвате специални умения, чието използване изисква мана. Всеки клас има различни умения, които се появяват след ниво 11 в Меню > Използване на умения. За разлика от здравето, маната не се възстановява напълно когато качите ниво. Тя се възстановява постепенно, когато изпълнявате добрите си навици, ежедневните си задачи и задачите си от списъка; тя се понижава, ако се поддавате на лошите си навици. Също така, малко мана се възстановява и след края на деня — колкото повече ежедневни задачи сте изпълнили през деня, толкова повече мана ще възстановите.",
|
||||
"androidFaqAnswer8": "Синята лента, която се появи след като достигнахте ниво 10 и избрахте клас, е лентата за маната. С качването на нива, ще отключвате специални умения, чието използване изисква мана. Всеки клас има различни умения, които се появяват след ниво 11 в Меню > Умения. За разлика от здравето, маната не се възстановява напълно, когато качите ниво. Тя се възстановява постепенно, когато изпълнявате добрите си навици, ежедневните си задачи и задачите си от списъка; тя се понижава, ако се поддавате на лошите си навици. Също така, малко мана се възстановява и след края на деня — колкото повече ежедневни задачи сте изпълнили през деня, толкова повече мана ще възстановите.",
|
||||
"webFaqAnswer8": "The blue bar that appeared when you hit level 10 and chose a Class is your Mana bar. As you continue to level up, you will unlock special Skills that cost Mana to use. Each Class has different Skills, which appear after level 11 in the action bar at the bottom of the screen. Unlike your Health bar, your Mana bar does not reset when you gain a level. Instead, Mana is gained when you complete good Habits, Dailies, and To-Dos, and lost when you indulge bad Habits. You'll also regain some Mana overnight -- the more Dailies you completed, the more you will gain.",
|
||||
"webFaqAnswer8": "Синята лента, която се появи след като достигнахте ниво 10 и избрахте клас, е лентата за маната. С качването на нива, ще отключвате специални умения, чието използване изисква мана. Всеки клас има различни умения, които се появяват след ниво 11 в лентата с действия в дъното на екрана. За разлика от здравето, маната не се възстановява напълно когато качите ниво. Тя се възстановява постепенно, когато изпълнявате добрите си навици, ежедневните си задачи и задачите си от списъка; тя се понижава, ако се поддавате на лошите си навици. Също така, малко мана се възстановява и след края на деня — колкото повече ежедневни задачи сте изпълнили през деня, толкова повече мана ще възстановите.",
|
||||
"faqQuestion9": "Как да се бия с чудовища и да изпълнявам мисии?",
|
||||
"iosFaqAnswer9": "First, you need to join or start a Party (see above). Although you can battle monsters alone, we recommend playing in a group, because this will make Quests much easier. Plus, having a friend to cheer you on as you accomplish your tasks is very motivating!\n\n Next, you need a Quest Scroll, which are stored under Menu > Items. There are three ways to get a scroll:\n\n - At level 15, you get a Quest-line, aka three linked quests. More Quest-lines unlock at levels 30, 40, and 60 respectively. \n - When you invite people to your Party, you'll be rewarded with the Basi-List Scroll!\n - You can buy Quests from the Quests Shop for Gold and Gems.\n\n To battle the Boss or collect items for a Collection Quest, simply complete your tasks normally, and they will be tallied into damage overnight. (Reloading by pulling down on the screen may be required to see the Boss's health bar go down.) If you are fighting a Boss and you missed any Dailies, the Boss will damage your Party at the same time that you damage the Boss. \n\n After level 11 Mages and Warriors will gain Skills that allow them to deal additional damage to the Boss, so these are excellent classes to choose at level 10 if you want to be a heavy hitter.",
|
||||
"androidFaqAnswer9": "First, you need to join or start a Party (see above). Although you can battle monsters alone, we recommend playing in a group, because this will make Quests much easier. Plus, having a friend to cheer you on as you accomplish your tasks is very motivating!\n\n Next, you need a Quest Scroll, which are stored under Menu > Items. There are three ways to get a scroll:\n\n - At level 15, you get a Quest-line, aka three linked quests. More Quest-lines unlock at levels 30, 40, and 60 respectively. \n - When you invite people to your Party, you'll be rewarded with the Basi-List Scroll!\n - You can buy Quests from the Quests Shop for Gold and Gems.\n\n To battle the Boss or collect items for a Collection Quest, simply complete your tasks normally, and they will be tallied into damage overnight. (Reloading by pulling down on the screen may be required to see the Boss's health bar go down.) If you are fighting a Boss and you missed any Dailies, the Boss will damage your Party at the same time that you damage the Boss. \n\n After level 11 Mages and Warriors will gain Skills that allow them to deal additional damage to the Boss, so these are excellent classes to choose at level 10 if you want to be a heavy hitter.",
|
||||
"webFaqAnswer9": "First, you need to join or start a Party by clicking \"Party\" in the navigation bar. Although you can battle monsters alone, we recommend playing in a group, because this will make quests much easier. Plus, having a friend to cheer you on as you accomplish your tasks is very motivating! Next, you need a Quest Scroll, which are stored under Inventory > Quests. There are four ways to get a scroll:\n * When you invite people to your Party, you'll be rewarded with the Basi-List Scroll!\n * At level 15, you get a Quest-line, i.e., three linked quests. More Quest-lines unlock at levels 30, 40, and 60 respectively.\n * You can buy Quests from the Quests Shop (Shops > Quests) for Gold and Gems.\n * When you check in to Habitica a certain number of times, you'll be rewarded with Quest Scrolls. You earn a Scroll during your 1st, 7th, 22nd, and 40th check-ins.\n To battle the Boss or collect items for a Collection Quest, simply complete your tasks normally, and they will be tallied into damage overnight. (Reloading may be required to see the Boss's Health bar go down.) If you are fighting a Boss and you missed any Dailies, the Boss will damage your Party at the same time that you damage the Boss. After level 11 Mages and Warriors will gain Skills that allow them to deal additional damage to the Boss, so these are excellent classes to choose at level 10 if you want to be a heavy hitter.",
|
||||
"iosFaqAnswer9": "Първо, ще трябва да създадете група или да се присъедините към такава (вижте малко по-нагоре). Въпреки че можете да се биете с чудовища и сам(а), ние Ви препоръчваме да го правите в група, тъй като така мисиите ще бъдат доста по-лесни. Освен това, приятелите ще Ви насърчават и мотивират да изпълнявате задачите си!\n\nСлед това ще Ви трябва свитък с мисия (свитъците с мисии се пазят в Меню > Предмети). Има три начина да се сдобиете с такъв:\n\n— На ниво 15 получавате последователност от мисии, тоест три свързани мисии. Подобни последователности се отключват и на ниво 30, 40 и 60;\n— Когато поканите хора в групата си, ще получите свитъка за Василисъка!\n— Можете да купувате мисии от магазина в замяна на злато и диаманти.\n\nЗа да се биете с главатаря или да събирате предмети за събираческа мисия, просто завършвайте задачите си както обикновено, а те ще бъдат превърнати в щети след края на деня. (Може да се наложи да презаредите, като плъзнете пръст надолу по екрана, за да видите как здравето на главатаря намалява.) Ако се биете с главатар и сте пропуснали дори една ежедневна задача, той ще нанесе щети на групата Ви в същото време, когато Вие нанасяте щети на него.\n\nСлед ниво 11, магьосниците и воините получават умения, с които могат да нанасят допълнителни щети на главатаря, така че тези класове са добър избор на ниво 10, ако искате да удряте здраво.",
|
||||
"androidFaqAnswer9": "Първо, ще трябва да създадете група или да се присъедините към такава (вижте малко по-нагоре). Въпреки че можете да се биете с чудовища и сам(а), ние Ви препоръчваме да го правите в група, тъй като така мисиите ще бъдат доста по-лесни. Освен това, приятелите ще Ви насърчават и мотивират да изпълнявате задачите си!\n\nСлед това ще Ви трябва свитък с мисия (свитъците с мисии се пазят в Меню > Предмети). Има три начина да се сдобиете с такъв:\n\n— На ниво 15 получавате последователност от мисии, тоест три свързани мисии. Подобни последователности се отключват и на ниво 30, 40 и 60;\n— Когато поканите хора в групата си, ще получите свитъка за Василисъка!\n— Можете да купувате мисии от магазина за мисии в замяна на злато и диаманти.\n\nЗа да се биете с главатаря или да събирате предмети за събираческа мисия, просто завършвайте задачите си както обикновено, а те ще бъдат превърнати в щети след края на деня. (Може да се наложи да презаредите, като плъзнете пръст надолу по екрана, за да видите как здравето на главатаря намалява.) Ако се биете с главатар и сте пропуснали дори една ежедневна задача, той ще нанесе щети на групата Ви в същото време, когато Вие нанасяте щети на него.\n\nСлед ниво 11, магьосниците и воините получават умения, с които могат да нанасят допълнителни щети на главатаря, така че тези класове са добър избор на ниво 10, ако искате да удряте здраво.",
|
||||
"webFaqAnswer9": "Първо, ще трябва да създадете група или да се присъедините към такава, като щракнете върху „Група“ в лентата за навигация. Въпреки че можете да се биете с чудовища и сам(а), ние Ви препоръчваме да го правите в група, тъй като така мисиите ще бъдат доста по-лесни. Освен това, приятелите ще Ви насърчават и мотивират да изпълнявате задачите си! След това ще Ви трябва свитък с мисия (свитъците с мисии се пазят в „Инвентар > Мисии“). Има четири начина да се сдобиете с такъв:\n* Когато поканите хора в групата си, ще получите свитъка за Василисъка!\n* На ниво 15 получавате последователност от мисии, тоест три свързани мисии. Подобни последователности се отключват и на ниво 30, 40 и 60;\n* Можете да купувате мисии от магазина за мисии („Магазини > Мисии“) в замяна на злато и диаманти.\n* Когато се отчитате в Хабитика определен брой пъти, ще получите като награда свитъци с зисии. Ще получите свитък при 1-вото, 7-мото, 22-рото и 40-тото си отчитане.\nЗа да се биете с главатаря или да събирате предмети за събираческа мисия, просто завършвайте задачите си както обикновено, а те ще бъдат превърнати в щети след края на деня. (Може да се наложи да презаредите, за да видите как здравето на главатаря намалява.) Ако се биете с главатар и сте пропуснали дори една ежедневна задача, той ще нанесе щети на групата Ви в същото време, когато Вие нанасяте щети на него. След ниво 11, магьосниците и воините получават умения, с които могат да нанасят допълнителни щети на главатаря, така че тези класове са добър избор на ниво 10, ако искате да удряте здраво.",
|
||||
"faqQuestion10": "Какво са диамантите и как да се сдобия с тях?",
|
||||
"iosFaqAnswer10": "Gems are purchased with real money by tapping on the Gem icon in the header. When people buy Gems, they are helping us to keep the site running. We're very grateful for their support!\n\n In addition to buying Gems directly, there are three other ways players can gain Gems:\n\n * Win a Challenge that has been set up by another player. Go to Social > Challenges to join some.\n * Subscribe and unlock the ability to buy a certain number of Gems per month.\n * Contribute your skills to the Habitica project. See this wiki page for more details: [Contributing to Habitica](http://habitica.wikia.com/wiki/Contributing_to_Habitica).\n\n Keep in mind that items purchased with Gems do not offer any statistical advantages, so players can still make use of the app without them!",
|
||||
"androidFaqAnswer10": "Gems are purchased with real money by tapping on the Gem icon in the header. When people buy Gems, they are helping us to keep the site running. We're very grateful for their support!\n\n In addition to buying Gems directly, there are three other ways players can gain Gems:\n\n * Win a Challenge that has been set up by another player. Go to Social > Challenges to join some.\n * Subscribe and unlock the ability to buy a certain number of Gems per month.\n * Contribute your skills to the Habitica project. See this wiki page for more details: [Contributing to Habitica](http://habitica.wikia.com/wiki/Contributing_to_Habitica).\n\n Keep in mind that items purchased with Gems do not offer any statistical advantages, so players can still make use of the app without them!",
|
||||
"webFaqAnswer10": "Gems are purchased with real money, although [subscribers](https://habitica.com/user/settings/subscription) can purchase them with Gold. When people subscribe or buy Gems, they are helping us to keep the site running. We're very grateful for their support! In addition to buying Gems directly or becoming a subscriber, there are two other ways players can gain Gems:\n* Win a Challenge that has been set up by another player. Go to Challenges > Discover Challenges to join some.\n * Contribute your skills to the Habitica project. See this wiki page for more details: [Contributing to Habitica](http://habitica.wikia.com/wiki/Contributing_to_Habitica). Keep in mind that items purchased with Gems do not offer any statistical advantages, so players can still make use of the site without them!",
|
||||
"iosFaqAnswer10": "Диамантите се купуват с истински пари; това става като докоснете иконката с диамант в горната част. Когато хората купуват диаманти, те ни помагат да поддържаме уеб сайта работещ. Благодарим за подкрепата им!\n\nОсвен да бъдат купени, има три други начина играчите да се сдобият с диаманти:\n\n* Чрез спечелване на предизвикателство, което е било създадено от друг играч. Идете в „Общност > Предизвикателства“, за да се присъедините към някое.\n* Чрез абониране, което дава възможност за купуване на определен брой диаманти всеки месец;\n* Чрез допринасяне към Хабитика. Вижте тази статия в уикито за повече информация: [Допринасяне към Хабитика](http://habitica.wikia.com/wiki/Contributing_to_Habitica).\n\nИмайте предвид, че предметите, купени с диаманти, не дават повече показатели, така че играчите, използващи приложението без тях, няма да бъдат ощетени)",
|
||||
"androidFaqAnswer10": "Диамантите се купуват с истински пари; това става като докоснете иконката с диамант в горната част. Когато хората купуват диаманти, те ни помагат да поддържаме уеб сайта работещ. Благодарим за подкрепата им!\n\nОсвен да бъдат купени, има три други начина играчите да се сдобият с диаманти:\n\n* Чрез спечелване на предизвикателство, което е било създадено от друг играч. Идете в „Общност > Предизвикателства“, за да се присъедините към някое.\n* Чрез абониране, което дава възможност за купуване на определен брой диаманти всеки месец;\n* Чрез допринасяне към Хабитика. Вижте тази статия в уикито за повече информация: [Допринасяне към Хабитика](http://habitica.wikia.com/wiki/Contributing_to_Habitica).\n\nИмайте предвид, че предметите, купени с диаманти, не дават повече показатели, така че играчите, използващи приложението без тях, няма да бъдат ощетени)",
|
||||
"webFaqAnswer10": "Диамантите се купуват с истински пари, но [абонираните](https://habitica.com/user/settings/subscription) могат да ги купуват със злато. Когато хората се абонират или купуват диаманти, те ни помагат да поддържаме уеб сайта работещ. Благодарим за подкрепата им! Освен да бъдат купени с пари или чрез абониране, има два други начина играчите да се сдобият с диаманти:\n* Чрез спечелване на предизвикателство, което е било създадено от друг играч. Идете в „Общност > Разглеждане на предизвикателствата“, за да се присъедините към някое.\n* Чрез допринасяне към Хабитика. Вижте тази статия в уикито за повече информация: [Допринасяне към Хабитика](http://habitica.wikia.com/wiki/Contributing_to_Habitica).\nИмайте предвид, че предметите, купени с диаманти, не дават повече показатели, така че играчите, използващи уеб сайта без тях, няма да бъдат ощетени!",
|
||||
"faqQuestion11": "Как да съобщя за проблем или да предложа нова функционалност?",
|
||||
"iosFaqAnswer11": "You can report a bug, request a feature, or send feedback under Menu > About > Report a Bug and Menu > About > Send Feedback! We'll do everything we can to assist you.",
|
||||
"iosFaqAnswer11": "Можете да докладвате проблеми, да предложите нова функционалност или да изпратите обратна връзка от „Меню > Относно > Докладване на проблем“ и „Меню > Относно > Изпращане на обратна връзка“! Ще направим всичко по силите си, за да Ви съдействаме.",
|
||||
"androidFaqAnswer11": "Можете да докладвате проблеми, да предложите нова функционалност или да изпратите обратна връзка от „Относно > Докладване на проблем“ и „Относно > Изпращане на обратна връзка“! Ще направим всичко по силите си, за да Ви съдействаме.",
|
||||
"webFaqAnswer11": "To report a bug, go to [Help > Report a Bug](https://habitica.com/groups/guild/a29da26b-37de-4a71-b0c6-48e72a900dac) and read the points above the chat box. If you're unable to log in to Habitica, send your login details (not your password!) to [<%= techAssistanceEmail %>](<%= wikiTechAssistanceEmail %>). Don't worry, we'll get you fixed up soon! Feature requests are collected on Trello. Go to [Help > Request a Feature](https://trello.com/c/odmhIqyW/440-read-first-table-of-contents) and follow the instructions. Ta-da!",
|
||||
"webFaqAnswer11": "За да докладвате проблем, идете в [Помощ > Докладване на проблем](https://habitica.com/groups/guild/a29da26b-37de-4a71-b0c6-48e72a900dac) и прочетете точките над текстовото поле. Ако не можете да влезете в Хабитика, изпратете ни данните си за влизане (без паролата!! на [<%= techAssistanceEmail %>](<%= wikiTechAssistanceEmail %>). Не се притеснявайте, ще Ви помогнем при първа възможност! Предложенията на нови функционалности се събират в Трело. Идете в [Помощ > Предлагане на функционалност](https://trello.com/c/odmhIqyW/440-read-first-table-of-contents) и следвайте инструкциите.",
|
||||
"faqQuestion12": "Как да се бия със световен главатар?",
|
||||
"iosFaqAnswer12": "World Bosses are special monsters that appear in the Tavern. All active users are automatically battling the Boss, and their tasks and Skills will damage the Boss as usual.\n\n You can also be in a normal Quest at the same time. Your tasks and Skills will count towards both the World Boss and the Boss/Collection Quest in your party.\n\n A World Boss will never hurt you or your account in any way. Instead, it has a Rage Bar that fills when users skip Dailies. If its Rage bar fills, it will attack one of the Non-Player Characters around the site and their image will change.\n\n You can read more about [past World Bosses](http://habitica.wikia.com/wiki/World_Bosses) on the wiki.",
|
||||
"androidFaqAnswer12": "World Bosses are special monsters that appear in the Tavern. All active users are automatically battling the Boss, and their tasks and Skills will damage the Boss as usual.\n\n You can also be in a normal Quest at the same time. Your tasks and Skills will count towards both the World Boss and the Boss/Collection Quest in your party.\n\n A World Boss will never hurt you or your account in any way. Instead, it has a Rage Bar that fills when users skip Dailies. If its Rage bar fills, it will attack one of the Non-Player Characters around the site and their image will change.\n\n You can read more about [past World Bosses](http://habitica.wikia.com/wiki/World_Bosses) on the wiki.",
|
||||
"webFaqAnswer12": "World Bosses are special monsters that appear in the Tavern. All active users are automatically battling the Boss, and their tasks and Skills will damage the Boss as usual. You can also be in a normal Quest at the same time. Your tasks and Skills will count towards both the World Boss and the Boss/Collection Quest in your party. A World Boss will never hurt you or your account in any way. Instead, it has a Rage Bar that fills when users skip Dailies. If its Rage bar fills, it will attack one of the Non-Player Characters around the site and their image will change. You can read more about [past World Bosses](http://habitica.wikia.com/wiki/World_Bosses) on the wiki.",
|
||||
"iosFaqAnswer12": "Световните главатари са специални чудовища, които се появяват в кръчмата. Всички активни потребители автоматично се бият с главатаря като задачите и уменията им автоматично му нанасят щети, както обикновено.\n\nВъзможно е в същото време да изпълнявате обикновена мисия. В такъв случай задачите и уменията Ви ще влияят както на световния главатар, така и на мисията на групата Ви.\n\nСветовният главатар не може да Ви нанесе щети или да навреди на профила Ви. Вместо това той има лента за ярост, която се запълва, когато потребителите пропускат ежедневните си задачи. Ако лентата за ярост се напълни до края, чудовището ще нападне някой от компютърните персонажи на уеб сайта и ще промени изображението му.\n\nМоже да прочетете повече относно [миналите световни главатари](http://habitica.wikia.com/wiki/World_Bosses) в уикито.",
|
||||
"androidFaqAnswer12": "Световните главатари са специални чудовища, които се появяват в кръчмата. Всички активни потребители автоматично се бият с главатаря като задачите и уменията им автоматично му нанасят щети, както обикновено.\n\nВъзможно е в същото време да изпълнявате обикновена мисия. В такъв случай задачите и уменията Ви ще влияят както на световния главатар, така и на мисията на групата Ви.\n\nСветовният главатар не може да Ви нанесе щети или да навреди на профила Ви. Вместо това той има лента за ярост, която се запълва, когато потребителите пропускат ежедневните си задачи. Ако лентата за ярост се напълни до края, чудовището ще нападне някой от компютърните персонажи на уеб сайта и ще промени изображението му.\n\nМоже да прочетете повече относно [миналите световни главатари](http://habitica.wikia.com/wiki/World_Bosses) в уикито.",
|
||||
"webFaqAnswer12": "Световните главатари са специални чудовища, които се появяват в кръчмата. Всички активни потребители автоматично се бият с главатаря като задачите и уменията им автоматично му нанасят щети, както обикновено. Възможно е в същото време да изпълнявате обикновена мисия. В такъв случай задачите и уменията Ви ще влияят както на световния главатар, така и на мисията на групата Ви. Световният главатар не може да Ви нанесе щети или да навреди на профила Ви. Вместо това той има лента за ярост, която се запълва, когато потребителите пропускат ежедневните си задачи. Ако лентата за ярост се напълни до края, чудовището ще нападне някой от компютърните персонажи на уеб сайта и ще промени изображението му. Може да прочетете повече относно [миналите световни главатари](http://habitica.wikia.com/wiki/World_Bosses) в уикито.",
|
||||
"iosFaqStillNeedHelp": "Ако имате въпрос, който не намирате в този списък или в [ЧЗВ в уикито](http://habitica.wikia.com/wiki/FAQ), задайте го в кръчмата чрез Меню > Кръчма! Ще се радваме да помогнем.",
|
||||
"androidFaqStillNeedHelp": "Ако имате въпрос, който не намирате в този списък или в [ЧЗВ в уикито](http://habitica.wikia.com/wiki/FAQ), задайте го в кръчмата чрез Меню > Кръчма! Ще се радваме да помогнем.",
|
||||
"webFaqStillNeedHelp": "If you have a question that isn't on this list or on the [Wiki FAQ](http://habitica.wikia.com/wiki/FAQ), come ask in the [Habitica Help guild](https://habitica.com/groups/guild/5481ccf3-5d2d-48a9-a871-70a7380cee5a)! We're happy to help."
|
||||
"webFaqStillNeedHelp": "Ако имате въпрос, който не намирате в този списък или в [ЧЗВ в уикито](http://habitica.wikia.com/wiki/FAQ), задайте го в [Помощната гилдия на Хабитика](https://habitica.com/groups/guild/5481ccf3-5d2d-48a9-a871-70a7380cee5a)! Ще се радваме да помогнем."
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"FAQ": "ЧЗВ",
|
||||
"termsAndAgreement": "By clicking the button below, you are indicating that you have read and agree to the <a href='/static/terms'>Terms of Service</a> and <a href='/static/privacy'>Privacy Policy</a>.",
|
||||
"termsAndAgreement": "Натискайки бутона по-долу, Вие заявявате, че сте прочули и се съгласявате с <a href='/static/terms'>Условията за ползване</a> и <a href='/static/privacy'>Политиката за поверителност</a>.",
|
||||
"accept1Terms": "С натискане на бутона по-долу, аз се съгласявам с",
|
||||
"accept2Terms": "и",
|
||||
"alexandraQuote": "Не можах да НЕ говоря за Хабитика по време на речта си в Мадрид. Задължителен инструмент за хора на свободна практика, които все още се нуждаят от шеф.",
|
||||
@@ -27,7 +27,7 @@
|
||||
"communityForum": "<a target='_blank' href='http://habitica.wikia.com/wiki/Special:Forum'>Форум</a>",
|
||||
"communityKickstarter": "Kickstarter",
|
||||
"communityReddit": "Reddit",
|
||||
"companyAbout": "How It Works",
|
||||
"companyAbout": "Как работи",
|
||||
"companyBlog": "Блог",
|
||||
"devBlog": "Блог на разработчиците",
|
||||
"companyDonate": "Дарете",
|
||||
@@ -38,10 +38,10 @@
|
||||
"dragonsilverQuote": "Вече не помня колко системи за следене на времето и задачите съм ползвал през годините… Хабитика е единственото нещо, което всъщност ми помага да свърша нещата, които искам, а не просто да ги изброя.",
|
||||
"dreimQuote": "Когато открих Хабитика миналото лято, тъкмо ме бяха скъсали на половината ми изпити. Благодарение на ежедневните задачи… успях да се организирам и дисциплинирам, и в крайна сметка взех всичките си изпити с много добри оценки преди месец.",
|
||||
"elmiQuote": "Всяка сутрин ставам с мисълта, че трябва да печеля злато!",
|
||||
"forgotPassword": "Forgot Password?",
|
||||
"forgotPassword": "Забравена парола?",
|
||||
"emailNewPass": "Изпращане на е-писмо с връзка за подновяване на паролата",
|
||||
"forgotPasswordSteps": "Enter the email address you used to register your Habitica account.",
|
||||
"sendLink": "Send Link",
|
||||
"forgotPasswordSteps": "Въведете е-пощата, с която сте се регистрирали в Хабитика.",
|
||||
"sendLink": "Изпращане на връзката",
|
||||
"evagantzQuote": "За първи път ми се случва зъболекарят ми да се впечатли от навиците ми за използване на конец за зъби. Благодаря, Хабитика!",
|
||||
"examplesHeading": "Играчите използват Хабитика, за да се справят с…",
|
||||
"featureAchievementByline": "Направили сте нещо страхотно? Вземете значка и я покажете на всички!",
|
||||
@@ -83,7 +83,7 @@
|
||||
"joinOthers": "<%= userCount %> хора се забавляват, докато постигат целите си. Присъединете се към тях!",
|
||||
"kazuiQuote": "Преди Хабитика, бях доникъде с дипломната си работа, както и не бях доволна от това, че не вършех домашните си задължения, не учех нови думи и не учех теорията на Го. Оказа се, че като раздробих задачите си на малки, изпълними списъци, успях да се мотивирам и да изпълнявам всичко.",
|
||||
"landingend": "Все още не сте убедени?",
|
||||
"landingend2": "See a more detailed list of [our features](/static/overview). Are you looking for a more private approach? Check out our [administrative packages](/static/plans), which are perfect for families, teachers, support groups, and businesses.",
|
||||
"landingend2": "Вижте по-подробен списък на [функционалностите](/static/overview). Нуждаете се от частен подход? Вижте нашите [административни пакети](/static/plans), които са идеални за семейства, учители, групи за подкрепа и офиси.",
|
||||
"landingp1": "Проблемът на повечето приложения за продуктивност на пазара е, че не осигуряват никакъв стимул човек да ги ползва. Хабитика се фокусира точно върху това и прави изграждането на добри навици забавно! Като Ви награждава за успехите и Ви наказва за пропуските и измъкването, Хабитика Ви дава стимул да завършите ежедневните си задачи.",
|
||||
"landingp2": "Всеки път, когато затвърждавате добър навик, изпълнявате ежедневна задача или такава, която сте си намислили отдавна, Хабитика веднага ви награждава с точки за опит и злато. Събирайки точки, Вие вдигате нива, увеличавате показателите на героя си и отключвате още функционалности като класове и любимци. Златото може да се използва за купуване на различни предмети, които променят играта Ви или на персонализирани награди, които можете да си създавате за допълнителна мотивация. Когато дори най-малките успехи Ви дават моментална награда, е много по-малко вероятно да отлагате задачите и целите си.",
|
||||
"landingp2header": "Моментално награждаване",
|
||||
@@ -98,23 +98,23 @@
|
||||
"loginGoogleAlt": "Вписване чрез Гугъл",
|
||||
"logout": "Изход",
|
||||
"marketing1Header": "Подобрете навиците си чрез игра",
|
||||
"marketing1Lead1Title": "Your Life, the Role Playing Game",
|
||||
"marketing1Lead1Title": "Животът Ви, превърнат в ролева игра",
|
||||
"marketing1Lead1": "Хабитика е видео игра, която Ви помага да подобрите навиците си в истинския живот. Тя превръща живота Ви в игра като преобразява всички Ваши задачи (навици, ежедневни задачи и списъци със задачи за изпълнение) в малки чудовища, които трябва да победите. Колкото по-добре се справяте с тях, толкова повече напредвате в играта. Ако направите грешна стъпка в реалния живот, Вашият герой получава щети в играта.",
|
||||
"marketing1Lead2Title": "Сдобийте се с невероятно снаряжение",
|
||||
"marketing1Lead2": "Improve your habits to build up your avatar. Show off the sweet gear you've earned!",
|
||||
"marketing1Lead2": "Подобрявайте навиците си, за да изграждате героя си. Покажете страхотната екипировка, която сте си спечелили!",
|
||||
"marketing1Lead3Title": "Намирайте случайна плячка",
|
||||
"marketing1Lead3": "For some, it's the gamble that motivates them: a system called \"stochastic rewards.\" Habitica accommodates all reinforcement and punishment styles: positive, negative, predictable, and random.",
|
||||
"marketing1Lead3": "Някои се мотивират от неочакваните печалби — за тях е системата, наречена „случайни награди“. Хабитика разполага с всички видове поощрения и наказания: положителни, отрицателни, предсказуеми и случайни. ",
|
||||
"marketing2Header": "Състезавайте се с приятели, присъединете се към групи по интереси",
|
||||
"marketing2Lead1Title": "Social Productivity",
|
||||
"marketing2Lead1Title": "Обществена продуктивност",
|
||||
"marketing2Lead1": "Можете да играете Хабитика самостоятелно, но е още по-интересно когато започнете да си помагате взаимно, да се състезавате и да държите един другиго отговорен. Най-ефикасната част от всяка система за самопомощ е отговорността пред други хора, а какво може да бъде по-добра среда за една такава отговорност и съревнование от една компютърна игра?",
|
||||
"marketing2Lead2Title": "Fight Monsters",
|
||||
"marketing2Lead2": "What's a Role Playing Game without battles? Fight monsters with your party. Monsters are \"super accountability mode\" - a day you miss the gym is a day the monster hurts *everyone!*",
|
||||
"marketing2Lead3Title": "Challenge Each Other",
|
||||
"marketing2Lead3": "Challenges let you compete with friends and strangers. Whoever does the best at the end of a challenge wins special prizes.",
|
||||
"marketing2Lead2Title": "Бийте се с чудовища",
|
||||
"marketing2Lead2": "Какво е една ролева игра без битки? Бийте се заедно с групата си срещу чудовища. Чудовищата са един вид режим на „свръхотговорност“ — ако пропуснете фитнеса, главатарят ще нанесе щети на *всички*!",
|
||||
"marketing2Lead3Title": "Предизвиквайте се взаимно",
|
||||
"marketing2Lead3": "Предизвикателствата Ви дават възможността да се състезавате с приятели и непознати. Който се справи най-добре, получава специални награди.",
|
||||
"marketing3Header": "Приложения и разширения",
|
||||
"marketing3Lead1": "The **iPhone & Android** apps let you take care of business on the go. We realize that logging into the website to click buttons can be a drag.",
|
||||
"marketing3Lead2Title": "Integrations",
|
||||
"marketing3Lead2": "Other **3rd Party Tools** tie Habitica into various aspects of your life. Our API provides easy integration for things like the [Chrome Extension](https://chrome.google.com/webstore/detail/habitica/pidkmpibnnnhneohdgjclfdjpijggmjj?hl=en-US), for which you lose points when browsing unproductive websites, and gain points when on productive ones. [See more here](http://habitica.wikia.com/wiki/Extensions,_Add-Ons,_and_Customizations).",
|
||||
"marketing3Lead1": "Приложенията за **iPhone и Андроид** Ви позволяват да се грижите за задачите си в движение. Наясно сме, че влизането в уеб сайта и натискането на бутони може би е старомодно.",
|
||||
"marketing3Lead2Title": "Интеграции",
|
||||
"marketing3Lead2": "Други **инструменти от трети страни** свързват Хабитика с различни страни на живота Ви. Нашият ППИ предоставя лесна интеграция с неща като [разширението за Chrome](https://chrome.google.com/webstore/detail/habitica/pidkmpibnnnhneohdgjclfdjpijggmjj?hl=bg-BG), чрез което губите точки живот, ако разглеждате непродуктивни уеб сайтове, и получавате точки, когато посещавате продуктивни такива. [Вижте повече тук](http://habitica.wikia.com/wiki/Extensions,_Add-Ons,_and_Customizations)",
|
||||
"marketing4Header": "Употреба в институции",
|
||||
"marketing4Lead1": "Образованието е една от най-добрите области за превръщане в игра. Всички знаем колко залепени за телефоните и игрите са днешните ученици — използвайте тази енергия! Поставете учениците си в среда на приятелско съревнование. Награждавайте доброто поведение с редки награди. Наблюдавайте как оценките и поведението им се подобряват.",
|
||||
"marketing4Lead1Title": "Игра в образованието",
|
||||
@@ -132,7 +132,7 @@
|
||||
"oldNews": "Новини",
|
||||
"newsArchive": "Архив на новините в „Wikia“ (на много езици)",
|
||||
"passConfirm": "Повторете паролата",
|
||||
"setNewPass": "Set New Password",
|
||||
"setNewPass": "Задаване на нова парола",
|
||||
"passMan": "В случай, че използвате мениджър за паролите си (например 1Password) и имате проблем с влизането, опитайте да въведете потребителското име и паролата си ръчно.",
|
||||
"password": "Парола",
|
||||
"playButton": "Играйте",
|
||||
@@ -194,8 +194,8 @@
|
||||
"unlockByline2": "Отключвайте нови средства за мотивация като любимци, случайни награди, заклинания и други!",
|
||||
"unlockHeadline": "Поддържайки продуктивността си, Вие отключвате ново съдържание!",
|
||||
"useUUID": "Използвайте идентификатор UUID / жетон за ППИ (за потребители на Фейсбук)",
|
||||
"username": "Login Name",
|
||||
"emailOrUsername": "Email or Login Name",
|
||||
"username": "Потребителско име",
|
||||
"emailOrUsername": "Е-поща или потребителско име",
|
||||
"watchVideos": "Вижте видеоклиповете",
|
||||
"work": "Работа",
|
||||
"zelahQuote": "Хабитика може да ме накара да си лягам навреме, тъй като знам, че ще спечеля точки за това; или ще изгубя здраве, ако закъснея!",
|
||||
@@ -245,9 +245,9 @@
|
||||
"altAttrSlack": "Slack",
|
||||
"missingAuthHeaders": "Липсват заглавки за удостоверяване.",
|
||||
"missingAuthParams": "Липсват параметри за удостоверяване.",
|
||||
"missingUsernameEmail": "Missing Login Name or email.",
|
||||
"missingUsernameEmail": "Липсва потребителско име или е-поща.",
|
||||
"missingEmail": "Липсва е-поща.",
|
||||
"missingUsername": "Missing Login Name.",
|
||||
"missingUsername": "Липсва потребителско име.",
|
||||
"missingPassword": "Липсва парола.",
|
||||
"missingNewPassword": "Липсва нова парола.",
|
||||
"invalidEmailDomain": "Не можете да се регистрирате, използвайки е-поща от следните сървъри: <%= domains %>",
|
||||
@@ -256,7 +256,7 @@
|
||||
"notAnEmail": "Грешен адрес на е-поща.",
|
||||
"emailTaken": "Тази е-поща вече се използва от съществуващ профил.",
|
||||
"newEmailRequired": "Липсва нов адрес на е-поща.",
|
||||
"usernameTaken": "Login Name already taken.",
|
||||
"usernameTaken": "Потребителското име е заето.",
|
||||
"passwordConfirmationMatch": "Повторената парола не съвпада с първата.",
|
||||
"invalidLoginCredentials": "Грешно потребителско име и/или е-поща и/или парола.",
|
||||
"passwordResetPage": "Нулиране на паролата",
|
||||
@@ -275,41 +275,41 @@
|
||||
"heroIdRequired": "„heroId“ трябва да бъде правилно форматиран идентификатор UUID.",
|
||||
"cannotFulfillReq": "Заявката Ви не може да бъде изпълнена. Пишете на admin@habitica.com , ако тази грешка продължи да се повтаря.",
|
||||
"modelNotFound": "Този модел не съществува.",
|
||||
"signUpWithSocial": "Sign up with <%= social %>",
|
||||
"loginWithSocial": "Log in with <%= social %>",
|
||||
"confirmPassword": "Confirm Password",
|
||||
"usernamePlaceholder": "e.g., HabitRabbit",
|
||||
"emailPlaceholder": "e.g., rabbit@example.com",
|
||||
"passwordPlaceholder": "e.g., ******************",
|
||||
"confirmPasswordPlaceholder": "Make sure it's the same password!",
|
||||
"joinHabitica": "Join Habitica",
|
||||
"alreadyHaveAccountLogin": "Already have a Habitica account? <strong>Log in.</strong>",
|
||||
"dontHaveAccountSignup": "Don’t have a Habitica account? <strong>Sign up.</strong>",
|
||||
"motivateYourself": "Motivate yourself to achieve your goals.",
|
||||
"timeToGetThingsDone": "It's time to have fun when you get things done! Join over 2.5 million Habiticans and improve your life one task at a time.",
|
||||
"singUpForFree": "Sign Up For Free",
|
||||
"or": "OR",
|
||||
"gamifyYourLife": "Gamify Your Life",
|
||||
"aboutHabitica": "Habitica is a free habit-building and productivity app that treats your real life like a game. With in-game rewards and punishments to motivate you and a strong social network to inspire you, Habitica can help you achieve your goals to become healthy, hard-working, and happy.",
|
||||
"trackYourGoals": "Track Your Habits and Goals",
|
||||
"trackYourGoalsDesc": "Stay accountable by tracking and managing your Habits, Daily goals, and To-Do list with Habitica’s easy-to-use mobile apps and web interface.",
|
||||
"earnRewards": "Earn Rewards for Your Goals",
|
||||
"earnRewardsDesc": "Check off tasks to level up your Avatar and unlock in-game features such as battle armor, mysterious pets, magic skills, and even quests!",
|
||||
"battleMonsters": "Battle Monsters with Friends",
|
||||
"battleMonstersDesc": "Fight monsters with other Habiticans! Use the Gold that you earn to buy in-game or custom rewards, like watching an episode of your favorite TV show.",
|
||||
"playersUseToImprove": "Players Use Habitica to Improve",
|
||||
"healthAndFitness": "Health and Fitness",
|
||||
"healthAndFitnessDesc": "Never motivated to floss? Can't seem to get to the gym? Habitica finally makes it fun to get healthy.",
|
||||
"schoolAndWork": "School and Work",
|
||||
"schoolAndWorkDesc": "Whether you're preparing a report for your teacher or your boss, it's easy to keep track of your progress as you tackle your toughest tasks.",
|
||||
"muchmuchMore": "And much, much more!",
|
||||
"muchmuchMoreDesc": "Our fully customizable task list means that you can shape Habitica to fit your personal goals. Work on creative projects, emphasize self-care, or pursue a different dream -- it's all up to you.",
|
||||
"levelUpAnywhere": "Level Up Anywhere",
|
||||
"levelUpAnywhereDesc": "Our mobile apps make it simple to keep track of your tasks on-the-go. Accomplish your goals with a single tap, no matter where you are.",
|
||||
"joinMany": "Join over 2,000,000 people having fun while accomplishing their goals!",
|
||||
"joinToday": "Join Habitica Today",
|
||||
"signup": "Sign Up",
|
||||
"getStarted": "Get Started",
|
||||
"mobileApps": "Mobile Apps",
|
||||
"learnMore": "Learn More"
|
||||
"signUpWithSocial": "Регистрирайте се чрез <%= social %>",
|
||||
"loginWithSocial": "Влезте чрез <%= social %>",
|
||||
"confirmPassword": "Повторете паролата",
|
||||
"usernamePlaceholder": "Например: HabitRabbit",
|
||||
"emailPlaceholder": "Например: име@пример.сървър",
|
||||
"passwordPlaceholder": "Например: ******************",
|
||||
"confirmPasswordPlaceholder": "Уверете се, че паролата е същата!",
|
||||
"joinHabitica": "Присъединете се в Хабитика",
|
||||
"alreadyHaveAccountLogin": "Вече имате регистрация? <strong>Влезте.</strong>",
|
||||
"dontHaveAccountSignup": "Нямате регистрация? <strong>Регистрирайте се.</strong>",
|
||||
"motivateYourself": "Мотивирайте се, за да изпълните целите си.",
|
||||
"timeToGetThingsDone": "Време е да започнете да се забавлявате, докато вършите нещата си! Присъединете се към над 2,5 милиона хабитиканци и подобрете живота си, задача по задача.",
|
||||
"singUpForFree": "Регистрирайте се безплатно",
|
||||
"or": "ИЛИ",
|
||||
"gamifyYourLife": "Превърнете живота си в игра",
|
||||
"aboutHabitica": "Хабитика е безплатно приложение за продуктивност и изграждане на навици, което превръща живота Ви в игра. Хабитика Ви помага да постигнете целите си и да живеете по-здравословно, да работите по-концентрирано и да сте по-щастлив(а) като Ви мотивира чрез награди и наказания и Ви осигурява силна общност, която да Ви насърчава.",
|
||||
"trackYourGoals": "Следете навиците и целите си",
|
||||
"trackYourGoalsDesc": "Поддържайте отговорността си, като следите и управлявате своите навици, ежедневни задачи и списък със задачи за изпълнение чрез удобния уеб сайт и приложенията на Хабитика за мобилни устройства.",
|
||||
"earnRewards": "Печелете награди за изпълнението на целите си",
|
||||
"earnRewardsDesc": "Отмятайте задачите си, за да повишавате нивото на героя си и да отключвате игрални функционалности, като бойно снаряжение, загадъчни любимци, вълшебни умения и дори мисии!",
|
||||
"battleMonsters": "Бийте се с чудовища заедно с приятелите си",
|
||||
"battleMonstersDesc": "Бийте се с чудовища заедно с другите хабитиканци! Използвайте спечеленото злато, за да си купувате игрални или персонални награди, като например правото да гледате епизод от любимия си сериал.",
|
||||
"playersUseToImprove": "Играчите използват Хабитика, за да подобряват",
|
||||
"healthAndFitness": "Здравословно състояние",
|
||||
"healthAndFitnessDesc": "Не Ви се ще да използвате конеца за зъби? Мързи Ви да отидете до фитнеса? Хабитика ще направи подобряването на здравословното Ви състояние по-забавно.",
|
||||
"schoolAndWork": "Училище и работа",
|
||||
"schoolAndWorkDesc": "Независимо дали подготвяте домашно или доклад за работата, е лесно да следите напредъка си, докато се борите с най-трудните задачи.",
|
||||
"muchmuchMore": "И още много!",
|
||||
"muchmuchMoreDesc": "Списъкът със задачи може да бъде напълно персонализиран и да оформи Хабитика според Вашите лични цели. Работете по творчески проекти, концентрирайте се върху себе си, или следвайте мечтата си – всичко зависи от Вас.",
|
||||
"levelUpAnywhere": "Повишавайте нивото си отвсякъде",
|
||||
"levelUpAnywhereDesc": "С мобилните ни приложения е лесно да проследявате задачите си където и да се намирате. Изпълнявайте целите си с едно докосване, без значение къде се намирате.",
|
||||
"joinMany": "Над 2 000 000 хора се забавляват, докато подобряват живота си. Присъединете се към тях!",
|
||||
"joinToday": "Присъединете се в Хабитика днес",
|
||||
"signup": "Регистриране",
|
||||
"getStarted": "Първи стъпки",
|
||||
"mobileApps": "Мобилни приложения",
|
||||
"learnMore": "Научете повече"
|
||||
}
|
||||
@@ -13,10 +13,10 @@
|
||||
"noGearItemsOfClass": "Вече имате всичката възможна класова екипировка! Още ще стане налична по време на големите празненства, около слънцестоенията и равноденствията.",
|
||||
"sortByType": "Тип",
|
||||
"sortByPrice": "Цена",
|
||||
"sortByCon": "CON",
|
||||
"sortByPer": "PER",
|
||||
"sortByStr": "STR",
|
||||
"sortByInt": "INT",
|
||||
"sortByCon": "ЯКО",
|
||||
"sortByPer": "УСЕ",
|
||||
"sortByStr": "СИЛ",
|
||||
"sortByInt": "ИНТ",
|
||||
"weapon": "оръжие",
|
||||
"weaponCapitalized": "Предмет за основната ръка",
|
||||
"weaponBase0Text": "Няма оръжие",
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
"titleIndex": "Хабитика | Животът Ви, превърнат в ролева игра",
|
||||
"habitica": "Хабитика",
|
||||
"habiticaLink": "<a href='http://habitica.wikia.com/wiki/Habitica' target='_blank'>Хабитика</a>",
|
||||
"onward": "Onward!",
|
||||
"done": "Done",
|
||||
"gotIt": "Got it!",
|
||||
"onward": "Напред!",
|
||||
"done": "Готово",
|
||||
"gotIt": "Разбрано!",
|
||||
"titleTasks": "Задачи",
|
||||
"titleAvatar": "Герой",
|
||||
"titleBackgrounds": "Фонови изображения",
|
||||
@@ -28,9 +28,9 @@
|
||||
"titleTimeTravelers": "Пътешественици във времето",
|
||||
"titleSeasonalShop": "Сезонен магазин",
|
||||
"titleSettings": "Настройки",
|
||||
"saveEdits": "Save Edits",
|
||||
"showMore": "Show More",
|
||||
"showLess": "Show Less",
|
||||
"saveEdits": "Запазване на промените",
|
||||
"showMore": "Показване на още",
|
||||
"showLess": "Показване на по-малко",
|
||||
"expandToolbar": "Разширяване на лентата",
|
||||
"collapseToolbar": "Свиване на лентата",
|
||||
"markdownBlurb": "Хабитика използва синтаксиса „markdown“ за форматиране на съобщенията. За повече информация, прегледайте <a href='http://habitica.wikia.com/wiki/Markdown_Cheat_Sheet' target='_blank'>помощната страница за използване на „markdown“</a>.",
|
||||
@@ -85,8 +85,8 @@
|
||||
"gemsPopoverTitle": "Диаманти",
|
||||
"gems": "Диаманти",
|
||||
"gemButton": "Имате <%= number %> диаманта.",
|
||||
"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!",
|
||||
"needMoreGems": "Нуждаете се от още диаманти?",
|
||||
"needMoreGemsInfo": "Купете диаманти сега, или станете абонат, за да можете да купувате диаманти със злато, да получавате тайнствени предмети, да имате по-високи ограничения за падания на предмети и още!",
|
||||
"moreInfo": "Повече информация",
|
||||
"moreInfoChallengesURL": "http://habitica.wikia.com/wiki/Challenges",
|
||||
"moreInfoTagsURL": "http://habitica.wikia.com/wiki/Tags",
|
||||
@@ -189,7 +189,7 @@
|
||||
"birthdayCardAchievementTitle": "Благополучие на рождения ден",
|
||||
"birthdayCardAchievementText": "Да Ви се връща! Изпратил(а) или получил(а) <%= count %> картички за рожден ден.",
|
||||
"congratsCard": "Поздравителна картичка",
|
||||
"congratsCardExplanation": "You both receive the Congratulatory Companion achievement!",
|
||||
"congratsCardExplanation": "И двамата получихте постижението „Поздравителен спътник“!",
|
||||
"congratsCardNotes": "Изпратете поздравителна картичка до член на групата.",
|
||||
"congrats0": "Поздравления за успеха!",
|
||||
"congrats1": "Гордея се с теб!",
|
||||
@@ -199,7 +199,7 @@
|
||||
"congratsCardAchievementTitle": "Поздравителен спътник",
|
||||
"congratsCardAchievementText": "Страхотно е да празнуваш постиженията на приятелите си! Изпратени или получени поздравителни картички: <%= count %>.",
|
||||
"getwellCard": "Картичка за скорошно оздравяване",
|
||||
"getwellCardExplanation": "You both receive the Caring Confidant achievement!",
|
||||
"getwellCardExplanation": "И двамата получихте постижението „Грижовен довереник“!",
|
||||
"getwellCardNotes": "Изпратете картичка за скорошно оздравяване на член на групата.",
|
||||
"getwell0": "Надявам се скоро да оздравееш!",
|
||||
"getwell1": "Оправяй се! <3",
|
||||
@@ -234,43 +234,43 @@
|
||||
"onlineCount": "<%= count %> на линия",
|
||||
"loading": "Зареждане…",
|
||||
"userIdRequired": "Нужен е потребителски идентификатор",
|
||||
"resetFilters": "Clear all filters",
|
||||
"applyFilters": "Apply Filters",
|
||||
"categories": "Categories",
|
||||
"habiticaOfficial": "Habitica Official",
|
||||
"animals": "Animals",
|
||||
"artDesign": "Art & Design",
|
||||
"booksWriting": "Books & Writing",
|
||||
"comicsHobbies": "Comics & Hobbies",
|
||||
"diyCrafts": "DIY & Crafts",
|
||||
"education": "Education",
|
||||
"foodCooking": "Food & Cooking",
|
||||
"healthFitness": "Health & Fitness",
|
||||
"music": "Music",
|
||||
"relationship": "Relationships",
|
||||
"scienceTech": "Science & Technology",
|
||||
"exercise": "Exercise",
|
||||
"creativity": "Creativity",
|
||||
"budgeting": "Budgeting",
|
||||
"health_wellness": "Health & Wellness",
|
||||
"self_care": "Self-Care",
|
||||
"habitica_official": "Habitica Official",
|
||||
"academics": "Academics",
|
||||
"advocacy_causes": "Advocacy + Causes",
|
||||
"entertainment": "Entertainment",
|
||||
"finance": "Finance",
|
||||
"health_fitness": "Health + Fitness",
|
||||
"hobbies_occupations": "Hobbies + Occupations",
|
||||
"location_based": "Location-based",
|
||||
"mental_health": "Mental Health + Self-Care",
|
||||
"getting_organized": "Getting Organized",
|
||||
"self_improvement": "Self-Improvement",
|
||||
"spirituality": "Spirituality",
|
||||
"time_management": "Time-Management + Accountability",
|
||||
"recovery_support_groups": "Recovery + Support Groups",
|
||||
"messages": "Messages",
|
||||
"emptyMessagesLine1": "You don't have any messages",
|
||||
"emptyMessagesLine2": "Send a message to start a conversation!",
|
||||
"letsgo": "Let's Go!",
|
||||
"selected": "Selected"
|
||||
"resetFilters": "Изчистване на всички филтри",
|
||||
"applyFilters": "Прилагане на филтрите",
|
||||
"categories": "Категории",
|
||||
"habiticaOfficial": "Хабитика – официално",
|
||||
"animals": "Животни",
|
||||
"artDesign": "Изкуство и дизайн",
|
||||
"booksWriting": "Книги и писане",
|
||||
"comicsHobbies": "Комикси и хобита",
|
||||
"diyCrafts": "Направи си сам и изработване",
|
||||
"education": "Образование",
|
||||
"foodCooking": "Храна и готвене",
|
||||
"healthFitness": "Здраве и тренировки",
|
||||
"music": "Музика",
|
||||
"relationship": "Връзки",
|
||||
"scienceTech": "Наука и технологии",
|
||||
"exercise": "Упражнения",
|
||||
"creativity": "Творчество",
|
||||
"budgeting": "Управление на бюджета",
|
||||
"health_wellness": "Здравословно състояние",
|
||||
"self_care": "Лична грижа",
|
||||
"habitica_official": "Хабитика – официално",
|
||||
"academics": "Академичност",
|
||||
"advocacy_causes": "Защита и обществени каузи",
|
||||
"entertainment": "Развлечение",
|
||||
"finance": "Финанси",
|
||||
"health_fitness": "Здраве и тренировки",
|
||||
"hobbies_occupations": "Хобита и работа",
|
||||
"location_based": "Според местоположението",
|
||||
"mental_health": "Душевно здраве и лична грижа",
|
||||
"getting_organized": "Самоорганизиране",
|
||||
"self_improvement": "Самоусъвършенстване",
|
||||
"spirituality": "Духовност",
|
||||
"time_management": "Управление на времето и отговорност",
|
||||
"recovery_support_groups": "Възстановяване и групи за подкрепа",
|
||||
"messages": "Съобщения",
|
||||
"emptyMessagesLine1": "Нямате съобщения",
|
||||
"emptyMessagesLine2": "Изпратете съобщение, за да започнете разговор",
|
||||
"letsgo": "Хойде!",
|
||||
"selected": "Избрано"
|
||||
}
|
||||
@@ -1,20 +1,20 @@
|
||||
{
|
||||
"tavern": "Чат на кръчмата",
|
||||
"tavernChat": "Tavern Chat",
|
||||
"tavernChat": "Чат на кръчмата",
|
||||
"innCheckOut": "Напускане на страноприемницата",
|
||||
"innCheckIn": "Почиване в страноприемницата",
|
||||
"innText": "Вие почивате в странноприемницата! Докато сте вътре, ежедневните Ви задачи няма да Ви нараняват в края на деня, но ще продължат да се опресняват всеки ден. Внимание: ако участвате в мисия срещу главатар, той ще Ви наранява, когато членовете на групата Ви не изпълняват ежедневните си задачи, освен ако и те не са в странноприемницата! Освен това, докато не напуснете странноприемницата, Вашите щети срещу главатаря няма да бъдат прилагани, както и няма да получите събраните си предмети.",
|
||||
"innTextBroken": "Вие почивате в странноприемницата, предполагам… Докато сте вътре, ежедневните Ви задачи няма да Ви нараняват в края на деня, но ще продължат да се опресняват всеки ден… Ако участвате в мисия срещу главатар, той ще Ви наранява, когато членовете на групата Ви не изпълняват ежедневните си задачи… освен ако и те не са в странноприемницата! Освен това, докато не напуснете странноприемницата, Вашите щети срещу главатаря няма да бъдат прилагани, както и няма да получите събраните си предмети… толкова съм уморен…",
|
||||
"helpfulLinks": "Helpful Links",
|
||||
"communityGuidelinesLink": "Community Guidelines",
|
||||
"lookingForGroup": "Looking for Group (Party Wanted) Posts",
|
||||
"dataDisplayTool": "Data Display Tool",
|
||||
"reportProblem": "Report a Bug",
|
||||
"requestFeature": "Request a Feature",
|
||||
"askAQuestion": "Ask a Question",
|
||||
"askQuestionGuild": "Ask a Question (Habitica Help guild)",
|
||||
"contributing": "Contributing",
|
||||
"faq": "FAQ",
|
||||
"helpfulLinks": "Полезни връзки",
|
||||
"communityGuidelinesLink": "Обществени правила",
|
||||
"lookingForGroup": "Публикации за търсене на група",
|
||||
"dataDisplayTool": "Инструмент за показване на данните",
|
||||
"reportProblem": "Докладване на проблем",
|
||||
"requestFeature": "Предлагане на функционалност",
|
||||
"askAQuestion": "Задаване на въпрос",
|
||||
"askQuestionGuild": "Задайте въпрос (помощна гилдия на Хабитика)",
|
||||
"contributing": "Принос",
|
||||
"faq": "ЧЗВ",
|
||||
"lfgPosts": "Публикации за търсене на група",
|
||||
"tutorial": "Инструктаж",
|
||||
"glossary": "<a target='_blank' href='http://habitica.wikia.com/wiki/Glossary'>Речник</a>",
|
||||
@@ -37,13 +37,13 @@
|
||||
"party": "Група",
|
||||
"createAParty": "Създаване на група",
|
||||
"updatedParty": "Настройките на групата бяха променени.",
|
||||
"errorNotInParty": "You are not in a Party",
|
||||
"errorNotInParty": "Не сте в група.",
|
||||
"noPartyText": "Или нямате група, или групата Ви изисква повече време за зареждане. Може да създадете нова група и да поканите приятелите си, или ако искате да се присъедините към съществуваща такава, изпратете им своя уникален потребителски идентификатор по-долу и след това се върнете тук, за да потърсите поканата:",
|
||||
"LFG": "За да рекламирате новата си група или да потърсите такава, към която да се присъедините, посетете гилдията <%= linkStart %>„Търсене на група“ (Looking for Group)<%= linkEnd %>.",
|
||||
"wantExistingParty": "Искате да се присъедините към съществуваща група? Посетете <%= linkStart %>гилдията „Търсене на група“ (Party Wanted Guild)<%= linkEnd %> и публикувайте потребителския си идентификатор:",
|
||||
"joinExistingParty": "Присъединете се към нечия друга група",
|
||||
"needPartyToStartQuest": "Опа! Трябва да <a href='http://habitica.wikia.com/wiki/Party' target='_blank'>създадете или да се присъедините към група</a>, преди да може да започнете мисия!",
|
||||
"createGroupPlan": "Create",
|
||||
"createGroupPlan": "Създаване",
|
||||
"create": "Създаване",
|
||||
"userId": "Потребителски идентификатор",
|
||||
"invite": "Покана",
|
||||
@@ -70,7 +70,7 @@
|
||||
"guildBankPop1": "Банка на гилдията",
|
||||
"guildBankPop2": "Диаманти, които водачът на гилдията може да използва за награди за предизвикателства.",
|
||||
"guildGems": "диаманти в гилдията",
|
||||
"group": "Group",
|
||||
"group": "Група",
|
||||
"editGroup": "Редактиране",
|
||||
"newGroupName": "Име (<%= groupType %>)",
|
||||
"groupName": "Име на групата",
|
||||
@@ -80,6 +80,7 @@
|
||||
"logoUrl": "Адрес на логото",
|
||||
"assignLeader": "Задаване на водач на групата",
|
||||
"members": "Членове",
|
||||
"memberList": "Списък на членовете",
|
||||
"partyList": "Подредба на членовете на групата",
|
||||
"banTip": "Изритване",
|
||||
"moreMembers": "още членове",
|
||||
@@ -93,7 +94,7 @@
|
||||
"search": "Търсене",
|
||||
"publicGuilds": "Обществени гилдии",
|
||||
"createGuild": "Създаване на гилдия",
|
||||
"createGuild2": "Create",
|
||||
"createGuild2": "Създаване",
|
||||
"guild": "Гилдия",
|
||||
"guilds": "Гилдии",
|
||||
"guildsLink": "<a href='http://habitica.wikia.com/wiki/Guilds' target='_blank'>Гилдии</a>",
|
||||
@@ -137,7 +138,7 @@
|
||||
"privateMessageGiftSubscriptionMessage": "<%= numberOfMonths %> месеца абонамент!",
|
||||
"cannotSendGemsToYourself": "Не можете да изпратите диаманти на себе си. Опитайте с абонамент.",
|
||||
"badAmountOfGemsToSend": "Стойността трябва да бъде между 1 и текущия Ви брой диаманти.",
|
||||
"report": "Report",
|
||||
"report": "Докладване",
|
||||
"abuseFlag": "Докладване на нарушение на Обществените правила",
|
||||
"abuseFlagModalHeading": "Докладване на <%= name %> за нарушение?",
|
||||
"abuseFlagModalBody": "Наистина ли искате да докладвате тази публикация? Трябва да докладвате САМО публикации, които нарушават <%= firstLinkStart %>Обществените правила<%= linkEnd %> и/или <%= secondLinkStart %>Условията на услугата<%= linkEnd %>. Неуместното докладване на публикация е нарушение на Обществените правила, и може да получите наказание. Правилните причина за докладване на публикация включват (но не се изчерпват):<br><br><ul style='margin-left: 10px;'><li>ругаене, религиозни клетви;</li><li>фанатизъм, обиди;</li><li>теми за възрастни;</li><li>насилие, дори и на шега;</li><li>нежелани или безсмислени съобщения.</li></ul>",
|
||||
@@ -147,7 +148,7 @@
|
||||
"needsText": "Моля, напишете съобщение.",
|
||||
"needsTextPlaceholder": "Въведете съобщението си тук.",
|
||||
"copyMessageAsToDo": "Копиране на съобщението като задача",
|
||||
"copyAsTodo": "Copy as To-Do",
|
||||
"copyAsTodo": "Копиране като задача за изпълнение",
|
||||
"messageAddedAsToDo": "Съобщението беше копирано като задача.",
|
||||
"messageWroteIn": "<%= user %> публикува нещо в <%= group %>",
|
||||
"taskFromInbox": "<%= from %> написа „<%= message %>“",
|
||||
@@ -159,7 +160,7 @@
|
||||
"partyMembersInfo": "В групата Ви в момента има <%= memberCount %> членове и още <%= invitationCount %> изпратени покани. Ограничението на броя на членовете в група е <%= limitMembers %>. След като достигнете това ограничение не може да изпращате повече покани.",
|
||||
"inviteByEmail": "Покана чрез е-поща",
|
||||
"inviteByEmailExplanation": "Ако приятел се присъедини към Хабитика през Вашето е-писмо, той автоматично ще бъде поканен в групата Ви!",
|
||||
"inviteMembersHowTo": "Invite people via a valid email or 36-digit User ID. If an email isn't registered yet, we'll invite them to join Habitica.",
|
||||
"inviteMembersHowTo": "Поканете хора чрез правилен адрес на е-поща или 36-цифров потребителски идентификатор. Ако е-пощата все още не е регистрирана, ние ще изпратим е-писмо с покана за присъединяване в Хабитика.",
|
||||
"inviteFriendsNow": "Поканете приятели сега",
|
||||
"inviteFriendsLater": "Поканете приятели по-късно",
|
||||
"inviteAlertInfo": "Ако имате приятели, които вече използват Хабитика, поканете ги чрез <a href='http://habitica.wikia.com/wiki/API_Options' target='_blank'>потребителски идентификатор</a> тук.",
|
||||
@@ -314,76 +315,80 @@
|
||||
"userMustBeMember": "Потребителят трябва да бъде член",
|
||||
"userIsNotManager": "Потребителят не е управител",
|
||||
"canOnlyApproveTaskOnce": "Тази задача е вече одобрена.",
|
||||
"addTaskToGroupPlan": "Create",
|
||||
"addTaskToGroupPlan": "Създаване",
|
||||
"leaderMarker": "— Водач",
|
||||
"managerMarker": "— Управител",
|
||||
"joinedGuild": "Присъединил(а) се към гилдия",
|
||||
"joinedGuildText": "Потопил(а) се в обществената част на Хабитика чрез присъединяване към гилдия!",
|
||||
"badAmountOfGemsToPurchase": "Стойността трябва да бъде поне 1.",
|
||||
"groupPolicyCannotGetGems": "Политиката на една от групите, в които членувате, не позволява на членовете си да получават диаманти.",
|
||||
"viewParty": "View Party",
|
||||
"newGuildPlaceholder": "Enter your guild's name.",
|
||||
"guildMembers": "Guild Members",
|
||||
"guildBank": "Guild Bank",
|
||||
"chatPlaceholder": "Type your message to Guild members here",
|
||||
"partyChatPlaceholder": "Type your message to Party members here",
|
||||
"fetchRecentMessages": "Fetch Recent Messages",
|
||||
"like": "Like",
|
||||
"liked": "Liked",
|
||||
"joinGuild": "Join Guild",
|
||||
"inviteToGuild": "Invite to Guild",
|
||||
"messageGuildLeader": "Message Guild Leader",
|
||||
"donateGems": "Donate Gems",
|
||||
"updateGuild": "Update Guild",
|
||||
"viewMembers": "View Members",
|
||||
"memberCount": "Member Count",
|
||||
"recentActivity": "Recent Activity",
|
||||
"myGuilds": "My Guilds",
|
||||
"guildsDiscovery": "Discover Guilds",
|
||||
"guildOrPartyLeader": "Leader",
|
||||
"guildLeader": "Guild Leader",
|
||||
"member": "Member",
|
||||
"goldTier": "Gold Tier",
|
||||
"silverTier": "Silver Tier",
|
||||
"bronzeTier": "Bronze Tier",
|
||||
"privacySettings": "Privacy Settings",
|
||||
"onlyLeaderCreatesChallenges": "Only the Leader can create Challenges",
|
||||
"privateGuild": "Private Guild",
|
||||
"charactersRemaining": "characters remaining",
|
||||
"guildSummary": "Summary",
|
||||
"guildSummaryPlaceholder": "Write a short description advertising your Guild to other Habiticans. What is the main purpose of your Guild and why should people join it? Try to include useful keywords in the summary so that Habiticans can easily find it when they search!",
|
||||
"groupDescription": "Description",
|
||||
"guildDescriptionPlaceholder": "Use this section to go into more detail about everything that Guild members should know about your Guild. Useful tips, helpful links, and encouraging statements all go here!",
|
||||
"markdownFormattingHelp": "Markdown formatting help",
|
||||
"partyDescriptionPlaceholder": "This is our party's description. It describes what we do in this party. If you want to learn more about what we do in this party, read the description. Party on.",
|
||||
"guildGemCostInfo": "A Gem cost promotes high quality Guilds and is transferred into your Guild's bank.",
|
||||
"noGuildsTitle": "You aren't a member of any Guilds.",
|
||||
"noGuildsParagraph1": "Guilds are social groups created by other players that can offer you support, accountability, and encouraging chat.",
|
||||
"noGuildsParagraph2": "Click the Discover tab to see recommended Guilds based on your interests, browse Habitica's public Guilds, or create your own Guild.",
|
||||
"privateDescription": "A private Guild will not be displayed in Habitica's Guild directory. New members can be added by invitation only.",
|
||||
"removeMember": "Remove Member",
|
||||
"sendMessage": "Send Message",
|
||||
"removeManager2": "Remove Manager",
|
||||
"promoteToLeader": "Promote to Leader",
|
||||
"inviteFriendsParty": "Inviting friends to your party will grant you an exclusive <br/> Quest Scroll to battle the Basi-List together!",
|
||||
"upgradeParty": "Upgrade Party",
|
||||
"createParty": "Create a Party",
|
||||
"inviteMembersNow": "Would you like to invite members now?",
|
||||
"playInPartyTitle": "Play Habitica in a Party!",
|
||||
"playInPartyDescription": "Take on amazing quests with friends or on your own. Battle monsters, create Challenges, and help yourself stay accountable through Parties.",
|
||||
"startYourOwnPartyTitle": "Start your own Party",
|
||||
"startYourOwnPartyDescription": "Battle monsters solo or invite as many of your friends as you'd like!",
|
||||
"shartUserId": "Share User ID",
|
||||
"wantToJoinPartyTitle": "Want to join a Party?",
|
||||
"wantToJoinPartyDescription": "Give your User ID to a friend who already has a Party, or head to the Party Wanted Guild to meet potential comrades!",
|
||||
"copy": "Copy",
|
||||
"inviteToPartyOrQuest": "Invite Party to Quest",
|
||||
"inviteInformation": "Clicking \"Invite\" will send an invitation to your party members. When all members have accepted or denied, the Quest begins.",
|
||||
"questOwnerRewards": "Quest Owner Rewards",
|
||||
"updateParty": "Update Party",
|
||||
"upgrade": "Upgrade",
|
||||
"selectPartyMember": "Select a Party Member",
|
||||
"areYouSureDeleteMessage": "Are you sure you want to delete this message?",
|
||||
"reverseChat": "Reverse Chat",
|
||||
"invites": "Invites"
|
||||
"viewParty": "Преглед на групата",
|
||||
"newGuildPlaceholder": "Въведете името на гилдията си.",
|
||||
"guildMembers": "Членове на гилдията",
|
||||
"guildBank": "Банка на гилдията",
|
||||
"chatPlaceholder": "Въведете съобщението си до членовете на гилдията тук.",
|
||||
"partyChatPlaceholder": "Въведете съобщението си до членовете на групата тук.",
|
||||
"fetchRecentMessages": "Изтегляне на последните съобщения",
|
||||
"like": "Харесвам",
|
||||
"liked": "Харесано",
|
||||
"joinGuild": "Присъединяване към гилдията",
|
||||
"inviteToGuild": "Покана в гилдията",
|
||||
"messageGuildLeader": "Съобщение до водача на гилдията",
|
||||
"donateGems": "Даряване на диаманти",
|
||||
"updateGuild": "Обновяване на гилдията",
|
||||
"viewMembers": "Преглед на членовете",
|
||||
"memberCount": "Брой на членовете",
|
||||
"recentActivity": "Последна дейност",
|
||||
"myGuilds": "Моите гилдии",
|
||||
"guildsDiscovery": "Разглеждане на гилдиите",
|
||||
"guildOrPartyLeader": "Водач",
|
||||
"guildLeader": "Водач на гилдията",
|
||||
"member": "Член",
|
||||
"goldTier": "Златно ниво",
|
||||
"silverTier": "Сребърно ниво",
|
||||
"bronzeTier": "Бронзово ниво",
|
||||
"privacySettings": "Настройки за поверителността",
|
||||
"onlyLeaderCreatesChallenges": "Само водачът може да създава предизвикателства",
|
||||
"privateGuild": "Частна гилдия",
|
||||
"charactersRemaining": "оставащи знака",
|
||||
"guildSummary": "Резюме",
|
||||
"guildSummaryPlaceholder": "Напишете кратко описание, което да представи гилдията Ви на другите хабитиканци. Каква е основната цел на гилдията Ви и защо хората биха искали да се присъединят? Опитайте се да използвате в резюмето ключови думи, така че хабитиканците да могат лесно да го открият при търсене!",
|
||||
"groupDescription": "Описание",
|
||||
"guildDescriptionPlaceholder": "Използвайте този раздел, за да добавите повече подробности за всичко, което членовете на гилдията трябва да знаят за нея. Тук можете да добавите и полезни съвети и връзки, както и вдъхновяващи лозунги!",
|
||||
"markdownFormattingHelp": "Помощ за форматирането чрез „Markdown“",
|
||||
"partyDescriptionPlaceholder": "Това е описанието на групата ни. То описва какво правим в тази група. Ако искате да научите повече относно това какво правим в тази група, прочетете описанието.",
|
||||
"guildGemCostInfo": "Цената за създаването на гилдия подсигурява качеството на гилдиите, а диамантите се прехвърлят в банката на гилдията.",
|
||||
"noGuildsTitle": "Не членувате в нито една гилдия.",
|
||||
"noGuildsParagraph1": "Гилдиите са обществени групи създадени от другите играчи, които могат да Ви помогнат, да поддържат отговорността Ви, както и да бъдат място за насърчаващи разговори.",
|
||||
"noGuildsParagraph2": "Отворете раздела за разглеждане, за да открите препоръчани за Вас гилдии според интересите Ви, разгледайте обществените гилдии на Хабитика, или създайте своя собствена гилдия.",
|
||||
"privateDescription": "Частните гилдии не се показват в списъка с гилдии на Хабитика. Нови членове могат да бъдат добавени само чрез покана.",
|
||||
"removeMember": "Премахване на члена",
|
||||
"sendMessage": "Изпращане на съобщение",
|
||||
"removeManager2": "Премахване на управителя",
|
||||
"promoteToLeader": "Повишаване във водач",
|
||||
"inviteFriendsParty": "Ако поканите приятели в групата си, ще получите изключителния <br/> свитък с мисията да се биете заедно срещу Василисъка!",
|
||||
"upgradeParty": "Надграждане на групата",
|
||||
"createParty": "Създаване на група",
|
||||
"inviteMembersNow": "Искате ли да поканите членове сега?",
|
||||
"playInPartyTitle": "Играйте Хабитика в група!",
|
||||
"playInPartyDescription": "Изпълнявайте невероятни мисии заедно с приятели или самостоятелно. Бийте се с чудовища, създавайте предизвикателства и поддържайте отговорността си чрез групите.",
|
||||
"startYourOwnPartyTitle": "Създайте своя група",
|
||||
"startYourOwnPartyDescription": "Бийте се с чудовища самостоятелно, или поканете колкото искате приятели!",
|
||||
"shartUserId": "Споделяне на потр. ид.",
|
||||
"wantToJoinPartyTitle": "Искате да се присъедините към група?",
|
||||
"wantToJoinPartyDescription": "Дайте своя потребителски идентификатор на приятел, който вече има група, или се отправете към гилдията „Търсене на група“ (Party Wanted), за да се срещнете с потенциални другари!",
|
||||
"copy": "Копиране",
|
||||
"inviteToPartyOrQuest": "Канене на групата за мисия",
|
||||
"inviteInformation": "Ако щракнете върху „Поканване“, ще изпратите покани до всички членове на групата. Мисията започва, когато всички приемат или отхвърлят поканата си.",
|
||||
"questOwnerRewards": "Надгради на притежателя на мисията",
|
||||
"updateParty": "Обновяване на групата",
|
||||
"upgrade": "Надграждане",
|
||||
"selectPartyMember": "Изберете член на групата",
|
||||
"areYouSureDeleteMessage": "Наистина ли искате да изтриете това съобщение?",
|
||||
"reverseChat": "Обръщане на разговора",
|
||||
"invites": "Покани",
|
||||
"details": "Подробности",
|
||||
"participantDesc": "След като всички членове са приели или отказали, мисията започва. Само онези, които са натиснали „Приемам“, могат да участват в мисията и да им се падат предмети.",
|
||||
"groupGems": "Групови диаманти",
|
||||
"groupGemsDesc": "Диамантите на гилдията могат да бъдат използвани за създаване на предизвикателства! В бъдеще ще можете да добавяте още диаманти в гилдията."
|
||||
}
|
||||
@@ -108,15 +108,16 @@
|
||||
"summer2017WhirlpoolMageSet": "Водовъртежен магьосник (магьосник)",
|
||||
"summer2017SeashellSeahealerSet": "Миден лечител (лечител)",
|
||||
"summer2017SeaDragonSet": "Морски дракон (мошеник)",
|
||||
"fall2017HabitoweenSet": "Habitoween Warrior (Warrior)",
|
||||
"fall2017MasqueradeSet": "Masquerade Mage (Mage)",
|
||||
"fall2017HauntedHouseSet": "Haunted House Healer (Healer)",
|
||||
"fall2017TrickOrTreatSet": "Trick or Treat Rogue (Rogue)",
|
||||
"fall2017HabitoweenSet": "Хабитоуински воин (воин)",
|
||||
"fall2017MasqueradeSet": "Маскараден магьосник (магьосник)",
|
||||
"fall2017HauntedHouseSet": "Прокълнат къщен лечител (лечител)",
|
||||
"fall2017TrickOrTreatSet": "Мошеник на лакомствата и пакостите (мошеник)",
|
||||
"eventAvailability": "Налично за купуване до <%= date(locale) %>.",
|
||||
"dateEndApril": "19 април",
|
||||
"dateEndMay": "17 май",
|
||||
"dateEndJune": "14 юни",
|
||||
"dateEndJuly": "29 юли",
|
||||
"dateEndAugust": "31 август",
|
||||
"dateEndOctober": "31 октомври",
|
||||
"discountBundle": "пакет"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"unlockedReward": "Получихте <%= reward %>",
|
||||
"earnedRewardForDevotion": "Получихте <%= reward %> за усърдието си в подобряването на живота Ви.",
|
||||
"earnedRewardForDevotion": "Получихте <%= reward %> за усърдието си в подобряването на живота си.",
|
||||
"nextRewardUnlocksIn": "Брой отчитания до следващата награда: <%= numberOfCheckinsLeft %>",
|
||||
"awesome": "Страхотно!",
|
||||
"totalCount": "Общо: <%= count %>",
|
||||
@@ -10,7 +10,7 @@
|
||||
"checkinEarned": "Броячът на отчитанията Ви се увеличи!",
|
||||
"unlockedCheckInReward": "Отключихте награда за отчитане!",
|
||||
"totalCheckinsTitle": "Общ брой отчитания",
|
||||
"checkinProgressTitle": "Напредък до следващата",
|
||||
"checkinProgressTitle": "Напредък до следващата награда",
|
||||
"incentiveBackgroundsUnlockedWithCheckins": "Заключените едноцветни фонови изображения ще се отключат чрез ежедневните Ви отчитания.",
|
||||
"checkinReceivedAllRewardsMessage": "Получили сте всички награди за отчитане! Поздравления!",
|
||||
"oneOfAllPetEggs": "по един брой от всяко обикновено яйце за любимец",
|
||||
|
||||
@@ -21,9 +21,9 @@
|
||||
"messageNotEnoughGold": "Няма достатъчно злато",
|
||||
"messageTwoHandedEquip": "Използването на <%= twoHandedText %> изисква две ръце, така че разекипирахте предмета <%= offHandedText %>.",
|
||||
"messageTwoHandedUnequip": "Използването на <%= twoHandedText %> изисква две ръце, така че го/я разекипирахте, когато се въоръжихте с <%= offHandedText %>.",
|
||||
"messageDropFood": "You've found <%= dropArticle %><%= dropText %>!",
|
||||
"messageDropEgg": "You've found a <%= dropText %> Egg!",
|
||||
"messageDropPotion": "You've found a <%= dropText %> Hatching Potion!",
|
||||
"messageDropFood": "Намерихте <%= dropArticle %><%= dropText %>!",
|
||||
"messageDropEgg": "Намерихте яйце на <%= dropText %>!",
|
||||
"messageDropPotion": "Намерихте излюпваща отвара с(ъс) <%= dropText %>!",
|
||||
"messageDropQuest": "Намерихте мисия!",
|
||||
"messageDropMysteryItem": "Отваряте кутията и намирате <%= dropText %>!",
|
||||
"messageFoundQuest": "Намерихте мисията „<%= questText %>“!",
|
||||
@@ -37,7 +37,7 @@
|
||||
"messageInsufficientGems": "Нямате достатъчно диаманти!",
|
||||
"messageAuthPasswordMustMatch": ":password и :confirmPassword не съвпадат",
|
||||
"messageAuthCredentialsRequired": ":username, :email, :password и :confirmPassword са задължителни",
|
||||
"messageAuthUsernameTaken": "Login Name already taken",
|
||||
"messageAuthUsernameTaken": "Потребителското име е заето",
|
||||
"messageAuthEmailTaken": "Е-пощата вече се използва",
|
||||
"messageAuthNoUserFound": "Потребителят не е намерен.",
|
||||
"messageAuthMustBeLoggedIn": "Трябва да сте влезли в системата.",
|
||||
|
||||
@@ -2,30 +2,30 @@
|
||||
"npc": "Компютърен персонаж",
|
||||
"npcAchievementName": "Компютърен персонаж — <%= key %>",
|
||||
"npcAchievementText": "Подкрепил кампанията в Kickstarter чрез най-високото ниво!",
|
||||
"welcomeTo": "Welcome to",
|
||||
"welcomeBack": "Welcome back!",
|
||||
"welcomeTo": "Добре дошли в",
|
||||
"welcomeBack": "Добре дошли отново!",
|
||||
"justin": "Джъстин",
|
||||
"justinIntroMessage1": "Hello there! You must be new here. My name is Justin, your guide to Habitica.",
|
||||
"justinIntroMessage2": "To start, you'll need to create an avatar.",
|
||||
"justinIntroMessage3": "Great! Now, what are you interested in working on throughout this journey?",
|
||||
"introTour": "Here we are! I've filled out some Tasks for you based on your interests, so you can get started right away. Click a Task to edit or add new Tasks to fit your routine!",
|
||||
"prev": "Prev",
|
||||
"next": "Next",
|
||||
"randomize": "Randomize",
|
||||
"justinIntroMessage1": "Здравейте! Не съм Ви виждал досега. Името ми е Джъстин и аз ще Ви помогна да се ориентирате в Хабитика.",
|
||||
"justinIntroMessage2": "За начало ще трябва да си създадете герой.",
|
||||
"justinIntroMessage3": "Чудесно! А сега, върху какво искате да работите по време на пътешествието си?",
|
||||
"introTour": "Готово! Аз попълних някои задачи с оглед на интересите Ви, така че можете да започнете веднага. Щракнете върху задача, за да я редактирате, или добавете нови задачи според желанията си!",
|
||||
"prev": "Назад",
|
||||
"next": "Напред",
|
||||
"randomize": "Разбъркване",
|
||||
"mattBoch": "Мат Бош",
|
||||
"mattShall": "Да изкарам ли жребеца Ви, <%= name %>? След като нахраните един любимец достатъчно, за да се превърне в превоз, той ще се появи тук. Щракнете някой превоз, за да го оседлаете!",
|
||||
"mattBochText1": "Добре дошли в конюшнята! Аз съм Мат, господарят на зверовете. След като достигнете ниво 3, ще започнете да получавате яйца и излюпващи отвари, с които да си излюпите любимци. Когато излюпите любимец на пазара, той ще се появи тук! Щракнете върху изображението на любимец, за да го добавите към героя си. Хранете любимците с храната, която намирате след достигане на ниво 3 и те ще се превърнат в силни превози.",
|
||||
"welcomeToTavern": "Welcome to The Tavern!",
|
||||
"sleepDescription": "Need a break? Check into Daniel's Inn to pause some of Habitica's more difficult game mechanics:",
|
||||
"sleepBullet1": "Missed Dailies won't damage you",
|
||||
"sleepBullet2": "Tasks won't lose streaks or decay in color",
|
||||
"sleepBullet3": "Bosses won't do damage for your missed Dailies",
|
||||
"sleepBullet4": "Your boss damage or collection Quest items will stay pending until check-out",
|
||||
"pauseDailies": "Pause Damage",
|
||||
"unpauseDailies": "Unpause Damage",
|
||||
"staffAndModerators": "Staff and Moderators",
|
||||
"communityGuidelinesIntro": "Habitica tries to create a welcoming environment for users of all ages and backgrounds, especially in public spaces like the Tavern. If you have any questions, please consult our <a href='/static/community-guidelines' target='_blank'>Community Guidelines</a>.",
|
||||
"acceptCommunityGuidelines": "I agree to follow the Community Guidelines",
|
||||
"welcomeToTavern": "Добре дошли в кръчмата!",
|
||||
"sleepDescription": "Нуждаете се от почивка? Идете в странноприемницата на Даниел, за да спрете някои от по-трудните механики на Хабитика:",
|
||||
"sleepBullet1": "Пропуснатите ежедневни задачи няма да Ви нараняват",
|
||||
"sleepBullet2": "Задачите няма да губят серията си или да променят цвета си",
|
||||
"sleepBullet3": "Главатарите няма да Ви нанасят щети заради пропуснатите ежедневни задачи",
|
||||
"sleepBullet4": "Щетите Ви срещу главатар и предметите от събираческите мисии ще Ви чакат",
|
||||
"pauseDailies": "Спиране на щетите",
|
||||
"unpauseDailies": "Продължаване на щетите",
|
||||
"staffAndModerators": "Екип и модератори",
|
||||
"communityGuidelinesIntro": "Хабитика се опитва да бъде приятна среда за потребители от всички възрасти и произход, особено в обществени места като кръчмата. Ако имате въпроси, моля, прегледайте <a href='/static/community-guidelines' target='_blank'>Обществените правила</a>.",
|
||||
"acceptCommunityGuidelines": "Съгласявам се да следвам Обществените правила",
|
||||
"daniel": "Даниел",
|
||||
"danielText": "Добре дошли в кръчмата! Останете и опознайте местните. Ако искате да си починете (почивка?, болест?), ще Ви настаня в странноприемницата. Докато сте там, ежедневните Ви задачи няма да Ви нараняват в края на деня, но все пак ще можете да ги отмятате.",
|
||||
"danielText2": "Внимание: ако участвате в мисия срещу главатар, той ще Ви наранява, когато членовете на групата Ви не изпълняват ежедневните си задачи! Освен това, докато не напуснете странноприемницата, Вашите щети срещу главатаря (или събраните предмети) няма да бъдат взимани под внимание.",
|
||||
@@ -33,45 +33,45 @@
|
||||
"danielText2Broken": "О… ако участвате в мисия срещу главатар, той ще Ви наранява, когато членовете на групата Ви не изпълняват ежедневните си задачи… Освен това, докато не напуснете странноприемницата, Вашите щети срещу главатаря няма да бъдат прилагани, както и няма да получите събраните си предмети…",
|
||||
"alexander": "Александър Търговеца",
|
||||
"welcomeMarket": "Добре дошли на пазара! Купувайте трудни за намиране яйца и отвари! Продавайте ненужното! Възползвайте се от полезните ни услуги! Елате и вижте какво предлагаме.",
|
||||
"welcomeMarketMobile": "Welcome to the Market! Buy hard-to-find eggs and potions! Come see what we have to offer.",
|
||||
"welcomeMarketMobile": "Добре дошли на пазара! Купувайте трудни за намиране яйца и отвари! Елате и вижте какво предлагаме.",
|
||||
"displayItemForGold": "Искате ли да продадете <strong><%= itemType %></strong>?",
|
||||
"displayEggForGold": "Искате ли да продадете <strong>яйце на <%= itemType %></strong>?",
|
||||
"displayPotionForGold": "Искате ли да продадете <strong>отвара с <%= itemType %></strong>?",
|
||||
"sellForGold": "Продаване за <%= gold %> злато",
|
||||
"howManyToSell": "How many would you like to sell?",
|
||||
"yourBalance": "Your balance",
|
||||
"sell": "Sell",
|
||||
"buyNow": "Buy Now",
|
||||
"sortByNumber": "Number",
|
||||
"featuredItems": "Featured Items!",
|
||||
"hideLocked": "Hide locked",
|
||||
"hidePinned": "Hide pinned",
|
||||
"amountExperience": "<%= amount %> Experience",
|
||||
"amountGold": "<%= amount %> Gold",
|
||||
"namedHatchingPotion": "<%= type %> Hatching Potion",
|
||||
"howManyToSell": "Колко искате да продадете?",
|
||||
"yourBalance": "Вашата наличност",
|
||||
"sell": "Продаване",
|
||||
"buyNow": "Купуване сега",
|
||||
"sortByNumber": "Брой",
|
||||
"featuredItems": "Препоръчани предмети!",
|
||||
"hideLocked": "Скриване на заключените",
|
||||
"hidePinned": "Скриване на закачените",
|
||||
"amountExperience": "<%= amount %> точки опит",
|
||||
"amountGold": "<%= amount %> злато",
|
||||
"namedHatchingPotion": "Излюпваща отвара с(ъс) <%= type %>",
|
||||
"buyGems": "Купуване на диаманти",
|
||||
"purchaseGems": "Купуване на диаманти",
|
||||
"items": "Items",
|
||||
"AZ": "A-Z",
|
||||
"sort": "Sort",
|
||||
"sortBy": "Sort By",
|
||||
"groupBy2": "Group By",
|
||||
"sortByName": "Name",
|
||||
"quantity": "Quantity",
|
||||
"cost": "Cost",
|
||||
"shops": "Shops",
|
||||
"custom": "Custom",
|
||||
"wishlist": "Wishlist",
|
||||
"wrongItemType": "The item type \"<%= type %>\" is not valid.",
|
||||
"wrongItemPath": "The item path \"<%= path %>\" is not valid.",
|
||||
"unpinnedItem": "You unpinned <%= item %>! It will no longer display in your Rewards column.",
|
||||
"cannotUnpinArmoirPotion": "The Health Potion and Enchanted Armoire cannot be unpinned.",
|
||||
"purchasedItem": "You bought <%= itemName %>",
|
||||
"items": "Предмети",
|
||||
"AZ": "А-Я",
|
||||
"sort": "Подреждане",
|
||||
"sortBy": "Подреждане по",
|
||||
"groupBy2": "Групиране по",
|
||||
"sortByName": "Име",
|
||||
"quantity": "Количество",
|
||||
"cost": "Цена",
|
||||
"shops": "Магазини",
|
||||
"custom": "Персонализирано",
|
||||
"wishlist": "Списък с желания",
|
||||
"wrongItemType": "„<%= type %>“ не е правилен тип на предмет.",
|
||||
"wrongItemPath": "„<%= path %>“ не е правилен път за предмет.",
|
||||
"unpinnedItem": "Откачихте „<%= item %>“! Този предмет вече няма да се показва в колоната Ви с награди.",
|
||||
"cannotUnpinArmoirPotion": "Лечебната отвара и Омагьосаният гардероб не могат да бъдат откачени.",
|
||||
"purchasedItem": "Закупихте <%= itemName %>",
|
||||
"ian": "Иън",
|
||||
"ianText": "Добре дошли в магазина за мисии! Тук може да използвате свитъците си с мисии, за да се биете срещу чудовища заедно с приятелите си. Вижте чудесните ни свитъци с мисии, налични за купуване, вдясно!",
|
||||
"ianTextMobile": "Мога ли да Ви заинтересовам с няколко свитъка за мисии? Активирайте ги, за да се биете срещу чудовища заедно с групата си!",
|
||||
"ianBrokenText": "Добре дошли в магазина за мисии… Тук може да използвате свитъците си с мисии, за да се биете срещу чудовища заедно с приятелите си… Вижте чудесните ни свитъци с мисии, налични за купуване, вдясно…",
|
||||
"featuredQuests": "Featured Quests!",
|
||||
"featuredQuests": "Препоръчани мисии!",
|
||||
"missingKeyParam": "„req.params.key“ е задължително.",
|
||||
"itemNotFound": "Предметът „<%= key %>“ не е намерен.",
|
||||
"cannotBuyItem": "Не може да купите този предмет.",
|
||||
@@ -94,7 +94,7 @@
|
||||
"alreadyUnlocked": "Пълният комплект вече е отключен.",
|
||||
"alreadyUnlockedPart": "Пълният комплект вече е частично отключен.",
|
||||
"USD": "(USD)",
|
||||
"newStuff": "New Stuff by [Bailey](https://twitter.com/Mihakuu)",
|
||||
"newStuff": "Нови неща от [Бейли](https://twitter.com/Mihakuu)",
|
||||
"cool": "Ще ми разкажете друг път",
|
||||
"dismissAlert": "Премахване на известието",
|
||||
"donateText1": "Добавя 20 диаманта към профила Ви. Диамантите се използват за купуване на специални предмети като облекло и прически.",
|
||||
@@ -111,9 +111,9 @@
|
||||
"classStats": "Това са показателите на класа Ви. Те оказват влияние върху играта Ви. Всеки път, когато качите ниво, получавате една точка, която може да приложите към избран показател. Посочете всеки от показателите с мишката за повече информация.",
|
||||
"autoAllocate": "Автоматично разпределяне",
|
||||
"autoAllocateText": "Ако автоматичното разпределение е избрано, героят Ви получава показатели автоматично според показателите на задачите Ви, които може да настроите в <strong>ЗАДАЧА > Редактиране > Разширени > Показатели</strong>. Например: ако често посещавате фитнеса и към ежедневната Ви задача „Фитнес“ е заден показател „сила“, ще получавате сила автоматично.",
|
||||
"spells": "Skills",
|
||||
"spellsText": "You can now unlock class-specific skills. You'll see your first at level 11. Your mana replenishes 10 points per day, plus 1 point per completed <a target='_blank' href='http://habitica.wikia.com/wiki/Todos'>To-Do</a>.",
|
||||
"skillsTitle": "Skills",
|
||||
"spells": "Умения",
|
||||
"spellsText": "Вече можете да отключвате уменията за класа си. Ще видите първото си такова когато достигнете ниво 11. Маната Ви се възстановява с по 10 точки на ден, плюс още 1 точка за всяка изпълнена <a target='_blank' href='http://habitica.wikia.com/wiki/Todos'>задача за изпълнение</a>.",
|
||||
"skillsTitle": "Умения",
|
||||
"toDo": "Задача",
|
||||
"moreClass": "За повече информация относно класовата система, вижте <a href='http://habitica.wikia.com/wiki/Class_System' target='_blank'>уикито</a>.",
|
||||
"tourWelcome": "Добре дошли в Хабитика! Това е Вашият списък със задачи. Отметнете задача, за да продължите!",
|
||||
@@ -128,7 +128,7 @@
|
||||
"tourScrollDown": "Превъртете до долу, за да видите всички възможности! Щракнете героя си отново, за да се върнете към страницата със задачите.",
|
||||
"tourMuchMore": "Когато приключите със задачите, можете да сформирате група с приятелите си, да си пишете в гилдиите за споделени интереси, да участвате в предизвикателства и още!",
|
||||
"tourStatsPage": "Това е страницата с показателите и статистиките Ви! Печелете постижения като изпълнявате описаните задачи.",
|
||||
"tourTavernPage": "Welcome to the Tavern, an all-ages chat room! You can keep your Dailies from hurting you in case of illness or travel by clicking \"Pause Damage\". Come say hi!",
|
||||
"tourTavernPage": "Добре дошли в кръчмата — мястото за разговори, достъпно за хора от всички възрасти! Може да избегнете нараняванията от ежедневните си задачи, в случай на болест или пътуване, като натиснете бутона „Спиране на щетите“. Елате и поздравете останалите!",
|
||||
"tourPartyPage": "Групата ще Ви помогне да бъдете по-отговорен/на. Поканете приятелите си, за да отключите свитък с мисия!",
|
||||
"tourGuildsPage": "Гилдиите са групи за разговори по общи интереси, създавани от играчите и за играчите. Прегледайте списъка и се присъединете към гилдиите, които Ви се струват интересни! Вижте също и популярната гилдия за задаване на въпроси и помощ, където всеки може да зададе въпросите си относно Хабитика!",
|
||||
"tourChallengesPage": "Предизвикателствата са тематични списъци от задачи, създавани от потребителите! Присъединявайки се към предизвикателство, Вие ще добавите задачите от него към профила си. Състезавайте се с други потребители, за да печелите диаманти като награда!",
|
||||
@@ -161,5 +161,5 @@
|
||||
"welcome4": "Избягвайте лошите навици, които отнемат здраве (ЖТ), или героят Ви ще умре!",
|
||||
"welcome5": "Сега ще персонализирате героя си и ще създадете задачите си…",
|
||||
"imReady": "Влизане в Хабитика",
|
||||
"limitedOffer": "Available until <%= date %>"
|
||||
"limitedOffer": "Налично до <%= date %>"
|
||||
}
|
||||
@@ -2,13 +2,13 @@
|
||||
"needTips": "Имате нужда от съвети как да започнете? Ето кратко ръководство!",
|
||||
|
||||
"step1": "Стъпка 1: Въведете задачите си",
|
||||
"webStep1Text": "Habitica is nothing without real-world goals, so enter a few tasks. You can add more later as you think of them! All tasks can be added by clicking the green \"Create\" button.\n* **Set up [To-Dos](http://habitica.wikia.com/wiki/To-Dos):** Enter tasks you do once or rarely in the To-Dos column, one at a time. You can click on the tasks to edit them and add checklists, due dates, and more!\n* **Set up [Dailies](http://habitica.wikia.com/wiki/Dailies):** Enter activities you need to do daily or on a particular day of the week, month, or year in the Dailies column. Click task to edit when it will be due and/or set a start date. You can also make it due on a repeating basis, for example, every 3 days.\n* **Set up [Habits](http://habitica.wikia.com/wiki/Habits):** Enter habits you want to establish in the Habits column. You can edit the Habit to change it to just a good habit :heavy_plus_sign: or a bad habit :heavy_minus_sign:\n* **Set up [Rewards](http://habitica.wikia.com/wiki/Rewards):** In addition to the in-game Rewards offered, add activities or treats which you want to use as a motivation to the Rewards column. It's important to give yourself a break or allow some indulgence in moderation!\n* If you need inspiration for which tasks to add, you can look at the wiki's pages on [Sample Habits](http://habitica.wikia.com/wiki/Sample_Habits), [Sample Dailies](http://habitica.wikia.com/wiki/Sample_Dailies), [Sample To-Dos](http://habitica.wikia.com/wiki/Sample_To-Dos), and [Sample Rewards](http://habitica.wikia.com/wiki/Sample_Custom_Rewards).",
|
||||
"webStep1Text": "Хабитика трябва да знае какви са истинските Ви цели, затова въведете няколко задачи за изпълнение. Винаги можете да добавите още по-късно, като натиснете бутона „Създаване“.\n * **Създаване на [задачи за изпълнение](http://habitica.wikia.com/wiki/To-Dos):** Въведете задачите, които трябва да свършите веднъж, или които се вършат много рядко, в колоната за задачи, една по една. След това можете да щракнете моливчето и да ги редактирате, като добавите списък от подзадачи, крайна дата и още!\n * **Създаване на [ежедневни задачи](http://habitica.wikia.com/wiki/Dailies):** Въведете дейностите, които трябва да вършите всекидневно или в един и същ ден всяка седмица, в колоната с ежедневни. Щракнете върху задача, за да редактирате дните от седмицата, през които въпросната задача трябва да се върши. Можете също да изискате изпълнението ѝ на определен период, например на всеки 3 дни.\n * **Създаване на [навици](http://habitica.wikia.com/wiki/Habits):** Въведете навиците, които искате да си създадете, в колоната с навици. Може да настроите дали навикът е добър :heavy_plus_sign: или лош :heavy_minus_sign:.\n * **Създаване на [награди](http://habitica.wikia.com/wiki/Rewards):** Освен наградите, предлагани от самата игра, можете да добавите различни дейности или примамливи неща, които да използвате като мотивация, в колоната с награди. Почивката и удоволствията също са важни!\n* Ако имате нужда от малко вдъхновение, можете да разгледате страниците в уикито с [примерни навици](http://habitica.wikia.com/wiki/Sample_Habits), [примерни ежедневни задачи](http://habitica.wikia.com/wiki/Sample_Dailies), [примерни задачи за изпълнение](http://habitica.wikia.com/wiki/Sample_To-Dos), и [примерни награди](http://habitica.wikia.com/wiki/Sample_Custom_Rewards).",
|
||||
|
||||
"step2": "Стъпка 2: Печелете точки като вършите неща в истинския живот",
|
||||
"webStep2Text": "А сега започнете да преследвате целите си от списъка! Когато завършвате задачи и ги отмятате в Хабитика, ще получавате [опит](http://habitica.wikia.com/wiki/Experience_Points), чрез който качвате ниво и [злато](http://habitica.wikia.com/wiki/Gold_Points), с което можете да си купувате награди. Ако се поддадете на лош навик или пропуснете ежедневна задача, ще загубите [здраве](http://habitica.wikia.com/wiki/Health_Points). Така лентите за опит и здраве представляват своеобразен показател за напредъка към целите Ви. Подобрявайки истинския си живот, Вашият герой в играта ще напредва.",
|
||||
|
||||
"step3": "Стъпка 3: Персонализирайте и изследвайте Хабитика",
|
||||
"webStep3Text": "Once you're familiar with the basics, you can get even more out of Habitica with these nifty features:\n * Organize your tasks with [tags](http://habitica.wikia.com/wiki/Tags) (edit a task to add them).\n * Customize your [avatar](http://habitica.wikia.com/wiki/Avatar) by clicking the user icon in the upper-right corner.\n * Buy your [Equipment](http://habitica.wikia.com/wiki/Equipment) under Rewards or from the [Shops](/shops/market), and change it under [Inventory > Equipment](/inventory/equipment).\n * Connect with other users via the [Tavern](http://habitica.wikia.com/wiki/Tavern).\n * Starting at Level 3, hatch [Pets](http://habitica.wikia.com/wiki/Pets) by collecting [eggs](http://habitica.wikia.com/wiki/Eggs) and [hatching potions](http://habitica.wikia.com/wiki/Hatching_Potions). [Feed](http://habitica.wikia.com/wiki/Food) them to create [Mounts](http://habitica.wikia.com/wiki/Mounts).\n * At level 10: Choose a particular [class](http://habitica.wikia.com/wiki/Class_System) and then use class-specific [skills](http://habitica.wikia.com/wiki/Skills) (levels 11 to 14).\n * Form a party with your friends (by clicking [Party](/party) in the navigation bar) to stay accountable and earn a Quest scroll.\n * Defeat monsters and collect objects on [quests](http://habitica.wikia.com/wiki/Quests) (you will be given a quest at level 15).",
|
||||
"webStep3Text": "След като свикнете с нещата, ще можете да се забавлявате още повече с Хабитика, с тези интересни функционалности:\n * Организирайте задачите си с [етикети](http://habitica.wikia.com/wiki/Tags) (етикетите се добавят в прозорчето за редактиране на задача);\n * Персонализирайте [героя](http://habitica.wikia.com/wiki/Avatar) си, като натиснете иконката на потребителя в горния десен ъгъл;\n * Купете си [екипировка](http://habitica.wikia.com/wiki/Equipment) от колоната с награди или от [магазините](/shops/market) и екипирайте героя си с нея в [Инвентар > Екипировка](/inventory/equipment);\n * Свържете се с други потребители посредством [кръчмата](http://habitica.wikia.com/wiki/Tavern);\n * След като достигнете ниво 3, ще можете да излюпвате [любимци](http://habitica.wikia.com/wiki/Pets) като събирате [яйца](http://habitica.wikia.com/wiki/Eggs) и [излюпващи отвари](http://habitica.wikia.com/wiki/Hatching_Potions). [Хранете](http://habitica.wikia.com/wiki/Food) ги, за да се превърнат в [превози](http://habitica.wikia.com/wiki/Mounts);\n * След като достигнете ниво 10, ще можете да изберете [клас](http://habitica.wikia.com/wiki/Class_System) и да използвате класово-специфични [умения](http://habitica.wikia.com/wiki/Skills) (нива 11 до 14);\n * Сформирайте група с приятелите си от [Група](/party), за да се държите отговорни взаимно и да получите свитък с мисия;\n * Побеждавайте чудовища и събирайте предмети от [мисии](http://habitica.wikia.com/wiki/Quests) (ще получите мисия, когато достигнете ниво 15).",
|
||||
|
||||
"overviewQuestions": "Have questions? Check out the [FAQ](/static/faq/)! If your question isn't mentioned there, you can ask for further help in the [Habitica Help guild](/groups/guild/5481ccf3-5d2d-48a9-a871-70a7380cee5a).\n\nGood luck with your tasks!"
|
||||
"overviewQuestions": "Имате въпрос? Вижте нашите [ЧЗВ](/static/faq/)! Ако не намирате въпроса си там, можете да ни попитате за помощ в [Помощната гилдия на Хабитика](/groups/guild/5481ccf3-5d2d-48a9-a871-70a7380cee5a).\n\nУспех със задачите Ви!"
|
||||
}
|
||||
|
||||