mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-10-29 12:12:36 +01:00
Compare commits
33 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fd8120c80d | ||
|
|
053e75562f | ||
|
|
1d50027f51 | ||
|
|
7fe74fd06a | ||
|
|
173a8561b6 | ||
|
|
f21bef707b | ||
|
|
9cf2ccf7c4 | ||
|
|
77d75c4669 | ||
|
|
1c17b95146 | ||
|
|
d89fc209d1 | ||
|
|
844c8bb3e6 | ||
|
|
569fb11db8 | ||
|
|
7671347d3a | ||
|
|
dc3a02bc2e | ||
|
|
1d8c126687 | ||
|
|
7ee49a43f2 | ||
|
|
900bc8dfc1 | ||
|
|
ec260016d3 | ||
|
|
ec770fb29e | ||
|
|
c1079e4eae | ||
|
|
3cb5637fd5 | ||
|
|
cf3a0118c9 | ||
|
|
895a383089 | ||
|
|
f730e7b345 | ||
|
|
d8f3d99d59 | ||
|
|
99fb1f6116 | ||
|
|
7c6dce2124 | ||
|
|
c757a3f52d | ||
|
|
ffb318fe8d | ||
|
|
19cd15ed62 | ||
|
|
3495662196 | ||
|
|
908a1340a4 | ||
|
|
31f4610b20 |
@@ -9,7 +9,7 @@ RUN npm install -g gulp grunt-cli bower mocha
|
||||
# Clone Habitica repo and install dependencies
|
||||
RUN mkdir -p /usr/src/habitrpg
|
||||
WORKDIR /usr/src/habitrpg
|
||||
RUN git clone --branch release https://github.com/HabitRPG/habitica.git /usr/src/habitrpg
|
||||
RUN git clone --branch v4.0.3 https://github.com/HabitRPG/habitica.git /usr/src/habitrpg
|
||||
RUN npm install
|
||||
RUN bower install --allow-root
|
||||
RUN gulp build:prod --force
|
||||
|
||||
2
package-lock.json
generated
2
package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "habitica",
|
||||
"version": "4.0.1",
|
||||
"version": "4.0.9",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "habitica",
|
||||
"description": "A habit tracker app which treats your goals like a Role Playing Game.",
|
||||
"version": "4.0.1",
|
||||
"version": "4.0.9",
|
||||
"main": "./website/server/index.js",
|
||||
"dependencies": {
|
||||
"@slack/client": "^3.8.1",
|
||||
|
||||
@@ -138,12 +138,21 @@ export default {
|
||||
// @TODO split up this file, it's too big
|
||||
// Set up Error interceptors
|
||||
axios.interceptors.response.use((response) => {
|
||||
if (this.user) {
|
||||
if (this.user && response.data && response.data.notifications) {
|
||||
this.$set(this.user, 'notifications', response.data.notifications);
|
||||
}
|
||||
return response;
|
||||
}, (error) => {
|
||||
if (error.response.status >= 400) {
|
||||
// Don't show errors from getting user details. These users have delete their account,
|
||||
// but their chat message still exists.
|
||||
let configExists = Boolean(error.response) && Boolean(error.response.config);
|
||||
if (configExists && error.response.config.method === 'get' && error.response.config.url.indexOf('/api/v3/members/') !== -1) {
|
||||
// @TODO: We resolve the promise because we need our caching to cache this user as tried
|
||||
// Chat paging should help this, but maybe we can also find another solution..
|
||||
return Promise.resolve(error);
|
||||
}
|
||||
|
||||
this.$store.state.notificationStore.push({
|
||||
title: 'Habitica',
|
||||
text: error.response.data.message,
|
||||
|
||||
@@ -6,35 +6,7 @@
|
||||
:hide-footer='true',
|
||||
)
|
||||
.modal-body
|
||||
.media
|
||||
.align-self-center.right-margin(:class='baileyClass')
|
||||
.media-body
|
||||
h1.align-self-center(v-markdown='$t("newStuff")')
|
||||
h2 9/28/2017 - HABITICA'S WEBSITE LEVELS UP!
|
||||
hr
|
||||
p Welcome to the new Habitica! We're so excited to share it with you at last. This project, which has been a labor of love since last December, is the single biggest update that Habitica has ever released (with over 150 pages of designs, an entire rewrite of all of our front-end code, countless rounds of testing and iteration, and many, many meetings). Just refresh your page to load the new website!
|
||||
.promo_login_screen.center-block
|
||||
p(v-markdown="'You can find a full list of changes [here](https://docs.google.com/document/d/1GZ0A2MK3JCZPuFUOStR7aVUI8fQlmjFDtLdj5Lg5XHY/edit), as well as explanations for why we made each, but here are a few quick tips to help you get oriented:'")
|
||||
.seasonal-shop-backdrop
|
||||
.daniel_front
|
||||
ul
|
||||
li There's a ton of new art around the site! Peek at the NPCs and Guild chats to admire some of the changes.
|
||||
li Click directly on your tasks to bring up the edit modal!
|
||||
li The navigation bar contains several changes to be more intuitive for new users, so we recommend taking some time to open the drop-down menus and familiarize yourself with the new locations. Notably, the User menu has moved to an icon in the upper-right corner.
|
||||
li You can now pin any purchasable item in the game to your Rewards. You can pin Backgrounds, too! Just hover over the shop icon and click the pin. When you head back to the tasks page, you'll see it in your Rewards column!
|
||||
li There are lots of new filtering options, especially for Guilds and Challenges!
|
||||
li There are visual upgrades for every aspect of the site, from the front page to the Seasonal Shop. We hope that you like them!
|
||||
li Some of these upgrades have made their way to our <a href='https://itunes.apple.com/us/app/habitica/id994882113?ls=1&mt=8' target='_blank'>iOS</a> and <a href='https://play.google.com/store/apps/details?id=com.habitrpg.android.habitica' target='_blank'>Android</a> apps! Be sure to download the latest updates for the best performance.
|
||||
.seasonal-shop-backdrop
|
||||
.sorceress_front
|
||||
p Have general questions about how the new site works? Come ask in the <a href='https://habitica.com/groups/guild/5481ccf3-5d2d-48a9-a871-70a7380cee5a'>Habitica Help Guild</a>, and we'll be glad to assist! Likewise, if you encounter a persistent bug that isn't fixed by refreshing your page, you can report it in the <a href='https://habitica.com/groups/guild/a29da26b-37de-4a71-b0c6-48e72a900dac'>Report a Bug Guild</a> and we will investigate as soon as possible.
|
||||
p If you have thoughts about the new design, we look forward to hearing them. On <strong>October 12th</strong> we will be opening a Trello card for public comments on the redesign. This delay will give us time to focus our attention on answering general questions and fixing any bugs that might arise. For this reason, we ask that you hold back on sharing your feedback about the new designs until that Trello card is live and linked in a Bailey announcement. Thanks for understanding!
|
||||
.promo_veteran_pets_2017.center-block
|
||||
p This is a major time of change for Habitica, so to thank you for your patience, we've given everyone a Veteran Pet! Newer users have received a Veteran Wolf, and older users have received (depending on which pets they already had) a Veteran Tiger, a Veteran Lion, or a Veteran Bear. Head to the new <a href='https://habitica.com/inventory/stable'>Stable</a> page and filter to the Special Pets section to see the latest addition to your menagerie!"')
|
||||
p We are so excited to continue to build Habitica with you. Now go check it out!
|
||||
p Thank you for playing, and good luck with your tasks <3
|
||||
.small by Apollo, piyorii, TheHollidayInn, Paglias, Negue, Sabe, Alys, viirus, Lemoness, redphoenix, beffymaroo, and all our awesome testers!
|
||||
br
|
||||
new-stuff
|
||||
.modal-footer
|
||||
a.btn.btn-info(href='http://habitica.wikia.com/wiki/Whats_New', target='_blank') {{ this.$t('newsArchive') }}
|
||||
button.btn.btn-default(@click='close()') {{ this.$t('cool') }}
|
||||
@@ -44,86 +16,36 @@
|
||||
<style lang='scss' scoped>
|
||||
@import '~client/assets/scss/static.scss';
|
||||
|
||||
.center-block {
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.grassy-meadow-backdrop {
|
||||
background-image: url('~assets/images/npc/normal/tavern_background.png');
|
||||
background-repeat: repeat-x;
|
||||
width: 100%;
|
||||
height: 246px;
|
||||
}
|
||||
|
||||
.daniel_front {
|
||||
background-image: url('~assets/images/npc/fall/seasonal_shop_opened_npc.png');
|
||||
height: 246px;
|
||||
width: 471px;
|
||||
background-repeat: no-repeat;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.seasonal-shop-backdrop {
|
||||
background: url('~assets/images/npc/fall/seasonal_shop_opened_background.png');
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
|
||||
.sorceress_front {
|
||||
background-image: url('~assets/images/npc/normal/tavern_npc.png');
|
||||
height: 246px;
|
||||
width: 471px;
|
||||
background-repeat: no-repeat;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
padding-top: 2em;
|
||||
}
|
||||
|
||||
.left-margin {
|
||||
margin-left: 1em;
|
||||
}
|
||||
|
||||
.right-margin {
|
||||
margin-right: 1em;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import bModal from 'bootstrap-vue/lib/components/modal';
|
||||
import { mapState } from 'client/libs/store';
|
||||
import markdown from 'client/directives/markdown';
|
||||
import bModal from 'bootstrap-vue/lib/components/modal';
|
||||
import { mapState } from 'client/libs/store';
|
||||
import markdown from 'client/directives/markdown';
|
||||
import newStuff from 'client/components/static/newStuff';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
bModal,
|
||||
},
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
},
|
||||
data () {
|
||||
let worldDmg = {
|
||||
bailey: false,
|
||||
};
|
||||
|
||||
return {
|
||||
baileyClass: {
|
||||
'npc_bailey_broken': worldDmg.bailey, // eslint-disable-line
|
||||
'npc_bailey': !worldDmg.bailey, // eslint-disable-line
|
||||
export default {
|
||||
components: {
|
||||
bModal,
|
||||
newStuff,
|
||||
},
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
},
|
||||
directives: {
|
||||
markdown,
|
||||
},
|
||||
methods: {
|
||||
close () {
|
||||
this.$root.$emit('hide::modal', 'new-stuff');
|
||||
},
|
||||
dismissAlert () {
|
||||
this.$store.dispatch('user:set', {'flags.newStuff': false});
|
||||
this.close();
|
||||
},
|
||||
};
|
||||
},
|
||||
directives: {
|
||||
markdown,
|
||||
},
|
||||
methods: {
|
||||
close () {
|
||||
this.$root.$emit('hide::modal', 'new-stuff');
|
||||
},
|
||||
dismissAlert () {
|
||||
this.$store.dispatch('user:set', {'flags.newStuff': false});
|
||||
this.close();
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
<template lang="pug">
|
||||
b-modal#quest-completed(v-if='user.party.quest.completed', :title="quests[user.party.quest.completed].text() + '' + $t('completed')",
|
||||
size='lg', :hide-footer="true")
|
||||
b-modal#quest-completed(v-if='user.party.quest.completed', :title="title",
|
||||
size='md', :hide-footer="true")
|
||||
.modal-body.text-center
|
||||
div(:class='`quest_${user.party.quest.completed}`')
|
||||
p(v-html='quests[user.party.quest.completed].completion()')
|
||||
.quest(:class='`quest_${user.party.quest.completed}`')
|
||||
p(v-html='this.questData.completion()')
|
||||
.quest-rewards(key='user.party.quest.completed', header-participant="$t('youReceived')", header-quest-owner="$t('questOwnerReceived')")
|
||||
.modal-footer
|
||||
button.btn.btn-primary(@click='setQuestCompleted()') {{ $t('ok') }}
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.quest {
|
||||
margin: 0 auto;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import bModal from 'bootstrap-vue/lib/components/modal';
|
||||
import quests from 'common/script/content/quests';
|
||||
@@ -29,6 +35,12 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
questData () {
|
||||
return this.quests.quests[this.user.party.quest.completed];
|
||||
},
|
||||
title () {
|
||||
return `${this.questData.text()} ${this.$t('completed')}`;
|
||||
},
|
||||
barStyle () {
|
||||
return {
|
||||
width: `${percent(this.user.stats.hp, maxHealth)}%`,
|
||||
|
||||
@@ -4,12 +4,12 @@
|
||||
modify-inventory(v-if="isUserLoaded")
|
||||
footer.container-fluid
|
||||
.row
|
||||
.col-2
|
||||
.col-12.col-md-2
|
||||
h3
|
||||
a(href='https://itunes.apple.com/us/app/habitica/id994882113?ls=1&mt=8', target='_blank') {{ $t('mobileIOS') }}
|
||||
h3
|
||||
a(href='https://play.google.com/store/apps/details?id=com.habitrpg.android.habitica', target='_blank') {{ $t('mobileAndroid') }}
|
||||
.col-2
|
||||
.col-12.col-md-2
|
||||
h3 Company
|
||||
ul
|
||||
li
|
||||
@@ -28,7 +28,7 @@
|
||||
a(href='/static/press-kit') {{ $t('presskit') }}
|
||||
li
|
||||
a(href='/static/contact') {{ $t('contactUs') }}
|
||||
.col-2
|
||||
.col-12.col-md-2
|
||||
h3 Community
|
||||
ul
|
||||
li
|
||||
@@ -36,7 +36,7 @@
|
||||
li
|
||||
router-link(to='/hall/contributors') {{ $t('hall') }}
|
||||
li
|
||||
router-link(to='/groups/a29da26b-37de-4a71-b0c6-48e72a900dac') {{ $t('reportBug') }}
|
||||
router-link(to='/groups/guild/a29da26b-37de-4a71-b0c6-48e72a900dac') {{ $t('reportBug') }}
|
||||
li
|
||||
a(href='https://trello.com/c/odmhIqyW/440-read-first-table-of-contents', target='_blank') {{ $t('requestFeature') }}
|
||||
li
|
||||
@@ -47,7 +47,7 @@
|
||||
a(href='https://www.facebook.com/Habitica', target='_blank') {{ $t('communityFacebook') }}
|
||||
li
|
||||
a(href='https://www.reddit.com/r/habitrpg/', target='_blank') {{ $t('communityReddit') }}
|
||||
.col-6
|
||||
.col-12.col-md-6
|
||||
.row
|
||||
.col-6
|
||||
h3 Developers
|
||||
@@ -55,7 +55,7 @@
|
||||
li
|
||||
a(href='/apidoc', target='_blank') {{ $t('APIv3') }}
|
||||
li
|
||||
a(href='http://data.habitrpg.com/?uuid=', target='_blank') {{ $t('dataDisplayTool') }}
|
||||
a(href='https://oldgods.net/habitrpg/habitrpg_user_data_display.html', target='_blank') {{ $t('dataDisplayTool') }}
|
||||
li
|
||||
a(href='http://habitica.wikia.com/wiki/Guidance_for_Blacksmiths', target='_blank') {{ $t('guidanceForBlacksmiths') }}
|
||||
li
|
||||
@@ -82,7 +82,7 @@
|
||||
.row
|
||||
.col-4
|
||||
| © 2017 Habitica. All rights reserved.
|
||||
.debug.float-left(v-if="!IS_PRODUCTION && isUserLoaded")
|
||||
// .debug.float-left(v-if="!IS_PRODUCTION && isUserLoaded")
|
||||
button.btn.btn-primary(@click="debugMenuShown = !debugMenuShown") Toggle Debug Menu
|
||||
.debug-group(v-if="debugMenuShown")
|
||||
a.btn.btn-default(@click="setHealthLow()") Health = 1
|
||||
|
||||
@@ -52,7 +52,7 @@ div
|
||||
a.dropdown-item(href="https://trello.com/c/odmhIqyW/440-read-first-table-of-contents", target='_blank') {{ $t('requestAF') }}
|
||||
a.dropdown-item(href="http://habitica.wikia.com/wiki/Contributing_to_Habitica", target='_blank') {{ $t('contributing') }}
|
||||
a.dropdown-item(href="http://habitica.wikia.com/wiki/Habitica_Wiki", target='_blank') {{ $t('wiki') }}
|
||||
.item-with-icon(v-if="userHourglasses != 0")
|
||||
.item-with-icon(v-if="userHourglasses > 0")
|
||||
.svg-icon(v-html="icons.hourglasses")
|
||||
span {{ userHourglasses }}
|
||||
.item-with-icon
|
||||
|
||||
@@ -485,14 +485,14 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
const res = await axios.post('/api/v3/user/reset-password', {
|
||||
const res = await axios.post('/api/v3/user/auth/reset-password-set-new-one', {
|
||||
newPassword: this.password,
|
||||
confirmPassword: this.passwordConfirm,
|
||||
code: this.resetPasswordSetNewOneData.code,
|
||||
});
|
||||
|
||||
if (res.message) {
|
||||
alert(res.message);
|
||||
if (res.data.message) {
|
||||
alert(res.data.message);
|
||||
}
|
||||
|
||||
this.password = '';
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
)
|
||||
|
||||
.row
|
||||
task-column.col-6(
|
||||
task-column.col-12.col-sm-6(
|
||||
v-for="column in columns",
|
||||
:type="column",
|
||||
:key="column",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
.card
|
||||
.row
|
||||
router-link.col-12(:to="{ name: 'challenge', params: { challengeId: challenge._id } }")
|
||||
h3 {{challenge.name}}
|
||||
h3(v-markdown='challenge.name')
|
||||
.row
|
||||
.col-6
|
||||
div.details
|
||||
@@ -163,6 +163,7 @@ import habitIcon from 'assets/svg/habit.svg';
|
||||
import todoIcon from 'assets/svg/todo.svg';
|
||||
import dailyIcon from 'assets/svg/daily.svg';
|
||||
import rewardIcon from 'assets/svg/reward.svg';
|
||||
import markdownDirective from 'client/directives/markdown';
|
||||
|
||||
export default {
|
||||
props: ['challenge'],
|
||||
@@ -179,5 +180,8 @@ export default {
|
||||
}),
|
||||
};
|
||||
},
|
||||
directives: {
|
||||
markdown: markdownDirective,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
.svg-icon.positive-icon(v-html="icons.positiveIcon")
|
||||
span(v-once) {{$t('createChallenge')}}
|
||||
.row
|
||||
.col-6(v-for='challenge in filteredChallenges', v-if='!memberOf(challenge)')
|
||||
.col-12.col-md-6(v-for='challenge in filteredChallenges', v-if='!memberOf(challenge)')
|
||||
challenge-item(:challenge='challenge')
|
||||
</template>
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
p(v-once) {{$t('challengeDescription2')}}
|
||||
|
||||
.row
|
||||
.col-6(v-for='challenge in filteredChallenges')
|
||||
.col-12.col-md-6(v-for='challenge in filteredChallenges')
|
||||
challenge-item(:challenge='challenge')
|
||||
</template>
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template lang="pug">
|
||||
.col-2.standard-sidebar
|
||||
.col-2.standard-sidebar.hidden-xs-down
|
||||
.form-group
|
||||
input.form-control.search(type="text", :placeholder="$t('search')", v-model='searchTerm')
|
||||
|
||||
|
||||
@@ -5,14 +5,14 @@
|
||||
copy-as-todo-modal(:copying-message='copyingMessage', :group-name='groupName', :group-id='groupId')
|
||||
report-flag-modal
|
||||
|
||||
div(v-for="(msg, index) in chat", v-if='chat && canViewFlag(msg)')
|
||||
div(v-for="(msg, index) in messages", v-if='chat && canViewFlag(msg)')
|
||||
// @TODO: is there a different way to do these conditionals? This creates an infinite loop
|
||||
//.hr(v-if='displayDivider(msg)')
|
||||
.hr-middle(v-once) {{ msg.timestamp }}
|
||||
.row(v-if='user._id !== msg.uuid')
|
||||
div(:class='inbox ? "col-4" : "col-2"')
|
||||
avatar(
|
||||
v-if='cachedProfileData[msg.uuid]',
|
||||
v-if='cachedProfileData[msg.uuid] && !cachedProfileData[msg.uuid].rejected',
|
||||
:member="cachedProfileData[msg.uuid]",
|
||||
:avatarOnly="true",
|
||||
:hideClassBadge='true',
|
||||
@@ -36,13 +36,15 @@
|
||||
.svg-icon(v-html="icons.like")
|
||||
span(v-if='!msg.likes[user._id]') {{ $t('like') }}
|
||||
span(v-if='msg.likes[user._id]') {{ $t('liked') }}
|
||||
span.action( @click='copyAsTodo(msg)')
|
||||
span.action(v-if='!inbox', @click='copyAsTodo(msg)')
|
||||
.svg-icon(v-html="icons.copy")
|
||||
| {{$t('copyAsTodo')}}
|
||||
span.action(v-if='user.contributor.admin || (msg.uuid !== user._id && user.flags.communityGuidelinesAccepted)', @click='report(msg)')
|
||||
// @TODO make copyAsTodo work in the inbox
|
||||
span.action(v-if='!inbox && user.flags.communityGuidelinesAccepted', @click='report(msg)')
|
||||
.svg-icon(v-html="icons.report")
|
||||
| {{$t('report')}}
|
||||
span.action(v-if='msg.uuid === user._id || inbox', @click='remove(msg, index)')
|
||||
// @TODO make flagging/reporting work in the inbox. NOTE: it must work even if the communityGuidelines are not accepted and it MUST work for messages that you have SENT as well as received. -- Alys
|
||||
span.action(v-if='msg.uuid === user._id || inbox || user.contributor.admin', @click='remove(msg, index)')
|
||||
.svg-icon(v-html="icons.delete")
|
||||
| {{$t('delete')}}
|
||||
span.action.float-right.liked(v-if='likeCount(msg) > 0')
|
||||
@@ -70,12 +72,15 @@
|
||||
.svg-icon(v-html="icons.like")
|
||||
span(v-if='!msg.likes[user._id]') {{ $t('like') }}
|
||||
span(v-if='msg.likes[user._id]') {{ $t('liked') }}
|
||||
span.action( @click='copyAsTodo(msg)')
|
||||
span.action(v-if='!inbox', @click='copyAsTodo(msg)')
|
||||
.svg-icon(v-html="icons.copy")
|
||||
| {{$t('copyAsTodo')}}
|
||||
// @TODO make copyAsTodo work in the inbox
|
||||
span.action(v-if='user.flags.communityGuidelinesAccepted', @click='report(msg)')
|
||||
span.action(v-if='!inbox && user.flags.communityGuidelinesAccepted', @click='report(msg)')
|
||||
.svg-icon(v-html="icons.report")
|
||||
| {{$t('report')}}
|
||||
// @TODO make flagging/reporting work in the inbox. NOTE: it must work even if the communityGuidelines are not accepted and it MUST work for messages that you have SENT as well as received. -- Alys
|
||||
span.action(v-if='msg.uuid === user._id', @click='remove(msg, index)')
|
||||
.svg-icon(v-html="icons.delete")
|
||||
| {{$t('delete')}}
|
||||
@@ -84,7 +89,7 @@
|
||||
| + {{ likeCount(msg) }}
|
||||
div(:class='inbox ? "col-4" : "col-2"')
|
||||
avatar(
|
||||
v-if='cachedProfileData[msg.uuid]',
|
||||
v-if='cachedProfileData[msg.uuid] && !cachedProfileData[msg.uuid].rejected',
|
||||
:member="cachedProfileData[msg.uuid]",
|
||||
:avatarOnly="true",
|
||||
:hideClassBadge='true',
|
||||
@@ -238,7 +243,7 @@ import axios from 'axios';
|
||||
import moment from 'moment';
|
||||
import cloneDeep from 'lodash/cloneDeep';
|
||||
import { mapState } from 'client/libs/store';
|
||||
import throttle from 'lodash/throttle';
|
||||
import debounce from 'lodash/debounce';
|
||||
import markdownDirective from 'client/directives/markdown';
|
||||
import Avatar from '../avatar';
|
||||
import styleHelper from 'client/mixins/styleHelper';
|
||||
@@ -277,12 +282,10 @@ export default {
|
||||
this.loadProfileCache();
|
||||
},
|
||||
created () {
|
||||
window.addEventListener('scroll', throttle(() => {
|
||||
this.loadProfileCache(window.scrollY / 1000);
|
||||
}, 1000));
|
||||
window.addEventListener('scroll', this.handleScroll);
|
||||
},
|
||||
destroyed () {
|
||||
// window.removeEventListener('scroll', this.handleScroll);
|
||||
window.removeEventListener('scroll', this.handleScroll);
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
@@ -308,6 +311,7 @@ export default {
|
||||
cachedProfileData: {},
|
||||
currentProfileLoadedCount: 0,
|
||||
currentProfileLoadedEnd: 10,
|
||||
loading: false,
|
||||
};
|
||||
},
|
||||
filters: {
|
||||
@@ -321,17 +325,22 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
// @TODO: We need a different lazy load mechnism.
|
||||
// But honestly, adding a paging route to chat would solve this
|
||||
messages () {
|
||||
return this.chat;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
messages () {
|
||||
// @TODO: MAybe we should watch insert and remove?
|
||||
messages (oldValue, newValue) {
|
||||
if (newValue.length === oldValue.length) return;
|
||||
this.loadProfileCache();
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
handleScroll () {
|
||||
this.loadProfileCache(window.scrollY / 1000);
|
||||
},
|
||||
isUserMentioned (message) {
|
||||
let user = this.user;
|
||||
|
||||
@@ -358,7 +367,10 @@ export default {
|
||||
if (!message.flagCount || message.flagCount < 2) return true;
|
||||
return this.user.contributor.admin;
|
||||
},
|
||||
async loadProfileCache (screenPosition) {
|
||||
loadProfileCache: debounce(function loadProfileCache (screenPosition) {
|
||||
this._loadProfileCache(screenPosition);
|
||||
}, 1000),
|
||||
async _loadProfileCache (screenPosition) {
|
||||
let promises = [];
|
||||
|
||||
// @TODO: write an explination
|
||||
@@ -368,11 +380,10 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
// @TODO: Not sure we need this hash
|
||||
let aboutToCache = {};
|
||||
this.messages.forEach(message => {
|
||||
let uuid = message.uuid;
|
||||
if (uuid && !this.cachedProfileData[uuid] && !aboutToCache[uuid]) {
|
||||
if (Boolean(uuid) && !this.cachedProfileData[uuid] && !aboutToCache[uuid]) {
|
||||
if (uuid === 'system' || this.currentProfileLoadedCount === this.currentProfileLoadedEnd) return;
|
||||
aboutToCache[uuid] = {};
|
||||
promises.push(axios.get(`/api/v3/members/${uuid}`));
|
||||
@@ -382,9 +393,21 @@ export default {
|
||||
|
||||
let results = await Promise.all(promises);
|
||||
results.forEach(result => {
|
||||
// We could not load the user. Maybe they were deleted. So, let's cache empty so we don't try again
|
||||
if (!result || !result.data || result.status >= 400) {
|
||||
return;
|
||||
}
|
||||
|
||||
let userData = result.data.data;
|
||||
this.$set(this.cachedProfileData, userData._id, userData);
|
||||
});
|
||||
|
||||
// Merge in any attempts that were rejected so we don't attempt again
|
||||
for (let uuid in aboutToCache) {
|
||||
if (!this.cachedProfileData[uuid]) {
|
||||
this.$set(this.cachedProfileData, uuid, {rejected: true});
|
||||
}
|
||||
}
|
||||
},
|
||||
displayDivider (message) {
|
||||
if (this.currentDayDividerDisplay !== moment(message.timestamp).day()) {
|
||||
|
||||
@@ -72,7 +72,7 @@
|
||||
v-on:taskEdited='taskEdited',
|
||||
)
|
||||
.row
|
||||
task-column.col-3(
|
||||
task-column.col-12.col-sm-6.col-3(
|
||||
v-for="column in columns",
|
||||
:type="column",
|
||||
:key="column",
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
group-form-modal(v-if='isParty')
|
||||
invite-modal(:group='this.group')
|
||||
start-quest-modal(:group='this.group')
|
||||
.col-8.standard-page
|
||||
quest-details-modal(:group='this.group')
|
||||
.col-12.col-sm-8.standard-page
|
||||
.row
|
||||
.col-6.title-details
|
||||
h1 {{group.name}}
|
||||
@@ -45,7 +46,7 @@
|
||||
.col-12.hr
|
||||
chat-message(:chat.sync='group.chat', :group-id='group._id', group-name='group.name')
|
||||
|
||||
.col-4.sidebar
|
||||
.col-12.col-sm-4.sidebar
|
||||
.row(:class='{"guild-background": !isParty}')
|
||||
.col-6
|
||||
.col-6
|
||||
@@ -80,9 +81,13 @@
|
||||
p(v-once) {{ $t('questDescription') }}
|
||||
button.btn.btn-secondary(v-once, @click="openStartQuestModal()", v-if='isLeader') {{ $t('startAQuest') }}
|
||||
.row.quest-active-section(v-if='isParty && onPendingQuest && !onActiveQuest')
|
||||
h2 Pending quest
|
||||
button.btn.btn-secondary(v-once, @click="questForceStart()") {{ $t('begin') }}
|
||||
button.btn.btn-secondary(v-once, @click="questCancel()") {{ $t('cancel') }}
|
||||
.col-2
|
||||
.quest(:class='`inventory_quest_scroll_${questData.key}`')
|
||||
.col-6.titles
|
||||
strong {{ questData.text() }}
|
||||
p {{acceptedCount}} / {{group.memberCount}}
|
||||
.col-4
|
||||
button.btn.btn-secondary(@click="openQuestDetails()") {{ $t('details') }}
|
||||
.row.quest-active-section(v-if='isParty && !onPendingQuest && onActiveQuest')
|
||||
.col-12.text-center
|
||||
.quest-boss(:class="'quest_' + questData.key")
|
||||
@@ -353,6 +358,10 @@
|
||||
}
|
||||
|
||||
.quest-active-section {
|
||||
.titles {
|
||||
padding-top: .5em;
|
||||
}
|
||||
|
||||
.quest-box {
|
||||
background-image: url('~client/assets/svg/for-css/quest-border.svg');
|
||||
background-size: 100% 100%;
|
||||
@@ -431,6 +440,9 @@
|
||||
</style>
|
||||
|
||||
<script>
|
||||
// @TODO: Break this down into components
|
||||
|
||||
import debounce from 'lodash/debounce';
|
||||
import extend from 'lodash/extend';
|
||||
import groupUtilities from 'client/mixins/groupsUtilities';
|
||||
import styleHelper from 'client/mixins/styleHelper';
|
||||
@@ -438,6 +450,7 @@ import { mapState } from 'client/libs/store';
|
||||
import * as Analytics from 'client/libs/analytics';
|
||||
import membersModal from './membersModal';
|
||||
import startQuestModal from './startQuestModal';
|
||||
import questDetailsModal from './questDetailsModal';
|
||||
import quests from 'common/script/content/quests';
|
||||
import percent from 'common/script/libs/percent';
|
||||
import groupFormModal from './groupFormModal';
|
||||
@@ -481,6 +494,7 @@ export default {
|
||||
inviteModal,
|
||||
groupChallenges,
|
||||
autocomplete,
|
||||
questDetailsModal,
|
||||
},
|
||||
directives: {
|
||||
bToggle,
|
||||
@@ -522,6 +536,17 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
acceptedCount () {
|
||||
let count = 0;
|
||||
|
||||
if (!this.group || !this.group.quest) return count;
|
||||
|
||||
for (let uuid in this.group.quest.members) {
|
||||
if (this.group.quest.members[uuid]) count += 1;
|
||||
}
|
||||
|
||||
return count;
|
||||
},
|
||||
communityGuidelinesAccepted () {
|
||||
return this.user.flags.communityGuidelinesAccepted;
|
||||
},
|
||||
@@ -655,7 +680,10 @@ export default {
|
||||
};
|
||||
document.body.removeChild(div);
|
||||
},
|
||||
updateCarretPosition (eventUpdate) {
|
||||
updateCarretPosition: debounce(function updateCarretPosition (eventUpdate) {
|
||||
this._updateCarretPosition(eventUpdate);
|
||||
}, 250),
|
||||
_updateCarretPosition (eventUpdate) {
|
||||
if (eventUpdate.metaKey && eventUpdate.keyCode === 13) {
|
||||
this.sendMessage();
|
||||
return;
|
||||
@@ -717,6 +745,9 @@ export default {
|
||||
openStartQuestModal () {
|
||||
this.$root.$emit('show::modal', 'start-quest-modal');
|
||||
},
|
||||
openQuestDetails () {
|
||||
this.$root.$emit('show::modal', 'quest-details');
|
||||
},
|
||||
checkForAchievements () {
|
||||
// Checks if user's party has reached 2 players for the first time.
|
||||
if (!this.user.achievements.partyUp && this.group.memberCount >= 2) {
|
||||
@@ -811,11 +842,6 @@ export default {
|
||||
this.$store.state.profileOptions.startingPage = 'profile';
|
||||
this.$root.$emit('show::modal', 'profile');
|
||||
},
|
||||
async questCancel () {
|
||||
if (!confirm(this.$t('sureCancel'))) return;
|
||||
let quest = await this.$store.dispatch('quests:sendAction', {groupId: this.group._id, action: 'quests/cancel'});
|
||||
this.group.quest = quest;
|
||||
},
|
||||
async questAbort () {
|
||||
if (!confirm(this.$t('sureAbort'))) return;
|
||||
if (!confirm(this.$t('doubleSureAbort'))) return;
|
||||
@@ -831,10 +857,6 @@ export default {
|
||||
let quest = await this.$store.dispatch('quests:sendAction', {groupId: this.group._id, action: 'quests/accept'});
|
||||
this.group.quest = quest;
|
||||
},
|
||||
async questForceStart () {
|
||||
let quest = await this.$store.dispatch('quests:sendAction', {groupId: this.group._id, action: 'quests/force-start'});
|
||||
this.group.quest = quest;
|
||||
},
|
||||
// @TODO: Move to notificaitons component?
|
||||
async questReject () {
|
||||
let quest = await this.$store.dispatch('quests:sendAction', {groupId: this.group._id, action: 'quests/reject'});
|
||||
|
||||
210
website/client/components/groups/questDetailsModal.vue
Normal file
210
website/client/components/groups/questDetailsModal.vue
Normal file
@@ -0,0 +1,210 @@
|
||||
<template lang="pug">
|
||||
b-modal#quest-details(title="Empty", size='md', :hide-footer="true", :hide-header="true")
|
||||
.left-panel.content
|
||||
h3.text-center {{ $t('participantsTitle') }}
|
||||
.row
|
||||
.col-10.offset-1.text-center
|
||||
span.description(v-once) {{ $t('participantDesc') }}
|
||||
.row
|
||||
.col-12.member(v-for='member in members')
|
||||
strong(:class="{'declined-name': member.accepted === false}") {{member.name}}
|
||||
.accepted.float-right(v-if='member.accepted === true') {{ $t('accepted') }}
|
||||
.declined.float-right(v-if='member.accepted === false') {{ $t('declined') }}
|
||||
.pending.float-right(v-if='member.accepted === null') {{ $t('pending') }}
|
||||
div(v-if='questData')
|
||||
questDialogContent(:item="questData")
|
||||
div.text-center.actions
|
||||
div
|
||||
button.btn.btn-secondary(v-once, @click="questForceStart()") {{ $t('begin') }}
|
||||
div
|
||||
.cancel(v-once, @click="questCancel()") {{ $t('cancel') }}
|
||||
.side-panel(v-if='questData')
|
||||
questDialogDrops(:item="questData")
|
||||
</template>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
@import '~client/assets/scss/colors.scss';
|
||||
|
||||
header {
|
||||
background-color: $white !important;
|
||||
border: none !important;
|
||||
|
||||
h5 {
|
||||
text-indent: -99999px;
|
||||
}
|
||||
}
|
||||
|
||||
.quest-details {
|
||||
margin: 0 auto;
|
||||
text-align: left;
|
||||
width: 180px;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
.left-panel {
|
||||
background: #4e4a57;
|
||||
color: $white;
|
||||
position: absolute;
|
||||
height: 460px;
|
||||
width: 320px;
|
||||
top: 2.5em;
|
||||
left: -22em;
|
||||
z-index: -1;
|
||||
padding: 2em;
|
||||
overflow: scroll;
|
||||
|
||||
h3 {
|
||||
color: $white;
|
||||
}
|
||||
|
||||
.selected .quest-wrapper {
|
||||
border: solid 1.5px #9a62ff;
|
||||
}
|
||||
|
||||
.quest-wrapper:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.quest-col .quest-wrapper {
|
||||
background: $white;
|
||||
padding: .2em;
|
||||
margin-bottom: 1em;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.description {
|
||||
text-align: center;
|
||||
color: #a5a1ac;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.side-panel {
|
||||
position: absolute;
|
||||
right: -350px;
|
||||
top: 25px;
|
||||
border-radius: 8px;
|
||||
background-color: $gray-600;
|
||||
box-shadow: 0 2px 16px 0 rgba(26, 24, 29, 0.32);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
width: 364px;
|
||||
z-index: -1;
|
||||
height: 93%;
|
||||
}
|
||||
|
||||
.member {
|
||||
padding: 1em .5em;
|
||||
border-top: 1px solid #686274;
|
||||
|
||||
.declined-name {
|
||||
color: #878190;
|
||||
}
|
||||
|
||||
.accepted {
|
||||
color: #1ed3a0;
|
||||
}
|
||||
|
||||
.declined {
|
||||
color: #f19595;
|
||||
}
|
||||
|
||||
.pending {
|
||||
color: #c3c0c7;
|
||||
}
|
||||
}
|
||||
|
||||
.actions {
|
||||
padding-top: 2em;
|
||||
padding-bottom: 2em;
|
||||
|
||||
.cancel {
|
||||
color: #f74e52;
|
||||
margin-top: 3em;
|
||||
}
|
||||
|
||||
.cancel:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'client/libs/store';
|
||||
import bModal from 'bootstrap-vue/lib/components/modal';
|
||||
|
||||
import quests from 'common/script/content/quests';
|
||||
|
||||
import copyIcon from 'assets/svg/copy.svg';
|
||||
import greyBadgeIcon from 'assets/svg/grey-badge.svg';
|
||||
import qrCodeIcon from 'assets/svg/qrCode.svg';
|
||||
import facebookIcon from 'assets/svg/facebook.svg';
|
||||
import twitterIcon from 'assets/svg/twitter.svg';
|
||||
import starIcon from 'assets/svg/star.svg';
|
||||
import goldIcon from 'assets/svg/gold.svg';
|
||||
import difficultyStarIcon from 'assets/svg/difficulty-star.svg';
|
||||
import questDialogDrops from '../shops/quests/questDialogDrops';
|
||||
import questDialogContent from '../shops/quests/questDialogContent';
|
||||
|
||||
export default {
|
||||
props: ['group'],
|
||||
components: {
|
||||
bModal,
|
||||
questDialogDrops,
|
||||
questDialogContent,
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
loading: false,
|
||||
selectedQuest: {},
|
||||
icons: Object.freeze({
|
||||
copy: copyIcon,
|
||||
greyBadge: greyBadgeIcon,
|
||||
qrCode: qrCodeIcon,
|
||||
facebook: facebookIcon,
|
||||
twitter: twitterIcon,
|
||||
starIcon,
|
||||
goldIcon,
|
||||
difficultyStarIcon,
|
||||
}),
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
user: 'user.data',
|
||||
partyMembers: 'party.members.data',
|
||||
}),
|
||||
questData () {
|
||||
return quests.quests[this.group.quest.key];
|
||||
},
|
||||
members () {
|
||||
return this.partyMembers.map(member => {
|
||||
return {
|
||||
name: member.profile.name,
|
||||
accepted: this.group.quest.members[member._id],
|
||||
};
|
||||
});
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async questForceStart () {
|
||||
let quest = await this.$store.dispatch('quests:sendAction', {groupId: this.group._id, action: 'quests/force-start'});
|
||||
this.group.quest = quest;
|
||||
this.close();
|
||||
},
|
||||
async questCancel () {
|
||||
if (!confirm(this.$t('sureCancel'))) return;
|
||||
let quest = await this.$store.dispatch('quests:sendAction', {groupId: this.group._id, action: 'quests/cancel'});
|
||||
this.group.quest = quest;
|
||||
this.close();
|
||||
},
|
||||
close () {
|
||||
this.$root.$emit('hide::modal', 'quest-details');
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -1,5 +1,5 @@
|
||||
<template lang="pug">
|
||||
.standard-sidebar
|
||||
.standard-sidebar.hidden-xs-down
|
||||
.form-group
|
||||
input.form-control.search(type="text", :placeholder="$t('search')", v-model='searchTerm')
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template lang="pug">
|
||||
.row
|
||||
.clearfix.col-8.standard-page
|
||||
.col-12.col-sm-8.clearfix.standard-page
|
||||
.row
|
||||
.col-6.title-details
|
||||
h1(v-once) {{ $t('welcomeToTavern') }}
|
||||
@@ -29,7 +29,7 @@
|
||||
.hr.col-12
|
||||
chat-message(:chat.sync='group.chat', :group-id='group._id', group-name='group.name')
|
||||
|
||||
.col-md-4.sidebar
|
||||
.col-12.col-sm-4.sidebar
|
||||
.section
|
||||
.grassy-meadow-backdrop
|
||||
.daniel_front
|
||||
@@ -350,6 +350,7 @@
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import debounce from 'lodash/debounce';
|
||||
import { mapState } from 'client/libs/store';
|
||||
|
||||
import { TAVERN_ID } from '../../../common/script/constants';
|
||||
@@ -527,7 +528,10 @@ export default {
|
||||
};
|
||||
document.body.removeChild(div);
|
||||
},
|
||||
updateCarretPosition (eventUpdate) {
|
||||
updateCarretPosition: debounce(function updateCarretPosition (eventUpdate) {
|
||||
this._updateCarretPosition(eventUpdate);
|
||||
}, 250),
|
||||
_updateCarretPosition (eventUpdate) {
|
||||
if (eventUpdate.metaKey && eventUpdate.keyCode === 13) {
|
||||
this.sendMessage();
|
||||
return;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<template lang="pug">
|
||||
// @TODO: breakdown to componentes and use some SOLID
|
||||
.row.stable(v-mousePosition="30", @mouseMoved="mouseMoved($event)")
|
||||
.standard-sidebar
|
||||
.standard-sidebar.col-3.hidden-xs-down
|
||||
div
|
||||
#npmMattStable.npc_matt
|
||||
b-popover(
|
||||
@@ -53,7 +54,7 @@
|
||||
@change="updateHideMissing"
|
||||
)
|
||||
|
||||
.standard-page
|
||||
.standard-page.col-12.col-sm-9
|
||||
.clearfix
|
||||
h1.float-left.mb-0.page-header(v-once) {{ $t('stable') }}
|
||||
|
||||
@@ -73,55 +74,49 @@
|
||||
span.badge.badge-pill.badge-default {{countOwnedAnimals(petGroups[0], 'pet')}}
|
||||
|
||||
div(
|
||||
v-for="petGroup in petGroups",
|
||||
v-for="(petGroup, index) in petGroups",
|
||||
v-if="viewOptions[petGroup.key].selected",
|
||||
:key="petGroup.key"
|
||||
)
|
||||
h4(v-if="viewOptions[petGroup.key].animalCount != 0") {{ petGroup.label }}
|
||||
h4(v-if="viewOptions[petGroup.key].animalCount !== 0") {{ petGroup.label }}
|
||||
|
||||
itemRows(
|
||||
:items="pets(petGroup, hideMissing, selectedSortBy, searchTextThrottled)",
|
||||
:itemWidth=94,
|
||||
:itemMargin=24,
|
||||
:type="petGroup.key",
|
||||
)
|
||||
template(slot="item", scope="context")
|
||||
div(
|
||||
v-drag.drop.food="context.item.key",
|
||||
@itemDragOver="onDragOver($event, context.item)",
|
||||
@itemDropped="onDrop($event, context.item)",
|
||||
@itemDragLeave="onDragLeave()",
|
||||
:class="{'last': context.item.isLastInRow}"
|
||||
.pet-row.d-flex(
|
||||
v-for="(group, key, index) in pets(petGroup, hideMissing, selectedSortBy, searchTextThrottled)",
|
||||
v-if='index === 0 || showMore === petGroup.key')
|
||||
.pet-group(
|
||||
v-for='item in group'
|
||||
v-drag.drop.food="item.key",
|
||||
@itemDragOver="onDragOver($event, item)",
|
||||
@itemDropped="onDrop($event, item)",
|
||||
@itemDragLeave="onDragLeave()",
|
||||
:class="{'last': item.isLastInRow}"
|
||||
)
|
||||
petItem(
|
||||
:item="item",
|
||||
:itemContentClass="getPetItemClass(item)",
|
||||
:popoverPosition="'top'",
|
||||
:progress="item.progress",
|
||||
:emptyItem="!item.isOwned()",
|
||||
:showPopover="currentDraggingFood == null",
|
||||
:highlightBorder="highlightPet == item.key",
|
||||
@click="petClicked(item)"
|
||||
)
|
||||
petItem(
|
||||
:item="context.item",
|
||||
:itemContentClass="getPetItemClass(context.item)",
|
||||
:popoverPosition="'top'",
|
||||
:progress="context.item.progress",
|
||||
:emptyItem="!context.item.isOwned()",
|
||||
:showPopover="currentDraggingFood == null",
|
||||
:highlightBorder="highlightPet == context.item.key",
|
||||
@click="petClicked(context.item)"
|
||||
)
|
||||
span(slot="popoverContent")
|
||||
div.hatchablePopover(v-if="context.item.isHatchable()")
|
||||
h4.popover-content-title {{ context.item.name }}
|
||||
div.popover-content-text(v-html="$t('haveHatchablePet', { potion: context.item.potionName, egg: context.item.eggName })")
|
||||
div.potionEggGroup
|
||||
div.potionEggBackground
|
||||
div(:class="'Pet_HatchingPotion_'+context.item.potionKey")
|
||||
div.potionEggBackground
|
||||
div(:class="'Pet_Egg_'+context.item.eggKey")
|
||||
span(slot="popoverContent")
|
||||
div.hatchablePopover(v-if="item.isHatchable()")
|
||||
h4.popover-content-title {{ item.name }}
|
||||
div.popover-content-text(v-html="$t('haveHatchablePet', { potion: item.potionName, egg: item.eggName })")
|
||||
div.potionEggGroup
|
||||
div.potionEggBackground
|
||||
div(:class="'Pet_HatchingPotion_'+item.potionKey")
|
||||
div.potionEggBackground
|
||||
div(:class="'Pet_Egg_'+item.eggKey")
|
||||
div(v-else)
|
||||
h4.popover-content-title {{ item.name }}
|
||||
template(slot="itemBadge", scope="context")
|
||||
starBadge(:selected="item.key === currentPet", :show="item.isOwned()", @click="selectPet(item)")
|
||||
|
||||
div(v-else)
|
||||
h4.popover-content-title {{ context.item.name }}
|
||||
|
||||
template(slot="itemBadge", scope="context")
|
||||
starBadge(
|
||||
:selected="context.item.key === currentPet",
|
||||
:show="context.item.isOwned()",
|
||||
@click="selectPet(context.item)",
|
||||
)
|
||||
.btn.btn-flat.btn-show-more(@click="setShowMore(petGroup.key)", v-if='petGroup.key !== "specialPets"')
|
||||
| {{ showMore === petGroup.key ? $t('showLess') : $t('showMore') }}
|
||||
|
||||
h2
|
||||
| {{ $t('mounts') }}
|
||||
@@ -135,31 +130,30 @@
|
||||
)
|
||||
h4(v-if="viewOptions[mountGroup.key].animalCount != 0") {{ mountGroup.label }}
|
||||
|
||||
itemRows(
|
||||
:items="mounts(mountGroup, hideMissing, selectedSortBy, searchTextThrottled)",
|
||||
:itemWidth=94,
|
||||
:itemMargin=24,
|
||||
:type="mountGroup.key",
|
||||
)
|
||||
template(slot="item", scope="context")
|
||||
.pet-row.d-flex(v-for="(group, key, index) in mounts(mountGroup, hideMissing, selectedSortBy, searchTextThrottled)"
|
||||
v-if='index === 0 || showMore === mountGroup.key')
|
||||
.pet-group(v-for='item in group')
|
||||
mountItem(
|
||||
:item="context.item",
|
||||
:itemContentClass="context.item.isOwned() ? ('Mount_Icon_' + context.item.key) : 'PixelPaw GreyedOut'",
|
||||
:key="context.item.key",
|
||||
:item="item",
|
||||
:itemContentClass="item.isOwned() ? ('Mount_Icon_' + item.key) : 'PixelPaw GreyedOut'",
|
||||
:key="item.key",
|
||||
:popoverPosition="'top'",
|
||||
:emptyItem="!context.item.isOwned()",
|
||||
:emptyItem="!item.isOwned()",
|
||||
:showPopover="true",
|
||||
@click="selectMount(context.item)"
|
||||
@click="selectMount(item)"
|
||||
)
|
||||
span(slot="popoverContent")
|
||||
h4.popover-content-title {{ context.item.name }}
|
||||
h4.popover-content-title {{ item.name }}
|
||||
template(slot="itemBadge", scope="context")
|
||||
starBadge(
|
||||
:selected="context.item.key === currentMount",
|
||||
:show="context.item.isOwned()",
|
||||
@click="selectMount(context.item)",
|
||||
:selected="item.key === currentMount",
|
||||
:show="item.isOwned()",
|
||||
@click="selectMount(item)",
|
||||
)
|
||||
|
||||
.btn.btn-flat.btn-show-more(@click="setShowMore(mountGroup.key)", v-if='mountGroup.key !== "specialMounts"')
|
||||
| {{ showMore === mountGroup.key ? $t('showLess') : $t('showMore') }}
|
||||
|
||||
drawer(
|
||||
:title="$t('quickInventory')",
|
||||
:errorMessage="(!hasDrawerTabItems(selectedDrawerTab)) ? ((selectedDrawerTab === 0) ? $t('noFoodAvailable') : $t('noSaddlesAvailable')) : null"
|
||||
@@ -219,7 +213,6 @@
|
||||
:visible="hatchablePet != null",
|
||||
@change="resetHatchablePet($event)"
|
||||
)
|
||||
|
||||
div.content(v-if="hatchablePet")
|
||||
div.potionEggGroup
|
||||
div.potionEggBackground
|
||||
@@ -253,11 +246,9 @@
|
||||
div.food-icon(:class="'Pet_Food_'+currentDraggingFood.key")
|
||||
div.popover
|
||||
div.popover-content {{ $t('clickOnPetToFeed', {foodName: currentDraggingFood.text() }) }}
|
||||
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
@import '~client/assets/scss/colors.scss';
|
||||
@import '~client/assets/scss/modal.scss';
|
||||
|
||||
@@ -283,6 +274,20 @@
|
||||
top: -16px !important;
|
||||
}
|
||||
|
||||
.group {
|
||||
height: 130px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.pet-row {
|
||||
max-width: 100%;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.item {
|
||||
margin-right: .5em;
|
||||
}
|
||||
}
|
||||
|
||||
.hatchablePopover {
|
||||
width: 180px
|
||||
}
|
||||
@@ -500,6 +505,7 @@
|
||||
import _filter from 'lodash/filter';
|
||||
import _flatMap from 'lodash/flatMap';
|
||||
import _throttle from 'lodash/throttle';
|
||||
import groupBy from 'lodash/groupBy';
|
||||
|
||||
import Item from '../item';
|
||||
import ItemRows from 'client/components/ui/itemRows';
|
||||
@@ -585,6 +591,7 @@
|
||||
currentDraggingFood: null,
|
||||
|
||||
selectedDrawerTab: 0,
|
||||
showMore: '',
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
@@ -712,7 +719,13 @@
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
|
||||
setShowMore (key) {
|
||||
if (this.showMore === key) {
|
||||
this.showMore = '';
|
||||
return;
|
||||
}
|
||||
this.showMore = key;
|
||||
},
|
||||
getAnimalList (animalGroup, type) {
|
||||
let key = animalGroup.key;
|
||||
|
||||
@@ -833,11 +846,39 @@
|
||||
},
|
||||
|
||||
pets (animalGroup, hideMissing, sortBy, searchText) {
|
||||
return this.listAnimals(animalGroup, 'pet', hideMissing, sortBy, searchText);
|
||||
let pets = this.listAnimals(animalGroup, 'pet', hideMissing, sortBy, searchText);
|
||||
|
||||
// Don't group special
|
||||
if (animalGroup.key === 'specialPets') {
|
||||
return {none: pets};
|
||||
}
|
||||
|
||||
let groupKey = 'eggKey';
|
||||
if (sortBy === 'sortByColor') {
|
||||
groupKey = 'potionKey';
|
||||
} else if (sortBy === 'AZ') {
|
||||
groupKey = '';
|
||||
}
|
||||
|
||||
return groupBy(pets, groupKey);
|
||||
},
|
||||
|
||||
mounts (animalGroup, hideMissing, sortBy, searchText) {
|
||||
return this.listAnimals(animalGroup, 'mount', hideMissing, sortBy, searchText);
|
||||
let mounts = this.listAnimals(animalGroup, 'mount', hideMissing, sortBy, searchText);
|
||||
|
||||
// Don't group special
|
||||
if (animalGroup.key === 'specialMounts') {
|
||||
return {none: mounts};
|
||||
}
|
||||
|
||||
let groupKey = 'eggKey';
|
||||
if (sortBy === 'sortByColor') {
|
||||
groupKey = 'potionKey';
|
||||
} else if (sortBy === 'AZ') {
|
||||
groupKey = '';
|
||||
}
|
||||
|
||||
return groupBy(mounts, groupKey);
|
||||
},
|
||||
|
||||
getPetItemClass (pet) {
|
||||
|
||||
@@ -37,11 +37,11 @@ div.item-with-icon.item-notifications.dropdown
|
||||
@click='go("/user/profile")')
|
||||
span.glyphicon.glyphicon-plus-sign
|
||||
span {{ $t('haveUnallocated', {points: user.stats.points}) }}
|
||||
a.dropdown-item(v-for='(message, key) in user.newMessages', v-if='message.value')
|
||||
span(@click='navigateToGroup(key)')
|
||||
a.dropdown-item(v-for='message in userNewMessages')
|
||||
span(@click='navigateToGroup(message.key)')
|
||||
span.glyphicon.glyphicon-comment
|
||||
span {{message.name}}
|
||||
span.clear-button(@click='clearMessages(key)', :popover="$t('clear')",
|
||||
span.clear-button(@click='clearMessages(message.key)', :popover="$t('clear')",
|
||||
popover-placement='right', popover-trigger='mouseenter', popover-append-to-body='true') Clear
|
||||
a.dropdown-item(v-for='(notification, index) in user.groupNotifications', @click='viewGroupApprovalNotification(notification, index, true)')
|
||||
span(:class="groupApprovalNotificationIcon(notification)")
|
||||
@@ -156,6 +156,18 @@ export default {
|
||||
return {name: ''};
|
||||
// return this.user.party;
|
||||
},
|
||||
userNewMessages () {
|
||||
// @TODO: For some reason data becomes corrupted. We should fix this on the server
|
||||
let userNewMessages = [];
|
||||
for (let key in this.user.newMessages) {
|
||||
let message = this.user.newMessages[key];
|
||||
if (message && message.name && message.value) {
|
||||
message.key = key;
|
||||
userNewMessages.push(message);
|
||||
}
|
||||
}
|
||||
return userNewMessages;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
// @TODO: I hate this function, we can do better with a hashmap
|
||||
@@ -244,8 +256,8 @@ export default {
|
||||
count += this.user.stats.points > 0 ? 1 : 0;
|
||||
}
|
||||
|
||||
if (this.user.newMessages) {
|
||||
count += Object.keys(this.user.newMessages).length;
|
||||
if (this.userNewMessages) {
|
||||
count += Object.keys(this.userNewMessages).length;
|
||||
}
|
||||
|
||||
return count;
|
||||
|
||||
@@ -303,6 +303,10 @@ export default {
|
||||
this.$root.$emit('show::modal', 'avatar-modal');
|
||||
}
|
||||
|
||||
if (this.questCompleted) {
|
||||
this.$root.$emit('show::modal', 'quest-completed');
|
||||
}
|
||||
|
||||
// @TODO: This is a timeout to ensure dom is loaded
|
||||
window.setTimeout(() => {
|
||||
this.initTour();
|
||||
|
||||
@@ -36,9 +36,9 @@
|
||||
h6(v-once) {{ $t('class') + ': ' }}
|
||||
// @TODO: what is classText
|
||||
span(v-if='classText') {{ classText }}
|
||||
button.btn.btn-danger.btn-xs(@click='changeClass(null)', v-once) {{ $t('changeClass') }}
|
||||
small.cost 3
|
||||
span.Pet_Currency_Gem1x.inline-gems
|
||||
button.btn.btn-danger.btn-xs(@click='changeClassForUser(true)', v-once) {{ $t('changeClass') }}
|
||||
small.cost 3 {{ $t('gems') }}
|
||||
// @TODO add icon span.Pet_Currency_Gem1x.inline-gems
|
||||
hr
|
||||
|
||||
div
|
||||
@@ -82,7 +82,7 @@
|
||||
|
||||
button.btn.btn-primary(@click='showBailey()', popover-trigger='mouseenter', popover-placement='right', :popover="$t('showBaileyPop')") {{ $t('showBailey') }}
|
||||
button.btn.btn-primary(@click='openRestoreModal()', popover-trigger='mouseenter', popover-placement='right', :popover="$t('fixValPop')") {{ $t('fixVal') }}
|
||||
button.btn.btn-primary(v-if='user.preferences.disableClasses == true', @click='changeClass({})',
|
||||
button.btn.btn-primary(v-if='user.preferences.disableClasses == true', @click='changeClassForUser(false)',
|
||||
popover-trigger='mouseenter', popover-placement='right', :popover="$t('enableClassPop')") {{ $t('enableClass') }}
|
||||
|
||||
hr
|
||||
@@ -378,8 +378,8 @@ export default {
|
||||
|
||||
this.$router.go('/tasks');
|
||||
},
|
||||
async changeClass () {
|
||||
if (!confirm('Are you sure you want to change your class for 3 gems?')) return;
|
||||
async changeClassForUser (confirmationNeeded) {
|
||||
if (confirmationNeeded && !confirm(this.$t('changeClassConfirmCost'))) return;
|
||||
try {
|
||||
changeClass(this.user);
|
||||
await axios.post('/api/v3/user/change-class');
|
||||
|
||||
90
website/client/components/static/newStuff.vue
Normal file
90
website/client/components/static/newStuff.vue
Normal file
@@ -0,0 +1,90 @@
|
||||
<template lang='pug'>
|
||||
div
|
||||
.media
|
||||
.align-self-center.right-margin(:class='baileyClass')
|
||||
.media-body
|
||||
h1.align-self-center(v-markdown='$t("newStuff")')
|
||||
h2 9/28/2017 - HABITICA'S WEBSITE LEVELS UP!
|
||||
hr
|
||||
p Welcome to the new Habitica! We're so excited to share it with you at last. This project, which has been a labor of love since last December, is the single biggest update that Habitica has ever released (with over 150 pages of designs, an entire rewrite of all of our front-end code, countless rounds of testing and iteration, and many, many meetings). Just refresh your page to load the new website!
|
||||
.promo_login_screen.center-block
|
||||
p(v-markdown="'You can find a full list of changes [here](http://habitica.wikia.com/wiki/Habitica_Redesign_Fact_Sheet), as well as explanations for why we made each, but here are a few quick tips to help you get oriented:'")
|
||||
.grassy-meadow-backdrop
|
||||
.daniel_front
|
||||
ul
|
||||
li There's a ton of new art around the site! Peek at the NPCs and Guild chats to admire some of the changes.
|
||||
li Click directly on your tasks to bring up the edit modal!
|
||||
li The navigation bar contains several changes to be more intuitive for new users, so we recommend taking some time to open the drop-down menus and familiarize yourself with the new locations. Notably, the User menu has moved to an icon in the upper-right corner.
|
||||
li You can now pin any purchasable item in the game to your Rewards. You can pin Backgrounds, too! Just hover over the shop icon and click the pin. When you head back to the tasks page, you'll see it in your Rewards column!
|
||||
li There are lots of new filtering options, especially for Guilds and Challenges!
|
||||
li There are visual upgrades for every aspect of the site, from the front page to the Seasonal Shop. We hope that you like them!
|
||||
li Some of these upgrades have made their way to our <a href='https://itunes.apple.com/us/app/habitica/id994882113?ls=1&mt=8' target='_blank'>iOS</a> and <a href='https://play.google.com/store/apps/details?id=com.habitrpg.android.habitica' target='_blank'>Android</a> apps! Be sure to download the latest updates for the best performance.
|
||||
.seasonal-shop-backdrop
|
||||
.sorceress_front
|
||||
p Have general questions about how the new site works? Come ask in the <a href='https://habitica.com/groups/guild/5481ccf3-5d2d-48a9-a871-70a7380cee5a'>Habitica Help Guild</a>, and we'll be glad to assist! Likewise, if you encounter a persistent bug that isn't fixed by refreshing your page, you can report it in the <a href='https://habitica.com/groups/guild/a29da26b-37de-4a71-b0c6-48e72a900dac'>Report a Bug Guild</a> and we will investigate as soon as possible.
|
||||
p If you have thoughts about the new design, we look forward to hearing them. On <strong>October 12th</strong> we will be opening a Trello card for public comments on the redesign. This delay will give us time to focus our attention on answering general questions and fixing any bugs that might arise. For this reason, we ask that you hold back on sharing your feedback about the new designs until that Trello card is live and linked in a Bailey announcement. Thanks for understanding!
|
||||
.promo_veteran_pets_2017.center-block
|
||||
p This is a major time of change for Habitica, so to thank you for your patience, we've given everyone a Veteran Pet! Newer users have received a Veteran Wolf, and older users have received (depending on which pets they already had) a Veteran Tiger, a Veteran Lion, or a Veteran Bear. Head to the new <a href='https://habitica.com/inventory/stable'>Stable</a> page and filter to the Special Pets section to see the latest addition to your menagerie!"')
|
||||
p We are so excited to continue to build Habitica with you. Now go check it out!
|
||||
p Thank you for playing, and good luck with your tasks <3
|
||||
.small by Apollo, piyorii, TheHollidayInn, Paglias, Negue, Sabe, Alys, viirus, Lemoness, redphoenix, beffymaroo, and all our awesome testers!
|
||||
br
|
||||
</template>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
@import '~client/assets/scss/static.scss';
|
||||
|
||||
.center-block {
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.grassy-meadow-backdrop {
|
||||
background-image: url('~assets/images/npc/fall/tavern_background.png');
|
||||
background-repeat: repeat-x;
|
||||
width: 100%;
|
||||
height: 246px;
|
||||
}
|
||||
|
||||
.daniel_front {
|
||||
background-image: url('~assets/images/npc/fall/tavern_npc.png');
|
||||
height: 246px;
|
||||
width: 471px;
|
||||
background-repeat: no-repeat;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.seasonal-shop-backdrop {
|
||||
background: url('~assets/images/npc/fall/seasonal_shop_opened_background.png');
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
|
||||
.sorceress_front {
|
||||
background-image: url('~assets/images/npc/fall/seasonal_shop_opened_npc.png');
|
||||
height: 246px;
|
||||
width: 471px;
|
||||
background-repeat: no-repeat;
|
||||
margin: 0 auto;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import markdown from 'client/directives/markdown';
|
||||
|
||||
export default {
|
||||
data () {
|
||||
let worldDmg = {
|
||||
bailey: false,
|
||||
};
|
||||
|
||||
return {
|
||||
baileyClass: {
|
||||
'npc_bailey_broken': worldDmg.bailey, // eslint-disable-line
|
||||
'npc_bailey': !worldDmg.bailey, // eslint-disable-line
|
||||
},
|
||||
};
|
||||
},
|
||||
directives: {
|
||||
markdown,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -1,14 +1,14 @@
|
||||
<template lang='pug'>
|
||||
div
|
||||
static-header(:class='{"home-header": $route.name === "home"}')
|
||||
static-header(v-if='showContentWrap', :class='{"home-header": $route.name === "home"}')
|
||||
|
||||
.static-wrapper
|
||||
router-view
|
||||
|
||||
#purple-footer
|
||||
#purple-footer(v-if='showContentWrap')
|
||||
app-footer
|
||||
|
||||
#bottom-wrap.purple-4
|
||||
#bottom-wrap.purple-4(v-if='showContentWrap')
|
||||
#bottom-background
|
||||
.seamless_mountains_demo_repeat
|
||||
.midground_foreground_extended2
|
||||
@@ -149,5 +149,10 @@
|
||||
AppFooter,
|
||||
StaticHeader,
|
||||
},
|
||||
computed: {
|
||||
showContentWrap () {
|
||||
return this.$route.name !== 'news';
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
@import '~client/assets/scss/colors.scss';
|
||||
|
||||
.tasks-column {
|
||||
height: 556px;
|
||||
min-height: 556px;
|
||||
}
|
||||
|
||||
.task-wrapper + .reward-items {
|
||||
@@ -60,11 +60,9 @@
|
||||
border-radius: 4px;
|
||||
background: $gray-600;
|
||||
padding: 8px;
|
||||
// not sure why but this is necessary or the last task will overflow the container
|
||||
padding-bottom: 0.1px;
|
||||
position: relative;
|
||||
height: calc(100% - 64px);
|
||||
overflow: auto;
|
||||
position: relative; // needed for the .bottom-gradient to be position: absolute
|
||||
height: calc(100% - 56px);
|
||||
padding-bottom: 30px;
|
||||
}
|
||||
|
||||
.bottom-gradient {
|
||||
@@ -161,6 +159,7 @@
|
||||
|
||||
<script>
|
||||
import Task from './task';
|
||||
import sortBy from 'lodash/sortBy';
|
||||
import { mapState, mapActions } from 'client/libs/store';
|
||||
import { shouldDo } from 'common/script/cron';
|
||||
import inAppRewards from 'common/script/libs/inAppRewards';
|
||||
@@ -205,7 +204,7 @@ export default {
|
||||
label: 'todos',
|
||||
filters: [
|
||||
{label: 'remaining', filter: t => !t.completed, default: true}, // active
|
||||
{label: 'scheduled', filter: t => !t.completed && t.date},
|
||||
{label: 'scheduled', filter: t => !t.completed && t.date, sort: t => t.date},
|
||||
{label: 'complete2', filter: t => t.completed},
|
||||
],
|
||||
},
|
||||
@@ -318,6 +317,10 @@ export default {
|
||||
this.loadCompletedTodos();
|
||||
}
|
||||
this.activeFilters[type] = filter;
|
||||
|
||||
if (filter.sort) {
|
||||
this.tasks[`${type}s`] = sortBy(this.tasks[`${type}s`], filter.sort);
|
||||
}
|
||||
},
|
||||
setColumnBackgroundVisibility () {
|
||||
this.$nextTick(() => {
|
||||
|
||||
@@ -72,7 +72,7 @@
|
||||
span.text {{$t(type)}}
|
||||
|
||||
.row.tasks-columns
|
||||
task-column.col-3(
|
||||
task-column.col-lg-3.col-md-6(
|
||||
v-for="column in columns",
|
||||
:type="column", :key="column",
|
||||
:isUser="true", :searchText="searchTextThrottled",
|
||||
@@ -114,25 +114,25 @@
|
||||
}
|
||||
|
||||
.dropdown-icon-item {
|
||||
.icon_habit {
|
||||
width: 30px;
|
||||
height: 20px;
|
||||
}
|
||||
.icon_habit {
|
||||
width: 30px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.icon_daily {
|
||||
width: 24px;
|
||||
height: 20px;
|
||||
}
|
||||
.icon_daily {
|
||||
width: 24px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.icon_todo {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
.icon_todo {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.icon_reward {
|
||||
width: 26px;
|
||||
height: 20px;
|
||||
}
|
||||
.icon_reward {
|
||||
width: 26px;
|
||||
height: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-icon-item:hover .svg-icon, .dropdown-item.active .svg-icon {
|
||||
|
||||
@@ -5,7 +5,7 @@ div
|
||||
.profile-actions
|
||||
button.btn.btn-secondary(@click='sendMessage()')
|
||||
.svg-icon.message-icon(v-html="icons.message")
|
||||
button.btn.btn-secondary(v-if='userLoggedIn.inbox.blocks.indexOf(user._id) === -1', :tooltip="$t('unblock')",
|
||||
button.btn.btn-secondary(v-if='user._id !== this.userLoggedIn._id && userLoggedIn.inbox.blocks.indexOf(user._id) === -1', :tooltip="$t('unblock')",
|
||||
@click="blockUser()", tooltip-placement='right')
|
||||
span.glyphicon.glyphicon-plus
|
||||
| {{$t('block')}}
|
||||
@@ -795,7 +795,7 @@ export default {
|
||||
return display;
|
||||
},
|
||||
allocate (stat) {
|
||||
allocate(this.user, stat);
|
||||
allocate(this.user, {query: { stat }});
|
||||
axios.post(`/api/v3/user/allocate?stat=${stat}`);
|
||||
},
|
||||
allocateNow () {
|
||||
|
||||
@@ -19,6 +19,7 @@ const FeaturesPage = () => import(/* webpackChunkName: "static" */'./components/
|
||||
const HomePage = () => import(/* webpackChunkName: "static" */'./components/static/home');
|
||||
const GroupPlansPage = () => import(/* webpackChunkName: "static" */'./components/static/groupPlans');
|
||||
const MerchPage = () => import(/* webpackChunkName: "static" */'./components/static/merch');
|
||||
const NewsPage = () => import(/* webpackChunkName: "static" */'./components/static/newStuff');
|
||||
const OverviewPage = () => import(/* webpackChunkName: "static" */'./components/static/overview');
|
||||
const PressKitPage = () => import(/* webpackChunkName: "static" */'./components/static/pressKit');
|
||||
const PrivacyPage = () => import(/* webpackChunkName: "static" */'./components/static/privacy');
|
||||
@@ -250,6 +251,7 @@ const router = new VueRouter({
|
||||
{ name: 'groupPlans', path: 'group-plans', component: GroupPlansPage, meta: {requiresLogin: false}},
|
||||
{ name: 'home', path: 'home', component: HomePage, meta: {requiresLogin: false} },
|
||||
{ name: 'merch', path: 'merch', component: MerchPage, meta: {requiresLogin: false}},
|
||||
{ name: 'news', path: 'new-stuff', component: NewsPage, meta: {requiresLogin: false}},
|
||||
{ name: 'overview', path: 'overview', component: OverviewPage, meta: {requiresLogin: false}},
|
||||
{ name: 'plans', path: 'plans', component: GroupPlansPage, meta: {requiresLogin: false}},
|
||||
{ name: 'pressKit', path: 'press-kit', component: PressKitPage, meta: {requiresLogin: false}},
|
||||
|
||||
@@ -125,6 +125,7 @@
|
||||
"mystery": "Mystery",
|
||||
"changeClass": "Change Class, Refund Attribute Points",
|
||||
"lvl10ChangeClass": "To change class you must be at least level 10.",
|
||||
"changeClassConfirmCost": "Are you sure you want to change your class for 3 Gems?",
|
||||
"invalidClass":"Invalid class. Please specify 'warrior', 'rogue', 'wizard', or 'healer'.",
|
||||
"levelPopover": "Each level earns you one point to assign to an attribute of your choice. You can do so manually, or let the game decide for you using one of the Automatic Allocation options.",
|
||||
"unallocated": "Unallocated Attribute Points",
|
||||
@@ -158,7 +159,7 @@
|
||||
"respawn": "Respawn!",
|
||||
"youDied": "You Died!",
|
||||
"dieText": "You've lost a Level, all your Gold, and a random piece of Equipment. Arise, Habiteer, and try again! Curb those negative Habits, be vigilant in completion of Dailies, and hold death at arm's length with a Health Potion if you falter!",
|
||||
"sureReset": "Are you sure? This will reset your character's class and allocated points (you'll get them all back to re-allocate), and costs 3 gems.",
|
||||
"sureReset": "Are you sure? This will reset your character's class and allocated points (you'll get them all back to re-allocate), and costs 3 Gems.",
|
||||
"purchaseFor": "Purchase for <%= cost %> Gems?",
|
||||
"notEnoughMana": "Not enough mana.",
|
||||
"invalidTarget": "You can't cast a skill on that.",
|
||||
|
||||
@@ -388,5 +388,7 @@
|
||||
"selectPartyMember": "Select a Party Member",
|
||||
"areYouSureDeleteMessage": "Are you sure you want to delete this message?",
|
||||
"reverseChat": "Reverse Chat",
|
||||
"invites": "Invites"
|
||||
"invites": "Invites",
|
||||
"details": "Details",
|
||||
"participantDesc": "Once all members have either accepted or declined, the Quest begins. Only those that clicked 'accept' will be able to participate in the Quest and receive the drops."
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ api.verifyGemPurchase = async function verifyGemPurchase (user, receipt, signatu
|
||||
let isValidated = iap.isValidated(googleRes);
|
||||
if (!isValidated) throw new NotAuthorized(this.constants.RESPONSE_INVALID_RECEIPT);
|
||||
|
||||
let receiptObj = JSON.parse(testObj.data); // passed as a string
|
||||
let receiptObj = typeof testObj.data === 'string' ? JSON.parse(testObj.data) : testObj.data; // passed as a string
|
||||
let token = receiptObj.token || receiptObj.purchaseToken;
|
||||
|
||||
let existingReceipt = await IapPurchaseReceipt.findOne({
|
||||
@@ -106,7 +106,7 @@ api.subscribe = async function subscribe (sku, user, receipt, signature, headers
|
||||
signature,
|
||||
};
|
||||
|
||||
let receiptObj = JSON.parse(receipt); // passed as a string
|
||||
let receiptObj = typeof receipt === 'string' ? JSON.parse(receipt) : receipt; // passed as a string
|
||||
let token = receiptObj.token || receiptObj.purchaseToken;
|
||||
|
||||
let existingUser = await User.findOne({
|
||||
|
||||
@@ -11,9 +11,19 @@ const BUILD_DIR = path.join(__dirname, '/../../build');
|
||||
|
||||
module.exports = function staticMiddleware (expressApp) {
|
||||
// Expose static files for new client
|
||||
// if (IS_PROD && IS_NEW_CLIENT_ENABLED) {
|
||||
expressApp.use('/static/js', express.static(`${PUBLIC_DIR}/../../dist-client/static/js`, { maxAge: MAX_AGE }));
|
||||
expressApp.use('/static/css', express.static(`${PUBLIC_DIR}/../../dist-client/static/css`, { maxAge: MAX_AGE }));
|
||||
expressApp.use('/static/img', express.static(`${PUBLIC_DIR}/../../dist-client/static/img`, { maxAge: MAX_AGE }));
|
||||
|
||||
// @TODO img/js/css under /static have their names hashed after every change so they can be cached
|
||||
// Not files in /audio and /sprites, that's why we don't cache them.
|
||||
// Hash their file names and cache the entire /static folder
|
||||
expressApp.use('/static', express.static(`${PUBLIC_DIR}/../../dist-client/static`));
|
||||
// }
|
||||
|
||||
|
||||
// @TODO all these paths are not used by the new client, remove them
|
||||
// But first check that they're not used anywhere else
|
||||
// In particular the images used by emails
|
||||
|
||||
// TODO move all static files to a single location (one for public and one for build)
|
||||
expressApp.use(express.static(BUILD_DIR, { maxAge: MAX_AGE }));
|
||||
|
||||
Reference in New Issue
Block a user