Compare commits
36 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| cef8a34c06 | |||
| 6432823eec | |||
| 563b780d85 | |||
| aa9b1b2cac | |||
| 401e541b86 | |||
| c13bed3bad | |||
| b3c4817fb4 | |||
| 7c9c45ac5f | |||
| 95142e3684 | |||
| dc1cce6ddb | |||
| 43cf77f33c | |||
| 93780d7056 | |||
| 2ad17d408e | |||
| b0f7567367 | |||
| 3f2b1d3f79 | |||
| 29eb8ca10b | |||
| 8c71ca12b8 | |||
| 72a753626f | |||
| 35ebb12bf2 | |||
| 1ff418f62d | |||
| e1aa437ea5 | |||
| 2a4239bf3c | |||
| 399563435b | |||
| 59f7e25c85 | |||
| ad845dff43 | |||
| fd1eb2d900 | |||
| 26cb6df9d9 | |||
| b0aafb079a | |||
| 58f0837c50 | |||
| a6378b3d43 | |||
| ddbf95da92 | |||
| 4d31e0286b | |||
| 7a74825121 | |||
| be0e8779d5 | |||
| fffbe17bcc | |||
| ca4ee8b513 |
@@ -7,5 +7,14 @@ module.exports = {
|
||||
rules: {
|
||||
'prefer-regex-literals': 'warn',
|
||||
'import/no-extraneous-dependencies': 'off',
|
||||
'require-await': 'error',
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
files: ['migrations/**', 'gulp/**'], // Or *.test.js
|
||||
rules: {
|
||||
'require-await': 'off',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
name: Test
|
||||
|
||||
on: [push, pull_request]
|
||||
on:
|
||||
push:
|
||||
branches-ignore:
|
||||
- 'phillip/**'
|
||||
- 'sabrecat/**'
|
||||
- 'kalista/**'
|
||||
- 'natalie/**'
|
||||
pull_request:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
+1
-1
@@ -47,5 +47,5 @@ webpack.webstorm.config
|
||||
|
||||
# mongodb replica set for local dev
|
||||
mongodb-*.tgz
|
||||
/mongodb-data
|
||||
/mongodb-data*
|
||||
/.nyc_output
|
||||
|
||||
+1
-1
@@ -2,4 +2,4 @@
|
||||
|
||||
This webpage includes the documentation for version 3 of the [Habitica](https://habitica.com) API.
|
||||
|
||||
If you're developing a 3rd party tool that uses the Habitica API you should read the [Guidance for Comrades](https://habitica.fandom.com/wiki/Guidance_for_Comrades) and in particular the section called [Rules for Third-Party Tools](https://habitica.fandom.com/wiki/Guidance_for_Comrades#Rules_for_Third-Party_Tools) which includes suggestions on how to best use the API and the rules to follow when interacting with it.
|
||||
If you're developing a 3rd party tool that uses the Habitica API, read the [API Usage Guidelines](https://github.com/HabitRPG/habitica/wiki/API-Usage-Guidelines), which describe how to be a responsible user of our server resources!
|
||||
|
||||
+2
-1
@@ -93,5 +93,6 @@
|
||||
"TRUSTED_DOMAINS": "localhost,https://habitica.com",
|
||||
"TIME_TRAVEL_ENABLED": "false",
|
||||
"DEBUG_ENABLED": "false",
|
||||
"CONTENT_SWITCHOVER_TIME_OFFSET": 8
|
||||
"CONTENT_SWITCHOVER_TIME_OFFSET": 8,
|
||||
"SLOW_REQUEST_THRESHOLD": 1000
|
||||
}
|
||||
|
||||
@@ -64,6 +64,15 @@ function filterFile (file) {
|
||||
if (file.relative.indexOf('icon_background') === 0) {
|
||||
return false;
|
||||
}
|
||||
if (file.relative.indexOf('notif_') === 0) {
|
||||
return false;
|
||||
}
|
||||
if (file.relative.indexOf('quest_') === 0) {
|
||||
return false;
|
||||
}
|
||||
if (file.relative.indexOf('inventory_quest_') === 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
Submodule habitica-images updated: dfb04339a4...8ee6f312cb
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "habitica",
|
||||
"version": "5.32.5",
|
||||
"version": "5.34.2",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "habitica",
|
||||
"version": "5.32.5",
|
||||
"version": "5.34.2",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.22.10",
|
||||
|
||||
+2
-1
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "habitica",
|
||||
"description": "A habit tracker app which treats your goals like a Role Playing Game.",
|
||||
"version": "5.32.5",
|
||||
"version": "5.34.2",
|
||||
"main": "./website/server/index.js",
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.22.10",
|
||||
@@ -110,6 +110,7 @@
|
||||
"start:simple": "node ./website/server/index.js",
|
||||
"debug": "gulp nodemon --inspect",
|
||||
"mongo:dev": "run-rs -v 5.0.23 -l ubuntu1804 --keep --dbpath mongodb-data --number 1 --quiet",
|
||||
"mongo:test": "run-rs -v 5.0.23 -l ubuntu1804 --keep --dbpath mongodb-data-testing --number 1 --quiet",
|
||||
"postinstall": "git config --global url.\"https://\".insteadOf git:// && gulp build && cd website/client && npm install",
|
||||
"apidoc": "gulp apidoc",
|
||||
"heroku-postbuild": ".heroku/report_deploy.sh"
|
||||
|
||||
@@ -60,12 +60,12 @@ describe('PUT /heroes/:heroId', () => {
|
||||
expect(heroRes.profile).to.have.all.keys(['name']);
|
||||
|
||||
// test response values
|
||||
expect(heroRes.balance).to.equal(3 + 0.75); // 3+0.75 for first contrib level
|
||||
expect(heroRes.balance).to.equal(3 + 2.5); // 3+2.5 for first contrib level
|
||||
expect(heroRes.contributor.level).to.equal(1);
|
||||
expect(heroRes.purchased.ads).to.equal(true);
|
||||
// test hero values
|
||||
await hero.sync();
|
||||
expect(hero.balance).to.equal(3 + 0.75); // 3+0.75 for first contrib level
|
||||
expect(hero.balance).to.equal(3 + 2.5); // 3+2.5 for first contrib level
|
||||
expect(hero.contributor.level).to.equal(1);
|
||||
expect(hero.purchased.ads).to.equal(true);
|
||||
expect(hero.auth.blocked).to.equal(prevBlockState);
|
||||
@@ -136,12 +136,12 @@ describe('PUT /heroes/:heroId', () => {
|
||||
expect(heroRes.profile).to.have.all.keys(['name']);
|
||||
|
||||
// test response values
|
||||
expect(heroRes.balance).to.equal(1); // 0+1 for sixth contrib level
|
||||
expect(heroRes.balance).to.equal(15); // 0+15 for sixth contrib level
|
||||
expect(heroRes.contributor.level).to.equal(6);
|
||||
expect(heroRes.items.pets['Dragon-Hydra']).to.equal(5);
|
||||
// test hero values
|
||||
await hero.sync();
|
||||
expect(hero.balance).to.equal(1); // 0+1 for sixth contrib level
|
||||
expect(hero.balance).to.equal(15); // 0+15 for sixth contrib level
|
||||
expect(hero.contributor.level).to.equal(6);
|
||||
expect(hero.items.pets['Dragon-Hydra']).to.equal(5);
|
||||
});
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
import {
|
||||
generateUser,
|
||||
translate as t,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
import common from '../../../../../website/common';
|
||||
|
||||
describe('GET /members/username/:username', () => {
|
||||
let user;
|
||||
|
||||
before(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('validates req.params.username', async () => {
|
||||
await expect(user.get('/members/username/')).to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: t('invalidReqParams'),
|
||||
});
|
||||
});
|
||||
|
||||
it('returns a member\'s public data only', async () => {
|
||||
// make sure user has all the fields that can be returned by the getMember call
|
||||
const member = await generateUser({
|
||||
contributor: { level: 1 },
|
||||
backer: { tier: 3 },
|
||||
preferences: {
|
||||
costume: false,
|
||||
background: 'volcano',
|
||||
},
|
||||
secret: {
|
||||
text: 'Clark Kent',
|
||||
},
|
||||
});
|
||||
const memberRes = await user.get(`/members/username/${member.auth.local.username}`);
|
||||
expect(memberRes).to.have.all.keys([ // works as: object has all and only these keys
|
||||
'_id', 'id', 'preferences', 'profile', 'stats', 'achievements', 'party',
|
||||
'backer', 'contributor', 'auth', 'items', 'inbox', 'loginIncentives', 'flags',
|
||||
]);
|
||||
expect(Object.keys(memberRes.auth)).to.eql(['local', 'timestamps']);
|
||||
expect(Object.keys(memberRes.preferences).sort()).to.eql([
|
||||
'size', 'hair', 'skin', 'shirt',
|
||||
'chair', 'costume', 'sleep', 'background', 'tasks', 'disableClasses',
|
||||
].sort());
|
||||
|
||||
expect(memberRes.stats.maxMP).to.exist;
|
||||
expect(memberRes.stats.maxHealth).to.equal(common.maxHealth);
|
||||
expect(memberRes.stats.toNextLevel).to.equal(common.tnl(memberRes.stats.lvl));
|
||||
expect(memberRes.inbox.optOut).to.exist;
|
||||
expect(memberRes.inbox.canReceive).to.exist;
|
||||
expect(memberRes.inbox.messages).to.not.exist;
|
||||
expect(memberRes.secret).to.not.exist;
|
||||
|
||||
expect(memberRes.blocks).to.not.exist;
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,104 @@
|
||||
import find from 'lodash/find';
|
||||
import {
|
||||
generateUser,
|
||||
translate as t,
|
||||
} from '../../../helpers/api-integration/v4';
|
||||
|
||||
/**
|
||||
* Checks the messages array if the uniqueMessageId has the like flag
|
||||
* @param {InboxMessage[]} messages
|
||||
* @param {String} uniqueMessageId
|
||||
* @param {String} userId
|
||||
* @param {Boolean} likeStatus
|
||||
*/
|
||||
function expectMessagesLikeStatus (messages, uniqueMessageId, userId, likeStatus) {
|
||||
const messageToCheck = find(messages, { uniqueMessageId });
|
||||
|
||||
expect(messageToCheck.likes[userId]).to.equal(likeStatus);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line mocha/no-exclusive-tests
|
||||
describe('POST /inbox/like-private-message/:messageId', () => {
|
||||
let userToSendMessage;
|
||||
const getLikeUrl = messageId => `/inbox/like-private-message/${messageId}`;
|
||||
|
||||
before(async () => {
|
||||
userToSendMessage = await generateUser();
|
||||
});
|
||||
|
||||
it('returns an error when private message is not found', async () => {
|
||||
await expect(userToSendMessage.post(getLikeUrl('some-unknown-id')))
|
||||
.to.eventually.be.rejected.and.eql({
|
||||
code: 404,
|
||||
error: 'NotFound',
|
||||
message: t('messageGroupChatNotFound'),
|
||||
});
|
||||
});
|
||||
|
||||
it('likes a message', async () => {
|
||||
const receiver = await generateUser();
|
||||
|
||||
const sentMessageResult = await userToSendMessage.post('/members/send-private-message', {
|
||||
message: 'some message :)',
|
||||
toUserId: receiver._id,
|
||||
});
|
||||
|
||||
const { uniqueMessageId } = sentMessageResult.message;
|
||||
|
||||
const likeResult = await receiver.post(getLikeUrl(uniqueMessageId));
|
||||
expect(likeResult.likes[receiver._id]).to.equal(true);
|
||||
|
||||
const senderMessages = await userToSendMessage.get('/inbox/messages');
|
||||
|
||||
expectMessagesLikeStatus(senderMessages, uniqueMessageId, receiver._id, true);
|
||||
|
||||
const receiversMessages = await receiver.get('/inbox/messages');
|
||||
|
||||
expectMessagesLikeStatus(receiversMessages, uniqueMessageId, receiver._id, true);
|
||||
});
|
||||
|
||||
it('allows a user to like their own private message', async () => {
|
||||
const receiver = await generateUser();
|
||||
|
||||
const sentMessageResult = await userToSendMessage.post('/members/send-private-message', {
|
||||
message: 'some message :)',
|
||||
toUserId: receiver._id,
|
||||
});
|
||||
|
||||
const { uniqueMessageId } = sentMessageResult.message;
|
||||
|
||||
const likeResult = await userToSendMessage.post(getLikeUrl(uniqueMessageId));
|
||||
expect(likeResult.likes[userToSendMessage._id]).to.equal(true);
|
||||
|
||||
const messages = await userToSendMessage.get('/inbox/messages');
|
||||
expectMessagesLikeStatus(messages, uniqueMessageId, userToSendMessage._id, true);
|
||||
|
||||
const receiversMessages = await receiver.get('/inbox/messages');
|
||||
|
||||
expectMessagesLikeStatus(receiversMessages, uniqueMessageId, userToSendMessage._id, true);
|
||||
});
|
||||
|
||||
it('unlikes a message', async () => {
|
||||
const receiver = await generateUser();
|
||||
|
||||
const sentMessageResult = await userToSendMessage.post('/members/send-private-message', {
|
||||
message: 'some message :)',
|
||||
toUserId: receiver._id,
|
||||
});
|
||||
|
||||
const { uniqueMessageId } = sentMessageResult.message;
|
||||
|
||||
const likeResult = await receiver.post(getLikeUrl(uniqueMessageId));
|
||||
|
||||
expect(likeResult.likes[receiver._id]).to.equal(true);
|
||||
|
||||
const unlikeResult = await receiver.post(getLikeUrl(uniqueMessageId));
|
||||
|
||||
expect(unlikeResult.likes[receiver._id]).to.equal(false);
|
||||
|
||||
const messages = await userToSendMessage.get('/inbox/messages');
|
||||
|
||||
const messageToCheck = find(messages, { id: sentMessageResult.message.id });
|
||||
expect(messageToCheck.likes[receiver._id]).to.equal(false);
|
||||
});
|
||||
});
|
||||
@@ -10,7 +10,7 @@ describe('events', () => {
|
||||
});
|
||||
|
||||
it('returns empty array when no events are active', () => {
|
||||
clock = sinon.useFakeTimers(new Date('2024-01-08'));
|
||||
clock = sinon.useFakeTimers(new Date('2024-01-11'));
|
||||
const events = getRepeatingEvents();
|
||||
expect(events).to.be.empty;
|
||||
});
|
||||
|
||||
@@ -7,7 +7,7 @@ module.exports = {
|
||||
extends: [
|
||||
'habitrpg/lib/vue',
|
||||
],
|
||||
ignorePatterns: ['dist/', 'node_modules/'],
|
||||
ignorePatterns: ['dist/', 'node_modules/', '*.d.ts'],
|
||||
rules: {
|
||||
'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
|
||||
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -101,8 +101,7 @@
|
||||
|
||||
.btn-secondary,
|
||||
.dropdown > .btn-secondary.dropdown-toggle:not(.btn-success),
|
||||
.show > .btn-secondary.dropdown-toggle:not(.btn-success)
|
||||
{
|
||||
.show > .btn-secondary.dropdown-toggle:not(.btn-success) {
|
||||
background: $white;
|
||||
border: 2px solid transparent;
|
||||
color: $gray-50;
|
||||
@@ -298,6 +297,16 @@
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.btn-flat,
|
||||
.dropdown > .btn-flat.dropdown-toggle:not(.btn-success),
|
||||
.show > .btn-flat.dropdown-toggle:not(.btn-success) {
|
||||
&.with-icon {
|
||||
.svg-icon.color {
|
||||
color: var(--icon-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.btn-cancel {
|
||||
color: $blue-10;
|
||||
}
|
||||
|
||||
@@ -38,7 +38,12 @@
|
||||
border-radius: 2px;
|
||||
box-shadow: 0 3px 6px 0 rgba($black, 0.16), 0 3px 6px 0 rgba($black, 0.24);
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.no-min-width {
|
||||
.dropdown-menu {
|
||||
min-width: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
// shared dropdown-item styles
|
||||
@@ -54,6 +59,8 @@
|
||||
color: $gray-50 !important;
|
||||
cursor: pointer;
|
||||
|
||||
--dropdown-item-hover-icon-color: #{$gray-200};
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
background-color: inherit;
|
||||
@@ -88,7 +95,7 @@
|
||||
|
||||
&:not(:hover) {
|
||||
.with-icon .svg-icon {
|
||||
color: $gray-200;
|
||||
color: var(dropdown-item-hover-icon-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -151,7 +158,7 @@
|
||||
|
||||
// selectList.vue items sizing
|
||||
.selectListItem .dropdown-item {
|
||||
padding: 0.25rem 0.75rem;
|
||||
padding: 0.25rem 1rem 0.25rem 0.75rem;
|
||||
height: 32px;
|
||||
|
||||
&:active, &:hover, &:focus, &.active {
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4,0C1.79,0,0,1.79,0,4v16c0,2.21,1.79,4,4,4h16c2.21,0,4-1.79,4-4V4c0-2.21-1.79-4-4-4H4ZM12,11.57c-.72-1.49-2.7-4.26-4.53-5.63-1.32-.99-3.47-1.75-3.47.68,0,.49.28,4.08.44,4.66.57,2.03,2.65,2.55,4.5,2.23-3.24.55-4.06,2.36-2.28,4.17,3.38,3.44,4.85-.86,5.23-1.97h0s0,0,0,0c.07-.2.1-.29.1-.21,0-.08.03.01.1.22h0c.38,1.1,1.85,5.41,5.23,1.97,1.78-1.81.95-3.63-2.28-4.17,1.85.31,3.93-.2,4.5-2.23.16-.58.44-4.18.44-4.66,0-2.43-2.14-1.67-3.47-.68-1.83,1.37-3.81,4.14-4.53,5.63Z" fill-rule="evenodd"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 572 B |
@@ -1,3 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<path d="M20,0H4A4,4,0,0,0,0,4V20a4,4,0,0,0,4,4H20a4,4,0,0,0,4-4V4A4,4,0,0,0,20,0ZM18.36,8.74c0,.14,0,.29,0,.43A9.34,9.34,0,0,1,4,17a6.85,6.85,0,0,0,.79,0,6.57,6.57,0,0,0,4.07-1.4A3.29,3.29,0,0,1,5.8,13.39a4.1,4.1,0,0,0,.62,0,3.49,3.49,0,0,0,.86-.11,3.28,3.28,0,0,1-2.63-3.22v0a3.35,3.35,0,0,0,1.48.42A3.29,3.29,0,0,1,4.67,7.76,3.22,3.22,0,0,1,5.12,6.1a9.3,9.3,0,0,0,6.76,3.43,3.67,3.67,0,0,1-.08-.75,3.28,3.28,0,0,1,5.67-2.24,6.54,6.54,0,0,0,2.08-.79,3.22,3.22,0,0,1-1.44,1.8A6.67,6.67,0,0,0,20,7.05,7.31,7.31,0,0,1,18.36,8.74Z" fill-rule="evenodd"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 622 B |
@@ -25,9 +25,9 @@
|
||||
<router-link to="/">
|
||||
Homepage
|
||||
</router-link>or
|
||||
<router-link :to="contactUsLink">
|
||||
<a href="mailto:admin@habitica.com">
|
||||
Contact Us
|
||||
</router-link>about the issue.
|
||||
</a>about the issue.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -40,12 +40,6 @@ import { mapState } from '@/libs/store';
|
||||
export default {
|
||||
computed: {
|
||||
...mapState(['isUserLoggedIn']),
|
||||
contactUsLink () {
|
||||
if (this.isUserLoggedIn) {
|
||||
return { name: 'guild', params: { groupId: 'a29da26b-37de-4a71-b0c6-48e72a900dac' } };
|
||||
}
|
||||
return { name: 'contact' };
|
||||
},
|
||||
retiredChatPage () {
|
||||
return this.$route.fullPath.indexOf('/groups') !== -1;
|
||||
},
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<div class="modal-body">
|
||||
<div class="row">
|
||||
<div class="col-6 offset-3">
|
||||
<div class="shop_armoire"></div>
|
||||
<Sprite image-name="shop_armoire" />
|
||||
<p>{{ $t('armoireLastItem') }}</p>
|
||||
<p>{{ $t('armoireNotesEmpty') }}</p>
|
||||
</div>
|
||||
@@ -34,7 +34,12 @@
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import Sprite from '@/components/ui/sprite';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Sprite,
|
||||
},
|
||||
methods: {
|
||||
close () {
|
||||
this.$root.$emit('bv::hide::modal', 'armoire-empty');
|
||||
|
||||
@@ -95,7 +95,11 @@
|
||||
@click="clickDisableClasses(); close();"
|
||||
>{{ $t('optOutOfClasses') }}</span>
|
||||
</div>
|
||||
<span class="opt-out-description">{{ $t('optOutOfClassesText') }}</span>
|
||||
<div
|
||||
v-once
|
||||
class="opt-out-description"
|
||||
v-html="$t('optOutOfClassesText')"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
></span>
|
||||
</div>
|
||||
|
||||
<div :class="questClass"></div>
|
||||
<Sprite :image-name="questClass" />
|
||||
</section>
|
||||
<!-- @TODO: Keep this? .checkboxinput(type='checkbox', v-model=
|
||||
'user.preferences.suppressModals.levelUp', @change='changeLevelupSuppress()')
|
||||
@@ -150,15 +150,12 @@ label(style='display:inline-block') {{ $t('dontShowAgain') }}
|
||||
section.greyed {
|
||||
padding-bottom: 17px
|
||||
}
|
||||
|
||||
.scroll {
|
||||
margin: -11px auto 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import Avatar from '../avatar';
|
||||
import Sprite from '@/components/ui/sprite';
|
||||
import { mapState } from '@/libs/store';
|
||||
import starGroup from '@/assets/svg/star-group.svg';
|
||||
import sparkles from '@/assets/svg/sparkles-left.svg';
|
||||
@@ -173,6 +170,7 @@ const levelQuests = {
|
||||
export default {
|
||||
components: {
|
||||
Avatar,
|
||||
Sprite,
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
@@ -191,7 +189,9 @@ export default {
|
||||
return this.user.stats.lvl in levelQuests;
|
||||
},
|
||||
questClass () {
|
||||
return `scroll inventory_quest_scroll_${levelQuests[this.user.stats.lvl]}`;
|
||||
const questKey = levelQuests[this.user.stats.lvl];
|
||||
if (questKey) return `inventory_quest_scroll_${questKey}`;
|
||||
return '';
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
<p v-html="$t('moreGearAchievements')"></p>
|
||||
<br>
|
||||
</div>
|
||||
<div class="shop_armoire"></div>
|
||||
<Sprite image-name="shop_armoire" />
|
||||
<p v-html="$t('armoireUnlocked')"></p>
|
||||
<br>
|
||||
<button
|
||||
@@ -87,11 +87,13 @@
|
||||
import achievementFooter from './achievementFooter';
|
||||
import achievementAvatar from './achievementAvatar';
|
||||
import { mapState } from '@/libs/store';
|
||||
import Sprite from '@/components/ui/sprite.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
achievementFooter,
|
||||
achievementAvatar,
|
||||
Sprite,
|
||||
},
|
||||
computed: {
|
||||
...mapState({ user: 'user.data' }),
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
{{ item.value }}
|
||||
</span>
|
||||
:
|
||||
{{ item.text || item.key }}
|
||||
{{ itemText(item) }}
|
||||
</span>
|
||||
|
||||
<div
|
||||
@@ -78,7 +78,7 @@
|
||||
{{ item.value }}
|
||||
</span>
|
||||
:
|
||||
{{ item.text || item.key }}
|
||||
{{ itemText(item) }}
|
||||
</span>
|
||||
|
||||
<div
|
||||
@@ -270,6 +270,15 @@ export default {
|
||||
item.value = !item.value;
|
||||
}
|
||||
},
|
||||
itemText (item) {
|
||||
if (item.key === 'npc') {
|
||||
return this.$t('npcAchievementName', { key: this.hero.backer && this.hero.backer.npc });
|
||||
}
|
||||
if (item.key === 'kickstarter') {
|
||||
return this.$t('kickstartName', { key: this.hero.backer && this.hero.backer.tier });
|
||||
}
|
||||
return item.text || item.key;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -37,9 +37,9 @@
|
||||
<h3>{{ $t('footerCompany') }}</h3>
|
||||
<ul>
|
||||
<li>
|
||||
<router-link to="/static/contact">
|
||||
<a href="mailto:admin@habitica.com">
|
||||
{{ $t('contactUs') }}
|
||||
</router-link>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<router-link to="/static/press-kit">
|
||||
@@ -55,9 +55,9 @@
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="https://habitica.fandom.com/wiki/Whats_New"
|
||||
target="_blank"
|
||||
>{{ $t('oldNews') }}
|
||||
@click="showBailey()"
|
||||
>
|
||||
{{ $t('oldNews') }}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -80,7 +80,7 @@
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="https://habitica.fandom.com/wiki/Contributing_to_Habitica"
|
||||
href="https://github.com/HabitRPG/habitica/wiki/Contributing-to-Habitica"
|
||||
target="_blank"
|
||||
>{{ $t('companyContribute') }}
|
||||
</a>
|
||||
@@ -158,13 +158,6 @@
|
||||
>{{ $t('guidanceForBlacksmiths') }}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="https://habitica.fandom.com/wiki/Extensions,_Add-Ons,_and_Customizations"
|
||||
target="_blank"
|
||||
>{{ $t('communityExtensions') }}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -205,12 +198,12 @@
|
||||
</a>
|
||||
<a
|
||||
class="social-circle"
|
||||
href="https://twitter.com/habitica/"
|
||||
href="https://bsky.app/profile/habitica.com"
|
||||
target="_blank"
|
||||
>
|
||||
<div
|
||||
class="social-icon svg-icon twitter"
|
||||
v-html="icons.twitter"
|
||||
class="social-icon svg-icon bluesky"
|
||||
v-html="icons.bluesky"
|
||||
></div>
|
||||
</a>
|
||||
<a
|
||||
@@ -518,7 +511,7 @@ footer {
|
||||
background-color: $gray-500;
|
||||
color: $gray-50;
|
||||
padding: 32px 142px 40px;
|
||||
a {
|
||||
a, a:not([href]) {
|
||||
color: $gray-50;
|
||||
}
|
||||
a:hover {
|
||||
@@ -807,7 +800,7 @@ h3 {
|
||||
}
|
||||
}
|
||||
|
||||
.twitter svg {
|
||||
.bluesky svg {
|
||||
background-color: #e1e0e3;
|
||||
fill: #878190;
|
||||
height: 24px;
|
||||
@@ -846,7 +839,7 @@ import Vue from 'vue';
|
||||
|
||||
// images
|
||||
import melior from '@/assets/svg/melior.svg';
|
||||
import twitter from '@/assets/svg/twitter.svg';
|
||||
import bluesky from '@/assets/svg/bluesky.svg';
|
||||
import facebook from '@/assets/svg/facebook.svg';
|
||||
import instagram from '@/assets/svg/instagram.svg';
|
||||
import tumblr from '@/assets/svg/tumblr.svg';
|
||||
@@ -878,7 +871,7 @@ export default {
|
||||
return {
|
||||
icons: Object.freeze({
|
||||
melior,
|
||||
twitter,
|
||||
bluesky,
|
||||
facebook,
|
||||
instagram,
|
||||
tumblr,
|
||||
@@ -996,7 +989,6 @@ export default {
|
||||
async bossRage () {
|
||||
await axios.post('/api/v4/debug/boss-rage');
|
||||
},
|
||||
|
||||
async makeAdmin () {
|
||||
await axios.post('/api/v4/debug/make-admin');
|
||||
// @TODO: Notification.text('You are now an admin!
|
||||
@@ -1006,6 +998,9 @@ export default {
|
||||
donate () {
|
||||
this.$root.$emit('bv::show::modal', 'buy-gems', { alreadyTracked: true });
|
||||
},
|
||||
showBailey () {
|
||||
this.$root.$emit('bv::show::modal', 'new-stuff');
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
v-if="member.preferences"
|
||||
class="avatar"
|
||||
:style="{width, height, paddingTop}"
|
||||
:class="backgroundClass"
|
||||
:class="topLevelClassList"
|
||||
@click.prevent="castEnd()"
|
||||
>
|
||||
<div
|
||||
@@ -55,7 +55,11 @@
|
||||
<span :class="[getGearClass('eyewear'), specialMountClass]"></span>
|
||||
<span :class="[getGearClass('head'), specialMountClass]"></span>
|
||||
<span :class="[getGearClass('headAccessory'), specialMountClass]"></span>
|
||||
<span :class="['hair_flower_' + member.preferences.hair.flower, specialMountClass]"></span>
|
||||
<span
|
||||
:class="[
|
||||
'hair_flower_' + member.preferences.hair.flower, specialMountClass
|
||||
]"
|
||||
></span>
|
||||
<span
|
||||
v-if="!hideGear('shield')"
|
||||
:class="[getGearClass('shield'), specialMountClass]"
|
||||
@@ -63,6 +67,7 @@
|
||||
<span
|
||||
v-if="!hideGear('weapon')"
|
||||
:class="[getGearClass('weapon'), specialMountClass]"
|
||||
class="weapon"
|
||||
></span>
|
||||
</template>
|
||||
<!-- Resting-->
|
||||
@@ -96,15 +101,23 @@
|
||||
|
||||
.avatar {
|
||||
width: 141px;
|
||||
height: 147px;
|
||||
image-rendering: pixelated;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
|
||||
&.centered-avatar {
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
// resetting the additional padding
|
||||
margin-bottom: -0.5rem !important;
|
||||
}
|
||||
|
||||
.character-sprites {
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.character-sprites span {
|
||||
@@ -123,6 +136,22 @@
|
||||
.invert {
|
||||
filter: invert(100%);
|
||||
}
|
||||
|
||||
.debug {
|
||||
border: 1px solid red;
|
||||
|
||||
.character-sprites {
|
||||
border: 1px solid blue;
|
||||
}
|
||||
|
||||
.weapon {
|
||||
border: 1px solid green;
|
||||
}
|
||||
|
||||
span {
|
||||
border: 1px solid yellow;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
@@ -133,12 +162,24 @@ import foolPet from '../mixins/foolPet';
|
||||
|
||||
import ClassBadge from '@/components/members/classBadge';
|
||||
|
||||
/**
|
||||
* TODO replace avatarOnly with multiple options like
|
||||
* - showMount
|
||||
* - showPet
|
||||
* - showBackground
|
||||
* - showWeapons
|
||||
*/
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ClassBadge,
|
||||
},
|
||||
mixins: [foolPet],
|
||||
props: {
|
||||
debugMode: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
member: {
|
||||
type: Object,
|
||||
required: true,
|
||||
@@ -156,14 +197,21 @@ export default {
|
||||
},
|
||||
overrideAvatarGear: {
|
||||
type: Object,
|
||||
default (data) {
|
||||
return data;
|
||||
},
|
||||
},
|
||||
width: {
|
||||
type: Number,
|
||||
default: 140,
|
||||
type: String,
|
||||
default: '140px',
|
||||
},
|
||||
height: {
|
||||
type: Number,
|
||||
default: 147,
|
||||
type: String,
|
||||
default: undefined,
|
||||
},
|
||||
centerAvatar: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
spritesMargin: {
|
||||
type: String,
|
||||
@@ -171,11 +219,16 @@ export default {
|
||||
},
|
||||
overrideTopPadding: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
showVisualBuffs: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
showWeapon: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
@@ -204,6 +257,19 @@ export default {
|
||||
|
||||
return val;
|
||||
},
|
||||
topLevelClassList () {
|
||||
const classes = [this.backgroundClass];
|
||||
|
||||
if (this.debugMode) {
|
||||
classes.push('debug');
|
||||
}
|
||||
|
||||
if (this.centerAvatar) {
|
||||
classes.push('centered-avatar');
|
||||
}
|
||||
|
||||
return classes.join(' ');
|
||||
},
|
||||
backgroundClass () {
|
||||
if (this.member) {
|
||||
const { background } = this.member.preferences;
|
||||
@@ -290,6 +356,10 @@ export default {
|
||||
},
|
||||
hideGear (gearType) {
|
||||
if (!this.member) return true;
|
||||
if (!this.showWeapon) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (gearType === 'weapon') {
|
||||
const equippedWeapon = this.member.items.gear[this.costumeClass][gearType];
|
||||
|
||||
|
||||
@@ -1,352 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<div
|
||||
v-if="isUserMentioned"
|
||||
class="mentioned-icon"
|
||||
></div>
|
||||
<div
|
||||
v-if="hasPermission(user, 'moderator') && msg.flagCount"
|
||||
class="message-hidden"
|
||||
>
|
||||
{{ flagCountDescription }}
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<user-link
|
||||
:user-id="msg.uuid"
|
||||
:name="msg.user"
|
||||
:backer="msg.backer"
|
||||
:contributor="msg.contributor"
|
||||
/>
|
||||
<p class="time">
|
||||
<span
|
||||
v-if="msg.username"
|
||||
class="mr-1"
|
||||
>@{{ msg.username }}</span>
|
||||
<span
|
||||
v-if="msg.username"
|
||||
class="mr-1"
|
||||
>•</span>
|
||||
<span
|
||||
v-b-tooltip.hover="messageDate"
|
||||
>{{ msg.timestamp | timeAgo }} </span>
|
||||
<span v-if="msg.client && user.contributor.level >= 4">({{ msg.client }})</span>
|
||||
</p>
|
||||
<div
|
||||
ref="markdownContainer"
|
||||
class="text markdown"
|
||||
dir="auto"
|
||||
v-html="parseMarkdown(msg.text)"
|
||||
></div>
|
||||
<hr>
|
||||
<div
|
||||
v-if="msg.id"
|
||||
class="d-flex"
|
||||
>
|
||||
<div
|
||||
class="action d-flex align-items-center"
|
||||
@click="copyAsTodo(msg)"
|
||||
>
|
||||
<div
|
||||
class="svg-icon"
|
||||
v-html="icons.copy"
|
||||
></div>
|
||||
<div>{{ $t('copyAsTodo') }}</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="(user.flags.communityGuidelinesAccepted && msg.uuid !== 'system')
|
||||
&& (!isMessageReported || hasPermission(user, 'moderator'))"
|
||||
class="action d-flex align-items-center"
|
||||
@click="report(msg)"
|
||||
>
|
||||
<div
|
||||
v-once
|
||||
class="svg-icon"
|
||||
v-html="icons.report"
|
||||
></div>
|
||||
<div v-once>
|
||||
{{ $t('report') }}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="msg.uuid === user._id || hasPermission(user, 'moderator')"
|
||||
class="action d-flex align-items-center"
|
||||
@click="remove()"
|
||||
>
|
||||
<div
|
||||
v-once
|
||||
class="svg-icon"
|
||||
v-html="icons.delete"
|
||||
></div>
|
||||
<div v-once>
|
||||
{{ $t('delete') }}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-b-tooltip="{title: likeTooltip(msg.likes[user._id])}"
|
||||
class="ml-auto d-flex"
|
||||
>
|
||||
<div
|
||||
v-if="likeCount > 0"
|
||||
class="action d-flex align-items-center mr-0"
|
||||
:class="{activeLike: msg.likes[user._id]}"
|
||||
@click="like()"
|
||||
>
|
||||
<div
|
||||
class="svg-icon"
|
||||
:title="$t('liked')"
|
||||
v-html="icons.liked"
|
||||
></div>
|
||||
+{{ likeCount }}
|
||||
</div>
|
||||
<div
|
||||
v-if="likeCount === 0"
|
||||
class="action d-flex align-items-center mr-0"
|
||||
:class="{activeLike: msg.likes[user._id]}"
|
||||
@click="like()"
|
||||
>
|
||||
<div
|
||||
class="svg-icon"
|
||||
:title="$t('like')"
|
||||
v-html="icons.like"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
<span v-if="!msg.likes[user._id]">{{ $t('like') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.at-highlight {
|
||||
background-color: rgba(213, 200, 255, 0.32);
|
||||
padding: 0.1rem;
|
||||
}
|
||||
|
||||
.at-text {
|
||||
color: #6133b4;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '~@/assets/scss/colors.scss';
|
||||
|
||||
.mentioned-icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border-radius: 50%;
|
||||
background-color: #bda8ff;
|
||||
box-shadow: 0 1px 1px 0 rgba(26, 24, 29, 0.12);
|
||||
position: absolute;
|
||||
right: -.5em;
|
||||
top: -.5em;
|
||||
}
|
||||
|
||||
.message-hidden {
|
||||
margin-left: 1.5em;
|
||||
margin-top: 1em;
|
||||
color: red;
|
||||
}
|
||||
|
||||
hr {
|
||||
margin-bottom: 0.5rem;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.card-body {
|
||||
padding: 0.75rem 1.25rem 0.75rem 1.25rem;
|
||||
|
||||
.time {
|
||||
font-size: 12px;
|
||||
color: #878190;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.text {
|
||||
font-size: 14px;
|
||||
color: #4e4a57;
|
||||
text-align: initial;
|
||||
min-height: 0rem;
|
||||
}
|
||||
}
|
||||
|
||||
.action {
|
||||
display: inline-block;
|
||||
color: #878190;
|
||||
margin-right: 1em;
|
||||
font-size: 12px;
|
||||
|
||||
:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.svg-icon {
|
||||
color: #A5A1AC;
|
||||
margin-right: .2em;
|
||||
width: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.activeLike {
|
||||
color: $purple-300;
|
||||
|
||||
.svg-icon {
|
||||
color: $purple-400;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import moment from 'moment';
|
||||
import cloneDeep from 'lodash/cloneDeep';
|
||||
import escapeRegExp from 'lodash/escapeRegExp';
|
||||
|
||||
import { CHAT_FLAG_LIMIT_FOR_HIDING, CHAT_FLAG_FROM_SHADOW_MUTE } from '@/../../common/script/constants';
|
||||
import renderWithMentions from '@/libs/renderWithMentions';
|
||||
import { userStateMixin } from '../../mixins/userState';
|
||||
import userLink from '../userLink';
|
||||
|
||||
import deleteIcon from '@/assets/svg/delete.svg';
|
||||
import copyIcon from '@/assets/svg/copy.svg';
|
||||
import likeIcon from '@/assets/svg/like.svg';
|
||||
import likedIcon from '@/assets/svg/liked.svg';
|
||||
import reportIcon from '@/assets/svg/report.svg';
|
||||
|
||||
export default {
|
||||
components: { userLink },
|
||||
filters: {
|
||||
timeAgo (value) {
|
||||
return moment(value).fromNow();
|
||||
},
|
||||
date (value) {
|
||||
// @TODO: Vue doesn't support this so we cant user preference
|
||||
return moment(value).toDate().toString();
|
||||
},
|
||||
},
|
||||
mixins: [userStateMixin],
|
||||
props: {
|
||||
msg: {},
|
||||
groupId: {},
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
icons: Object.freeze({
|
||||
like: likeIcon,
|
||||
copy: copyIcon,
|
||||
report: reportIcon,
|
||||
delete: deleteIcon,
|
||||
liked: likedIcon,
|
||||
}),
|
||||
reported: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
isUserMentioned () {
|
||||
const message = this.msg;
|
||||
|
||||
if (message.highlight) return true;
|
||||
|
||||
const { user } = this;
|
||||
const displayName = user.profile.name;
|
||||
const { username } = user.auth.local;
|
||||
const pattern = `@(${escapeRegExp(displayName)}|${escapeRegExp(username)})(\\b)`;
|
||||
message.highlight = new RegExp(pattern, 'i').test(message.text);
|
||||
|
||||
return message.highlight;
|
||||
},
|
||||
likeCount () {
|
||||
const message = this.msg;
|
||||
if (!message.likes) return 0;
|
||||
|
||||
let likeCount = 0;
|
||||
for (const key of Object.keys(message.likes)) {
|
||||
const like = message.likes[key];
|
||||
if (like) likeCount += 1;
|
||||
}
|
||||
return likeCount;
|
||||
},
|
||||
isMessageReported () {
|
||||
return (this.msg.flags && this.msg.flags[this.user.id]) || this.reported;
|
||||
},
|
||||
flagCountDescription () {
|
||||
if (!this.msg.flagCount) return '';
|
||||
if (this.msg.flagCount < CHAT_FLAG_LIMIT_FOR_HIDING) return 'Message flagged once, not hidden';
|
||||
if (this.msg.flagCount < CHAT_FLAG_FROM_SHADOW_MUTE) return 'Message hidden';
|
||||
return 'Message hidden (shadow-muted)';
|
||||
},
|
||||
messageDate () {
|
||||
const date = moment(this.msg.timestamp).toDate();
|
||||
return date.toString();
|
||||
},
|
||||
},
|
||||
mounted () {
|
||||
const links = this.$refs.markdownContainer.getElementsByTagName('a');
|
||||
for (let i = 0; i < links.length; i += 1) {
|
||||
let link = links[i].pathname;
|
||||
|
||||
// Internet Explorer does not provide the leading slash character in the pathname
|
||||
link = link.charAt(0) === '/' ? link : `/${link}`;
|
||||
|
||||
if (link.startsWith('/profile/')) {
|
||||
links[i].onclick = ev => {
|
||||
ev.preventDefault();
|
||||
this.$router.push({ path: link });
|
||||
};
|
||||
}
|
||||
}
|
||||
this.CHAT_FLAG_LIMIT_FOR_HIDING = CHAT_FLAG_LIMIT_FOR_HIDING;
|
||||
this.CHAT_FLAG_FROM_SHADOW_MUTE = CHAT_FLAG_FROM_SHADOW_MUTE;
|
||||
this.$emit('chat-card-mounted', this.msg.id);
|
||||
},
|
||||
methods: {
|
||||
async like () {
|
||||
const message = cloneDeep(this.msg);
|
||||
|
||||
await this.$store.dispatch('chat:like', {
|
||||
groupId: this.groupId,
|
||||
chatId: message.id,
|
||||
});
|
||||
|
||||
message.likes[this.user._id] = !message.likes[this.user._id];
|
||||
|
||||
this.$emit('message-liked', message);
|
||||
this.$root.$emit('bv::hide::tooltip');
|
||||
},
|
||||
likeTooltip (likedStatus) {
|
||||
if (!likedStatus) return this.$t('like');
|
||||
return null;
|
||||
},
|
||||
copyAsTodo (message) {
|
||||
this.$root.$emit('habitica::copy-as-todo', message);
|
||||
},
|
||||
report () {
|
||||
this.$root.$on('habitica:report-result', data => {
|
||||
if (data.ok) {
|
||||
this.reported = true;
|
||||
}
|
||||
|
||||
this.$root.$off('habitica:report-result');
|
||||
});
|
||||
|
||||
this.$root.$emit('habitica::report-chat', {
|
||||
message: this.msg,
|
||||
groupId: this.groupId || 'privateMessage',
|
||||
});
|
||||
},
|
||||
async remove () {
|
||||
if (!window.confirm(this.$t('areYouSureDeleteMessage'))) return; // eslint-disable-line no-alert
|
||||
|
||||
const message = this.msg;
|
||||
this.$emit('message-removed', message);
|
||||
|
||||
await this.$store.dispatch('chat:deleteChat', {
|
||||
groupId: this.groupId,
|
||||
chatId: message.id,
|
||||
});
|
||||
},
|
||||
parseMarkdown (text) {
|
||||
return renderWithMentions(text, this.user);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -3,15 +3,6 @@
|
||||
ref="container"
|
||||
class="container-fluid"
|
||||
>
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<copy-as-todo-modal
|
||||
:group-type="groupType"
|
||||
:group-name="groupName"
|
||||
:group-id="groupId"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row loadmore">
|
||||
<div v-if="canLoadMore">
|
||||
<div class="loadmore-divider"></div>
|
||||
@@ -33,6 +24,8 @@
|
||||
<div
|
||||
v-for="msg in messages.filter(m => chat && canViewFlag(m))"
|
||||
:key="msg.id"
|
||||
class="message-row"
|
||||
:class="{ 'margin-right': user._id !== msg.uuid}"
|
||||
>
|
||||
<div class="d-flex">
|
||||
<avatar
|
||||
@@ -45,16 +38,14 @@
|
||||
:override-top-padding="'14px'"
|
||||
@click.native="showMemberModal(msg.uuid)"
|
||||
/>
|
||||
<div class="card">
|
||||
<chat-card
|
||||
:msg="msg"
|
||||
:group-id="groupId"
|
||||
@message-liked="messageLiked"
|
||||
@message-removed="messageRemoved"
|
||||
@show-member-modal="showMemberModal"
|
||||
@chat-card-mounted="itemWasMounted"
|
||||
/>
|
||||
</div>
|
||||
<message-card
|
||||
:msg="msg"
|
||||
:group-id="groupId"
|
||||
:user-sent-message="user._id === msg.uuid"
|
||||
@message-liked="messageLiked"
|
||||
@message-removed="messageRemoved"
|
||||
@message-card-mounted="itemWasMounted"
|
||||
/>
|
||||
<avatar
|
||||
v-if="user._id === msg.uuid"
|
||||
:class="{ invisible: avatarUnavailable(msg) }"
|
||||
@@ -105,11 +96,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.avatar-left {
|
||||
margin-left: -1.5rem;
|
||||
margin-right: 2rem;
|
||||
}
|
||||
|
||||
.hr {
|
||||
width: 100%;
|
||||
height: 20px;
|
||||
@@ -137,11 +123,27 @@
|
||||
margin-bottom: .5em;
|
||||
padding: 0rem;
|
||||
width: 90%;
|
||||
|
||||
&.system-message {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.message-scroll .d-flex {
|
||||
min-width: 1px;
|
||||
}
|
||||
|
||||
.message-row {
|
||||
margin-left: 12px;
|
||||
margin-right: 0;
|
||||
margin-bottom: 1.2rem;
|
||||
|
||||
&:not(.margin-right) {
|
||||
.d-flex {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
@@ -152,13 +154,13 @@ import findIndex from 'lodash/findIndex';
|
||||
import { userStateMixin } from '../../mixins/userState';
|
||||
|
||||
import Avatar from '../avatar';
|
||||
import copyAsTodoModal from './copyAsTodoModal';
|
||||
import chatCard from './chatCard';
|
||||
import MessageCard from '@/components/messages/messageCard.vue';
|
||||
|
||||
// TODO merge chatMessages.vue (party message list) with messageList.vue (private message list)
|
||||
|
||||
export default {
|
||||
components: {
|
||||
copyAsTodoModal,
|
||||
chatCard,
|
||||
MessageCard,
|
||||
Avatar,
|
||||
},
|
||||
mixins: [userStateMixin],
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
<template>
|
||||
<b-modal
|
||||
id="copyAsTodo"
|
||||
:title="$t('copyMessageAsToDo')"
|
||||
:hide-footer="true"
|
||||
size="md"
|
||||
>
|
||||
<div class="form-group">
|
||||
<input
|
||||
v-model="task.text"
|
||||
class="form-control"
|
||||
type="text"
|
||||
>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<textarea
|
||||
v-model="task.notes"
|
||||
class="form-control"
|
||||
rows="5"
|
||||
focus-element="true"
|
||||
></textarea>
|
||||
</div>
|
||||
<hr>
|
||||
<task
|
||||
v-if="task._id"
|
||||
:is-user="isUser"
|
||||
:task="task"
|
||||
/>
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
class="btn btn-secondary"
|
||||
@click="close()"
|
||||
>
|
||||
{{ $t('close') }}
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-primary"
|
||||
@click="saveTodo()"
|
||||
>
|
||||
{{ $t('submit') }}
|
||||
</button>
|
||||
</div>
|
||||
</b-modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import taskDefaults from '@/../../common/script/libs/taskDefaults';
|
||||
import { mapActions } from '@/libs/store';
|
||||
import markdownDirective from '@/directives/markdown';
|
||||
import notificationsMixin from '@/mixins/notifications';
|
||||
import Task from '@/components/tasks/task';
|
||||
|
||||
const baseUrl = 'https://habitica.com';
|
||||
|
||||
export default {
|
||||
directives: {
|
||||
markdown: markdownDirective,
|
||||
},
|
||||
components: {
|
||||
Task,
|
||||
},
|
||||
mixins: [notificationsMixin],
|
||||
props: ['copyingMessage', 'groupType', 'groupName', 'groupId'],
|
||||
data () {
|
||||
return {
|
||||
isUser: true,
|
||||
task: {},
|
||||
};
|
||||
},
|
||||
mounted () {
|
||||
this.$root.$on('habitica::copy-as-todo', message => {
|
||||
const notes = `${message.user || 'system message'}${message.user ? ' wrote' : ''} in [${this.groupName}](${this.groupPath()})`;
|
||||
const newTask = {
|
||||
text: message.text,
|
||||
type: 'todo',
|
||||
notes,
|
||||
};
|
||||
this.task = taskDefaults(newTask, this.$store.state.user.data);
|
||||
this.$root.$emit('bv::show::modal', 'copyAsTodo');
|
||||
});
|
||||
},
|
||||
beforeDestroy () {
|
||||
this.$root.$off('habitica::copy-as-todo');
|
||||
},
|
||||
methods: {
|
||||
...mapActions({
|
||||
createTask: 'tasks:create',
|
||||
}),
|
||||
groupPath () {
|
||||
if (this.groupType === 'party') {
|
||||
return `${baseUrl}/party`;
|
||||
}
|
||||
return `${baseUrl}/groups/guild/${this.groupId}`;
|
||||
},
|
||||
close () {
|
||||
this.$root.$emit('bv::hide::modal', 'copyAsTodo');
|
||||
},
|
||||
saveTodo () {
|
||||
this.createTask(this.task);
|
||||
this.text(this.$t('messageAddedAsToDo'));
|
||||
this.$root.$emit('bv::hide::modal', 'copyAsTodo');
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -22,13 +22,13 @@
|
||||
:placeholder="placeholder"
|
||||
:class="{'user-entry': newMessage}"
|
||||
:maxlength="MAX_MESSAGE_LENGTH"
|
||||
@keydown="updateCarretPosition"
|
||||
@keydown="autoCompleteMixinUpdateCarretPosition"
|
||||
@keyup.ctrl.enter="sendMessageShortcut()"
|
||||
@keydown.tab="handleTab($event)"
|
||||
@keydown.up="selectPreviousAutocomplete($event)"
|
||||
@keydown.down="selectNextAutocomplete($event)"
|
||||
@keypress.enter="selectAutocomplete($event)"
|
||||
@keydown.esc="handleEscape($event)"
|
||||
@keydown.tab="autoCompleteMixinHandleTab($event)"
|
||||
@keydown.up="autoCompleteMixinSelectPreviousAutocomplete($event)"
|
||||
@keydown.down="autoCompleteMixinSelectNextAutocomplete($event)"
|
||||
@keypress.enter="autoCompleteMixinSelectAutocomplete($event)"
|
||||
@keydown.esc="autoCompleteMixinHandleEscape($event)"
|
||||
@paste="disableMessageSendShortcut()"
|
||||
></textarea>
|
||||
<span>{{ currentLength }} / {{ MAX_MESSAGE_LENGTH }}</span>
|
||||
@@ -36,8 +36,8 @@
|
||||
ref="autocomplete"
|
||||
:text="newMessage"
|
||||
:textbox="textbox"
|
||||
:coords="coords"
|
||||
:caret-position="caretPosition"
|
||||
:coords="mixinData.autoComplete.coords"
|
||||
:caret-position="mixinData.autoComplete.caretPosition"
|
||||
:chat="group.chat"
|
||||
@select="selectedAutocomplete"
|
||||
/>
|
||||
@@ -74,7 +74,7 @@
|
||||
<slot name="additionRow"></slot>
|
||||
<div class="row">
|
||||
<div class="hr col-12"></div>
|
||||
<chat-message
|
||||
<chat-messages
|
||||
:chat.sync="group.chat"
|
||||
:group-type="group.type"
|
||||
:group-id="group._id"
|
||||
@@ -86,16 +86,15 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import debounce from 'lodash/debounce';
|
||||
|
||||
import { MAX_MESSAGE_LENGTH } from '@/../../common/script/constants';
|
||||
import externalLinks from '../../mixins/externalLinks';
|
||||
|
||||
import autocomplete from '../chat/autoComplete';
|
||||
import communityGuidelines from './communityGuidelines';
|
||||
import chatMessage from '../chat/chatMessages';
|
||||
import chatMessages from '../chat/chatMessages';
|
||||
import { mapState } from '@/libs/store';
|
||||
import markdownDirective from '@/directives/markdown';
|
||||
import { autoCompleteHelperMixin } from '@/mixins/autoCompleteHelper';
|
||||
|
||||
export default {
|
||||
directives: {
|
||||
@@ -104,23 +103,18 @@ export default {
|
||||
components: {
|
||||
autocomplete,
|
||||
communityGuidelines,
|
||||
chatMessage,
|
||||
chatMessages,
|
||||
},
|
||||
mixins: [externalLinks],
|
||||
mixins: [externalLinks, autoCompleteHelperMixin],
|
||||
props: ['label', 'group', 'placeholder'],
|
||||
data () {
|
||||
return {
|
||||
newMessage: '',
|
||||
sending: false,
|
||||
caretPosition: 0,
|
||||
chat: {
|
||||
submitDisable: false,
|
||||
submitTimeout: null,
|
||||
},
|
||||
coords: {
|
||||
TOP: 0,
|
||||
LEFT: 0,
|
||||
},
|
||||
textbox: null,
|
||||
MAX_MESSAGE_LENGTH: MAX_MESSAGE_LENGTH.toString(),
|
||||
};
|
||||
@@ -142,35 +136,6 @@ export default {
|
||||
this.handleExternalLinks();
|
||||
},
|
||||
methods: {
|
||||
// https://medium.com/@_jh3y/how-to-where-s-the-caret-getting-the-xy-position-of-the-caret-a24ba372990a
|
||||
getCoord (e, text) {
|
||||
this.caretPosition = text.selectionEnd;
|
||||
const div = document.createElement('div');
|
||||
const span = document.createElement('span');
|
||||
const copyStyle = getComputedStyle(text);
|
||||
|
||||
[].forEach.call(copyStyle, prop => {
|
||||
div.style[prop] = copyStyle[prop];
|
||||
});
|
||||
|
||||
div.style.position = 'absolute';
|
||||
document.body.appendChild(div);
|
||||
div.textContent = text.value.substr(0, this.caretPosition);
|
||||
span.textContent = text.value.substr(this.caretPosition) || '.';
|
||||
div.appendChild(span);
|
||||
this.coords = {
|
||||
TOP: span.offsetTop,
|
||||
LEFT: span.offsetLeft,
|
||||
};
|
||||
document.body.removeChild(div);
|
||||
},
|
||||
updateCarretPosition: debounce(function updateCarretPosition (eventUpdate) {
|
||||
this._updateCarretPosition(eventUpdate);
|
||||
}, 250),
|
||||
_updateCarretPosition (eventUpdate) {
|
||||
const text = eventUpdate.target;
|
||||
this.getCoord(eventUpdate, text);
|
||||
},
|
||||
async sendMessageShortcut () {
|
||||
// If the user recently pasted in the text field, don't submit
|
||||
if (!this.chat.submitDisable) {
|
||||
@@ -221,50 +186,6 @@ export default {
|
||||
}, 500);
|
||||
},
|
||||
|
||||
handleTab (e) {
|
||||
if (this.$refs.autocomplete.searchActive) {
|
||||
e.preventDefault();
|
||||
if (e.shiftKey) {
|
||||
this.$refs.autocomplete.selectPrevious();
|
||||
} else {
|
||||
this.$refs.autocomplete.selectNext();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
handleEscape (e) {
|
||||
if (this.$refs.autocomplete.searchActive) {
|
||||
e.preventDefault();
|
||||
this.$refs.autocomplete.cancel();
|
||||
}
|
||||
},
|
||||
|
||||
selectNextAutocomplete (e) {
|
||||
if (this.$refs.autocomplete.searchActive) {
|
||||
e.preventDefault();
|
||||
this.$refs.autocomplete.selectNext();
|
||||
}
|
||||
},
|
||||
|
||||
selectPreviousAutocomplete (e) {
|
||||
if (this.$refs.autocomplete.searchActive) {
|
||||
e.preventDefault();
|
||||
this.$refs.autocomplete.selectPrevious();
|
||||
}
|
||||
},
|
||||
|
||||
selectAutocomplete (e) {
|
||||
if (this.$refs.autocomplete.searchActive) {
|
||||
if (this.$refs.autocomplete.selected !== null) {
|
||||
e.preventDefault();
|
||||
this.$refs.autocomplete.makeSelection();
|
||||
} else {
|
||||
// no autocomplete selected, newline instead
|
||||
this.$refs.autocomplete.cancel();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
selectedAutocomplete (newText, newCaret) {
|
||||
this.newMessage = newText;
|
||||
// Wait for v-modal to update
|
||||
@@ -273,7 +194,6 @@ export default {
|
||||
this.textbox.focus();
|
||||
});
|
||||
},
|
||||
|
||||
fetchRecentMessages () {
|
||||
this.$emit('fetchRecentMessages');
|
||||
},
|
||||
@@ -284,10 +204,7 @@ export default {
|
||||
beforeRouteUpdate (to, from, next) {
|
||||
// Reset chat
|
||||
this.newMessage = '';
|
||||
this.coords = {
|
||||
TOP: 0,
|
||||
LEFT: 0,
|
||||
};
|
||||
this.autoCompleteMixinResetCoordsPosition();
|
||||
|
||||
next();
|
||||
},
|
||||
|
||||
@@ -225,10 +225,9 @@
|
||||
</a>
|
||||
</div>
|
||||
<div class="quest-icon">
|
||||
<div
|
||||
<Sprite
|
||||
class="quest"
|
||||
:class="`inventory_quest_scroll_${questData.key}`"
|
||||
></div>
|
||||
:image-name="`inventory_quest_scroll_${questData.key}`" />
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
|
||||
@@ -297,7 +297,7 @@
|
||||
<div class="topbar-dropdown">
|
||||
<router-link
|
||||
v-if="user.permissions.fullAccess ||
|
||||
user.permissions.userSupport || user.permissions.newsPoster"
|
||||
user.permissions.userSupport"
|
||||
class="topbar-dropdown-item dropdown-item"
|
||||
:to="{name: 'adminPanel'}"
|
||||
>
|
||||
|
||||
@@ -12,20 +12,21 @@
|
||||
<strong> {{ notification.data.title }} </strong>
|
||||
<span> {{ notification.data.text }} </span>
|
||||
</div>
|
||||
<div
|
||||
<Sprite
|
||||
slot="icon"
|
||||
class="mt-3"
|
||||
:class="notification.data.icon"
|
||||
></div>
|
||||
:image-name="notification.data.icon" />
|
||||
</base-notification>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import BaseNotification from './base';
|
||||
import Sprite from '@/components/ui/sprite.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
BaseNotification,
|
||||
Sprite,
|
||||
},
|
||||
props: {
|
||||
notification: {
|
||||
|
||||
@@ -10,20 +10,21 @@
|
||||
slot="content"
|
||||
v-html="$t('newSubscriberItem')"
|
||||
></div>
|
||||
<div
|
||||
<Sprite
|
||||
slot="icon"
|
||||
:class="mysteryClass"
|
||||
></div>
|
||||
:image-name="mysteryClass" />
|
||||
</base-notification>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import moment from 'moment';
|
||||
import BaseNotification from './base';
|
||||
import Sprite from '@/components/ui/sprite.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
BaseNotification,
|
||||
Sprite,
|
||||
},
|
||||
props: ['notification', 'canRemove'],
|
||||
computed: {
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
:name="member.profile.name"
|
||||
:backer="member.backer"
|
||||
:contributor="member.contributor"
|
||||
:smaller-style="true"
|
||||
/>
|
||||
<inline-class-badge
|
||||
v-if="member.stats"
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
<template>
|
||||
<div
|
||||
class="d-inline-flex like-button"
|
||||
@click="like()"
|
||||
>
|
||||
<div
|
||||
v-b-tooltip="{title: likeTooltip(likeCount)}"
|
||||
class="d-flex"
|
||||
>
|
||||
<div
|
||||
v-if="likeCount > 0"
|
||||
class="action d-flex align-items-center mr-0"
|
||||
:class="{isLiked: true, currentUserLiked: likedByCurrentUser}"
|
||||
>
|
||||
<div
|
||||
class="svg-icon mr-1"
|
||||
:title="$t('liked')"
|
||||
v-html="icons.liked"
|
||||
></div>
|
||||
+{{ likeCount }}
|
||||
</div>
|
||||
<div
|
||||
v-if="likeCount === 0"
|
||||
class="action d-flex align-items-center mr-1"
|
||||
>
|
||||
<div
|
||||
class="svg-icon"
|
||||
:title="$t('like')"
|
||||
v-html="icons.like"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
<span v-if="likeCount === 0">{{ $t('like') }}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '~@/assets/scss/colors.scss';
|
||||
@import '~@/assets/scss/tiers.scss';
|
||||
|
||||
.action {
|
||||
display: inline-block;
|
||||
margin-right: 1em;
|
||||
|
||||
.svg-icon {
|
||||
color: $gray-100;
|
||||
width: 16px;
|
||||
}
|
||||
|
||||
&.isLiked {
|
||||
color: $purple-200;
|
||||
font-weight: bold;
|
||||
|
||||
.svg-icon {
|
||||
color: $purple-300;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.like-button {
|
||||
color: $gray-100;
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
color: $purple-200;
|
||||
|
||||
.svg-icon {
|
||||
color: $purple-300;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import likeIcon from '@/assets/svg/like.svg';
|
||||
import likedIcon from '@/assets/svg/liked.svg';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
likeCount: {
|
||||
type: Number,
|
||||
},
|
||||
likedByCurrentUser: {
|
||||
type: Boolean,
|
||||
},
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
icons: Object.freeze({
|
||||
like: likeIcon,
|
||||
liked: likedIcon,
|
||||
}),
|
||||
};
|
||||
},
|
||||
|
||||
methods: {
|
||||
async like () {
|
||||
this.$emit('toggle-like');
|
||||
},
|
||||
likeTooltip (likedStatus) {
|
||||
if (!likedStatus) return this.$t('like');
|
||||
return null;
|
||||
},
|
||||
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -1,73 +1,157 @@
|
||||
<template>
|
||||
<div class="card-body">
|
||||
<user-link
|
||||
:user-id="msg.uuid"
|
||||
:name="msg.user"
|
||||
:backer="msg.backer"
|
||||
:contributor="msg.contributor"
|
||||
/>
|
||||
<p class="time">
|
||||
<span
|
||||
v-if="msg.username"
|
||||
class="mr-1"
|
||||
>@{{ msg.username }}</span><span
|
||||
v-if="msg.username"
|
||||
class="mr-1"
|
||||
>•</span>
|
||||
<span
|
||||
v-b-tooltip.hover="messageDate"
|
||||
>{{ msg.timestamp | timeAgo }} </span>
|
||||
<span v-if="msg.client && user.contributor.level >= 4"> ({{ msg.client }})</span>
|
||||
</p>
|
||||
<div
|
||||
class="card"
|
||||
:class="{
|
||||
'system-message': isSystemMessage
|
||||
}"
|
||||
>
|
||||
<div
|
||||
class="text markdown"
|
||||
dir="auto"
|
||||
v-html="parseMarkdown(msg.text)"
|
||||
></div>
|
||||
<div
|
||||
v-if="isMessageReported"
|
||||
class="reported"
|
||||
>
|
||||
<span v-once>{{ $t('reportedMessage') }}</span><br>
|
||||
<span v-once>{{ $t('canDeleteNow') }}</span>
|
||||
</div>
|
||||
<hr>
|
||||
<div
|
||||
v-if="msg.id"
|
||||
class="d-flex"
|
||||
v-b-tooltip.hover="messageDateForSystemMessage"
|
||||
class="message-card"
|
||||
|
||||
:class="{
|
||||
'user-sent-message': userSentMessage,
|
||||
'user-received-message': !userSentMessage && !isSystemMessage,
|
||||
'system-message': isSystemMessage
|
||||
}"
|
||||
>
|
||||
<div
|
||||
v-if="!isMessageReported"
|
||||
class="action d-flex align-items-center"
|
||||
@click="report(msg)"
|
||||
v-if="isUserMentioned"
|
||||
class="mentioned-icon"
|
||||
></div>
|
||||
<div
|
||||
v-if="userIsModerator && msg.flagCount"
|
||||
class="message-hidden"
|
||||
>
|
||||
<div
|
||||
v-once
|
||||
class="svg-icon"
|
||||
v-html="icons.report"
|
||||
></div>
|
||||
<div v-once>
|
||||
{{ $t('report') }}
|
||||
</div>
|
||||
{{ flagCountDescription }}
|
||||
</div>
|
||||
<div
|
||||
class="action d-flex align-items-center"
|
||||
@click="remove()"
|
||||
class="card-body"
|
||||
>
|
||||
<user-link
|
||||
v-if="!isSystemMessage"
|
||||
:user-id="msg.uuid"
|
||||
:name="msg.user"
|
||||
:backer="msg.backer"
|
||||
:contributor="msg.contributor"
|
||||
/>
|
||||
<p
|
||||
v-if="!isSystemMessage"
|
||||
class="time"
|
||||
>
|
||||
<span
|
||||
v-if="msg.username"
|
||||
class="mr-1"
|
||||
>@{{ msg.username }}</span><span
|
||||
v-if="msg.username"
|
||||
class="mr-1"
|
||||
>•</span>
|
||||
<span v-b-tooltip.hover="messageDate">{{ msg.timestamp | timeAgo }} </span>
|
||||
<span v-if="msg.client && user.contributor.level >= 4">
|
||||
({{ msg.client }})
|
||||
</span>
|
||||
</p>
|
||||
|
||||
<b-dropdown
|
||||
v-if="!isSystemMessage"
|
||||
right="right"
|
||||
variant="flat"
|
||||
toggle-class="with-icon"
|
||||
class="card-menu no-min-width"
|
||||
:no-caret="true"
|
||||
>
|
||||
<template #button-content>
|
||||
<span
|
||||
v-once
|
||||
class="svg-icon inline menuIcon color"
|
||||
v-html="icons.menuIcon"
|
||||
>
|
||||
</span>
|
||||
</template>
|
||||
<b-dropdown-item
|
||||
class="selectListItem"
|
||||
@click="copy(msg)"
|
||||
>
|
||||
<span class="with-icon">
|
||||
<span
|
||||
v-once
|
||||
class="svg-icon icon-16 color"
|
||||
v-html="icons.copy"
|
||||
></span>
|
||||
<span v-once>
|
||||
{{ $t('copy') }}
|
||||
</span>
|
||||
</span>
|
||||
</b-dropdown-item>
|
||||
<b-dropdown-item
|
||||
v-if="canReportMessage"
|
||||
class="selectListItem custom-hover--red"
|
||||
@click="report(msg)"
|
||||
>
|
||||
<span class="with-icon">
|
||||
<span
|
||||
v-once
|
||||
class="svg-icon icon-16 color"
|
||||
v-html="icons.report"
|
||||
></span>
|
||||
<span v-once>
|
||||
{{ $t('report') }}
|
||||
</span>
|
||||
</span>
|
||||
</b-dropdown-item>
|
||||
<b-dropdown-item
|
||||
v-if="canDeleteMessage"
|
||||
class="selectListItem custom-hover--red"
|
||||
@click="remove()"
|
||||
>
|
||||
<span class="with-icon">
|
||||
<span
|
||||
v-once
|
||||
class="svg-icon icon-16 color"
|
||||
v-html="icons.delete"
|
||||
></span>
|
||||
<span v-once>
|
||||
{{ $t('delete') }}
|
||||
</span>
|
||||
</span>
|
||||
</b-dropdown-item>
|
||||
</b-dropdown>
|
||||
|
||||
<div
|
||||
v-once
|
||||
class="svg-icon"
|
||||
v-html="icons.delete"
|
||||
></div>
|
||||
<div v-once>
|
||||
{{ $t('delete') }}
|
||||
v-if="isSystemMessage"
|
||||
class="system-message-body"
|
||||
>
|
||||
{{ msg.unformattedText }}
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
ref="markdownContainer"
|
||||
class="text markdown"
|
||||
dir="auto"
|
||||
v-html="parseMarkdown(msg.text)"
|
||||
></div>
|
||||
<div
|
||||
v-if="isMessageReported"
|
||||
class="reported"
|
||||
>
|
||||
<span v-once>{{ $t('reportedMessage') }}</span><br>
|
||||
<span v-once>{{ $t('canDeleteNow') }}</span>
|
||||
</div>
|
||||
|
||||
<like-button
|
||||
v-if="canLikeMessage"
|
||||
class="mt-75"
|
||||
:liked-by-current-user="msg.likes[user._id]"
|
||||
:like-count="likeCount"
|
||||
@toggle-like="like()"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.message-card {
|
||||
.at-highlight {
|
||||
background-color: rgba(213, 200, 255, 0.32);
|
||||
padding: 0.1rem;
|
||||
@@ -76,43 +160,76 @@
|
||||
.at-text {
|
||||
color: #6133b4;
|
||||
}
|
||||
|
||||
.card-menu button {
|
||||
justify-content: center;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 1rem;
|
||||
width: 1rem;
|
||||
}
|
||||
|
||||
.markdown p:last-of-type {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '~@/assets/scss/colors.scss';
|
||||
@import '~@/assets/scss/tiers.scss';
|
||||
@import '~@/assets/scss/colors.scss';
|
||||
@import '~@/assets/scss/tiers.scss';
|
||||
|
||||
.action {
|
||||
display: inline-block;
|
||||
color: $gray-200;
|
||||
margin-right: 1em;
|
||||
font-size: 12px;
|
||||
.card {
|
||||
background: transparent !important;
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
.message-card:not(.system-message) {
|
||||
background: white;
|
||||
}
|
||||
|
||||
.svg-icon {
|
||||
color: $gray-300;
|
||||
margin-right: .2em;
|
||||
width: 16px;
|
||||
}
|
||||
.mentioned-icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border-radius: 50%;
|
||||
background-color: $purple-500;
|
||||
box-shadow: 0 1px 1px 0 rgba(26, 24, 29, 0.12);
|
||||
position: absolute;
|
||||
right: -.5em;
|
||||
top: -.5em;
|
||||
}
|
||||
|
||||
.message-hidden {
|
||||
margin-left: 1.5em;
|
||||
margin-top: 1em;
|
||||
color: red;
|
||||
}
|
||||
|
||||
.active {
|
||||
color: $purple-300;
|
||||
|
||||
.svg-icon {
|
||||
color: $purple-400;
|
||||
}
|
||||
}
|
||||
|
||||
.active {
|
||||
color: $purple-300;
|
||||
.message-card {
|
||||
border-radius: 7px;
|
||||
margin: 0;
|
||||
padding: 1rem 0.75rem 0.5rem 1rem;
|
||||
|
||||
.svg-icon {
|
||||
color: $purple-400;
|
||||
}
|
||||
&.system-message {
|
||||
padding-top: 0.5rem;
|
||||
}
|
||||
|
||||
.card-body {
|
||||
padding: 0.75rem 1.25rem 0.75rem 1.25rem;
|
||||
position: relative;
|
||||
padding: 0;
|
||||
|
||||
.time {
|
||||
font-size: 12px;
|
||||
color: $gray-200;
|
||||
color: $gray-100;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
@@ -123,49 +240,173 @@
|
||||
min-height: 0rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hr {
|
||||
margin-bottom: 0.5rem;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
.card-menu {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
|
||||
.reported {
|
||||
margin-top: 18px;
|
||||
color: $red-50;
|
||||
&:not(.show) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.card-body:hover {
|
||||
.card-menu {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
hr {
|
||||
margin-bottom: 0.5rem;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.reported {
|
||||
margin-top: 18px;
|
||||
color: $red-50;
|
||||
}
|
||||
|
||||
.selectListItem:not(:hover) .svg-icon.icon-16.color {
|
||||
color: #{$gray-100}
|
||||
}
|
||||
|
||||
.custom-hover--red {
|
||||
--hover-color: #{$maroon-50};
|
||||
--hover-background: #{rgba($red-500, 0.25)};
|
||||
}
|
||||
|
||||
.user-sent-message {
|
||||
border: 1px solid $purple-400;
|
||||
}
|
||||
|
||||
.system-message {
|
||||
border: 1px solid $purple-400;
|
||||
}
|
||||
|
||||
.user-received-message {
|
||||
border: 1px solid $gray-500;
|
||||
}
|
||||
|
||||
.card-menu {
|
||||
// icon-color is the menu icon itself
|
||||
--icon-color: #{$gray-100};
|
||||
|
||||
--dropdown-item-hover-icon-color: #{$gray-100};
|
||||
|
||||
&:hover {
|
||||
--icon-color: #{$purple-300};
|
||||
}
|
||||
}
|
||||
|
||||
.menuIcon {
|
||||
width: 4px;
|
||||
height: 1rem;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.system-message-body {
|
||||
line-height: 1.71;
|
||||
text-align: center;
|
||||
color: $purple-300;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import axios from 'axios';
|
||||
import moment from 'moment';
|
||||
|
||||
import cloneDeep from 'lodash/cloneDeep';
|
||||
import escapeRegExp from 'lodash/escapeRegExp';
|
||||
import { CHAT_FLAG_FROM_SHADOW_MUTE, CHAT_FLAG_LIMIT_FOR_HIDING } from '@/../../common/script/constants';
|
||||
import externalLinks from '../../mixins/externalLinks';
|
||||
|
||||
import { CopyToClipboardMixin } from '@/mixins/copyToClipboard';
|
||||
|
||||
import renderWithMentions from '@/libs/renderWithMentions';
|
||||
import { mapState } from '@/libs/store';
|
||||
import userLink from '../userLink';
|
||||
|
||||
import deleteIcon from '@/assets/svg/delete.svg';
|
||||
import reportIcon from '@/assets/svg/report.svg';
|
||||
import menuIcon from '@/assets/svg/menu.svg';
|
||||
import { userStateMixin } from '@/mixins/userState';
|
||||
import copyIcon from '@/assets/svg/copy.svg';
|
||||
import LikeButton from '@/components/messages/likeButton.vue';
|
||||
|
||||
const LikeLogicMixin = {
|
||||
computed: {
|
||||
likeCount () {
|
||||
const message = this.msg;
|
||||
if (!message.likes) return 0;
|
||||
|
||||
let likeCount = 0;
|
||||
for (const key of Object.keys(message.likes)) {
|
||||
const like = message.likes[key];
|
||||
if (like) likeCount += 1;
|
||||
}
|
||||
return likeCount;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
|
||||
async like () {
|
||||
const message = cloneDeep(this.msg);
|
||||
|
||||
await this.$store.dispatch('chat:like', {
|
||||
groupId: this.groupId,
|
||||
chatMessageId: this.privateMessageMode ? message.uniqueMessageId : message.id,
|
||||
});
|
||||
|
||||
message.likes[this.user._id] = !message.likes[this.user._id];
|
||||
|
||||
this.$emit('message-liked', message);
|
||||
this.$root.$emit('bv::hide::tooltip');
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default {
|
||||
components: {
|
||||
LikeButton,
|
||||
userLink,
|
||||
},
|
||||
filters: {
|
||||
timeAgo (value) {
|
||||
return moment(value).fromNow();
|
||||
},
|
||||
date (value) {
|
||||
// @TODO: Vue doesn't support this so we cant user preference
|
||||
return moment(value).toDate().toString();
|
||||
},
|
||||
},
|
||||
mixins: [externalLinks],
|
||||
mixins: [
|
||||
externalLinks, userStateMixin, LikeLogicMixin,
|
||||
CopyToClipboardMixin,
|
||||
],
|
||||
props: {
|
||||
msg: {},
|
||||
msg: {
|
||||
type: Object,
|
||||
},
|
||||
groupId: {
|
||||
type: String,
|
||||
},
|
||||
privateMessageMode: {
|
||||
type: Boolean,
|
||||
},
|
||||
userSentMessage: {
|
||||
type: Boolean,
|
||||
},
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
icons: Object.freeze({
|
||||
delete: deleteIcon,
|
||||
report: reportIcon,
|
||||
copy: copyIcon,
|
||||
menuIcon,
|
||||
}),
|
||||
reported: false,
|
||||
};
|
||||
@@ -175,19 +416,100 @@ export default {
|
||||
isMessageReported () {
|
||||
return (this.msg.flags && this.msg.flags[this.user.id]) || this.reported;
|
||||
},
|
||||
messageDateForSystemMessage () {
|
||||
return this.isSystemMessage ? this.messageDate : '';
|
||||
},
|
||||
messageDate () {
|
||||
const date = moment(this.msg.timestamp).toDate();
|
||||
return date.toString();
|
||||
},
|
||||
userIsModerator () {
|
||||
return this.hasPermission(this.user, 'moderator');
|
||||
},
|
||||
isSystemMessage () {
|
||||
return this.msg.uuid === 'system';
|
||||
},
|
||||
canLikeMessage () {
|
||||
if (this.isSystemMessage) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.privateMessageMode) {
|
||||
return Boolean(this.msg.uniqueMessageId);
|
||||
}
|
||||
|
||||
return this.msg.id;
|
||||
},
|
||||
canDeleteMessage () {
|
||||
return this.privateMessageMode
|
||||
|| this.msg.uuid === this.user._id
|
||||
|| this.userIsModerator;
|
||||
},
|
||||
canReportMessage () {
|
||||
if (this.privateMessageMode) {
|
||||
return !this.isMessageReported;
|
||||
}
|
||||
return (this.user.flags.communityGuidelinesAccepted && this.msg.uuid !== 'system')
|
||||
&& (!this.isMessageReported || this.userIsModerator);
|
||||
},
|
||||
isUserMentioned () {
|
||||
const message = this.msg;
|
||||
|
||||
if (message.highlight) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const { user } = this;
|
||||
const displayName = user.profile.name;
|
||||
const { username } = user.auth.local;
|
||||
const pattern = `@(${escapeRegExp(displayName)}|${escapeRegExp(username)})(\\b)`;
|
||||
message.highlight = new RegExp(pattern, 'i').test(message.text);
|
||||
|
||||
return message.highlight;
|
||||
},
|
||||
flagCountDescription () {
|
||||
if (!this.msg.flagCount) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (this.msg.flagCount < CHAT_FLAG_LIMIT_FOR_HIDING) {
|
||||
return 'Message flagged once, not hidden';
|
||||
}
|
||||
|
||||
if (this.msg.flagCount < CHAT_FLAG_FROM_SHADOW_MUTE) {
|
||||
return 'Message hidden';
|
||||
}
|
||||
|
||||
return 'Message hidden (shadow-muted)';
|
||||
},
|
||||
},
|
||||
mounted () {
|
||||
this.$emit('message-card-mounted');
|
||||
this.handleExternalLinks();
|
||||
this.mapProfileLinksToModal();
|
||||
},
|
||||
updated () {
|
||||
this.handleExternalLinks();
|
||||
this.mapProfileLinksToModal();
|
||||
},
|
||||
methods: {
|
||||
mapProfileLinksToModal () {
|
||||
const links = this.$refs.markdownContainer.getElementsByTagName('a');
|
||||
for (let i = 0; i < links.length; i += 1) {
|
||||
let link = links[i].pathname;
|
||||
|
||||
// Internet Explorer does not provide the leading slash character in the pathname
|
||||
link = link.charAt(0) === '/' ? link : `/${link}`;
|
||||
|
||||
if (link.startsWith('/profile/')) {
|
||||
links[i].onclick = ev => {
|
||||
ev.preventDefault();
|
||||
this.$router.push({ path: link });
|
||||
};
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
report () {
|
||||
this.$root.$on('habitica:report-result', data => {
|
||||
if (data.ok) {
|
||||
@@ -199,16 +521,29 @@ export default {
|
||||
|
||||
this.$root.$emit('habitica::report-chat', {
|
||||
message: this.msg,
|
||||
groupId: 'privateMessage',
|
||||
groupId: this.groupId,
|
||||
});
|
||||
},
|
||||
async remove () {
|
||||
if (!window.confirm(this.$t('areYouSureDeleteMessage'))) return; // eslint-disable-line no-alert
|
||||
// eslint-disable-next-line no-alert
|
||||
if (!window.confirm(this.$t('areYouSureDeleteMessage'))) {
|
||||
return;
|
||||
}
|
||||
|
||||
const message = this.msg;
|
||||
this.$emit('message-removed', message);
|
||||
|
||||
await axios.delete(`/api/v4/inbox/messages/${message.id}`);
|
||||
if (this.privateMessageMode) {
|
||||
await axios.delete(`/api/v4/inbox/messages/${message.id}`);
|
||||
} else {
|
||||
await this.$store.dispatch('chat:deleteChat', {
|
||||
groupId: this.groupId,
|
||||
chatId: message.id,
|
||||
});
|
||||
}
|
||||
},
|
||||
copy (message) {
|
||||
this.mixinCopyToClipboard(message.text, this.$t('messageCopiedToClipboard'));
|
||||
},
|
||||
parseMarkdown (text) {
|
||||
return renderWithMentions(text, this.user);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<template>
|
||||
<div
|
||||
ref="container"
|
||||
class="container-fluid"
|
||||
class="message-list"
|
||||
>
|
||||
<div class="row loadmore">
|
||||
<div class="loadmore">
|
||||
<div v-if="canLoadMore && !isLoading">
|
||||
<div class="loadmore-divider-holder">
|
||||
<div class="loadmore-divider"></div>
|
||||
@@ -28,7 +28,7 @@
|
||||
<div
|
||||
v-for="(msg) in messages"
|
||||
:key="msg.id"
|
||||
class="row message-row"
|
||||
class="message-row"
|
||||
:class="{ 'margin-right': user._id !== msg.uuid}"
|
||||
>
|
||||
<div
|
||||
@@ -39,28 +39,31 @@
|
||||
class="avatar-left"
|
||||
:member="conversationOpponentUser"
|
||||
:avatar-only="true"
|
||||
:override-top-padding="'14px'"
|
||||
:show-weapon="true"
|
||||
:debug-mode="false"
|
||||
:override-top-padding="'0'"
|
||||
:hide-class-badge="true"
|
||||
@click.native="showMemberModal(msg.uuid)"
|
||||
/>
|
||||
<div
|
||||
class="card"
|
||||
:class="{'card-right': user._id !== msg.uuid, 'card-left': user._id === msg.uuid}"
|
||||
>
|
||||
<message-card
|
||||
:msg="msg"
|
||||
@message-removed="messageRemoved"
|
||||
@show-member-modal="showMemberModal"
|
||||
@message-card-mounted="itemWasMounted"
|
||||
/>
|
||||
</div>
|
||||
<message-card
|
||||
:msg="msg"
|
||||
:user-sent-message="user._id === msg.uuid"
|
||||
:group-id="'privateMessage'"
|
||||
:private-message-mode="true"
|
||||
@message-liked="messageLiked"
|
||||
@message-removed="messageRemoved"
|
||||
@show-member-modal="showMemberModal"
|
||||
@message-card-mounted="itemWasMounted"
|
||||
/>
|
||||
<avatar
|
||||
v-if="user && user._id === msg.uuid"
|
||||
class="avatar-right"
|
||||
:member="user"
|
||||
:avatar-only="true"
|
||||
:show-weapon="true"
|
||||
:debug-mode="false"
|
||||
:hide-class-badge="true"
|
||||
:override-top-padding="'14px'"
|
||||
:override-top-padding="'0'"
|
||||
@click.native="showMemberModal(msg.uuid)"
|
||||
/>
|
||||
</div>
|
||||
@@ -69,121 +72,110 @@
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '~@/assets/scss/colors.scss';
|
||||
@import '~@/assets/scss/colors.scss';
|
||||
|
||||
.avatar {
|
||||
width: 170px;
|
||||
min-width: 8rem;
|
||||
height: 120px;
|
||||
padding-top: 0 !important;
|
||||
.avatar-left, .avatar-right {
|
||||
align-self: center;
|
||||
|
||||
::v-deep .character-sprites {
|
||||
margin-bottom: -5px !important;
|
||||
padding-bottom: 0 !important;
|
||||
margin-top: -1px !important;
|
||||
}
|
||||
|
||||
.avatar-right {
|
||||
margin-left: -1rem;
|
||||
::v-deep .avatar {
|
||||
margin-left: -1.75rem;
|
||||
margin-right: -0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep .character-sprites {
|
||||
margin-right: 1rem !important;
|
||||
.card {
|
||||
border: 0px;
|
||||
margin-bottom: 1rem;
|
||||
padding: 0rem;
|
||||
width: 684px;
|
||||
|
||||
}
|
||||
|
||||
.message-list {
|
||||
width: 100%;
|
||||
padding-right: 10px;
|
||||
margin-right: 0 !important;
|
||||
}
|
||||
|
||||
.message-row {
|
||||
margin-left: 12px;
|
||||
margin-right: 0;
|
||||
margin-bottom: 1.2rem;
|
||||
|
||||
&:not(.margin-right) {
|
||||
.d-flex {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.card {
|
||||
border: 0px;
|
||||
margin-bottom: 1rem;
|
||||
padding: 0rem;
|
||||
width: 684px;
|
||||
}
|
||||
.message-row {
|
||||
margin-left: 12px;
|
||||
margin-right: 12px;
|
||||
.hr {
|
||||
width: 100%;
|
||||
height: 20px;
|
||||
border-bottom: 1px solid $gray-500;
|
||||
text-align: center;
|
||||
margin: 2em 0;
|
||||
}
|
||||
|
||||
&:not(.margin-right) {
|
||||
.d-flex {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
}
|
||||
@media only screen and (max-width: 1200px) {
|
||||
.card {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
.hr-middle {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
font-family: 'Roboto Condensed';
|
||||
line-height: 1.5;
|
||||
text-align: center;
|
||||
color: $gray-200;
|
||||
background-color: $gray-700;
|
||||
padding: .2em;
|
||||
margin-top: .2em;
|
||||
display: inline-block;
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
@media only screen and (min-width: 1400px) {
|
||||
.message-row {
|
||||
margin-left: -15px;
|
||||
margin-right: -30px;
|
||||
}
|
||||
}
|
||||
.loadmore {
|
||||
justify-content: center;
|
||||
margin-right: 12px;
|
||||
margin-top: 12px;
|
||||
margin-bottom: 24px;
|
||||
|
||||
.card-left {
|
||||
border: 1px solid $purple-500;
|
||||
}
|
||||
|
||||
.card-right {
|
||||
border: 1px solid $gray-500;
|
||||
}
|
||||
|
||||
.hr {
|
||||
> div {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 20px;
|
||||
border-bottom: 1px solid $gray-500;
|
||||
text-align: center;
|
||||
margin: 2em 0;
|
||||
}
|
||||
align-items: center;
|
||||
|
||||
.hr-middle {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
font-family: 'Roboto Condensed';
|
||||
line-height: 1.5;
|
||||
text-align: center;
|
||||
color: $gray-200;
|
||||
background-color: $gray-700;
|
||||
padding: .2em;
|
||||
margin-top: .2em;
|
||||
display: inline-block;
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.loadmore {
|
||||
justify-content: center;
|
||||
margin-right: 12px;
|
||||
margin-top: 12px;
|
||||
margin-bottom: 24px;
|
||||
|
||||
> div {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
|
||||
button {
|
||||
text-align: center;
|
||||
color: $gray-50;
|
||||
}
|
||||
button {
|
||||
text-align: center;
|
||||
color: $gray-50;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.loadmore-divider-holder {
|
||||
flex: 1;
|
||||
margin-left: 24px;
|
||||
margin-right: 24px;
|
||||
.loadmore-divider-holder {
|
||||
flex: 1;
|
||||
margin-left: 24px;
|
||||
margin-right: 24px;
|
||||
|
||||
&:last-of-type {
|
||||
margin-right: 0;
|
||||
}
|
||||
&:last-of-type {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.loadmore-divider {
|
||||
height: 1px;
|
||||
border-top: 1px $gray-500 solid;
|
||||
width: 100%;
|
||||
.loadmore-divider {
|
||||
height: 1px;
|
||||
border-top: 1px $gray-500 solid;
|
||||
width: 100%;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.loading {
|
||||
padding-left: 1.5rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.loading {
|
||||
padding-left: 1.5rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -280,6 +272,9 @@ export default {
|
||||
// container.style.overflowY = 'scroll';
|
||||
}
|
||||
}, 50),
|
||||
messageLiked (message) {
|
||||
this.$emit('message-liked', message);
|
||||
},
|
||||
messageRemoved (message) {
|
||||
this.$emit('message-removed', message);
|
||||
},
|
||||
|
||||
@@ -692,7 +692,7 @@
|
||||
<div class="form-inline clearfix">
|
||||
<Sprite
|
||||
class="pull-left"
|
||||
:class="'inventory_quest_scroll_' + item.key"
|
||||
:image-name="'inventory_quest_scroll_' + item.key"
|
||||
style="margin-right: 10px"
|
||||
/>
|
||||
<p>{{ item.text() }}</p>
|
||||
|
||||
@@ -107,7 +107,7 @@ export default {
|
||||
if (lastPublishedPost) this.posts.push(lastPublishedPost);
|
||||
|
||||
// If the user is authorized, show any draft
|
||||
if (this.user && this.user.contributor.newsPoster) {
|
||||
if (this.user && (this.user.permissions.news || this.user.permissions.fullAccess)) {
|
||||
this.posts.unshift(
|
||||
...postsFromServer
|
||||
.filter(p => !p.published || moment().isBefore(p.publishDate)),
|
||||
|
||||
@@ -31,13 +31,6 @@
|
||||
:
|
||||
<a href="mailto:admin@habitica.com">admin@habitica.com</a>
|
||||
<br>
|
||||
{{ $t('generalQuestionsSite') }}
|
||||
:
|
||||
<a
|
||||
target="_blank"
|
||||
@click.prevent="openBugReportModal(true)"
|
||||
> {{ $t('askQuestion') }}</a>
|
||||
<br>
|
||||
{{ $t('businessInquiries') }}
|
||||
:
|
||||
<a href="mailto:admin@habitica.com">admin@habitica.com</a>
|
||||
@@ -54,10 +47,8 @@
|
||||
<script>
|
||||
import { mapState } from '@/libs/store';
|
||||
import { goToModForm } from '@/libs/modform';
|
||||
import reportBug from '@/mixins/reportBug.js';
|
||||
|
||||
export default {
|
||||
mixins: [reportBug],
|
||||
computed: {
|
||||
...mapState({
|
||||
user: 'user.data',
|
||||
|
||||
@@ -66,16 +66,13 @@
|
||||
class="nav-link"
|
||||
>{{ $t('presskit') }}</a>
|
||||
</router-link>
|
||||
<router-link
|
||||
class="nav-item"
|
||||
tag="li"
|
||||
to="/static/contact"
|
||||
>
|
||||
<li class="nav-item">
|
||||
<a
|
||||
v-once
|
||||
class="nav-link"
|
||||
href="mailto:admin@habitica.com"
|
||||
>{{ $t('contactUs') }}</a>
|
||||
</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
<ul
|
||||
v-else
|
||||
|
||||
@@ -135,7 +135,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
.twitter svg {
|
||||
.bluesky svg {
|
||||
background-color: $purple-50;
|
||||
fill: $purple-500;
|
||||
&:hover {
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
}"
|
||||
>
|
||||
<input
|
||||
ref="textInput"
|
||||
:value="value"
|
||||
class="form-control"
|
||||
:type="inputType"
|
||||
@@ -29,19 +30,23 @@
|
||||
}"
|
||||
:readonly="readonly"
|
||||
:aria-readonly="readonly"
|
||||
autocomplete="off"
|
||||
|
||||
:placeholder="placeholder"
|
||||
@keyup="handleChange"
|
||||
@keyup.enter="$emit('enter')"
|
||||
@blur="$emit('blur')"
|
||||
>
|
||||
</div>
|
||||
<div
|
||||
v-for="issue in invalidIssues"
|
||||
:key="issue"
|
||||
class="input-error"
|
||||
>
|
||||
{{ issue }}
|
||||
</div>
|
||||
<template v-if="!hideErrorLine">
|
||||
<div
|
||||
v-for="issue in invalidIssues"
|
||||
:key="issue"
|
||||
class="input-error"
|
||||
>
|
||||
{{ issue }}
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -85,6 +90,10 @@ export default {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
hideErrorLine: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
@@ -107,6 +116,9 @@ export default {
|
||||
this.wasChanged = true;
|
||||
this.$emit('update:value', value);
|
||||
},
|
||||
focus () {
|
||||
this.$refs.textInput.focus();
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -128,4 +140,12 @@ export default {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/* this removes safari "save username" UI, we only search for one, we dont want to save it */
|
||||
input::-webkit-contacts-auto-fill-button,
|
||||
input::-webkit-credentials-auto-fill-button {
|
||||
visibility: hidden;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -29,20 +29,12 @@
|
||||
@import '~@/assets/scss/colors.scss';
|
||||
|
||||
.user-link { // this is the user name
|
||||
font-family: 'Roboto Condensed', sans-serif;
|
||||
font-weight: bold;
|
||||
margin-bottom: 0;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
font-size: 16px;
|
||||
|
||||
// currently used in the member-details-new.vue
|
||||
&.smaller {
|
||||
font-family: Roboto;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
line-height: 1.71;
|
||||
}
|
||||
font-size: 14px;
|
||||
line-height: 1.71;
|
||||
display: inline-flex !important;
|
||||
|
||||
&.no-tier {
|
||||
color: $gray-50;
|
||||
@@ -111,7 +103,6 @@ export default {
|
||||
'backer',
|
||||
'contributor',
|
||||
'hideTooltip',
|
||||
'smallerStyle',
|
||||
'showBuffed',
|
||||
'context',
|
||||
],
|
||||
@@ -173,7 +164,7 @@ export default {
|
||||
return this.hideTooltip ? '' : achievementsLib.getContribText(this.contributor, this.isNPC) || '';
|
||||
},
|
||||
levelStyle () {
|
||||
return `${this.userLevelStyleFromLevel(this.level, this.isNPC)} ${this.smallerStyle ? 'smaller' : ''}`;
|
||||
return `${this.userLevelStyleFromLevel(this.level, this.isNPC)}`;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
import debounce from 'lodash/debounce';
|
||||
|
||||
export const autoCompleteHelperMixin = {
|
||||
data () {
|
||||
return {
|
||||
mixinData: {
|
||||
autoComplete: {
|
||||
caretPosition: 0,
|
||||
coords: {
|
||||
TOP: 0,
|
||||
LEFT: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
autoCompleteMixinHandleTab (e) {
|
||||
if (this.$refs.autocomplete.searchActive) {
|
||||
e.preventDefault();
|
||||
if (e.shiftKey) {
|
||||
this.$refs.autocomplete.selectPrevious();
|
||||
} else {
|
||||
this.$refs.autocomplete.selectNext();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
autoCompleteMixinHandleEscape (e) {
|
||||
if (this.$refs.autocomplete.searchActive) {
|
||||
e.preventDefault();
|
||||
this.$refs.autocomplete.cancel();
|
||||
}
|
||||
},
|
||||
|
||||
autoCompleteMixinSelectNextAutocomplete (e) {
|
||||
if (this.$refs.autocomplete.searchActive) {
|
||||
e.preventDefault();
|
||||
this.$refs.autocomplete.selectNext();
|
||||
}
|
||||
},
|
||||
|
||||
autoCompleteMixinSelectPreviousAutocomplete (e) {
|
||||
if (this.$refs.autocomplete.searchActive) {
|
||||
e.preventDefault();
|
||||
this.$refs.autocomplete.selectPrevious();
|
||||
}
|
||||
},
|
||||
|
||||
autoCompleteMixinSelectAutocomplete (e) {
|
||||
if (this.$refs.autocomplete.searchActive) {
|
||||
if (this.$refs.autocomplete.selected !== null) {
|
||||
e.preventDefault();
|
||||
this.$refs.autocomplete.makeSelection();
|
||||
} else {
|
||||
// no autocomplete selected, newline instead
|
||||
this.$refs.autocomplete.cancel();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
autoCompleteMixinUpdateCarretPosition: debounce(function updateCarretPosition (eventUpdate) {
|
||||
this._updateCarretPosition(eventUpdate);
|
||||
}, 250),
|
||||
|
||||
autoCompleteMixinResetCoordsPosition () {
|
||||
this.mixinData.autoComplete.coords = {
|
||||
TOP: 0,
|
||||
LEFT: 0,
|
||||
};
|
||||
},
|
||||
|
||||
// https://medium.com/@_jh3y/how-to-where-s-the-caret-getting-the-xy-position-of-the-caret-a24ba372990a
|
||||
_getCoord (e, text) {
|
||||
const caretPosition = text.selectionEnd;
|
||||
this.mixinData.autoComplete.caretPosition = caretPosition;
|
||||
|
||||
const div = document.createElement('div');
|
||||
const span = document.createElement('span');
|
||||
const copyStyle = getComputedStyle(text);
|
||||
|
||||
[].forEach.call(copyStyle, prop => {
|
||||
div.style[prop] = copyStyle[prop];
|
||||
});
|
||||
|
||||
div.style.position = 'absolute';
|
||||
document.body.appendChild(div);
|
||||
div.textContent = text.value.substr(0, caretPosition);
|
||||
span.textContent = text.value.substr(caretPosition) || '.';
|
||||
div.appendChild(span);
|
||||
this.mixinData.autoComplete.coords = {
|
||||
TOP: span.offsetTop,
|
||||
LEFT: span.offsetLeft,
|
||||
};
|
||||
document.body.removeChild(div);
|
||||
},
|
||||
_updateCarretPosition (eventUpdate) {
|
||||
const text = eventUpdate.target;
|
||||
this._getCoord(eventUpdate, text);
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -1,7 +1,7 @@
|
||||
import notifications from './notifications';
|
||||
import { NotificationMixins } from './notifications';
|
||||
|
||||
export default {
|
||||
mixins: [notifications],
|
||||
export const CopyToClipboardMixin = {
|
||||
mixins: [NotificationMixins],
|
||||
methods: {
|
||||
async mixinCopyToClipboard (valueToCopy, notificationToShow = null) {
|
||||
if (navigator.clipboard) {
|
||||
@@ -21,3 +21,5 @@ export default {
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default CopyToClipboardMixin;
|
||||
|
||||
+620
-417
File diff suppressed because it is too large
Load Diff
+3
-3
@@ -62,7 +62,7 @@
|
||||
|
||||
<script>
|
||||
import moment from 'moment';
|
||||
import userLabel from '../userLabel';
|
||||
import userLabel from '../../components/userLabel.vue';
|
||||
|
||||
import dots from '@/assets/svg/dots.svg';
|
||||
import block from '@/assets/svg/block.svg';
|
||||
@@ -117,7 +117,7 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import '~@/assets/scss/colors.scss';
|
||||
@import '~@/assets/scss/colors';
|
||||
|
||||
.action-padding {
|
||||
height: 24px !important;
|
||||
@@ -153,7 +153,7 @@ export default {
|
||||
</style>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '~@/assets/scss/colors.scss';
|
||||
@import '~@/assets/scss/colors';
|
||||
|
||||
.conversation {
|
||||
padding: 1rem 1.5rem;
|
||||
@@ -0,0 +1,64 @@
|
||||
<template>
|
||||
<div
|
||||
v-if="filtersConversations.length > 0"
|
||||
class="conversations"
|
||||
>
|
||||
<conversation-item
|
||||
v-for="conversation in filtersConversations"
|
||||
:key="conversation.key"
|
||||
:active-key="selectedConversation?.key"
|
||||
:contributor="conversation.contributor"
|
||||
:backer="conversation.backer"
|
||||
:uuid="conversation.key"
|
||||
:display-name="conversation.name"
|
||||
:username="conversation.username"
|
||||
:last-message-date="conversation.date"
|
||||
:last-message-text="conversation.lastMessageText
|
||||
? removeTags(parseMarkdown(conversation.lastMessageText)) : ''"
|
||||
@click="selectConversation(conversation.key)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.conversations {
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
|
||||
import { defineComponent } from 'vue';
|
||||
import habiticaMarkdown from 'habitica-markdown';
|
||||
import conversationItem from '@/pages/private-messages/pm-conversation-item.vue';
|
||||
|
||||
export default defineComponent({
|
||||
components: { conversationItem },
|
||||
props: {
|
||||
filtersConversations: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
selectedConversation: {
|
||||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
removeTags (html) {
|
||||
const tmp = document.createElement('DIV');
|
||||
tmp.innerHTML = html;
|
||||
return tmp.textContent || tmp.innerText || '';
|
||||
},
|
||||
parseMarkdown (text) {
|
||||
if (!text) return null;
|
||||
return habiticaMarkdown.render(String(text));
|
||||
},
|
||||
selectConversation (conversationKey) {
|
||||
this.$emit('selectConversation', conversationKey);
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,37 @@
|
||||
<template>
|
||||
<div
|
||||
class="pm-disabled-caption text-center"
|
||||
>
|
||||
<h4>{{ disabledTexts.title }}</h4>
|
||||
<p>{{ disabledTexts.description }}</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '~@/assets/scss/colors.scss';
|
||||
|
||||
.pm-disabled-caption {
|
||||
padding-top: 1.5em;
|
||||
z-index: 2;
|
||||
|
||||
h4, p {
|
||||
color: $gray-200;
|
||||
}
|
||||
|
||||
h4 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0.4em;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 12px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: ['disabledTexts'],
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,71 @@
|
||||
<template>
|
||||
<div
|
||||
class="empty-messages m-auto text-center empty-sidebar"
|
||||
>
|
||||
<div class="no-messages-box">
|
||||
<div
|
||||
v-once
|
||||
class="svg-icon envelope mb-4"
|
||||
v-html="icons.mailIcon"
|
||||
></div>
|
||||
<strong
|
||||
v-once
|
||||
class="mb-1"
|
||||
>
|
||||
{{ $t('emptyMessagesLine1') }}
|
||||
</strong>
|
||||
<p v-if="!chatRevoked">
|
||||
{{ $t('emptyMessagesLine2') }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<button
|
||||
class="btn btn-primary mt-4 d-flex align-items-center"
|
||||
@click="$emit('newMessageClicked')"
|
||||
>
|
||||
<div
|
||||
class="svg-icon icon-10 color mr-2"
|
||||
v-html="icons.positive"
|
||||
></div>
|
||||
{{ $t('newMessage') }}
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '~@/assets/scss/colors.scss';
|
||||
|
||||
strong {
|
||||
line-height: 1.71;
|
||||
color: $gray-100;
|
||||
}
|
||||
|
||||
.svg-icon.icon-10 {
|
||||
margin: 3px;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import mailIcon from '@/assets/svg/mail.svg';
|
||||
import positiveIcon from '@/assets/svg/positive.svg';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
chatRevoked: Boolean,
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
icons: Object.freeze({
|
||||
mailIcon,
|
||||
positive: positiveIcon,
|
||||
}),
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,69 @@
|
||||
<template>
|
||||
<div
|
||||
v-once
|
||||
class="centered empty-messages m-auto text-center"
|
||||
>
|
||||
<avatar
|
||||
v-if="memberObj"
|
||||
:member="memberObj"
|
||||
:avatar-only="true"
|
||||
:show-weapon="false"
|
||||
:hide-class-badge="true"
|
||||
:override-top-padding="'0px'"
|
||||
:sprites-margin="'0 0 0 -30px'"
|
||||
:debug-mode="false"
|
||||
:center-avatar="true"
|
||||
class="mb-3"
|
||||
/>
|
||||
|
||||
<strong>{{ memberObj.profile.name }}</strong>
|
||||
<div class="username mb-3">
|
||||
@{{ memberObj.auth.local.username }}
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="kind-text"
|
||||
v-html="$t('rememberToBeKind')"
|
||||
></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '~@/assets/scss/colors.scss';
|
||||
|
||||
.centered {
|
||||
align-content: center;
|
||||
}
|
||||
|
||||
.center-avatar {
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
strong {
|
||||
line-height: 1.71;
|
||||
}
|
||||
|
||||
.username {
|
||||
font-size: 12px;
|
||||
line-height: 1.33;
|
||||
color: $gray-100;
|
||||
margin-top: -4px;
|
||||
}
|
||||
|
||||
.kind-text {
|
||||
width: 330px;
|
||||
line-height: 1.71;
|
||||
color: $gray-100;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import Avatar from '@/components/avatar.vue';
|
||||
|
||||
export default {
|
||||
components: { Avatar },
|
||||
props: {
|
||||
memberObj: null,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,44 @@
|
||||
export namespace PrivateMessages {
|
||||
// Shared properties between message types
|
||||
interface SharedMessageProps {
|
||||
username: string;
|
||||
contributor: Record<string, unknown>;
|
||||
userStyles: Record<string, unknown>;
|
||||
canReceive: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the Type we get from our API
|
||||
*/
|
||||
interface ConversationSummaryMessageEntry extends SharedMessageProps {
|
||||
uuid: string;
|
||||
user: string;
|
||||
|
||||
timestamp: string;
|
||||
text: string;
|
||||
count: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* The Visual (Sidebar) Entry
|
||||
*/
|
||||
interface ConversationEntry extends SharedMessageProps {
|
||||
/**
|
||||
* UUID
|
||||
*/
|
||||
key: string;
|
||||
name: string;
|
||||
|
||||
|
||||
lastMessageText: '',
|
||||
canLoadMore: boolean;
|
||||
page: 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Loaded Private Messages, partial type
|
||||
*/
|
||||
interface PrivateMessageEntry extends SharedMessageProps {
|
||||
text: string;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
<template>
|
||||
<div class="ml-4">
|
||||
<strong
|
||||
v-once
|
||||
v-html="$t('to')"
|
||||
></strong>
|
||||
<validated-text-input
|
||||
id="selectUser"
|
||||
ref="targetUserInput"
|
||||
v-model="targetUserInputValue"
|
||||
class="mx-2"
|
||||
:is-valid="foundUser._id"
|
||||
:only-show-invalid-state="foundUser._id === undefined"
|
||||
|
||||
:hide-error-line="true"
|
||||
:placeholder="$t('usernameOrUserId')"
|
||||
:invalid-issues="userInputInvalidIssues"
|
||||
@enter="triggerNewConversation"
|
||||
/>
|
||||
|
||||
<button
|
||||
class="btn btn-primary"
|
||||
:disabled="preventTrigger"
|
||||
@click="triggerNewConversation()"
|
||||
>
|
||||
{{ $t('confirm') }}
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="ml-2 btn btn-secondary"
|
||||
@click="$emit('cancelNewConversation')"
|
||||
>
|
||||
{{ $t('cancel') }}
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '~@/assets/scss/colors.scss';
|
||||
|
||||
div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
div > * {
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
strong {
|
||||
line-height: 1.71;
|
||||
align-content: center;
|
||||
}
|
||||
|
||||
input {
|
||||
border-radius: 2px;
|
||||
border-width: 2px;
|
||||
|
||||
width: 420px;
|
||||
}
|
||||
|
||||
#selectUser {
|
||||
/* changing the style of validate-text-input to the same as others */
|
||||
::v-deep {
|
||||
.input-group {
|
||||
border-width: 2px;
|
||||
|
||||
input {
|
||||
width: 420px;
|
||||
height: 100%;
|
||||
color: $gray-50;
|
||||
}
|
||||
}
|
||||
|
||||
.input-group {
|
||||
&:focus, &:active, &:focus-within {
|
||||
border: solid 2px $purple-400;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<script>
|
||||
|
||||
import debounce from 'lodash/debounce';
|
||||
import isUUID from 'validator/es/lib/isUUID';
|
||||
import ValidatedTextInput from '@/components/ui/validatedTextInput.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ValidatedTextInput,
|
||||
},
|
||||
mixins: [],
|
||||
data () {
|
||||
return {
|
||||
targetUserInputValue: '',
|
||||
userNotFound: false,
|
||||
foundUser: {},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
preventTrigger () {
|
||||
return this.targetUserInputValue.length < 2;
|
||||
},
|
||||
userInputInvalidIssues () {
|
||||
return this.targetUserInputValue.length > 0 && this.userNotFound
|
||||
? [this.$t('userWithUsernameOrUserIdNotFound')]
|
||||
: [''];
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
targetUserInputValue: {
|
||||
handler () {
|
||||
this.searchUser(this.targetUserInputValue.replace('@', ''));
|
||||
},
|
||||
},
|
||||
},
|
||||
mounted () {
|
||||
this.$refs.targetUserInput.focus();
|
||||
},
|
||||
methods: {
|
||||
close () {
|
||||
this.$root.$emit('bv::hide::modal', 'select-user-modal');
|
||||
},
|
||||
searchUser: debounce(async function userSearch (searchTerm = '') {
|
||||
this.foundUser = {};
|
||||
if (searchTerm.length < 1) {
|
||||
this.userNotFound = false;
|
||||
return;
|
||||
}
|
||||
let result;
|
||||
if (isUUID(searchTerm)) {
|
||||
try {
|
||||
result = await this.$store.dispatch('members:fetchMember', {
|
||||
memberId: searchTerm,
|
||||
});
|
||||
} catch {
|
||||
result = null;
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
result = await this.$store.dispatch('members:fetchMemberByUsername', {
|
||||
username: searchTerm,
|
||||
});
|
||||
} catch {
|
||||
result = null;
|
||||
}
|
||||
}
|
||||
if (!result) {
|
||||
this.userNotFound = true;
|
||||
return;
|
||||
}
|
||||
this.userNotFound = false;
|
||||
this.foundUser = result;
|
||||
}, 500),
|
||||
triggerNewConversation () {
|
||||
const userWithoutAt = this.$refs.targetUserInput.value.replace('@', '');
|
||||
|
||||
this.$emit('startNewConversation', userWithoutAt);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -49,7 +49,7 @@ const GroupPlanIndex = () => import(/* webpackChunkName: "group-plans" */ '@/com
|
||||
const GroupPlanTaskInformation = () => import(/* webpackChunkName: "group-plans" */ '@/components/group-plans/taskInformation');
|
||||
const GroupPlanBilling = () => import(/* webpackChunkName: "group-plans" */ '@/components/group-plans/billing');
|
||||
|
||||
const MessagesIndex = () => import(/* webpackChunkName: "private-messages" */ '@/pages/private-messages');
|
||||
const MessagesIndex = () => import(/* webpackChunkName: "private-messages" */ '@/pages/private-messages/index.vue');
|
||||
|
||||
// Challenges
|
||||
const ChallengeIndex = () => import(/* webpackChunkName: "challenges" */ '@/components/challenges/index');
|
||||
@@ -190,7 +190,6 @@ const router = new VueRouter({
|
||||
meta: {
|
||||
privilegeNeeded: [ // any one of these is enough to give access
|
||||
'userSupport',
|
||||
'newsPoster',
|
||||
],
|
||||
},
|
||||
children: [
|
||||
@@ -219,7 +218,7 @@ const router = new VueRouter({
|
||||
|
||||
// Only used to handle some redirects
|
||||
// See router.beforeEach
|
||||
{ path: '/static/faq/tavern-and-guilds', redirect: '/static/tavern-and-guilds' },
|
||||
{ path: '/static/tavern-and-guilds', redirect: '/static/faq/tavern-and-guilds' },
|
||||
{ path: '/redirect/:redirect', name: 'redirect' },
|
||||
{ path: '*', redirect: { name: 'notFound' } },
|
||||
],
|
||||
|
||||
@@ -43,7 +43,14 @@ export async function deleteChat (store, payload) {
|
||||
}
|
||||
|
||||
export async function like (store, payload) {
|
||||
const url = `/api/v4/groups/${payload.groupId}/chat/${payload.chatId}/like`;
|
||||
let url = '';
|
||||
|
||||
if (payload.groupId === 'privateMessage') {
|
||||
url = `/api/v4/inbox/like-private-message/${payload.chatMessageId}`;
|
||||
} else {
|
||||
url = `/api/v4/groups/${payload.groupId}/chat/${payload.chatMessageId}/like`;
|
||||
}
|
||||
|
||||
const response = await axios.post(url);
|
||||
return response.data.data;
|
||||
}
|
||||
|
||||
@@ -72,7 +72,11 @@ describe('LevelUp', () => {
|
||||
|
||||
it('generates the right test class for level 15', () => {
|
||||
const questClass = testFunction('questClass', 15);
|
||||
expect(questClass()).to.equal('inventory_quest_scroll_atom1');
|
||||
});
|
||||
|
||||
expect(questClass()).to.equal('scroll inventory_quest_scroll_atom1');
|
||||
it('generates empty test class for level 14', () => {
|
||||
const questClass = testFunction('questClass', 14);
|
||||
expect(questClass()).to.equal('');
|
||||
});
|
||||
});
|
||||
|
||||
+5
-3
@@ -1,14 +1,16 @@
|
||||
import Vue from 'vue';
|
||||
import { shallowMount, createLocalVue } from '@vue/test-utils';
|
||||
|
||||
import ChatCard from '@/components/chat/chatCard.vue';
|
||||
import BootstrapVue from 'bootstrap-vue';
|
||||
import MessageCard from '@/components/messages/messageCard.vue';
|
||||
import Store from '@/libs/store';
|
||||
|
||||
const localVue = createLocalVue();
|
||||
localVue.use(Store);
|
||||
localVue.use(Vue.directive('b-tooltip', {}));
|
||||
localVue.use(BootstrapVue);
|
||||
|
||||
describe('ChatCard', () => {
|
||||
describe('MessageCard', () => {
|
||||
function createMessage (text) {
|
||||
return { text, likes: {} };
|
||||
}
|
||||
@@ -26,7 +28,7 @@ describe('ChatCard', () => {
|
||||
let wrapper;
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = shallowMount(ChatCard, {
|
||||
wrapper = shallowMount(MessageCard, {
|
||||
propsData: { msg: message },
|
||||
store: new Store({
|
||||
state: {
|
||||
@@ -43,6 +43,7 @@ envVars
|
||||
});
|
||||
|
||||
const webpackPlugins = [
|
||||
new webpack.ProvidePlugin({ 'window.jQuery': 'jquery' }),
|
||||
new webpack.DefinePlugin(envObject),
|
||||
new MomentLocalesPlugin({
|
||||
localesToKeep: ['bg',
|
||||
|
||||
@@ -91,7 +91,7 @@
|
||||
"conText": "Якостта намалява щетите от отрицателни навици и пропуснати ежедневни задачи.",
|
||||
"perception": "Усет",
|
||||
"perText": "Усетът увеличава спечеленото злато, а след отключването на пазара увеличава вероятността за намиране на предмети след приключване на задачи.",
|
||||
"intelligence": "Интелигентност",
|
||||
"intelligence": "Интелект",
|
||||
"intText": "Интелигентността увеличава спечеления опит, а след отключването на класовете определя максималната мана за използване за класовите умения.",
|
||||
"levelBonus": "Бонус за ниво",
|
||||
"allocatedPoints": "Разпределени точки",
|
||||
|
||||
@@ -95,9 +95,6 @@
|
||||
"whyReportingPostPlaceholder": "Моля, помогнете на модераторите, като ни кажете защо докладвате тази публикация за нарушение, например: защото е нежелана, включва ругатни, клетви, фанатизъм, обиди, теми за възрастни, насилие.",
|
||||
"optional": "Незадължително",
|
||||
"needsTextPlaceholder": "Въведете съобщението си тук.",
|
||||
"copyMessageAsToDo": "Копиране на съобщението като задача",
|
||||
"copyAsTodo": "Копиране като задача за изпълнение",
|
||||
"messageAddedAsToDo": "Съобщението беше копирано като задача.",
|
||||
"leaderOnlyChallenges": "Само водачът на групата може да създава предизвикателства",
|
||||
"sendGift": "Изпращане на подарък",
|
||||
"inviteFriends": "Поканете приятели",
|
||||
|
||||
@@ -46,10 +46,8 @@
|
||||
"messageNotAbleToBuyInBulk": "Не може да се закупи повече от един брой от този предмет.",
|
||||
"notificationsRequired": "Идентификаторите на известията са задължителни.",
|
||||
"unallocatedStatsPoints": "Имате <span class=\"notification-bold-blue\"><%= points %> неразпределени показателни точки</span>",
|
||||
"beginningOfConversation": "Това е началото на разговора Ви с <%= userName %>.",
|
||||
"messageDeletedUser": "Съжаляваме, но този потребител е изтрил профила си.",
|
||||
"messageMissingDisplayName": "Липсва екранно име.",
|
||||
"canDeleteNow": "Вече може да изтриете съобщението, ако желаете.",
|
||||
"reportedMessage": "Вие докладвахте това съобщние на модераторите.",
|
||||
"beginningOfConversationReminder": "Не забравяйте да бъдете мили, уважителни и да следвате Обществените Правила!"
|
||||
"reportedMessage": "Вие докладвахте това съобщние на модераторите."
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"quests": "Мисии",
|
||||
"quest": "мисия",
|
||||
"petQuests": "Мисии за любимци и превози",
|
||||
"petQuests": "Мисии за домашни любимци и ездитни животни",
|
||||
"unlockableQuests": "Мисии, които могат да бъдат отключени",
|
||||
"goldQuests": "Последователности от мисии на класовите повелители",
|
||||
"questDetails": "Подробности за мисията",
|
||||
@@ -29,7 +29,7 @@
|
||||
"collected": "Събрани",
|
||||
"abort": "Прекратяване",
|
||||
"leaveQuest": "Напускане на мисията",
|
||||
"sureLeave": "Наистина ли искате да се откажете от текущата мисия? Ще загубите целия си напредък.",
|
||||
"sureLeave": "Сигурни ли сте, че искате да се откажете от мисията? Ще загубите целия си напредък.",
|
||||
"mustComplete": "Трябва първо да завършите <%= quest %>.",
|
||||
"mustLvlQuest": "Трябва да бъдете ниво <%= level %>, за да купите тази мисия!",
|
||||
"unlockByQuesting": "За да отключите тази мисия, първо завършете <%= title %>.",
|
||||
@@ -78,5 +78,8 @@
|
||||
"questAlreadyStartedFriendly": "Мисията вече е започнала, но винаги може да хванете следващата!",
|
||||
"questAlreadyStarted": "Мисията вече е започнала.",
|
||||
"questInvitationNotificationInfo": "Получихте покана за присъединяване към мисия",
|
||||
"hatchingPotionQuests": "Мисии за Магическа Излюпваща Отвара"
|
||||
"hatchingPotionQuests": "Мисии за Магическа Излюпваща Отвара",
|
||||
"bossDamage": "Нанесохте вреда на главатаря!",
|
||||
"questItemsPending": "<%= amount %> предмета ще бъдат събрани",
|
||||
"sureLeaveInactive": "Сигурни ли сте, че искате да се откажете от мисията? Няма да можете да участвате в нея."
|
||||
}
|
||||
|
||||
@@ -58,11 +58,11 @@
|
||||
"foundNewItemsCTA": "Podívej se do tvého Inventáře a zkus zkombinovat tvůj nový líhnoucí lektvar a vajíčko!",
|
||||
"foundNewItemsExplanation": "Splnění úkolů ti dá šanci najít předměty jako vajíčka, líhnoucí lektvary a jídlo pro mazlíčky.",
|
||||
"foundNewItems": "Nové předměty nalezeny!",
|
||||
"hideAchievements": "Schovat <%= kategorie %>",
|
||||
"hideAchievements": "Schovat <%= category %>",
|
||||
"onboardingCompleteDesc": "Získáváš <strong>5 úspěchů</strong> a <strong class=\"gold-amount\">100 zlaťáků</strong> za dokončení seznamu.",
|
||||
"onboardingProgress": "<%= percentage %>% postup",
|
||||
"gettingStartedDesc": "Splň tyto základní úkoly a získej <strong>5 úspěchů</strong> a <strong class=\"gold-amount\">100 zlaťáků</strong>, jakmile budeš hotový/á!",
|
||||
"showAllAchievements": "Zobrazit všechny <%= kategorie %>",
|
||||
"showAllAchievements": "Zobrazit všechny <%= category %>",
|
||||
"yourProgress": "Tvůj postup",
|
||||
"achievementBareNecessitiesModalText": "Splnil/a jsi výpravy za opicí, lenochodem a stromečkem!",
|
||||
"achievementBareNecessitiesText": "Splnil/a výpravy za opicí, lenochodem a stromečkem.",
|
||||
|
||||
@@ -95,9 +95,6 @@
|
||||
"whyReportingPostPlaceholder": "Prosím pomož našim moderatorům a vysvětli, proč ohlašuješ tento příspěvek kvůli porušení pravidel, tedy zda je to spam, sprostá slova, náboženské přísahy, netolerance, urážky, témata nevhodná pro mladistvé, násilí.",
|
||||
"optional": "Možný",
|
||||
"needsTextPlaceholder": "Napiš svou zprávu sem.",
|
||||
"copyMessageAsToDo": "Zkopírovat zprávu jako úkol",
|
||||
"copyAsTodo": "Zkopírovat jako úkol",
|
||||
"messageAddedAsToDo": "Zpráva zkopírována jako úkol.",
|
||||
"leaderOnlyChallenges": "Pouze velitel družiny může vytvářet Výzvy",
|
||||
"sendGift": "Poslat dárek",
|
||||
"inviteFriends": "Pozvat přátele",
|
||||
|
||||
@@ -46,12 +46,10 @@
|
||||
"messageNotAbleToBuyInBulk": "Tento předmět nelze nakoupit v množství větším, než je 1.",
|
||||
"notificationsRequired": "Id upozornění je potřeba.",
|
||||
"unallocatedStatsPoints": "Máš <span class=\"notification-bold-blue\"><%= points %> nepřidělený(ch) vlastnostní(ch) bod(ů)</span>",
|
||||
"beginningOfConversation": "Toto je začátek tvé konverzace s uživatelem <%= userName %>.",
|
||||
"messageDeletedUser": "Omlouváme se, ale tento uživatel smazal svůj účet.",
|
||||
"messageMissingDisplayName": "Chybí zobrazované jméno.",
|
||||
"canDeleteNow": "Nyní můžete zprávu smazat.",
|
||||
"reportedMessage": "Tuto zprávu jste nahlásili moderátorům.",
|
||||
"beginningOfConversationReminder": "Nezapomeňte být milí, taktní a respektujte Zásady komunity!",
|
||||
"messageAllUnEquipped": "Vše odloženo.",
|
||||
"messageBackgroundUnEquipped": "Pozadí odloženo.",
|
||||
"messagePetMountUnEquipped": "Mazlíček a zvíře odloženi.",
|
||||
|
||||
@@ -95,9 +95,6 @@
|
||||
"whyReportingPostPlaceholder": "Du kan hjælpe vores moderatorer ved at lade os vide, hvorfor du anmelder denne besked som en overtrædelse - fx spam, banden, religiøse kraftudtryk, fordomme, nedladende skældsord, emner for aldersgruppen +18 eller vold.",
|
||||
"optional": "Valgfri",
|
||||
"needsTextPlaceholder": "Skriv din besked her.",
|
||||
"copyMessageAsToDo": "Kopier besked som To-Do",
|
||||
"copyAsTodo": "Kopier som To-Do",
|
||||
"messageAddedAsToDo": "Besked kopieret som To-Do.",
|
||||
"leaderOnlyChallenges": "Kun gruppelederen kan oprette udfordringer",
|
||||
"sendGift": "Send gave",
|
||||
"inviteFriends": "Invitér venner",
|
||||
|
||||
@@ -46,13 +46,11 @@
|
||||
"messageNotAbleToBuyInBulk": "Denne genstand kan ikke købes i antal større end 1.",
|
||||
"notificationsRequired": "Notafikation ID'er er krævet.",
|
||||
"unallocatedStatsPoints": "Du har <span class=\"notification-bold-blue\"><%= points %> ufordelte Egenskabspoint</span>",
|
||||
"beginningOfConversation": "Dette er begyndelsen på din samtale med <%= userName %>.",
|
||||
"messageDeletedUser": "Sorry, this user has deleted their account.",
|
||||
"messageMissingDisplayName": "Missing display name.",
|
||||
"newsPostNotFound": "News Post er ikke fundet eller du har ikke adgang.",
|
||||
"canDeleteNow": "Du kan nu slette beskeden, hvis du ønsker det.",
|
||||
"reportedMessage": "Du har indrapporteret denne besked til moderatorerne.",
|
||||
"beginningOfConversationReminder": "Husk at være venlig, respektful og følge Retningslinjerne for Fællesskabet!",
|
||||
"messageAllUnEquipped": "Alt fjernet.",
|
||||
"messageBackgroundUnEquipped": "Baggrund fjernet.",
|
||||
"messageCostumeUnEquipped": "Kostume fjernet.",
|
||||
|
||||
@@ -152,7 +152,7 @@
|
||||
"achievementDinosaurDynastyModalText": "Du hast alle Vogel- und Dinosaurier-Haustiere gesammelt!",
|
||||
"achievementDinosaurDynasty": "Dinosaurier Dynastie",
|
||||
"achievementBonelessBoss": "Knochenloser Boss",
|
||||
"achievementBonelessBossText": "Hat alle wirbellosen Tiere ausgebrütet: Käfer, Schmetterling, Tintenfisch, Nacktschnecke, Oktopus, Schnecke und Spinnen!",
|
||||
"achievementBonelessBossText": "Hat alle wirbellosen Tiere ausgebrütet: Käfer, Schmetterling, Tintenfisch, Nacktschnecke, Oktopus, Schnecke und Spinne!",
|
||||
"achievementBonelessBossModalText": "Du hast alle wirbellosen Tiere gesammelt!",
|
||||
"achievementDuneBuddyText": "Hat alle Standardfarben der Wüstenbewohnern ausgebrütet: Gürteltier, Kaktus, Fuchs, Frosch, Schlange und Spinne!",
|
||||
"achievementRoughRider": "Harter Reiter",
|
||||
|
||||
@@ -789,7 +789,7 @@
|
||||
"backgroundBirthdayBashNotes": "Habitica feiert eine Geburtstagsparty und alle sind eingeladen!",
|
||||
"eventBackgrounds": "Ereignis-Hintergründe",
|
||||
"backgroundBirthdayBashText": "Geburtstagsparty",
|
||||
"backgroundInsideACrystalNotes": "Schau aus dem Inneren eines Kristalls heraus.",
|
||||
"backgroundInsideACrystalNotes": "Schau aus dem Inneren eines Kristalls hinaus.",
|
||||
"backgrounds072023": "SET 110: Veröffentlicht im Juli 2023",
|
||||
"backgroundOnAPaddlewheelBoatText": "Auf einem Schaufelradboot",
|
||||
"backgroundOnAPaddlewheelBoatNotes": "Fahre mit einem Schaufelradboot.",
|
||||
@@ -891,5 +891,11 @@
|
||||
"backgroundFirstSnowForestNotes": "Tritt in den ersten Schnee im Wald.",
|
||||
"backgrounds012025": "Set 128: Veröffentlicht im Januar 2025",
|
||||
"backgroundWinterLandscapeWithCabinText": "Winterlandschaft mit Hütte",
|
||||
"backgroundWinterLandscapeWithCabinNotes": "Macht es dir in einer Winterlandschaft mit einer Hütte gemütlich."
|
||||
"backgroundWinterLandscapeWithCabinNotes": "Macht es dir in einer Winterlandschaft mit einer Hütte gemütlich.",
|
||||
"backgroundOldFashionedTeaShopText": "Altmodischer Teeladen",
|
||||
"backgroundOldFashionedTeaShopNotes": "Genieße ein Getränk in einem Altmodischen Teeladen.",
|
||||
"backgrounds022025": "Set 129: Veröffentlicht im Februar 2025",
|
||||
"backgrounds032025": "SET 130: Veröffentlicht im März 2025",
|
||||
"backgroundMountainSceneWithBlossomsText": "Bergszene mit Blüten",
|
||||
"backgroundMountainSceneWithBlossomsNotes": "Erlebe den entzückenden Anblick und Geruch einer Bergszene mit Blüten."
|
||||
}
|
||||
|
||||
@@ -392,5 +392,12 @@
|
||||
"questEggDogText": "Welpe",
|
||||
"questEggDogMountText": "Hund",
|
||||
"questEggDogAdjective": "ein freundlicher",
|
||||
"hatchingPotionGingerbread": "Lebkuchen"
|
||||
"hatchingPotionGingerbread": "Lebkuchen",
|
||||
"questEggCatText": "Kätzchen",
|
||||
"questEggCatMountText": "Katze",
|
||||
"questEggCatAdjective": "ein schelmisches",
|
||||
"questEggOtterText": "Otter",
|
||||
"questEggOtterMountText": "Otter",
|
||||
"hatchingPotionJade": "Jade",
|
||||
"questEggOtterAdjective": "Ein perfider"
|
||||
}
|
||||
|
||||
@@ -5,13 +5,13 @@
|
||||
"webFaqStillNeedHelp": "Wenn Du eine Frage hast, die hier oder im [Wiki FAQ](https://habitica.fandom.com/wiki/FAQ) nicht beantwortet wurde, verwende das Stell eine Frage Formular [LINK NEEDED]! Wir helfen Dir gerne.",
|
||||
"parties": "Partys",
|
||||
"webFaqAnswer25": "Habitica verwendet drei verschiedene Aufgabentypen, um deinen Bedürfnissen gerecht zu werden: Gewohnheiten, tägliche Aufgaben und To-Dos.\n\nGewohnheiten können positiv oder negativ sein und stellen etwas dar, das Sie vielleicht mehrmals am Tag oder nach einem nicht festgelegten Zeitplan verfolgen möchten. Positive Gewohnheiten bringen euch Belohnungen wie Gold und Erfahrung (Exp), während ihr bei negativen Gewohnheiten Lebenspunkte (HP) verliert.\n\nDailies sind wiederkehrende Aufgaben, die du nach einem strukturierten Zeitplan erledigen möchtest. Zum Beispiel einmal am Tag, dreimal in der Woche oder viermal im Monat. Wenn du Dailies verpasst, verlierst du HP, aber je schwieriger sie sind, desto besser ist die Belohnung!\n\nTo-Dos sind einmalige Aufgaben, für deren Erledigung es Belohnungen gibt. To-Dos können ein Fälligkeitsdatum haben, aber du verlierst keine HP, wenn du es verpasst.\n\nWähle die Aufgabenart, die am besten zu dem passt, was du erreichen willst!",
|
||||
"commonQuestions": "Häufige Fragenj",
|
||||
"commonQuestions": "Häufige Fragen",
|
||||
"faqQuestion25": "Welche Aufgabentypen gibt es?",
|
||||
"faqQuestion26": "Was sind einige Beispielaufgaben?",
|
||||
"webFaqAnswer31": "Wenn du eine Aufgabe erfüllst und HP verlierst, obwohl du das nicht hättest tun sollen, kam es zu einer Verzögerung, während der Server die auf anderen Plattformen vorgenommenen Änderungen synchronisiert hat. Wenn du zum Beispiel Gold oder Mana verwendest oder HP in der mobilen App verlierst und dann eine Aufgabe auf der Website erledigst, bestätigt der Server lediglich, dass alles synchronisiert ist.",
|
||||
"webFaqAnswer49": "Wenn Du Habitica mit anderen erleben möchtest, aber keine anderen Spieler kennst, ist die Suche nach einer Party die beste Option! Wenn Du bereits andere Spieler kennst, die eine Party haben, kannst Du deinen @Benutzernamen mit ihnen teilen, um eingeladen zu werden. Alternativ kannst Du auch eine neue Gruppe erstellen und sie mit ihrem @Nutzernamen oder ihrer E-Mail-Adresse einladen.\n\nUm eine Party zu erstellen oder zu suchen, wähle \"Party\" im Navigationsmenü und wähle dann die Option, die Dir am besten gefällt.",
|
||||
"webFaqAnswer62": "Gruppenpläne bieten dir die einzigartige Möglichkeit, anderen Mitgliedern deines Gruppenplans gemeinsame Aufgaben zuzuweisen. Wenn eine gemeinsame Aufgabe einem Mitglied zugewiesen wird, können andere Mitglieder sie nicht mehr erledigen.\n\nDu kannst eine Aufgabe auch mehreren Mitgliedern zuweisen. Wenn sich zum Beispiel alle Mitglieder die Zähne putzen müssen, erstellst du eine Aufgabe und weist sie jedem Mitglied zu. Jedes Mitglied kann die Aufgabe erledigen und sich seine individuelle Belohnung verdienen. Die Hauptaufgabe wird als erledigt angezeigt, sobald alle Mitglieder sie erledigt haben.",
|
||||
"webFaqAnswer32": "In Habitica gibt es vier Klassen: Krieger, Magier, Schurke und Heiler. Alle Spieler beginnen in der Klasse des Kriegers, bis du Stufe 10 erreicht hast. Sobald du Stufe 10 erreichst, hast du die Wahl, eine neue Klasse zu wählen oder als Krieger weiterzuspielen.\n\nJede Klasse verfügt über unterschiedliche Ausrüstungen und Fertigkeiten. Wenn du dich nicht für eine Klasse entscheiden möchtest, kannst du \"Aussteigen\" wählen. Wenn du dich dafür entscheidest, kannst du das Klassensystem später in den Einstellungen wieder aktivieren.",
|
||||
"webFaqAnswer32": "Alle Spieler beginnen in der Klasse des Kriegers, bis sie Stufe 10 erreicht haben. Sobald du Stufe 10 erreichst, hast du die Wahl, eine neue Klasse zu wählen oder als Krieger weiterzuspielen.\n\nJede Klasse verfügt über unterschiedliche Ausrüstungen und Fertigkeiten. Wenn du dich nicht für eine Klasse entscheiden möchtest, kannst du \"Abbrechen\" wählen. Wenn du dich später doch entscheidest, kannst du das Klassensystem in den Einstellungen wieder aktivieren.\n\nWenn Du Deine Klasse nach Level 10 noch einmal ändern möchtest, kannst Du die Sphäre der Wiedergeburt hierfür nutzen. Die Sphäre der Wiedergeburt ist mit Level 50 auf demMarktplatz für 6 Edelsteine verfügbar und mit Level 100 bekommst Du sie umsonst.\n\nAlternativ kannst Du Deine Klasse jederzeit in den Einstellungen für 3 Edelsteine ändern. Dies wird Dein Level nicht wie die Sphäre der Wiedergeburt zurücksetzen, aber es erlaubt Dir, die Fertigkeits-Punkte, die Du beim Leveln gesammelt hast, Deiner neuen Klasse zuzuordnen.",
|
||||
"sunsetFaqPara14": "<strong>Linguists</strong><br />Wir freuen uns auch weiterhin über Hilfe bei der Übersetzung der Apps und der Website und werden für qualifizierte Beiträge nach wie vor Beitragsstufen vergeben. Die Methode, mit der wir Übersetzungen annehmen, wird sich jedoch ändern. Wir möchten unsere Ressourcen auf die Unterstützung einer bestimmten Auswahl von Sprachen für alle Plattformen konzentrieren. Um dies zu erreichen, werden wir die Anzahl der für Übersetzungen verfügbaren Sprachen reduzieren. Zuvor nicht fertiggestellte Sprachen werden in Github archiviert. Wir hoffen, dass diese Änderung das plattformübergreifende Habitica-Erlebnis konsistenter macht. Sie können unsere aktuellsten Richtlinien für das Übersetzungsverfahren auf unserer Website lesen <a href='https://translate.habitica.com/projects/habitica/#information'>Übersetzungswebsite</a>.",
|
||||
"webFaqAnswer34": "Haustiere mögen Futter, das zu ihrer Farbe passt. Basis-Tiere sind die Ausnahme, aber alle Basis-Tiere mögen den gleichen Gegenstand. Im Folgenden siehst du, welche Nahrungsmittel jedes Haustier mag:\n\n * Basistiere mögen Fleisch\n * Weiße Haustiere mögen Milch\n * Wüstenhaustiere mögen Kartoffeln\n * Rote Haustiere mögen Erdbeeren\n * Schattentiere mögen Schokolade\n * Skelett-Tiere mögen Fisch\n * Zombie-Tiere mögen verdorbenes Fleisch\n * Zuckerwatte rosa Haustiere mögen rosa Zuckerwatte\n * Zuckerwatte blaue Haustiere mögen blaue Zuckerwatte\n * Goldene Haustiere mögen Honig",
|
||||
"webFaqAnswer35": "Sobald du dein Haustier genug gefüttert hast, um es zu einem Reittier zu machen, musst du diese Art von Haustier erneut ausbrüten, um es in deinem Stall zu haben.\n\nUm Reittiere in den mobilen Apps zu sehen:\n\n * Wähle im Menü \"Haustiere & Reittiere\" und wechseln zur Registerkarte \"Reittiere\".\n\nSo zeigst du Reittiere auf der Website an:\n\n * Wähle im Menü \"Inventar\" die Option \"Haustiere und Reittiere\" und scrollen nach unten zum Abschnitt \"Reittiere\"",
|
||||
@@ -36,7 +36,7 @@
|
||||
"faqQuestion30": "Was passiert, wenn ich keine HP mehr habe?",
|
||||
"webFaqAnswer30": "Wenn deine HP Null erreichen, verlierst du eine Stufe, dein gesamtes Gold und ein Ausrüstungsstück, das du wieder kaufen kannst.",
|
||||
"faqQuestion31": "Warum habe ich HP verloren, wenn ich mit einer nicht-negativen Aufgabe interagiere?",
|
||||
"faqQuestion32": "Wann kann ich eine Klasse wählen?",
|
||||
"faqQuestion32": "Wie kann ich eine Klasse wählen?",
|
||||
"faqQuestion33": "Was ist der blaue Balken, der nach Level 10 erscheint?",
|
||||
"webFaqAnswer33": "Nachdem du das Klassensystem freigeschaltet hast, schaltest du auch Fertigkeiten frei, die Mana benötigen, um genutzt werden zu können. Mana wird durch deinen INT-Wert bestimmt und kann durch Fertigkeiten und Ausrüstung angepasst werden.",
|
||||
"faqQuestion34": "Welches Futter mag mein Haustier?",
|
||||
@@ -241,5 +241,7 @@
|
||||
"subscriptionDetail430": "Die Kündigung eines aktiven Abonnements wird ein Enddatum für deine Vorteile festsetzen, bis zu dem du vollen Zugang zu allen Abo-Vorteilen hast. Das bedeutet, dass du weiterhin am Start jedes Monats Mystische Sanduhren und Erhöhungen der Edelsteinobergrenze erhältst, solange du Zugang zu diesen Vorteilen hast.",
|
||||
"subscriptionDetail440": "Am Tag, an dem diese Änderungen in Kraft treten, erhalten aktive Abonnenten mit einer ungeraden Anzahl an Edelsteinen pro Monat folgende Anpassungen ihrer Edelsteinobergrenze:",
|
||||
"subscriptionDetail470": "Gruppenabonnentenvorteile verhalten sich genauso wie die eines wiederkehrenden 1-Monats-Abonnements. Du erhältst eine Mystische Sanduhr am Anfang jedes Monats und die Anzahl an Edelsteinen, die du jeden Monat auf dem Marktplatz kaufen kannst, wird sich erhöhen bis zu einem Limit von 50.",
|
||||
"subscriptionPara3": "Wir hoffen, dass dieser neue Rhythmus besser vorhersagbar ist, mehr Zugang zur fantastischen Gegenstandauswahl im Laden des Zeitreisenden ermöglicht und noch mehr Motivation bietet, jeden Monat Fortschritte an deinen Aufgaben zu machen!"
|
||||
"subscriptionPara3": "Wir hoffen, dass dieser neue Rhythmus besser vorhersagbar ist, mehr Zugang zur fantastischen Gegenstandauswahl im Laden des Zeitreisenden ermöglicht und noch mehr Motivation bietet, jeden Monat Fortschritte an deinen Aufgaben zu machen!",
|
||||
"faqQuestion67": "Was sind die Klassen in Habitica?",
|
||||
"webFaqAnswer67": "Klassen sind verschiedene Rollen, die dein Charakter spielen kann. Jede Klasse bietet ihre eigene Reihe von einzigartigen Vorteilen und Fähigkeiten beim Aufsteigen auf höhere Level. Diese Fähigkeiten können das Bearbeiten deiner Aufgaben ergänzen oder dabei helfen, deine Party beim Abschließen von Quests zu unterstützen.\n\nDeine Klasse bestimmt auch, welche Ausrüstung für dich in den Belohnungen, im Marktplatz und im Jahreszeitenmarkt zum Kauf erhältlich ist.\n\nHier ist eine Zusammenfassung jeder Klasse, um dir dabei zu helfen, diejenige zu wählen, welche am besten zu deinem Spielstil passt:\n#### **Krieger**\n* Krieger verursachen hohen Schaden bei Bossen und haben eine hohe Chance für kritische Treffer beim Abschließen von Aufgaben, was dich mit extra Erfahrung und Gold belohnt.\n* Stärke ist ihr primäres Attribut, welches den Schaden erhöht, den sie verursachen.\n* Ausdauer ist ihr sekundäres Attribut, welches den Schaden verringert, den sie erhalten.\n* Die Fähigkeiten der Krieger erhöhen die Ausdauer und Stärke der Party Kameraden.\n* Erwäge, einen Krieger zu spielen, wenn du es liebst, Bosse zu bekämpfen und auch ein wenig Schutz möchtest, wenn du gelegentlich Aufgaben versäumst.\n#### **Heiler**\n* Heiler haben eine starke Verteidigung und können sich selbst, sowie die Party Kameraden, heilen.\n* Ausdauer ist ihr primäres Attribut, welches ihre Heilungen verstärkt und den Schaden, den sie erhalten, verringert.\n* Intelligenz ist ihr sekundäres Attribut, welches ihr Mana und ihre Erfahrung erhöht.\n* Die Fähigkeiten der Heiler bewirken, dass ihre Aufgaben weniger rot werden und erhöhen die Ausdauer der Party Kameraden.\n* Erwäge, einen Heiler zu spielen, wenn du oft Aufgaben versäumst, und die Fähigkeit benötigst, dich selbst und deine Party Kameraden zu heilen. Heiler erreichen schnell neue Level.\n#### **Magier**\n* Magier gewinnen schnell neue Level und viel Mana, und verursachen Schaden bei Bossen in Quests.\n* Intelligenz ist ihr primäres Attribut, welches ihr Mana und ihre Erfahrung erhöht.\n* Wahrnehmung ist ihr sekundäres Attribut, welches ihr gefundenes Gold und ihre gefundenen Gegenstände vermehrt.\n* Die Fähigkeiten der Magier bewirken, dass ihre Aufgaben Strähnen eingefroren werden, stellen das Mana ihrer Party Kameraden wieder her, und erhöhen ihre Intelligenz.\n* Erwäge, einen Magier zu spielen, wenn du durch das schnelle Erreichen neuer Level und das Beisteuern von Schaden in Boss Quests motiviert wirst.\n#### **Schurke**\n* Schurken bekommen die meisten erbeuteten Gegenstände und das meiste Gold beim Erledigen von Aufgaben, und haben eine höhere Chance, kritische Treffer zu erzielen, was ihnen noch mehr Erfahrung und Gold beschert.\n* Wahrnehmung ist ihr primäres Attribut, welches ihr gefundenes Gold und ihre gefundenen Gegenstände vermehrt.\n* Stärke ist ihr sekundäres Attribut, welches den Schaden erhöht, den sie verursachen.\n* Die Fähigkeiten der Schurken helfen ihnen, versäumten Tagesaufgaben auszuweichen, Gold zu klauen, und die Wahrnehmung ihrer Party Kameraden zu erhöhen.\n* Erwäge, einen Schurken zu spielen, wenn du durch Belohnungen sehr motiviert wirst."
|
||||
}
|
||||
|
||||
@@ -170,7 +170,7 @@
|
||||
"joinMany": "Schließe Dich über <%= userCountInMillions %> Millionen Leuten an und habe Spaß, während Du Deine Aufgaben erfüllst!",
|
||||
"joinToday": "Tritt Habitica heute bei",
|
||||
"signup": "Registrieren",
|
||||
"getStarted": "Auf gehts",
|
||||
"getStarted": "Auf geht's",
|
||||
"mobileApps": "Mobile Apps",
|
||||
"learnMore": "Mehr erfahren",
|
||||
"communityInstagram": "Instagram",
|
||||
|
||||
@@ -2800,12 +2800,12 @@
|
||||
"armorMystery202406Text": "Phantom-Seeräuber Kleidung",
|
||||
"headMystery202406Text": "Phantom-Seeräuber Hut",
|
||||
"eyewearMystery202406Text": "Phantom-Seeräuber Maske",
|
||||
"weaponArmoirePaintbrushNotes": "Ein Ruck purer Inspiration durchdringt dich, wenn du diesen Frabpinsel aufhebst, und ermöglicht dir, alles zu malen, was du dir vorstellen kannst. Erhöht Intelligenz um <%= int %>.Verzauberter Schrank: Maler Set (Gegenstand 3 von 4).",
|
||||
"weaponArmoirePaintbrushNotes": "Ein Ruck purer Inspiration durchdringt dich, wenn du diesen Frabpinsel aufhebst, und ermöglicht dir, alles zu malen, was du dir vorstellen kannst. Erhöht Intelligenz um <%= int %>.Verzauberter Schrank: Malerset (Gegenstand 3 von 4).",
|
||||
"weaponArmoirePaintbrushText": "Farbpinsel",
|
||||
"weaponArmoireMopText": "Mopp",
|
||||
"weaponArmoireCleaningClothText": "Putzlappen",
|
||||
"weaponArmoireMopNotes": "Schritt 1: Tauche den Mopp in einen Eimer mit Wasser und Schaum. Schritt 2: Ziehe den Mopp über den Boden. Schritt 3: Tu so, als wäre das Ende des Mopp Stiels ein Mikrofon und singe mit voller Inbrunst. Schritt 4: Wiederhole Schritte 1-3, bis der Boden sauber ist. Erhöht Ausdauer und Wahrnehmung um jeweils <%= attrs %>. Putzausrüstungs-Set Zwei (Gegenstand 2 von 3)",
|
||||
"weaponArmoireCleaningClothNotes": "Nimm dieses Putzwerkzeug auf deine Abenteuer mit und sei immer bereit, eine hübsche Gedenktafel zu polieren oder eine hölzerne Fensterbank zu wischen. Erhöht Stärke und Ausdauer um jeweils <%= attrs %>. Verzauberter Schrank: Putzausrüstung Set Zwei (Gegenstand 3 von 3)",
|
||||
"weaponArmoireMopNotes": "Schritt 1: Tauche den Mopp in einen Eimer mit Wasser und Schaum. Schritt 2: Ziehe den Mopp über den Boden. Schritt 3: Tu so, als wäre das Ende des Mopp Stiels ein Mikrofon und singe mit voller Inbrunst. Schritt 4: Wiederhole Schritte 1-3, bis der Boden sauber ist. Erhöht Ausdauer und Wahrnehmung um jeweils <%= attrs %>. Reinigungs-Set Zwei (Gegenstand 2 von 3)",
|
||||
"weaponArmoireCleaningClothNotes": "Nimm dieses Putzwerkzeug auf deine Abenteuer mit und sei immer bereit, eine hübsche Gedenktafel zu polieren oder eine hölzerne Fensterbank zu wischen. Erhöht Stärke und Ausdauer um jeweils <%= attrs %>. Verzauberter Schrank: Reinigungs-Set Zwei (Gegenstand 3 von 3)",
|
||||
"weaponArmoireRidingBroomText": "Reitbesen",
|
||||
"weaponArmoireRidingBroomNotes": "Reite auf diesem feinen Besen zu all deinen magischsten Besorgungen--oder nimm ihn für eine Spritztour durch die Nachbarschaft. Wuui! Erhöht Stärke um <%= str %> und Intelligenz um <%= int %>. Verzauberter Schrank: Spukhaftes Zauberer Set (Gegenstand 1 von 3)",
|
||||
"weaponArmoireHattersShearsText": "Scharfe Scheren",
|
||||
@@ -2969,7 +2969,7 @@
|
||||
"armorArmoireBasketballUniformNotes": "Fragst du dich, was auf dem Rücken dieser Uniform aufgedruckt ist? Deine Glückszahl, natürlich! Erhöht Wahrnehmung um <%= per %>.Verzauberter Schrank: Altertümliches Basketballset (Gegenstand 1 von 2).",
|
||||
"armorArmoireShawlCollarCoatNotes": "Ein weiser Zauberer sagte einst, dass nichts besser ist als es sowohl gemütlich zu haben als auch produktiv zu sein! Trage diesen warmen und stylischen Mantel, wenn du die diesjährigen Herausforderungen meisterst. Erhöht Ausdauer um <%= con %>.",
|
||||
"armorArmoirePaintersApronText": "Schürze des Malers",
|
||||
"armorArmoirePaintersApronNotes": "Diese Schürze kann deine Kleidung vor Farbe und deine kreativen Projekte vor harschen Kritiken schützen. Erhöht Ausdauer um <%= con %>. Verzauberter Schrank: Maler-Set (Gegenstand 1 von 4).",
|
||||
"armorArmoirePaintersApronNotes": "Diese Schürze kann deine Kleidung vor Farbe und deine kreativen Projekte vor harschen Kritiken schützen. Erhöht Ausdauer um <%= con %>. Verzauberter Schrank: Malerset (Gegenstand 1 von 4).",
|
||||
"weaponSpecialWinter2025WarriorText": "Axt des Elchkriegers",
|
||||
"weaponSpecialWinter2025WarriorNotes": "Eine mächtige Axt für einen mächtigen Elch! Du wirst unaufhaltbar sein! Erhöht Stärke um <%= str %>. Limitierte Ausgabe 2024-2025 Winterausrüstung.",
|
||||
"weaponSpecialWinter2025RogueText": "Schneeflockenausbruch",
|
||||
@@ -3124,7 +3124,7 @@
|
||||
"headArmoireWhiteFloppyHatNotes": "Viele Zaubersprüche wurden in diesen einfachen Hut eingenäht und verleihen ihm eine wundersame weiße Farbe. Erhöht Stärke, Intelligenz und Ausdauer um jeweils <%= attrs %>. Verzauberter Schrank: Weißes Loungewear -Set (Gegenstand 1 von 3).",
|
||||
"headArmoireCorsairsBandanaNotes": "Egal, ob du deinen Kopf bedecken willst, falls eine Möwe über dich hinwegfliegt, oder ob du sicherstellen willst, dass deine Feinde dich nicht schwitzen sehen, dieses Tuch ist unverzichtbar. Füge einfach eine Zierperle für jedes Abenteuer hinzu, das du bestehst. Erhöht Intelligenz um <%= int %>. Verzauberter Schrank: Korsaren-Set (Gegenstand 2 von 3)",
|
||||
"headArmoireFunnyFoolCapNotes": "Die Glöckchen an diesem Hut könnten deine Gegner zum Kichern bringen, aber dir helfen sie nur, dich zu konzentrieren. Erhöht Ausdauer um <%= con %>. Verzauberter Schrank: Lustiges Narren-Set (Gegenstand 1 von 3)",
|
||||
"headArmoireDragonKnightsHelmNotes": "Mit den feurigen Elementen auf diesem Helm könnten Drachen dich für einen der ihren halten. Erhöht Intelligenz um <%= int %>. Verzauberter Schrank: Drachenritter-Set (Gegenstand 1 von 3)",
|
||||
"headArmoireDragonKnightsHelmNotes": "Mit den feurigen Elementen auf diesem Helm könnten dich Drachen für einen der ihren halten. Erhöht Intelligenz um <%= int %>. Verzauberter Schrank: Drachenritter-Set (Gegenstand 1 von 3)",
|
||||
"headArmoireStormKnightHelmText": "Sturmritterhelm",
|
||||
"headArmoireGreenTrapperHatText": "Grüne Trappermütze",
|
||||
"headArmoireGreenTrapperHatNotes": "Alle sagen, dass deine Mütze so warm aussieht! Und das ist sie tatsächlich. Achte nur darauf, dass du die Klappen von deinen Ohren ziehst, wenn die Leute mit dir reden, sonst hört sich das Ganze eher nach „dne ütze sht ss wrrm ss!“ an. Erhöht Ausdauer und Wahrnehmung um jeweils <%= attrs %> . Verzauberter Schrank: Trappermützen-Set (Gegenstand 1 von 2).",
|
||||
@@ -3189,5 +3189,103 @@
|
||||
"shieldSpecialFall2024WarriorNotes": "Komplikationen bei Aufgaben werden von deinem Schild absorbiert und machen dich entschlossener. Erhöht Ausdauer um <%= con %>. Limitierte Ausgabe Herbstausrüstung 2024.",
|
||||
"shieldSpecialFallRogue2024Notes": "Die Aufgaben selbst werden von den Wirbeln und Spiralen dieser hypnotischen Waffe überwältigt. Erhöht Stärke um <%= str %>. Limitierte Ausgabe Herbstausrüstung 2024.",
|
||||
"headAccessoryMystery202212Notes": "Mit dieser verschnörkelten goldenen Tiara werden deine Herzlichkeit und Freundschaft neue Höhen erreichen. Gewährt keinen Attributbonus. Dezember 2022 Abonnentengegenstand.",
|
||||
"headAccessoryMystery202212Text": "Eis-Tiara"
|
||||
"headAccessoryMystery202212Text": "Eis-Tiara",
|
||||
"shieldMystery202408Notes": "Die magischen Lichter beleuchten das Innere deines Seifenblasenverstecks oder jeden anderen Ort, an dem du ein wenig Licht brauchst! Gewährt keinen Attributbonus. August 2024 Abonnentengegenstand.",
|
||||
"shieldArmoireJewelersPliersText": "Juwelierzange",
|
||||
"shieldArmoireJewelersPliersNotes": "Sie schneidet, biegt, presst und vieles mehr. Mit diesem Werkzeug kannst du alles machen, was du dir vorstellen kannst. Erhöht Stärke um <%= str %>. Verzauberter Schrank: Juwelierset (Gegenstand 3 von 4).",
|
||||
"shieldArmoireTeaKettleNotes": "In diesem Kessel kannst du all deine geschmackvollen Lieblingstees aufbrühen. Hast du Lust auf schwarzen Tee, grünen Tee, Oolong oder vielleicht einen Kräutertee? Erhöht Ausdauer um <%= con %>. Verzauberter Schrank: Teekränzchen Set (Gegenstand 3 von 3).",
|
||||
"shieldArmoireBasketballNotes": "Zisch! Wann immer du diesen magischen Basketball abschießt, wird es nichts als Treffer geben. Erhöht Ausdauer und Stärke um jeweils <%= attrs %> . Verzauberter Schrank: Altmodisches Basketballset (Gegenstand 2 von 2).",
|
||||
"shieldArmoirePaintersPaletteNotes": "Farben in allen Facetten des Regenbogens stehen dir zur Verfügung. Ist es Magie, die sie so lebendig macht, wenn du sie benutzt, oder ist es dein Talent? Erhöht Stärke um <%= str %>. Verzauberter Schrank: Malerset (Gegenstand 4 von 4).",
|
||||
"shieldArmoireBucketNotes": "Obwohl dieser Eimer für eine Mischung aus Wasser und Reinigungslösung gedacht ist, kannst du ihn auch zum Sammeln, Tragen und Transportieren von allem verwenden, was hineinpasst! Erhöht Stärke und Intelligenz um jeweils <%= attrs %>. Verzauberter Schrank: Reinigungs-Set 2 (Gegenstand 1 von 3)",
|
||||
"backMystery202402Text": "Paradiesische Pinke Herzen",
|
||||
"shieldArmoireSaucepanNotes": "Schau in diesen dampfenden Kochtopf und finde die Antwort auf das bestgehütete Geheimnis des Lebens! (Suppe. Die Antwort ist immer Suppe.) ErhöhtWahrnehmung um <%= per %>. Verzauberter Schrank: Küchenwerkzeugset 2 (Gegenstand 1 von 2).",
|
||||
"shieldArmoireBuoyantBeachBallNotes": "Hast du schon zu viele Bälle in der Luft? Hier ist einer, den du sicher absetzen, rollen, hüpfen und hüpfen und hüpfen lassen kannst... Erhöht Stärke um <%= str %>. Verzauberter Schrank: Strand-Set (Gegenstand 4 von 4).",
|
||||
"shieldArmoireTrustyPencilNotes": "Du weißt, was man sagt: Der Bleistift ist mächtiger als der Schwertstift. Moment... das klingt nicht ganz richtig... Erhöht Intelligenz um <%= int %>. Verzauberter Schrank: Schuluniformset (Gegenstand 4 von 4).",
|
||||
"backMystery202401Notes": "Beschwöre sanftes Schneegestöber herauf oder rufe einen mächtigen Schneesturm herbei. Du hast die Wahl! Gewährt keinen Attributbonus. Jänner 2024 Abonnentengegenstand.",
|
||||
"backMystery202402Notes": "Lass dich von einer Aura liebevoller Energie umgeben, wohin du auch gehst! Gewährt keinen Attributbonus. Februar 2024 Abonnentengegenstand.",
|
||||
"backMystery202302Text": "Betrügerischer Tabby-Schweif",
|
||||
"backMystery202301Notes": "Diese flauschigen Schweife enthalten ätherische Kräfte und auch ein hohes Maß an Charme! Gewährt keinen Attributbonus. Jänner 2023 Abonnentengegenstand.",
|
||||
"backMystery202302Notes": "Wann immer du diesen Schweif trägst, wird es ein toller Tag werden! Callooh! Callay! Gewährt keinen Attributbonus. Februar 2023 Abonnentengegenstand.",
|
||||
"backMystery202305Text": "Abendliche Flügel",
|
||||
"backMystery202305Notes": "Fang das Funkeln des Abendsterns ein und schwebe auf diesen Flügeln in fremde Gefilde. Gewährt keinen Attributbonus. Mai 2023 Abonnentengegenstand.",
|
||||
"backMystery202309Text": "Kolossale Kometenmottenflügel",
|
||||
"backMystery202309Notes": "Flattere durch Wälder, gleite über Berge und schwebe über Ozeane auf diesen hellen und schönen Flügeln. Gewährt keinen Attributbonus. September 2023 Abonnentengegenstand.",
|
||||
"backSpecialAnniversaryText": "Habitica Helden Cape",
|
||||
"backSpecialAnniversaryNotes": "Lass dieses stolze Cape im Wind flattern und erzähle jedem, dass du ein Habitica Held bist. Gewährt keinen Attributbonus. Gegenstands-Sonderausgabe zur 10. Geburtstagsfeier.",
|
||||
"backSpecialHeroicAureoleText": "Heroische Aureole",
|
||||
"backSpecialHeroicAureoleNotes": "Die Edelsteine auf dieser Aureole schimmern, wenn du deine ruhmvollen Geschichten erzählst. Erhöht alle Eigenschaften um <%= attrs %>.",
|
||||
"bodySpecialAnniversaryText": "Habiticas Heldenkragen",
|
||||
"bodySpecialAnniversaryNotes": "Ergänzt dein königspurpurnes Kostüm perfekt! Gewährt keinen Attributbonus. Sonderausgaben-Gegenstand zur 10. Geburtstagsfeier.",
|
||||
"eyewearMystery202312Text": "Winterliche blaue Augen",
|
||||
"bodyArmoireKarateBrownBeltNotes": "Dieser Gürtel ist für diejenigen, deren Techniken und Fähigkeiten ausgereift sind. Erhöht Stärke um <%= str %>. Verzauberter Schrank: Karateset (Gegenstand 9 von 10).",
|
||||
"bodyArmoireKarateBrownBeltText": "Brauner Gürtel",
|
||||
"headAccessoryMystery202302Text": "Trickbetrüger Tabby-Ohren",
|
||||
"headAccessoryMystery202307Text": "Krakenkrone",
|
||||
"bodyArmoireKarateOrangeBeltNotes": "Dieser Gürtel ist für diejenigen, die sich gesteigert und das Einsteigerlevel gemeistert haben. Erhöht Ausdauer um <%= con %>. Verzauberter Schrank: Karateset (Gegenstand 4 von 10).",
|
||||
"bodyArmoireKarateGreenBeltText": "Grüner Gürtel",
|
||||
"bodyArmoireKarateBlackBeltText": "Schwarzer Gürtel",
|
||||
"bodyArmoireKarateYellowBeltNotes": "Dieser Gürtel ist für Einsteiger, welche die Grundlagen gelernt haben. Erhöht Wahrnehmung um <%= per %>. Verzauberter Schrank: Karateset (Gegenstand 3 von 10).",
|
||||
"eyewearMystery202312Notes": "Kein Grund zur Sorge, diese eisigen Blautöne helfen dir, hinter der kalten und dunklen Jahreszeit die Wärme der nachfolgenden Monate zu erspähen. Gewährt keinen Attributbonus. Dezemeber 2023 Abonnentengegenstand.",
|
||||
"eyewearMystery202406Notes": "Versuch zu vermeiden, dass dies von einer Bande aufdringlicher Kinder und ihrem sprechenden Hund abgezogen wird. Gewährt keinen Attributbonus. Juni 2024 Abonnentengegenstand.",
|
||||
"bodyArmoireKarateOrangeBeltText": "Orangener Gürtel",
|
||||
"headAccessoryMystery202305Text": "Abendzeitliche Hörner",
|
||||
"eyewearMystery202303Notes": "Vermittle deinen Feinden durch deinen lässigen Gesichtsausdruck ein falsches Gefühl der Sicherheit. Gewährt keinen Attributbonus. März 2023 Abonnentengegenstand.",
|
||||
"eyewearMystery202308Notes": "Bist du schläfrig oder ruhst du deine Augen nur in Erwartung deines nächsten tollen Kampfes aus? Gewährt keinen Attributbonus. August 2023 Abonnentengegenstand.",
|
||||
"bodyArmoireKarateWhiteBeltText": "Weißer Gürtel",
|
||||
"bodyArmoireKarateWhiteBeltNotes": "Dieser niedrigste Gürtel ist für jene, die ihre Reise gerade erst beginnen. Erhöht Intelligenz um <%= int %>. Verzauberter Schrank: Karateset (Gegenstand 2 von 10).",
|
||||
"bodyArmoireKarateGreenBeltNotes": "Dieser Gürtel ist für diejenigen gedacht, die auf fortgeschrittenem Niveau lernen, ihre Fähigkeiten zu verbessern. Erhöht Stärke um <%= str %>. Verzauberter Schrank: Karateset (Gegenstand 5 von 10).",
|
||||
"bodyArmoireKarateBlueBeltNotes": "Dieser Gürtel ist für diejenigen, die mehr lernen und ihren Geist und Körper entwickeln wollen. Erhöht Ausdauer um <%= con %>. Verzauberter Schrank: Karateset (Gegenstand 6 von 10).",
|
||||
"headAccessoryMystery202302Notes": "Das schnurrige Accessoire, das dein bezauberndes Grinsen unterstreicht. Gewährt keinen Attributbonus. Februar 2023 Abonnentengegenstand.",
|
||||
"headAccessoryMystery202307Notes": "Dieser mächtige Stirnreif beschwört Wirbelstürme und stürmisches Wetter herauf! Gewährt keinen Attributbonus. Juli 2023 Abonnentengegenstand.",
|
||||
"headAccessoryMystery202305Notes": "Diese Hörner leuchten durch das reflektierte Mondlicht. Gewährt keinen Attributbonus. Mai 2023 Abonnentengegenstand.",
|
||||
"bodyArmoireKarateYellowBeltText": "Gelber Gürtel",
|
||||
"bodyArmoireKaratePurpleBeltNotes": "Dieser Gürtel ist für diejenigen, die für anspruchsvolle Übungen bereit sind. Erhöht Ausdauer um <%= con %>. Verzauberter Schrank: Karateset (Gegenstand 7 von 10).",
|
||||
"bodyArmoireKarateRedBeltText": "Roter Gürtel",
|
||||
"bodyArmoireKarateRedBeltNotes": "Dieser Gürtel ist für diejenigen, die gelernt haben, bei ihren Übungen vorsichtig vorzugehen. Erhöht Wahrnehmung um <%= per %>. Verzauberter Schrank: Karateset (Gegenstand 8 von 10).",
|
||||
"bodyArmoireKarateBlackBeltNotes": "Dieser höchste Gürtelgrad ist für diejenigen, die ein tieferes Verständnis anstreben und ihr Wissen an andere weitergeben dürfen. Erhöht Intelligenz um <%= int %>. Verzauberter Schrank: Karateset (Gegenstand 10 von 10).",
|
||||
"headAccessorySpecialHeroicCircletText": "Heldenhafter Stirnreif",
|
||||
"headAccessorySpecialHeroicCircletNotes": "Schwer ist das Haupt, das die Krone trägt, aber dieser Stirnreif ist so leicht wie dein großzügiger Geist. Erhöht alle Werte um <%= attrs %>.",
|
||||
"headAccessoryMystery202309Text": "Kolossale Kometenmotten-Antennen",
|
||||
"headAccessoryMystery202309Notes": "Diese Antennen sind modisch und gefiedert, helfen aber auch bei der Navigation! Gewährt keinen Attributbonus. September 2023 Abonnentengegenstand.",
|
||||
"headAccessoryMystery202310Text": "Geisterlicht-Krone",
|
||||
"headAccessoryMystery202310Notes": "Wie ein Irrlicht können diese unheimlichen Lichter neugierige Seelen in ihr Verderben locken. Gewährt keinen Attributbonus. Oktober 2023 Abonnentengegenstand.",
|
||||
"eyewearSpecialAnniversaryNotes": "Schau durch die Augen eines Habitica-Helden - durch deine! Gewährt keinen Attributbonus. Sonderausgaben-Gegenstand zur 10. Geburtstagsfeier.",
|
||||
"bodyArmoireKarateBlueBeltText": "Blauer Gürtel",
|
||||
"bodyArmoireKaratePurpleBeltText": "Violetter Gürtel",
|
||||
"eyewearMystery202303Text": "Verträumte Augen",
|
||||
"eyewearSpecialAnniversaryText": "Habiticas Heldenmaske",
|
||||
"shieldArmoireFancyFloralFanNotes": "Beende deinen bezaubernden Look mit diesem erstklassigen Fächer aus besonders blumiger Baumwolle. Erhöht Wahrnehmung um <%= per %>. Verzauberter Schrank: Bezauberndes Blumenset (Gegenstand 2 von 2).",
|
||||
"armorMystery202502Notes": "Du bist voller gutmütiger Witze und Scherze, von deinem zerrupften Kragen bis zu deinen gigantischen Schuhen! Gewährt keinen Attributbonus. Februar 2025 Abonnentengegenstand.",
|
||||
"armorMystery202502Text": "Herzvoller Harlekin Anzug",
|
||||
"headMystery202502Text": "Herzvoller Harlekin Hut",
|
||||
"headMystery202502Notes": "Dieses fröhliche Hütchen wird bei jedem, der dich sieht, für Freude sorgen! Gewährt keinen Attributbonus. Februar 2025 Abonnentengegenstand.",
|
||||
"headArmoireFancyFloralHatText": "Bezaubernder Blumenhut",
|
||||
"shieldMystery202502Text": "Herzvolle Harlekin Ballons",
|
||||
"shieldMystery202502Notes": "Möge dein Herz an diesen Valentinstag, und auch jeden anderen Tag, so leicht sein wie diese beschwingten Luftballons. Gewährt keinen Attributbonus. Februar 2025 Abonnentengegenstand.",
|
||||
"shieldArmoireFancyFloralFanText": "Bezaubernder Blumenfächer",
|
||||
"headArmoireFancyFloralHatNotes": "Bestaune diesen bezaubernden Hut voller hinreißender Blumen und verschnörkelten Schnallen. Erhöht Intelligenz um <%= int %>. Verzauberter Schrank: Bezauberndes Blumen Zubehör Set (Gegenstand 1 von 2).",
|
||||
"weaponSpecialSpring2025WarriorNotes": "Mit einem Schnitt kannst du durch Blumenstängel säbeln, um ein Bukett zu machen, oder direkt durch Hindernisse, um deine Aufgaben zu erfüllen. Erhöht Stärke um <%= str %>. Limitierte Ausgabe Frühlingsausrüstung 2025.",
|
||||
"weaponSpecialSpring2025WarriorText": "Sonnenschein-Säbel",
|
||||
"weaponSpecialSpring2025RogueText": "Kristallspitzen-Morgenstern",
|
||||
"weaponSpecialSpring2025RogueNotes": "Mit einem Schwinger kannst du jedes Hindernis eliminieren, das deinen Zielen im Weg steht. Erhöht Stärke um <%= str %>. Limitierte Ausgabe 2025 Frühlingsausrüstung.",
|
||||
"weaponSpecialSpring2025MageNotes": "Mit einem Schwinger kannst du Elementarmagie nutzen, um deine Umgebung zu kontrollieren. Nutze den Vorteil und spring vorwärts! Erhöht Intelligenz um <%= int %> und Wahrnehmung um <%= per %>. Limitierte Ausgabe Frühlingsausrüstung 2025.",
|
||||
"weaponSpecialSpring2025MageText": "Fangschrecken Stab",
|
||||
"armorSpecialSpring2025WarriorText": "Sonnenschein Rüstung",
|
||||
"armorSpecialSpring2025WarriorNotes": "Diese atemberaubende Rüstung hat Farben, die du inmitten eines sonnigen Frühlingstages am Himmel sehen kannst. Erhöht Ausdauer um <%= con %>. Limitierte Ausgabe Frühlingsausrüstung 2025.",
|
||||
"armorSpecialSpring2025HealerText": "Plumeria Robe",
|
||||
"armorSpecialSpring2025HealerNotes": "Diese atemberaubende Robe enthält Plumeria Blütenblätter, die weich und raschelig sind. Erhöht Ausdauer um <%= con %>. Limitierte Ausgabe 2025 Frühlingsausrüstung.",
|
||||
"weaponSpecialSpring2025HealerText": "Plumeria Hirtenstab",
|
||||
"weaponSpecialSpring2025HealerNotes": "Mit einem Schwinger kannst du Bestäuber an deine Seite rufen, um dir bei deinen Abenteuern zu helfen. Erhöht Intelligenz um <%= int %>. Limitierte Ausgabe 2025 Frühlingsausrüstung.",
|
||||
"armorSpecialSpring2025RogueText": "Kristallnadel-Umhang",
|
||||
"armorSpecialSpring2025RogueNotes": "Dieser atemberaubende Umhang enthält extra Kristalle mit speziellen, geheimen Kräften, von denen nur du weißt. Erhöht Wahrnehmung um <%= per %>. Limitierte Ausgabe 2025 Frühlingsausrüstung.",
|
||||
"armorSpecialSpring2025MageText": "Fangschrecken Uniform",
|
||||
"armorSpecialSpring2025MageNotes": "Diese atemberaubende Uniform enthält auffällige Farben, lässt dich aber trotzdem heimlich an deine schwierigsten Aufgaben anpirschen. Erhöht Intelligenz um <%= int %>. Limitierte Ausgabe 2025 Frühlingsausrüstung.",
|
||||
"headSpecialSpring2025WarriorText": "Sonnenschein Helm",
|
||||
"headSpecialSpring2025WarriorNotes": "Der Kamm auf diesem Helm erinnert an den Lauf der Sonne, sieht aber auch aus wie eine Bürste, die du für den Frühjahrsputz nutzen könntest. Erhöht Stärke um <%= str %>. Limitierte Ausgabe 2025 Frühlingsausrüstung.",
|
||||
"headSpecialSpring2025RogueText": "Kristallnadel-Hut",
|
||||
"headSpecialSpring2025RogueNotes": "Die Kristalle in diesem Hut ermuntern zu produktiver Energie und leuchten außerdem hell, damit du zu jeder Tages- und Nachtzeit arbeiten kannst. Erhöht Wahrnehmung um <%= per %>. Limitierte Ausgabe 2025 Frühlingsausrüstung.",
|
||||
"headSpecialSpring2025HealerText": "Plumeria Kopfschmuck",
|
||||
"headSpecialSpring2025HealerNotes": "Diese Blume symbolisiert Geburt, Liebe und Neubeginn! Sie verbreitet auch einen schönen Duft, den du genießen kannst, während du an deinen Aufgaben arbeitest. Erhöht Intelligenz um <%= int %>. Limitierte Ausgabe 2025 Frühlingsausrüstung.",
|
||||
"headSpecialSpring2025MageText": "Fangschrecken Maske",
|
||||
"headSpecialSpring2025MageNotes": "Die Fangschrecke ist bekannt dafür, sich zu tarnen oder sich langsam zu bewegen. Wähle eine Taktik zu Hilfe für jedes einzelne Ziel, sei aber gewiss, daß du Taktiken jederzeit ändern kannst, wenn du musst. Erhöht Wahrnehmung um <%= per %>. Limitierte Ausgabe 2025 Frühlingsausrüstung."
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
"user": "Benutzer",
|
||||
"market": "Marktplatz",
|
||||
"newSubscriberItem": "Du hast einen neuen <span class=\"notification-bold-blue\">Überraschungsgegenstand</span>",
|
||||
"subscriberItemText": "Abonnenten bekommen jeden Monat einen Überraschungsgegenstand. Er wird Anfang des Monats verfügbar. Schaue auf der 'Überraschungsgegenstand'-Seite des Wikis für mehr Informationen nach.",
|
||||
"subscriberItemText": "Abonnenten bekommen jeden Monatsanfang ein neues Überraschungsausrüstungsset!",
|
||||
"all": "Alle",
|
||||
"none": "Keine",
|
||||
"more": "<%= count %> mehr",
|
||||
@@ -238,5 +238,8 @@
|
||||
"mutePlayer": "Stumm",
|
||||
"skipExternalLinkModal": "Halte STRG (Windows) oder Command (Mac) beim Anklicken eines Links, um dieses Modal zu überspringen.",
|
||||
"shadowMute": "Unsichtbare Stummschaltung",
|
||||
"titleCustomizations": "Individualisierungen"
|
||||
"titleCustomizations": "Individualisierungen",
|
||||
"targetUserNotExist": "Zielbenutzer: '<%= userName %>' existiert nicht.",
|
||||
"newMessage": "Neue Nachricht",
|
||||
"rememberToBeKind": "Bitte sei freundlich, respektvoll, und folge den <a href='/static/community-guidelines' target='_blank'>Community-Richtlinien</a>."
|
||||
}
|
||||
|
||||
@@ -95,9 +95,6 @@
|
||||
"whyReportingPostPlaceholder": "Grund für die Beschwerde",
|
||||
"optional": "Optional",
|
||||
"needsTextPlaceholder": "Gib Deine Nachricht hier ein.",
|
||||
"copyMessageAsToDo": "Nachricht als To-Do übernehmen",
|
||||
"copyAsTodo": "Als To-Do kopieren",
|
||||
"messageAddedAsToDo": "Nachricht als To-Do übernommen.",
|
||||
"leaderOnlyChallenges": "Nur die Gruppenleitung kann Herausforderungen erstellen",
|
||||
"sendGift": "Ein Geschenk schicken",
|
||||
"inviteFriends": "Lade Freunde ein",
|
||||
@@ -245,7 +242,7 @@
|
||||
"guildSummaryPlaceholder": "Schreibe eine Kurzbeschreibung über deine Gruppe.. Was ist der Hauptzweck der Gruppe und was werden die Gruppenmitglieder tun?",
|
||||
"groupDescription": "Beschreibung",
|
||||
"guildDescriptionPlaceholder": "Nutze diesen Abschnitt um alles, was Mitglieder über Deine Gruppe wissen sollten, ausführlicher darzustellen. Nützliche Tipps, hilfreiche Links und ermutigende Worte gehören hier hin!",
|
||||
"markdownFormattingHelp": "[Markdown Formatierungshilfe](https://habitica.fandom.com/wiki/Markdown_Cheat_Sheet)",
|
||||
"markdownFormattingHelp": "[Markdown Formatierungshilfe](https://github.com/HabitRPG/habitica/wiki/Markdown-in-Habitica)",
|
||||
"partyDescriptionPlaceholder": "Das ist unsere Partybeschreibung. Sie beschreibt, was wir in unserer Party so tun. Wenn Du mehr darüber wissen willst, was wir in unserer Party so machen, lies die Beschreibung. Party on!",
|
||||
"guildGemCostInfo": "Eine Edelstein-Gebühr fördert die Qualität der Gilden und wird der Gildenbank gutgeschrieben.",
|
||||
"noGuildsTitle": "Du bist nicht Mitglied einer Gilde.",
|
||||
@@ -430,5 +427,6 @@
|
||||
"createGroupTitle": "Erstelle Gruppe",
|
||||
"readyToUpgrade": "Bereit zum Aufrüsten?",
|
||||
"interestedLearningMore": "Willst du mehr erfahren?",
|
||||
"checkGroupPlanFAQ": "Schau in die <a href='/static/faq#what-is-group-plan'>Gruppenpläne FAQ</a> um herauszufinden, wie du deine gemeinsamen Aufgaben optimal nutzen kannst."
|
||||
"checkGroupPlanFAQ": "Schau in die <a href='/static/faq#what-is-group-plan'>Gruppenpläne FAQ</a> um herauszufinden, wie du deine gemeinsamen Aufgaben optimal nutzen kannst.",
|
||||
"messageCopiedToClipboard": "Nachricht in Zwischenablage kopiert."
|
||||
}
|
||||
|
||||
@@ -270,5 +270,9 @@
|
||||
"winter2025StringLightsHealerSet": "Lichterketten Heiler Set",
|
||||
"winter2025SnowRogueSet": "Schneeschurken Set",
|
||||
"winter2025MooseWarriorSet": "Elchkrieger Set",
|
||||
"winter2025AuroraMageSet": "Aurora Magier Set"
|
||||
"winter2025AuroraMageSet": "Aurora Magier Set",
|
||||
"spring2025PlumeriaHealerSet": "Plumeria Heiler Set",
|
||||
"spring2025MantisMageSet": "Fangschrecken Magier Set",
|
||||
"spring2025SunshineWarriorSet": "Sonnenschein Krieger Set",
|
||||
"spring2025CrystalPointRogueSet": "Kristallspitzen Schurken Set"
|
||||
}
|
||||
|
||||
@@ -46,12 +46,10 @@
|
||||
"messageNotAbleToBuyInBulk": "Dieser Gegenstand kann nicht in größeren Mengen als 1 gekauft werden.",
|
||||
"notificationsRequired": "Mitteilungs-IDs werden benötigt.",
|
||||
"unallocatedStatsPoints": "Du kannst <span class=\"notification-bold-blue\"><%= points %> Attributpunkt(e)</span> verteilen",
|
||||
"beginningOfConversation": "Dies ist der Anfang Deiner Unterhaltung mit <%= userName %>.",
|
||||
"messageDeletedUser": "Tut uns leid, dieser Benutzer hat sein Konto gelöscht.",
|
||||
"messageMissingDisplayName": "Fehlender Anzeigename.",
|
||||
"reportedMessage": "Du hast diese Nachricht den Moderatoren gemeldet.",
|
||||
"canDeleteNow": "Du kannst diese Nachricht nun löschen, wenn Du willst.",
|
||||
"beginningOfConversationReminder": "Denke an einen freundlichen und respektvollen Umgang und halte Dich an die Community-Richtlinien!",
|
||||
"newsPostNotFound": "News-Eintrag nicht gefunden, oder Du hast keinen Zugriff.",
|
||||
"messagePetMountUnEquipped": "Haus- und Reittier in die Stallungen gebracht.",
|
||||
"messageCostumeUnEquipped": "Kostüm abgelegt.",
|
||||
|
||||
@@ -126,7 +126,7 @@
|
||||
"limitedAvailabilityHours": "Für t <%= hours %>std und <%= minutes %>min verfügbar",
|
||||
"limitedAvailabilityDays": "Für <%= days %>t <%= hours %>std und <%= minutes %>min verfügbar",
|
||||
"amountExp": "<%= amount %> Exp",
|
||||
"helpSupportHabitica": "Hilf Habitica zu unterstützen",
|
||||
"helpSupportHabitica": "Hilf dabei, Habitica zu unterstützen",
|
||||
"groupsPaymentSubBilling": "Dein nächstes Rechnungsdatum ist <strong><%= renewalDate %></strong>.",
|
||||
"groupsPaymentAutoRenew": "Dieses Abonnement läuft automatisch weiter, bis es gekündigt wird. Du kannst es im Gruppen-Abrechnungs-Tab kündigen.",
|
||||
"sellItems": "Items verkaufen",
|
||||
|
||||
@@ -786,7 +786,7 @@
|
||||
"questChameleonNotes": "Es ist ein schöner Tag in einer warmen, regnerischen Ecke der Aufgabenwälder. Du bist auf der Jagd nach Neuzugängen für deine Blattsammlung, als ein Ast vor dir ohne Vorwarnung seine Farbe ändert! Und dann bewegt er sich!<br><br>Rückwärts stolpernd realisierst du, dass dies überhaupt kein Ast ist, sondern ein großes Chamäleon! Jeder Teil seines Körpers wechselt andauernd seine Farbe, während seine Augen in unterschiedliche Richtungen zucken.<br><br>“Geht es dir gut?“ fragst du das Chamäleon.<br><br>“Ahhh, na ja,“ sagt es und wirkt ein wenig durcheinander. „Ich habe versucht, mich anzupassen… aber es ist so überwältigend… die Farben kommen und gehen ständig! Es ist schwer, sich auf nur eine zu konzentrieren….“<br><br>“Aha,“ sagst du, „Ich glaube, ich kann helfen. Wir schärfen deine Konzentration mit einer kleinen Herausforderung! Halte deine Farben bereit!“<br><br>“Die Wette gilt!“ erwidert das Chamäleon.",
|
||||
"questGiraffeBoss": "Gear-affe",
|
||||
"questGiraffeCompletion": "Nachdem du der Gear-affe mit ein bisschen grundlegender Organisation ihres Stapels geholfen hast, fühlt ihr euch beide energiegeladener und motivierter!<br><br>Sie nimmt ihre Gitarre und ein Heft mit Anfängerübungen und spielt ein paar Noten. \"Es fühlt sich gut an, einen Schritt in die richtige Richtung zu machen, selbst wenn es nur ein kleiner ist. Vielen Dank, dass du mir geholfen hast! Nimm diese hier, ich habe gehört, du hast einige Haustiere und diese Kameraden könnten eine nette Ergänzung sein!\"",
|
||||
"questCrabDropCrabEgg": "Kabbe (Ei)",
|
||||
"questCrabDropCrabEgg": "Krabbe (Ei)",
|
||||
"questCrabUnlockText": "Schaltet Krabbeneier zum Kauf auf dem Marktplatz frei.",
|
||||
"questChameleonCompletion": "Nach ein paar lebhaften Drehungen durchlief das Chamäleon alle Farben des Regenbogens und traf perfekt alle Farben, die du verlangt hattest.<br><br>\"Wow,\" sagt es, \"zusammenzuarbeiten, und es zu einem Spiel zu machen, hat mir wirklich geholfen, mich zu konzentrieren! Bitte nimm diese als Belohnung, du hast sie verdient! Bring diesen kleinen Jungen bei, wie man in alle Regenbogenfarben wechselt, wenn sie schlüpfen.\"",
|
||||
"questCrabNotes": "Es ist ein warmer, sonniger Morgen, und Du genießt einen Besuch am Strand, um ein paar Bücher von Deiner Sommerleseliste zu lesen. Du schreckst auf, als du fast auf einen glänzenden Kristall in der Nähe eines flachen Lochs im Sand trittst.<br><br>„Ey, pass auf, wo du hingehst! Ich baue hier eine Wohnhöhle!“, sagt eine Stimme. Eine überraschend große Krabbe mit einem dekorativen Panzer buddelt sich vor Deinen Zehen aus dem Loch und schnappt mit ihrer Schere, während sie spricht.<br><br>„Hm, ist das eine Höhle?“, fragst Du und betrachtest die flache Vertiefung. Es sind Muscheln und Kristalle um sie herum angeordnet, aber es deutet nicht viel auf einen Rückzugsort hin.<br><br>Die Krabbe stottert. „Ey, das ist eine vorurteilsfreie Zone! Ich komme schon noch dazu, ich komme schon noch dazu... Ich bin gerade beim Dekorieren hängen geblieben. Manchmal muss eine Krabbe eben ein wenig Zeit vertrödeln“, sagt sie und rückt eine Schale zurecht.<br><br>„Warum hilfst Du nicht mit, wenn Du schon so großartige Vorstellungen davon hast, wie eine Höhle aussehen soll?“",
|
||||
@@ -810,5 +810,26 @@
|
||||
"questDogDropDogEgg": "Hund (Ei)",
|
||||
"questDogUnlockText": "Schaltet den Kauf von Hundeeiern auf dem Marktplatz frei.",
|
||||
"questDogNotes": "Du wurdest für eine Expedition ausgewählt, um die unterirdischen Höhlensysteme von Habitica zu kartieren! Forscher in Habit City vermuten, dass es in diesen Tiefen neue Werkzeuge für die Bewältigung von Aufgaben oder sogar unentdeckte Kreaturen geben könnte.<br><br>Während du felsige Tunnel in der Nähe der Ausläufer des Wandernden Gebirges erkundest, bemerkst du ein Leuchten, das von einem zerklüfteten Eingang vor dir ausgeht. Als du näher kommst, siehst du... Spielzeug? Plüschtiere und Gummibälle liegen auf dem Höhlenboden verstreut. Hörst du da ein Bellen?<br><br>Ein riesiger, dreiköpfiger Hund springt heraus und stürzt sich auf das Spielzeug, das du gerade aufheben wolltest! Du erstarrst und verlierst dabei fast deinen Arm! Aber... die Mäuler des Hundes scheinen zu sehr mit Spielzeug beschäftigt zu sein, um anzugreifen?<br><br>„Wuff!“, bellt eines der Hundemäuler und lässt einen zerrissenen Spielzeugwaschbären fallen. „Bist du hier, um mir beim Aufräumen zu helfen? Ich muss wirklich aufräumen, aber jedes Mal, wenn ich ein Spielzeug in die Hand nehme, spiele ich nur damit... Hier, denk schnell!!!“<br><br> Der Hund wirft dir einen Ball zu, und dann noch einen und noch einen. Diese zusätzlichen Köpfe machen das Ausweichen zu einem echten Training!",
|
||||
"questDogCompletion": "Nachdem du alle Spielzeuge eingesammelt hast, denen du (zum Glück) ausgewichen bist, gibst du Shiberus einen sanften Klaps auf seinen mittleren Kopf.<br><br>„Es ist schön, sich auf eine große Aufgabe zu freuen, aber es könnte hilfreich sein, mit einem Plan vorzugehen. Vielleicht solltest du das nächste Mal am Eingang anfangen und dich rückwärts vorarbeiten? Oder 30 Minuten am Stück arbeiten und dann eine kurze Spielpause einschieben.\"<br><br>„Das ist eine gute Idee“, meldet sich der linke Kopf des Hundes zu Wort. Der rechte Kopf legt ein paar Gegenstände in deine Nähe, darunter auch etwas, das aussieht wie Eier... „Ich habe ein paar Dinge gefunden, die dir gefallen könnten, während wir gespielt haben. Danke für deine Hilfe!“"
|
||||
"questDogCompletion": "Nachdem du alle Spielzeuge eingesammelt hast, denen du (zum Glück) ausgewichen bist, gibst du Shiberus einen sanften Klaps auf seinen mittleren Kopf.<br><br>„Es ist schön, sich auf eine große Aufgabe zu freuen, aber es könnte hilfreich sein, mit einem Plan vorzugehen. Vielleicht solltest du das nächste Mal am Eingang anfangen und dich rückwärts vorarbeiten? Oder 30 Minuten am Stück arbeiten und dann eine kurze Spielpause einschieben.\"<br><br>„Das ist eine gute Idee“, meldet sich der linke Kopf des Hundes zu Wort. Der rechte Kopf legt ein paar Gegenstände in deine Nähe, darunter auch etwas, das aussieht wie Eier... „Ich habe ein paar Dinge gefunden, die dir gefallen könnten, während wir gespielt haben. Danke für deine Hilfe!“",
|
||||
"questCatNotes": "An diesem schönen Tag befindest du dich in der Werkstatt des im Effizienz Emporium von Habit City. Du hast eine schwierige Aufgabe: Du sollst neue magische Motivationszaubersprüche entwickeln, um allen Habiticans das Erreichen ihrer Ziele zu vereinfachen.<br><br>Auf dem Tisch vor dir liegt eine Vielzahl magischer Objekte. In all den Büchern stand, dass sie zusammen mit produktiver Energie harmonisieren sollen... Aber bisher ist da noch nicht mal einen Funken Motivation.<br><br>Das Knarren der Tür macht dich auf einen neuen Gast aufmerksam, der die Werkstatt betritt. Herumtollende Füße und ein Ball aus Flausch schnellen auf den Tisch. Eine... Katze? Bevor du überhaupt die Chance hast, ihr ein Kompliment zu machen, wie flauschig sie ist, hebt sie eine Pfote in Richtung der Kristalle, die du aufgestellt hast und... wirft einen vom Tisch!<br><br>\"Hey!\" rufst du, \"Du bist sehr süß, aber ich versuche hier zu arbeiten...\"<br><br>Sie schaut dich mit ihren schönen blauen Augen an, kippt ihren Kopf, und legt ein Bündel Kräuter auf den Tisch. \"Ich helfe doch!\" schnurrt sie.<br><br>Du siehst ihre Pfote, die sie nach den anderen Gegenständen, die du gesammelt hast, ausstreckt und springst zu Boden, um den nächsten Gegenstand aufzufangen!",
|
||||
"questCatText": "Ein verwirrendes Dilemma",
|
||||
"questCatDropCatEgg": "Katze (Ei)",
|
||||
"questCatUnlockText": "Schaltet Katzeneier zum Kauf im Marktplatz frei.",
|
||||
"questCatBoss": "Der schnurrende Verwirrer",
|
||||
"questCatRageTitle": "Wütendes Klopfen",
|
||||
"questCatRageDescription": "Diese Leiste füllt sich, wenn du deine Tagesaufgaben nicht erledigst. Wenn sie voll ist, nimmt der Schnurrende Verwirrer einen Teil der MP deiner Party weg!",
|
||||
"questCatRageEffect": "Der Schnurrende Verwirrer stößt die magischen Gegenstände, die du gesammelt hast, vom Tisch! Die MP der Party werden reduziert!",
|
||||
"questCatCompletion": "Zum Glück hast du alles aufgefangen, was die zudringliche Katze vom Tisch geworfen hat. Als du auf dem Boden sitzt, bemerkst du ein helles Leuchten, das von den Gegenständen vor dir ausgeht. Wenn du nach oben schaust, reagieren die Gegenstände auf dem Tisch auch! Sie auf verschiedene Höhen zu stellen, könnte ein Durchbruch in deinen Forschungen sein! <br><br>„Weißt du, am Ende hast du mir doch geholfen. Ich glaube, ich habe einfach einen neuen Blick auf meine Aufgabe gebraucht, um mich aus der Patsche zu befreien. Ich wünschte allerdings, du hättest mich ein bisschen vorgewarnt, bevor du anfängst, die Dinge herumzuschieben“, sagst du zu der Katze und streichelst sie sanft. <br><br>„Das ist ein sehr vernünftiges Anliegen, bitte nimm das als meine Entschuldigung“, schnurrt sie und schiebt dir ein paar lustig aussehende Eier zu. „Es freut mich, dass ich dir helfen konnte, die Dinge aus einer anderen Schnurrspektive zu sehen.“",
|
||||
"questOtterText": "Der Perfide Verschwörer!",
|
||||
"questOtterDropOtterEgg": "Otter (Ei)",
|
||||
"questOtterUnlockText": "Schält Otter Eier zum Kauf im Marktplatz frei",
|
||||
"questOtterBoss": "Der Verschwörer",
|
||||
"questOtterRageEffect": "Der Verschwörer wirft Teile deiner To-Do Liste in die Luft! Der Boss erhält 30% seiner Lebenspunkte zurück!",
|
||||
"questOtterRageDescription": "Dieser Balken füllt sich, wenn du deine Tagesaufgaben nicht erledigst. Wenn er voll ist, erhält Der Verschwörer einige Lebenspunkte zurück!",
|
||||
"questJadeText": "Ein Matter Unglücksbringer",
|
||||
"questJadeBoss": "Matter Unglücksbringer",
|
||||
"questJadeDropJadePotion": "Jade Schlüpfelixier",
|
||||
"questJadeUnlockText": "Schält Jade Schlüpfelixier zum Kauf im Marktplatz frei.",
|
||||
"questOtterRageTitle": "To-Do Abriß!",
|
||||
"questJadeNotes": "Du bist zu Hause und starrst auf den Stapel dreckigen Geschirrs in der Spüle. Auf den Haufen schmutziger Wäsche in einer wahllosen Ecke des Raums. Auf die leeren Tassen und Snackverpackungen um deinen Tisch herum...<br><br>Du seufzt. „Warum ist da immer noch mehr dreckiges Geschirr... Die Sauerei hört nie auf.“ Es ist so demotivierend. Du findest dich auf der Couch wieder, endlos durch die neuesten Trends scrollend. Wer weiß, wie lang du da warst...<br><br>Als du von deinem Telefon hochsiehst, ist alles grün. Dies ist nicht dein Wohnzimmer. Als du aufstehst, findest du dich an der Seite eines leuchtend grünen Berges wieder.<br><br>Bewegung in der Ferne erregt deine Aufmerksamkeit. Eine steinerne grüne Gestalt grunzt, während sie einen Fels das steinige Terrain hoch wälzt. Er macht einige Fortschritte, aber ein kleiner Ausrutscher seines Fußes lässt den glänzenden Fels zurück nach unten rollen, direkt auf dich zu!<br><br>Er entdeckt dich, als er zu dem Brocken Jade rennt, der auf dich zu poltert! „Du denkst also, der Abwasch ist übel?“ schreit die Gestalt, „Versuch das!“"
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
"confirmPass": "Neues Passwort bestätigen",
|
||||
"newUsername": "Neuer Benutzername",
|
||||
"dangerZone": "Gefahrenzone",
|
||||
"resetText1": "Sei vorsichtig! Es werden große Teile Deines Accounts zurückgesetzt. Wir raten dringend davon ab. Jedoch finden einige Spieler diese Funktion sinnvoll, um nach einem anfänglichen Testen der Seite neu beginnen zu können.",
|
||||
"resetText1": "<b>Sei vorsichtig!</b> Es werden große Teile Deines Accounts zurückgesetzt. Wir raten dringend davon ab. Jedoch finden einige Spieler diese Funktion sinnvoll, um nach einem anfänglichen Testen der Seite neu beginnen zu können.",
|
||||
"resetText2": "Eine andere Möglichkeit ist die Verwendung einer <b>Sphäre der Wiedergeburt</b>, die alles andere zurücksetzt, während deine Aufgaben und Ausrüstung erhalten bleiben.",
|
||||
"deleteLocalAccountText": "<b>Bist Du sicher?</b> Dies wird Dein Konto für immer löschen und es kann nicht wiederhergestellt werden! Wenn Du Habitica wieder verwenden möchtest, musst Du ein neues Konto registrieren. Gesparte oder verbrauchte Edelsteine werden nicht ersetzt. Wenn Du absolut sicher bist, dann tippe Dein Passwort in das Textfeld unten ein.",
|
||||
"deleteSocialAccountText": "<b>Bist Du sicher?</b> Dies wird Dein Konto für immer löschen und es kann nicht wiederhergestellt werden! Wenn Du Habitica wieder verwenden möchtest, musst Du ein neues Konto registrieren. Gesparte oder verbrauchte Edelsteine werden nicht ersetzt. Wenn Du absolut sicher bist, dann tippe <b>\"<%= magicWord %>\"</b> in das Textfeld unten ein.",
|
||||
@@ -188,7 +188,7 @@
|
||||
"transaction_release_pets": "Haustiere freigelassen",
|
||||
"transaction_release_mounts": "Reittiere freigelassen",
|
||||
"addPasswordAuth": "Passwort hinzufügen",
|
||||
"nextHourglassDescription": "Abonnierende erhalten eine Mystische Sanduhr, ein Mystisches Ausrüstungsset und Edelsteine, die innerhalb der ersten zwei Tage des Monats auf dem Markt wieder aufgefüllt werden.",
|
||||
"nextHourglassDescription": "Abonnierende erhalten eine Mystische Sanduhr, ein Mystisches Ausrüstungsset und Edelsteine, die innerhalb der ersten zwei Tage des Monats auf dem Markt wieder aufgefüllt werden",
|
||||
"gemCap": "Edelsteinobergrenze",
|
||||
"nextHourglass": "Nächste Lieferung einer Mystischen Sanduhr",
|
||||
"adjustment": "Änderung",
|
||||
|
||||
@@ -259,5 +259,7 @@
|
||||
"earn2Gems": "Verdiene <strong>+2 Edelsteine</strong> für jeden Monat, in dem du abonniert hast",
|
||||
"subscribeAgainContinueHourglasses": "Erneuere Dein Abonnement, um weiterhin Mystische Sanduhren zu erhalten",
|
||||
"mysterySet202411": "Borstenkämpfer Set",
|
||||
"mysterySet202501": "Frostbinder-Set"
|
||||
"mysterySet202501": "Frostbinder-Set",
|
||||
"mysterySet202502": "Herzliches Harlekin-Set",
|
||||
"mysterySet202503": "Jade Juggernaut Set"
|
||||
}
|
||||
|
||||
@@ -1011,6 +1011,14 @@
|
||||
"backgroundWinterLandscapeWithCabinText": "Winter Landscape with Cabin",
|
||||
"backgroundWinterLandscapeWithCabinNotes": "Stay cozy in a Winter Landscape with a Cabin.",
|
||||
|
||||
"backgrounds022025": "SET 129: Released February 2025",
|
||||
"backgroundOldFashionedTeaShopText": "Old Fashioned Tea Shop",
|
||||
"backgroundOldFashionedTeaShopNotes": "Enjoy a cozy beverage in an Old Fashioned Tea Shop.",
|
||||
|
||||
"backgrounds032025": "SET 130: Released March 2025",
|
||||
"backgroundMountainSceneWithBlossomsText": "Mountain Scene with Blossoms",
|
||||
"backgroundMountainSceneWithBlossomsNotes": "Take in the lovely sights and scents of a Mountain Scene with Blossoms.",
|
||||
|
||||
"timeTravelBackgrounds": "Steampunk Backgrounds",
|
||||
"backgroundAirshipText": "Airship",
|
||||
"backgroundAirshipNotes": "Become a sky sailor on board your very own Airship.",
|
||||
|
||||
@@ -87,7 +87,7 @@
|
||||
"allocatePerPop": "Add a Point to Perception",
|
||||
"allocateInt": "Points allocated to Intelligence:",
|
||||
"allocateIntPop": "Add a Point to Intelligence",
|
||||
"noMoreAllocate": "Now that you've hit level 100, you won't gain any more Stat Points. You can continue leveling up, or start a new adventure at level 1 by using the <a href='https://habitica.fandom.com/wiki/Orb_of_Rebirth' target='_blank'>Orb of Rebirth</a>!",
|
||||
"noMoreAllocate": "Now that you've hit level 100, you won't gain any more Stat Points. You can continue leveling up, or start a new adventure at level 1 by using the <a href='/shops/market'>Orb of Rebirth</a>!",
|
||||
"stats": "Stats",
|
||||
"strength": "Strength",
|
||||
"strText": "Strength increases the chance of random \"critical hits\" and the Gold, Experience, and drop chance boost from them. It also helps deal damage to boss monsters.",
|
||||
@@ -133,8 +133,8 @@
|
||||
"healerText": "Healers stand impervious against harm, and extend that protection to others. Missed Dailies and bad Habits don't faze them much, and they have ways to recover Health from failure. Play a Healer if you enjoy assisting others in your Party, or if the idea of cheating Death through hard work inspires you!",
|
||||
"optOutOfClasses": "Opt Out",
|
||||
"chooseClass": "Choose your Class",
|
||||
"chooseClassLearnMarkdown": "[Learn more about Habitica's class system](https://habitica.fandom.com/wiki/Class_System)",
|
||||
"optOutOfClassesText": "Can't be bothered with classes? Want to choose later? Opt out - you'll be a warrior with no special abilities. You can read about the class system later on the wiki and enable classes at any time under User Icon > Settings.",
|
||||
"chooseClassLearnMarkdown": "[Learn more about Habitica's class system](/static/faq#what-classes)",
|
||||
"optOutOfClassesText": "Not ready to choose? There's no rush! If you opt out, you can read about each Class in <a href='/static/faq#what-classes' target='_blank'>our FAQ</a> and visit Settings to enable the Class System when you're ready.",
|
||||
"selectClass": "Select <%= heroClass %>",
|
||||
"select": "Select",
|
||||
"stealth": "Stealth",
|
||||
|
||||
@@ -269,7 +269,15 @@
|
||||
|
||||
"questEggDogText": "Puppy",
|
||||
"questEggDogMountText": "Dog",
|
||||
"questEggDogAdjective": "a friendly",
|
||||
"questEggDogAdjective": "a friendly",
|
||||
|
||||
"questEggCatText": "Kitten",
|
||||
"questEggCatMountText": "Cat",
|
||||
"questEggCatAdjective": "a mischievous",
|
||||
|
||||
"questEggOtterText": "Otter",
|
||||
"questEggOtterMountText": "Otter",
|
||||
"questEggOtterAdjective": "a perfidious",
|
||||
|
||||
"eggNotes": "Find a hatching potion to pour on this egg, and it will hatch into <%= eggAdjective(locale) %> <%= eggText(locale) %>.",
|
||||
|
||||
@@ -336,6 +344,7 @@
|
||||
"hatchingPotionFungi": "Fungi",
|
||||
"hatchingPotionKoi": "Koi",
|
||||
"hatchingPotionGingerbread": "Gingerbread",
|
||||
"hatchingPotionJade": "Jade",
|
||||
|
||||
"hatchingPotionNotes": "Pour this on an egg, and it will hatch as a <%= potText(locale) %> Pet.",
|
||||
"premiumPotionUnlimitedNotes": "Not usable on Quest Pet eggs.",
|
||||
|
||||
@@ -41,9 +41,9 @@
|
||||
"backerTier": "Backer Tier",
|
||||
"playerTiers": "Player Tiers",
|
||||
"tier": "Tier",
|
||||
"conRewardsURL": "https://habitica.fandom.com/wiki/Contributor_Rewards",
|
||||
"conRewardsURL": "https://github.com/HabitRPG/habitica/wiki/Contributing-to-Habitica#contributor-tier-rewards",
|
||||
"surveysSingle": "Helped Habitica grow, either by filling out a survey or helping with a major testing effort. Thank you!",
|
||||
"surveysMultiple": "Helped Habitica grow on <%= count %> occasions, either by filling out a survey or helping with a major testing effort. Thank you!",
|
||||
"blurbHallPatrons": "This is the Hall of Patrons, where we honor the noble adventurers who backed Habitica's original Kickstarter. We thank them for helping us bring Habitica to life!",
|
||||
"blurbHallContributors": "This is the Hall of Contributors, where open-source contributors to Habitica are honored. Whether through code, art, music, writing, or even just helpfulness, they have earned <a href='https://habitica.fandom.com/wiki/Contributor_Rewards' target='_blank'> gems, exclusive equipment</a>, and <a href='https://habitica.fandom.com/wiki/Contributor_Titles' target='_blank'>prestigious titles</a>. You can contribute to Habitica, too! <a href='https://habitica.fandom.com/wiki/Contributing_to_Habitica' target='_blank'> Find out more here. </a>"
|
||||
"blurbHallContributors": "This is the Hall of Contributors, where open-source contributors to Habitica are honored. Whether through code, art, music, writing, or even just helpfulness, they have earned <a href='https://github.com/HabitRPG/habitica/wiki/Contributing-to-Habitica#contributor-tier-rewards' target='_blank'>Gems, exclusive Equipment</a>, and <a href='https://github.com/HabitRPG/habitica/wiki/Contributing-to-Habitica#contributor-tiers' target='_blank'>prestigious titles</a>. You can contribute to Habitica, too! <a href='https://github.com/HabitRPG/habitica/wiki/Contributing-to-Habitica' target='_blank'>Find out more here.</a>"
|
||||
}
|
||||
|
||||
@@ -23,8 +23,8 @@
|
||||
"faqQuestion31": "Why did I lose HP when interacting with a non-negative task?",
|
||||
"webFaqAnswer31": "If you complete a task and lose HP when you shouldn’t have, you encountered a delay while the server is syncing changes made on other platforms. For example, if you use Gold, Mana, or lose HP on the mobile app and then complete a task on the website, the server is simply confirming everything is in sync.",
|
||||
|
||||
"faqQuestion32": "When can I choose a class?",
|
||||
"webFaqAnswer32": "There are four classes in Habitica: Warrior, Mage, Rogue, and Healer. All players start as the Warrior class until they reach level 10. Once you reach level 10, you’ll be given the choice between selecting a new class or continuing as a Warrior.\n\nEach class has different Equipment and Skills. If you don't want to choose a class, you can select \"Opt Out.\" If you choose to opt out, you can always enable the Class System from Settings later.",
|
||||
"faqQuestion32": "How can I choose a class?",
|
||||
"webFaqAnswer32": "All players start as the Warrior class until they reach level 10. Once you reach level 10, you’ll be given the choice between selecting a new class or continuing as a Warrior.\n\nEach class has different Equipment and Skills. If you don't want to choose a class, you can select \"Opt Out.\" If you choose to opt out, you can always enable the Class System from Settings later.\n\nIf you’d like to change your class after Level 10, you can do so by using the Orb of Rebirth. The Orb of Rebirth becomes available in the Market for 6 Gems at level 50 or for free at level 100.\n\nAlternatively, you can change class at any time from Settings for 3 Gems. This will not reset your level like Orb of Rebirth, but it will allow you to re-allocate the skill points you’ve accumulated as you’ve leveled up to match your new class.",
|
||||
|
||||
"faqQuestion33": "What is the blue bar that appears after level 10?",
|
||||
"webFaqAnswer33": "After you unlock the Class System, you also unlock Skills that require Mana to be cast. Mana is determined by your INT stat and can be adjusted by Skills and Equipment.",
|
||||
@@ -132,6 +132,9 @@
|
||||
"faqQuestion66": "What’s the difference between a Group Plan’s shared tasks and Challenge tasks?",
|
||||
"webFaqAnswer66": "Group Plan shared task boards are more dynamic than Challenges, in that they can constantly be updated and interacted with. Challenges are great if you have one set of tasks to send out to many people.\n\nGroup Plans are also a paid feature, while Challenges are available free to everyone.\n\nYou cannot assign specific tasks in Challenges, and Challenges do not have a shared day reset. In general, Challenges offer less control and direct interaction.",
|
||||
|
||||
"faqQuestion67": "What are the classes in Habitica?",
|
||||
"webFaqAnswer67": "Classes are different roles that your character can play. Each class provides its own set of unique benefits and skills as you level up. These skills can complement the way you interact with your tasks or help you contribute to completing Quests in your Party.\n\nYour class also determines the Equipment that will be available to you for purchase in your Rewards, the Market, and the Seasonal Shop.\n\nHere’s a rundown of each class to help you choose which one is best suited to your playstyle:\n#### **Warrior**\n* Warriors deal high damage to bosses and have a high chance of critical hits when completing tasks, rewarding you extra Experience and Gold.\n* Strength is their primary stat, raising the damage they do.\n* Constitution is their secondary stat, lowering the damage they take.\n* Warriors' skills buff their Party mates' Constitution and Strength.\n* Consider playing as a Warrior if you love to fight bosses but also want some protection if you miss tasks occasionally.\n#### **Healer**\n* Healers have high defense and can heal themselves as well as their Party mates.\n* Constitution is their primary stat, increasing their heals and lowering the damage they take.\n* Intelligence is their secondary stat, increasing their Mana and Experience.\n* Healers' skills make their tasks less red and buff their Party mates' Constitution.\n* Consider playing as a Healer if you miss tasks often and need the ability to heal yourself or your Party members. Healers also level up quickly.\n#### **Mage**\n* Mages level up quickly, gain lots of Mana, and damage bosses in Quests.\n* Intelligence is their primary stat, increasing their Mana and Experience.\n* Perception is their secondary stat, increasing their Gold and item drops.\n* Mages' skills freeze their task streaks, restore their Party mates' Mana, and buff their Intelligence.\n* Consider playing as a Mage if you are motivated by progressing quickly through levels and contributing damage to boss Quests.\n#### **Rogue**\n* Rogues get the most item drops and Gold from completing tasks, and have a high chance of critical hits, getting even more Experience and Gold.\n* Perception is their primary stat, increasing their Gold and item drops.\n* Strength is their secondary stat, raising the damage they do.\n* Rogues' skills help them dodge missed Dailies, pilfer Gold, and buff their Party mates’ Perception.\n* Consider playing as a Rogue if you’re highly motivated by rewards.",
|
||||
|
||||
"iosFaqStillNeedHelp": "If you have a question that isn't on this list or on the [Wiki FAQ](https://habitica.fandom.com/wiki/FAQ), use the Ask a Question form [LINK NEEDED]! We're happy to help.",
|
||||
"androidFaqStillNeedHelp": "If you have a question that isn't on this list or on the [Wiki FAQ](https://habitica.fandom.com/wiki/FAQ), use the Ask a Question form [LINK NEEDED]! We're happy to help.",
|
||||
"webFaqStillNeedHelp": "If you have a question that isn't on this list or on the [Wiki FAQ](https://habitica.fandom.com/wiki/FAQ), use the Ask a Question form [LINK NEEDED]! We're happy to help.",
|
||||
|
||||
@@ -475,7 +475,7 @@
|
||||
"weaponSpecialSpring2023WarriorText": "Hummingbird Foil",
|
||||
"weaponSpecialSpring2023WarriorNotes": "En garde! Fend off foes from your flowers with this foil! Increases Strength by <%= str %>. Limited Edition 2023 Spring Gear.",
|
||||
"weaponSpecialSpring2023MageText": "Moonstone Magic",
|
||||
"weaponSpecialSpring2023MageNotes": "The greater their glow, the more potent is their power. Increases Intelligence by <%= int %>. Limited Edition 2023 Spring Gear.",
|
||||
"weaponSpecialSpring2023MageNotes": "The greater their glow, the more potent is their power. Increases Intelligence by <%= int %> and Perception by <%= per %>. Limited Edition 2023 Spring Gear.",
|
||||
"weaponSpecialSpring2023HealerText": "Lilium Pollen",
|
||||
"weaponSpecialSpring2023HealerNotes": "With a puff and a sparkle, you deploy new growth, joy, and color. Increases Intelligence by <%= int %>. Limited Edition 2023 Spring Gear.",
|
||||
|
||||
@@ -542,6 +542,15 @@
|
||||
"weaponSpecialWinter2025MageText": "Northern Lights Display",
|
||||
"weaponSpecialWinter2025MageNotes": "This stunning, colorful show provides the perfect backdrop! You’ll be unstoppable! Increases Intelligence by <%= int %> and Perception by <%= per %>. Limited Edition Winter 2024-2025 Gear.",
|
||||
|
||||
"weaponSpecialSpring2025WarriorText": "Sunshine Scimitar",
|
||||
"weaponSpecialSpring2025WarriorNotes": "With one slice, you can cut through flower stems to make a bouquet or right through obstacles to get your tasks accomplished. Increases Strength by <%= str %>. Limited Edition Spring 2025 Gear.",
|
||||
"weaponSpecialSpring2025RogueText": "Crystal Point Flail",
|
||||
"weaponSpecialSpring2025RogueNotes": "With one swing, you can eliminate any obstacles standing in the way of your goals. Increases Strength by <%= str %>. Limited Edition Spring 2025 Gear.",
|
||||
"weaponSpecialSpring2025HealerText": "Plumeria Crook",
|
||||
"weaponSpecialSpring2025HealerNotes": "With one wave, you can summon pollinators to your side to help you in your adventures. Increases Intelligence by <%= int %>. Limited Edition Spring 2025 Gear.",
|
||||
"weaponSpecialSpring2025MageText": "Mantis Staff",
|
||||
"weaponSpecialSpring2025MageNotes": "With one slash, you can use elemental magic to control the environment around you. Take advantage and spring forward! Increases Intelligence by <%= int %> and Perception by <%= per %>. Limited Edition Spring 2025 Gear.",
|
||||
|
||||
"weaponMystery201411Text": "Pitchfork of Feasting",
|
||||
"weaponMystery201411Notes": "Stab your enemies or dig in to your favorite foods - this versatile pitchfork does it all! Confers no benefit. November 2014 Subscriber Item.",
|
||||
"weaponMystery201502Text": "Shimmery Winged Staff of Love and Also Truth",
|
||||
@@ -1322,6 +1331,14 @@
|
||||
"armorSpecialWinter2025MageText": "Aurora Cloak",
|
||||
"armorSpecialWinter2025MageNotes": "Wonder, whimsy, enchantment, and splendor will fill your days when you dance in this cloak. Increases Intelligence by <%= int %>. Limited Edition Winter 2024-2025 Gear.",
|
||||
|
||||
"armorSpecialSpring2025WarriorText": "Sunshine Armor",
|
||||
"armorSpecialSpring2025WarriorNotes": "This stunning armor contains colors you might see in the sky in the middle of a sunny spring day. Increases Constitution by <%= con %>. Limited Edition Spring 2025 Gear.",
|
||||
"armorSpecialSpring2025RogueText": "Crystal Point Cloak",
|
||||
"armorSpecialSpring2025RogueNotes": "This stunning cloak contains extra crystals with special, secret powers only you know about. Increases Perception by <%= per %>. Limited Edition Spring 2025 Gear.",
|
||||
"armorSpecialSpring2025HealerText": "Plumeria Robes",
|
||||
"armorSpecialSpring2025HealerNotes": "This stunning robe contains plumeria petals that are soft and swishy. Increases Constitution by <%= con %>. Limited Edition Spring 2025 Gear.",
|
||||
"armorSpecialSpring2025MageText": "Mantis Uniform",
|
||||
"armorSpecialSpring2025MageNotes": "This stunning uniform contains flashy colors but also lets you stalk your hardest tasks with stealth. Increases Intelligence by <%= int %>. Limited Edition Spring 2025 Gear.",
|
||||
|
||||
"armorMystery201402Text": "Messenger Robes",
|
||||
"armorMystery201402Notes": "Shimmering and strong, these robes have many pockets to carry letters. Confers no benefit. February 2014 Subscriber Item.",
|
||||
@@ -1453,6 +1470,8 @@
|
||||
"armorMystery202407Notes": "Glide through lakes and canals with your sweeping pink tail! Confers no benefit. July 2024 Subscriber Item.",
|
||||
"armorMystery202412Text": "Candy Cane Cottontail Coat",
|
||||
"armorMystery202412Notes": "A fun and fluffy look to keep you snug on a winter day. Confers no benefit. December 2024 Subscriber Item.",
|
||||
"armorMystery202502Text": "Heartfelt Harlequin Suit",
|
||||
"armorMystery202502Notes": "You’re full of kind-hearted jokes and japes from your ruffled collar to your gigantic shoes! Confers no benefit. February 2025 Subscriber Item.",
|
||||
|
||||
"armorMystery301404Text": "Steampunk Suit",
|
||||
"armorMystery301404Notes": "Dapper and dashing, wot! Confers no benefit. February 3015 Subscriber Item.",
|
||||
@@ -1681,6 +1700,8 @@
|
||||
"armorArmoireFestiveHelperOverallsNotes": "Durable and comfortable, these overalls are great for working, playing, and assisting others. Plus, it has pockets! Increases Constitution by <%= con %>. Enchanted Armoire: Festive Helper Set (Item 2 of 2)",
|
||||
"armorArmoireSnowyFluffTrimmedCoatText": "Snowy Fluff-Trimmed Coat",
|
||||
"armorArmoireSnowyFluffTrimmedCoatNotes": "As the first flakes fall around you, this coat will not only keep you toasty but also help you blend in perfectly with your snowy surroundings. Glide along the ice in style! Increases Strength and Intelligence by <%= attrs %> each. Enchanted Armoire: Snowy Trapper Hat Set (Item 2 of 2).",
|
||||
"armorArmoireSpringPetalYukataText": "Spring Petal Yukata",
|
||||
"armorArmoireSpringPetalYukataNotes": "This yukata is perfect to wear to celebrate the coming of spring. Be sure to pose by cherry blossoms for a photo. Increases Constitution and Strength by <%= attrs %> each. Enchanted Armoire: Spring Petal Set (Item 1 of 2).",
|
||||
|
||||
"headgear": "helm",
|
||||
"headgearCapitalized": "Headgear",
|
||||
@@ -2197,6 +2218,15 @@
|
||||
"headSpecialWinter2025MageText": "Aurora Hat",
|
||||
"headSpecialWinter2025MageNotes": "More than just a fancy fascinator, this hat makes you look like the aurora borealis itself. Increases Perception by <%= per %>. Limited Edition Winter 2024-2025 Gear.",
|
||||
|
||||
"headSpecialSpring2025WarriorText": "Sunshine Helmet",
|
||||
"headSpecialSpring2025WarriorNotes": "The crest atop this shiny helmet resembles the path of the sun but also looks like a brush you could use for spring cleaning. Increases Strength by <%= str %>. Limited Edition Spring 2025 Gear.",
|
||||
"headSpecialSpring2025RogueText": "Crystal Point Hat",
|
||||
"headSpecialSpring2025RogueNotes": "The crystals in this hat encourage productive energy but also shine bright so you can work any time of day or night. Increases Perception by <%= per %>. Limited Edition Spring 2025 Gear.",
|
||||
"headSpecialSpring2025HealerText": "Plumeria Headdress",
|
||||
"headSpecialSpring2025HealerNotes": "This flower symbolizes birth, love, and new beginnings! It also provides a beautiful scent you can enjoy as you work on your tasks. Increases Intelligence by <%= int %>. Limited Edition Spring 2025 Gear.",
|
||||
"headSpecialSpring2025MageText": "Mantis Mask",
|
||||
"headSpecialSpring2025MageNotes": "The mantis is known for camouflaging or moving slowly. Choose one tactic to help with each goal but know you can always change tactics if you need to. Increases Perception by <%= per %>. Limited Edition Spring 2025 Gear.",
|
||||
|
||||
"headSpecialGaymerxText": "Rainbow Warrior Helm",
|
||||
"headSpecialGaymerxNotes": "In celebration of the GaymerX Conference, this special helmet is decorated with a radiant, colorful rainbow pattern! GaymerX is a game convention celebrating LGTBQ and gaming and is open to everyone.",
|
||||
|
||||
@@ -2372,6 +2402,10 @@
|
||||
"headMystery202412Notes": "Warm and cozy, just like a cup of minty hot cocoa on a winter night! Confers no benefit. December 2024 Subscriber Item.",
|
||||
"headMystery202501Text": "Frostbinder’s Hat",
|
||||
"headMystery202501Notes": "This sparkling hat generates a light and festive flurry around you at all times. Confers no benefit. January 2025 Subscriber Item.",
|
||||
"headMystery202502Text": "Heartfelt Harlequin Hat",
|
||||
"headMystery202502Notes": "This jaunty little hat is sure to inspire joy in anyone who sees you! Confers no benefit. February 2025 Subscriber Item.",
|
||||
"headMystery202503Text": "Jade Juggernaut Hair",
|
||||
"headMystery202503Notes": "This verdant hairdo perfectly suits a brave warrior and defender of the planet. Confers no benefit. March 2025 Subscriber Item.",
|
||||
|
||||
"headMystery301404Text": "Fancy Top Hat",
|
||||
"headMystery301404Notes": "A fancy top hat for the finest of gentlefolk! January 3015 Subscriber Item. Confers no benefit.",
|
||||
@@ -2580,6 +2614,8 @@
|
||||
"headArmoireFestiveHelperHatNotes": "Holiday tip #27: have a helper hat handy. This one is big enough to keep an emergency toy underneath! Increases Intelligence by <%= int %>. Enchanted Armoire: Festive Helper Set (Item 1 of 2)",
|
||||
"headArmoireSnowyTrapperHatText": "Snowy Trapper Hat",
|
||||
"headArmoireSnowyTrapperHatNotes": "Blue, frostbitten ears will be a thing of the past. Embrace cozy warmth in style! Increases Constitution and Perception by <%= attrs %> each. Enchanted Armoire: Snowy Trapper Hat Set (Item 1 of 2).",
|
||||
"headArmoireFancyFloralHatText": "Fancy Floral Hat",
|
||||
"headArmoireFancyFloralHatNotes": "Feast your eyes on this fancy hat full of fantastic flowers and frilly fastenings. Increases Intelligence by <%= int %>. Enchanted Armoire: Fancy Floral Accessories Set (Item 1 of 2).",
|
||||
|
||||
"offhand": "off-hand item",
|
||||
"offHandCapitalized": "Off-Hand Item",
|
||||
@@ -2873,6 +2909,13 @@
|
||||
"shieldSpecialWinter2025HealerText": "The Perfect Gift",
|
||||
"shieldSpecialWinter2025HealerNotes": "The perfect gift is just waiting to be opened. What could be inside? Increases Constitution by <%= con %>. Limited Edition Winter 2024-2025 Gear.",
|
||||
|
||||
"shieldSpecialSpring2025WarriorText": "Sunburst Shield",
|
||||
"shieldSpecialSpring2025WarriorNotes": "You can momentarily blind your enemies when sun hits this shield just right. Take advantage and spring forward! Increases Constitution by <%= con %>. Limited Edition Spring 2025 Gear.",
|
||||
"shieldSpecialSpring2025RogueText": "Crystal Point Flail",
|
||||
"shieldSpecialSpring2025RogueNotes": "You can use the crystal to divine a productive future for yourself. Take advantage and spring forward! Increases Strength by <%= str %>. Limited Edition Spring 2025 Gear.",
|
||||
"shieldSpecialSpring2025HealerText": "Plumeria Shield",
|
||||
"shieldSpecialSpring2025HealerNotes": "You can use this special petal to gather goodness or flick negative thoughts away. Take advantage and spring forward! Increases Constitution by <%= con %>. Limited Edition 2025 Gear.",
|
||||
|
||||
"shieldMystery201601Text": "Resolution Slayer",
|
||||
"shieldMystery201601Notes": "This blade can be used to parry away all distractions. Confers no benefit. January 2016 Subscriber Item.",
|
||||
"shieldMystery201701Text": "Time-Freezer Shield",
|
||||
@@ -2895,6 +2938,8 @@
|
||||
"shieldMystery202409Notes": "The glowing ruby on this staff draws its power from the late summer sun. Confers no benefit. September 2024 Subscriber Item.",
|
||||
"shieldMystery202501Text": "Frostbinder's Staff",
|
||||
"shieldMystery202501Notes": "Decorate any outdoor scenery with a diamond coat of shimmering frost. Confers no benefit. January 2025 Subscriber Item.",
|
||||
"shieldMystery202502Text": "Heartfelt Harlequin Balloons",
|
||||
"shieldMystery202502Notes": "This Valentine’s Day and every day, may your heart be as light as these buoyant balloons. Confers no benefit. February 2025 Subscriber Item.",
|
||||
|
||||
"shieldMystery301405Text": "Clock Shield",
|
||||
"shieldMystery301405Notes": "Time is on your side with this towering clock shield! Confers no benefit. June 3015 Subscriber Item.",
|
||||
@@ -3063,6 +3108,10 @@
|
||||
"shieldArmoireBuoyantBeachBallNotes": "Got too many balls up in the air already? Here’s one that you can safely set down, roll, bounce and bounce and bounce... Increases Strength by <%= str %>. Enchanted Armoire: Beachside Set (Item 4 of 4).",
|
||||
"shieldArmoireSafetyFlashlightText": "Safety Flashlight",
|
||||
"shieldArmoireSafetyFlashlightNotes": "Wait, did you hear that noise? Quick! Shine your flashlight into the shadows over there. Hmmm. Looks like it was just the wind. Or was it…? Increases Constitution by <%= con %>. Enchanted Armoire: Fright Night Set (Item 1 of 2)",
|
||||
"shieldArmoireFancyFloralFanText": "Fancy Floral Fan",
|
||||
"shieldArmoireFancyFloralFanNotes": "Finish your fancy look with this first-rate fan made of fabulous floral fabric. Increases Perception by <%= per %>. Enchanted Armoire: Fancy Floral Accessories Set (Item 2 of 2).",
|
||||
"shieldArmoireSpringPetalUchiwaText": "Spring Petal Uchiwa",
|
||||
"shieldArmoireSpringPetalUchiwaNotes": "This handheld fan featuring a beautiful petal pattern creates a small breeze just for you as the weather warms. Increases Intelligence and Perception by <%= attrs %> each. Enchanted Armoire: Spring Petal Set (Item 2 of 2).",
|
||||
|
||||
"back": "Back Accessory",
|
||||
"backBase0Text": "No Back Accessory",
|
||||
@@ -3489,6 +3538,8 @@
|
||||
"eyewearMystery202312Notes": "No need to worry, these icy blues will help you see through the cold and dark season to the warmth of months ahead. Confers no benefit. December 2023 Subscriber Item.",
|
||||
"eyewearMystery202406Text": "Phantom Buccaneer’s Mask",
|
||||
"eyewearMystery202406Notes": "Try to avoid having this pulled off by a gang of meddling kids and their talking dog. Confers no benefit. June 2024 Subscriber Item.",
|
||||
"eyewearMystery202503Text": "Jade Juggernaut Eyes",
|
||||
"eyewearMystery202503Notes": "This piercing gaze will strike terror into any fighter who dares to challenge you! Confers no benefit. March 2025 Subscriber Item.",
|
||||
|
||||
|
||||
"eyewearMystery301404Text": "Eyewear Goggles",
|
||||
|
||||
@@ -207,7 +207,8 @@
|
||||
"dismissAll": "Dismiss All",
|
||||
"messages": "Messages",
|
||||
"emptyMessagesLine1": "You don't have any messages",
|
||||
"emptyMessagesLine2": "You can send a new message to a user by visiting their profile and clicking the \"Message\" button.",
|
||||
"emptyMessagesLine2": "Send a message to start a conversation with your Party members or another Habitica player",
|
||||
"newMessage": "New Message",
|
||||
"userSentMessage": "<span class=\"notification-bold\"><%- user %></span> sent you a message",
|
||||
"letsgo": "Let's Go!",
|
||||
"selected": "Selected",
|
||||
@@ -238,5 +239,7 @@
|
||||
"submitQuestion": "Submit Question",
|
||||
"whyReportingPlayer": "Why are you reporting this player?",
|
||||
"whyReportingPlayerPlaceholder": "Reason for report",
|
||||
"playerReportModalBody": "You should only report a player who violates the <%= firstLinkStart %>Community Guidelines<%= linkEnd %> and/or <%= secondLinkStart %>Terms of Service<%= linkEnd %>. Submitting a false report is a violation of Habitica’s Community Guidelines."
|
||||
"playerReportModalBody": "You should only report a player who violates the <%= firstLinkStart %>Community Guidelines<%= linkEnd %> and/or <%= secondLinkStart %>Terms of Service<%= linkEnd %>. Submitting a false report is a violation of Habitica’s Community Guidelines.",
|
||||
"targetUserNotExist": "Target User: '<%= userName %>' does not exist.",
|
||||
"rememberToBeKind": "Please remember to be kind, respectful, and follow the <a href='/static/community-guidelines' target='_blank'>Community Guidelines</a>."
|
||||
}
|
||||
|
||||
@@ -114,9 +114,7 @@
|
||||
"whyReportingPostPlaceholder": "Reason for report",
|
||||
"optional": "Optional",
|
||||
"needsTextPlaceholder": "Type your message here.",
|
||||
"copyMessageAsToDo": "Copy message as To Do",
|
||||
"copyAsTodo": "Copy as To Do",
|
||||
"messageAddedAsToDo": "Message copied as To Do.",
|
||||
"messageCopiedToClipboard": "Message copied to clipboard.",
|
||||
"leaderOnlyChallenges": "Only group leader can create challenges",
|
||||
"sendGift": "Send a Gift",
|
||||
"selectGift": "Select Gift",
|
||||
|
||||
@@ -226,7 +226,11 @@
|
||||
"winter2025MooseWarriorSet": "Moose Warrior Set",
|
||||
"winter2025AuroraMageSet": "Aurora Mage Set",
|
||||
"winter2025StringLightsHealerSet": "String Lights Healer Set",
|
||||
"winter2025SnowRogueSet": "Snow Rogue Set",
|
||||
"winter2025SnowRogueSet": "Snow Rogue Set",
|
||||
"spring2025SunshineWarriorSet": "Sunshine Warrior Set",
|
||||
"spring2025CrystalPointRogueSet": "Crystal Point Rogue Set",
|
||||
"spring2025PlumeriaHealerSet": "Plumeria Healer Set",
|
||||
"spring2025MantisMageSet": "Mantis Mage Set",
|
||||
"winterPromoGiftHeader": "GIFT A SUBSCRIPTION, GET ONE FREE!",
|
||||
"winterPromoGiftDetails1": "Until January 6th only, when you gift somebody a subscription, you get the same subscription for yourself for free!",
|
||||
"winterPromoGiftDetails2": "Please note that if you or your gift recipient already have a recurring subscription, the gifted subscription will only start after that subscription is cancelled or has expired. Thanks so much for your support! <3",
|
||||
|
||||
@@ -51,8 +51,6 @@
|
||||
"messageNotAbleToBuyInBulk": "This item cannot be purchased in quantities above 1.",
|
||||
"notificationsRequired": "Notification ids are required.",
|
||||
"unallocatedStatsPoints": "You have <span class=\"notification-bold-blue\"><%= points %> unallocated Stat Points</span>",
|
||||
"beginningOfConversation": "This is the beginning of your conversation with <%= userName %>.",
|
||||
"beginningOfConversationReminder": "Remember to be kind, respectful, and follow the Community Guidelines!",
|
||||
"messageDeletedUser": "Sorry, this user has deleted their account.",
|
||||
"messageMissingDisplayName": "Missing display name.",
|
||||
"reportedMessage": "You have reported this message to moderators.",
|
||||
|
||||
@@ -115,7 +115,7 @@
|
||||
"tourPartyPage": "Welcome to your new Party! You can invite other players to your Party by username, email, or from a list of players looking for a Party to earn the exclusive Basi-List Quest Scroll.<br/><br/>Select <a href='/static/faq#parties'>FAQ</a> from the Help dropdown to learn more about how Parties work.",
|
||||
"tourChallengesPage": "Challenges are themed task lists created by users! Joining a Challenge will add its tasks to your account. Compete against other users to win Gem prizes!",
|
||||
"tourMarketPage": "Every time you complete a task, you'll have a random chance at receiving an Egg, a Hatching Potion, or a piece of Pet Food. You can also buy these items here.",
|
||||
"tourHallPage": "Welcome to the Hall of Heroes, where open-source contributors to Habitica are honored. Whether through code, art, music, writing, or even just helpfulness, they have earned Gems, exclusive equipment, and prestigious titles. You can contribute to Habitica, too!",
|
||||
"tourHallPage": "Welcome to the Hall of Heroes, where open-source contributors to Habitica are honored. Whether through code, art, music, writing, or even just helpfulness, they have earned Gems, exclusive Equipment, and prestigious titles. You can contribute to Habitica, too!",
|
||||
"tourPetsPage": "Welcome to the stable! Every time you complete a task, you'll have a random chance at receiving an Egg or a Hatching Potion to hatch Pets. When you hatch a Pet, it will appear here! Click a Pet's image to add it to your Avatar. Feed them with the Pet Food you find and they'll grow into hardy Mounts.",
|
||||
"tourMountsPage": "Once you've fed a pet enough food to turn it into a mount, it will appear here. Click a mount to saddle up!",
|
||||
"tourEquipmentPage": "This is where your Equipment is stored! Your Battle Gear affects your Stats. If you want to show different Equipment on your avatar without changing your Stats, click \"Enable Costume.\"",
|
||||
|
||||
@@ -933,6 +933,34 @@
|
||||
"questDogDropDogEgg": "Dog (Egg)",
|
||||
"questDogUnlockText": "Unlocks Dog Eggs for purchase in the Market.",
|
||||
|
||||
"questCatText": "A Purrplexing Predicament",
|
||||
"questCatNotes": "On this fine day you find yourself in Habit City's Enchanted Efficiency Emporium workshop. You've been assigned a tough task: create a new magic motivation spell to help Habiticans everywhere complete their goals with ease.<br><br>Sitting on a table in front of you is a variety of magical objects. All the tomes said they were supposed to resonate together with productive energy… but so far there's not even a spark of motivation.<br><br>The creaking of a door alerts you to a new guest entering your workshop. Scampering feet and a blur of fluff dart onto the table. A... cat? Before you even have a chance to compliment how fluffy she is, she's lifting a paw to one of the crystals you set up and… knocking it off the table!<br><br>\"Hey!\" you shout, \"You're really cute but I'm trying to do some work over here...\"<br><br>She looks at you with her pretty blue eyes, tilts her head, and bats a bundle of herbs off the table. \"I'm helping!\" she purrs.<br><br>You see her paw reaching out toward the rest of the items you've collected and dive to the floor to catch the next one to go down!",
|
||||
"questCatCompletion": "You've thankfully caught everything that pushy cat knocked off the table. As you sit on the floor you notice a bright glow coming from the objects in front of you. Looking up, the ones on the table are reacting too! Putting them at different elevations seems to be a breakthrough in your research!<br><br>\"You know, in the end you did help me. I guess I just needed some fresh eyes on my task to get me unstuck. I wish you would have given me a bit of a heads-up before you started pushing things around, though,\" you say to the cat, patting her gently.<br><br>\"That's a purrfectly reasonable request, please take these as my apology!\" she purrs, nudging some funny-looking eggs in your direction. \"I'm glad I could help you see things from a different purrspective.\"",
|
||||
"questCatBoss": "The Purrplexer",
|
||||
"questCatRageTitle": "Furious Bapping",
|
||||
"questCatRageDescription": "This bar fills when you don't complete your Dailies. When it's full, the Purrplexer will take away some of your party's MP!",
|
||||
"questCatRageEffect": "The Purrplexer knocks the magic objects you've collected off the table! The party's MP is reduced!",
|
||||
"questCatDropCatEgg": "Cat (Egg)",
|
||||
"questCatUnlockText": "Unlocks Cat Eggs for Purchase in the Market.",
|
||||
|
||||
"questOtterText": "The Perfidious Plotter!",
|
||||
"questOtterNotes": "To-do lists are great! You can spend hours meticulously documenting each step you need to take and feel productive without actually doing those things. Your three-page list gets stuffed into your pocket. Time for a refreshing walk!<br><br>You depart towards the Routine River to take a stroll along the banks. This is exactly what you needed to finally get started! Time to take out your to-do list and–ah! A gust of wind has your list flying out of your hand and headed right for the water!<br><br>Right before the paper hits the water, an otter head pops up to the surface intercepting the sheet’s sure demise. Phew! He grasps the list in his paws and a mischievous grin spreads across his face… uh-oh.<br><br>“Hmmm,” he hums, flipping the paper around to read your list. “Looks like you need some help prioritizing.” Riiiip.<br><br>The otter just ripped your carefully crafted list into pieces! “If you want to get these done, you’re going to have to decide what’s the most important first!” he says, tossing your list items into the breeze one at a time.",
|
||||
"questOtterCompletion": "As you caught the pieces of your list you started sorting them by which tasks were the most important and ended up with quite a manageable place to start!<br><br>“I see!” you tell the otter, “that goofy stunt really did help me think about what tasks I needed to prioritize.”<br><br>The otter splashes about, rubbing its cheeks with glee, “I’m glad my little plot got you thinking about your tasks in a different way.” He dives under the water, resurfacing nearby, “Remember to keep your lists achievable. Rewards help too, so take these!”",
|
||||
"questOtterBoss": "The Plotter",
|
||||
"questOtterRageTitle": "To-do Tear-Up!",
|
||||
"questOtterRageDescription": "This bar fills when you don't complete your Dailies. When it's full, The Plotter will regain some of its health!",
|
||||
"questOtterRageEffect": "The Plotter throws pieces of your to-do list into the air! The boss regains 30% of its health!",
|
||||
"questOtterDropOtterEgg": "Otter (egg)",
|
||||
"questOtterUnlockText": "Unlocks Otter Eggs for Purchase in the Market",
|
||||
|
||||
"questJadeText": "A Jaded Jinx",
|
||||
"questJadeNotes": "You’re in your home staring at the stack of dirty dishes in the sink. The pile of dirty laundry in a random corner of the room. The empty cups and snack wrappers around your desk...<br><br>You sigh. “Why are there always more dishes…The mess is neverending.” It’s so demotivating. You find yourself dazing on the couch, mindlessly scrolling the latest trends. Who knows how long you’ve been there...<br><br>When you look up from your phone, everything is green. This isn’t your living room. Standing up, you find yourself on the side of a shiny verdant mountain.<br><br>Movement in the distance draws your attention. A stony green figure grunts, pushing a boulder up the rocky terrain. He makes some progress, but a small slip of his foot has the shiny boulder rolling back down, right towards you!<br><br>He spots you as he runs towards the chunk of jade hurdling your way! “So you think the dishes are bad?” the figure shouts, “Try this!”",
|
||||
"questJadeCompletion": "After countless setbacks, you somehow managed to roll the jade boulder up to the top of the mountain! The stony figure catches up to you and smiles. He gives the boulder a gentle push and you watch in horror as it rolls all the way back to the bottom.<br><br>“Why would you do that? Someone will have to do it all over again now!” you balk.<br><br>“Just because you have to do something more than once doesn’t mean your accomplishments are meaningless,” the stone figure says. “For now, focus on what you achieved and enjoy a reward!”<br><br>You jolt awake back on your couch, phone fallen to the floor. In its place are three bottles filled with flowing jade! Maybe it’s time to clean today’s dishes and then take a break to see how these potions work on some pet eggs...",
|
||||
"questJadeBoss": "Jaded Jinx",
|
||||
"questJadeDropJadePotion": "Jade Hatching Potion",
|
||||
"questJadeUnlockText": "Unlocks Jade Hatching Potion for Purchase in the Market.",
|
||||
|
||||
|
||||
"questFungiText": "The Moody Mushroom",
|
||||
"questFungiNotes": "It’s been a rainy spring in Habitica and the ground around the stables is spongy and damp. You notice quite a few mushrooms have appeared along the wooden stable walls and fences. There’s a fog hanging about, not quite letting the sun peek through, and it’s a bit dispiriting.<br><br>Out of the mist you see the outline of the April Fool, not at all his usual bouncy self.<br><br>”I’d hoped to bring you all some delightful Fungi Magic Hatching Potions so that you can keep your mushroom friends from my special day forever,” he says, his expression alarmingly unsmiling. “But this cold fog is really getting to me, it’s making me feel too tired and dismal to work my usual magic.”<br><br>“Oh no, sorry to hear that,” you say, noticing your own increasingly somber mood. “This fog is really making the day gloomy. I wonder where it came from…”<br><br>A low rumble sounds across the fields, and you see an outline emerging from the mist. You’re alarmed to see a gigantic and unhappy looking mushroom creature, and the mist appears to be emanating from it.<br><br>“Aha,” says the Fool, “I think this fungal fellow may be the source of our blues. Let’s see if we can summon a little cheer for our friend here and ourselves.”",
|
||||
"questFungiCompletion": "You and the April Fool look at each other with a sign of relief as the mushroom retreats to the forest.<br><br>“Ah,” the Fool exclaims, “that was quite a mycelial melancholy. I’m glad we could improve his mood, and ours too! I feel my energy coming back. Come with me and we’ll get those Fungi potions going together.”",
|
||||
|
||||
@@ -164,7 +164,7 @@
|
||||
"generate": "Generate",
|
||||
"getCodes": "Get Codes",
|
||||
"webhooks": "Webhooks",
|
||||
"webhooksInfo": "Webhooks provide a way for developers to receive notifications when a particular action is performed, such as scoring or updating a Task, or sending a message in a Group. By creating a webhook, you will be able to listen to changes in Habitica and build apps that respond to these changes.<br><br>For additional information and examples on webhooks, please visit our <a target=\"_blank\" href=\"https://habitica.fandom.com/wiki/Webhooks\">API Docs</a>",
|
||||
"webhooksInfo": "Webhooks provide a way for developers to receive notifications when a particular action is performed, such as scoring or updating a Task, or sending a message in a Group. By creating a webhook, you will be able to listen to changes in Habitica and build apps that respond to these changes.<br><br>For additional information and examples on webhooks, please visit our <a target=\"_blank\" href=\"https://habitica.com/apidoc/#api-Webhook-AddWebhook\">API Docs</a>.",
|
||||
"enabled": "Enabled",
|
||||
"webhookURL": "Webhook URL",
|
||||
"addWebhook": "Add Webhook",
|
||||
|
||||
@@ -170,6 +170,8 @@
|
||||
"mysterySet202411": "Bristled Brawler Set",
|
||||
"mysterySet202412": "Candy Cane Cottontail Set",
|
||||
"mysterySet202501": "Frostbinder Set",
|
||||
"mysterySet202502": "Heartfelt Harlequin Set",
|
||||
"mysterySet202503": "Jade Juggernaut Set",
|
||||
"mysterySet301404": "Steampunk Standard Set",
|
||||
"mysterySet301405": "Steampunk Accessories Set",
|
||||
"mysterySet301703": "Peacock Steampunk Set",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user