Merge develop into release (#9154)

* Client: fix Apidoc and move email files (#9139)

* fix apidoc

* move emails files

* quest leader can start/end quest; admins can edit challenges/guilds; reverse chat works; remove static/videos link; etc (#9140)

* enable link to markdown info on group and challenge edit screen

* allow admin (moderators and staff) to edit challenges

* allow admin (moderators and staff) to edit guilds

Also add some unrelated TODO comments.

* allow any party member (not just leader) to start quest from party page

* allow quest owner to cancel, begin, abort quest

Previously only the party leader could see those buttons. The leader still can.

This also hides those buttons from all other party members.

* enable reverse chat in guilds and party

* remove outdated videos from press kit

* adjust various wordings

* Be consistent with capitalization of Check-In. (#9118)

* limit for inlined svg images and make home leaner by not bundling it with the rest of static pages

* sep 27 fixes (#9088)

* fix item paddings / drawer width

* expand the width of item-rows by the margin of an item

* fix hatchedPet-dialog

* fix hatching-modal

* remove min-height

* Oct 3 fixes (#9148)

* Only show level after yesterdailies modal

* Fixed zindex

* Added spcial spells to rewards column

* Added single click buy for health and armoire

* Prevented task scoring when casting a spell

* Renamed generic purchase method

* Updated nav for small screen

* Hide checklist while casting

* fix some text describing menu items (#9145)
This commit is contained in:
Keith Holliday
2017-10-03 21:15:00 -05:00
committed by GitHub
parent 122cc510d3
commit b0a980d56e
39 changed files with 341 additions and 260 deletions

View File

@@ -47,10 +47,5 @@ gulp.task('build:prod', [
'build:server', 'build:server',
'prepare:staticNewStuff', 'prepare:staticNewStuff',
'build:client', 'build:client',
], (done) => {
runSequence(
'grunt-build:prod',
'apidoc', 'apidoc',
done ]);
);
});

View File

@@ -105,7 +105,12 @@ const baseConfig = {
{ {
test: /\.svg$/, test: /\.svg$/,
use: [ use: [
{ loader: 'svg-url-loader' }, {
loader: 'svg-url-loader',
options: {
limit: 10000,
},
},
{ loader: 'svgo-loader' }, { loader: 'svgo-loader' },
], ],
include: [path.resolve(projectRoot, 'website/client/assets/svg/for-css')], include: [path.resolve(projectRoot, 'website/client/assets/svg/for-css')],

View File

@@ -2,7 +2,7 @@
.items > div { .items > div {
display: inline-block; display: inline-block;
margin-right: 23px; margin-right: 24px;
} }
.items > div:last-of-type { .items > div:last-of-type {

View File

@@ -3,9 +3,10 @@ div
inbox-modal inbox-modal
creator-intro creator-intro
profile profile
nav.navbar.navbar-inverse.fixed-top.navbar-toggleable-lg nav.navbar.navbar-inverse.fixed-top.navbar-toggleable-md
.navbar-header .navbar-header
.logo.svg-icon(v-html="icons.logo") .logo.svg-icon.hidden-lg-down(v-html="icons.logo")
.svg-icon.gryphon.hidden-xl-up
b-collapse#nav_collapse.collapse.navbar-collapse(is-nav) b-collapse#nav_collapse.collapse.navbar-collapse(is-nav)
ul.navbar-nav.mr-auto ul.navbar-nav.mr-auto
router-link.nav-item(tag="li", :to="{name: 'tasks'}", exact) router-link.nav-item(tag="li", :to="{name: 'tasks'}", exact)
@@ -93,58 +94,34 @@ div
@import '~client/assets/scss/colors.scss'; @import '~client/assets/scss/colors.scss';
@import '~client/assets/scss/utils.scss'; @import '~client/assets/scss/utils.scss';
/* Less than Desktops and laptops ----------- */
@media only screen and (max-width : 1224px) {
#nav_collapse {
background: $purple-100;
margin-top: 1em;
margin-left: 70%;
padding-bottom: 1em;
a {
padding: .5em !important;
}
}
.dropdown-menu {
position: absolute !important;
left: -10em;
top: -.5em;
}
}
@media only screen and (max-width : 1224px) and (min-width: 1200px) {
#nav_collapse {
margin-top: 37em !important;
a {
width: 100%;
}
}
.navbar-collapse.collapse {
display: none !important;
}
.navbar-collapse.collapse.show {
display: block !important;
}
.navbar-toggler, .navbar-nav {
display: block;
}
.navbar-toggleable-lg .navbar-collapse {
display: block;
}
}
@media only screen and (max-width: 1280px) { @media only screen and (max-width: 1280px) {
.nav-link { .nav-link {
padding: .8em 1em !important; padding: .8em 1em !important;
} }
} }
@media only screen and (max-width: 1200px) {
.navbar-header {
margin-right: 0px;
}
.gryphon {
background-image: url('~assets/images/melior@3x.png');
width: 30px;
height: 30px;
background-size: cover;
color: $white;
margin: 0 auto;
}
}
@media only screen and (max-width: 990px) {
#nav_collapse {
margin-top: 1.3em;
background-color: $purple-200;
}
}
nav.navbar { nav.navbar {
background: $purple-100 url(~assets/svg/for-css/bits.svg) right no-repeat; background: $purple-100 url(~assets/svg/for-css/bits.svg) right no-repeat;
padding-left: 25px; padding-left: 25px;

View File

@@ -35,7 +35,7 @@
b-dropdown.create-dropdown(text="Select a Participant") b-dropdown.create-dropdown(text="Select a Participant")
b-dropdown-item(v-for="member in members", :key="member._id", @click="openMemberProgressModal(member._id)") b-dropdown-item(v-for="member in members", :key="member._id", @click="openMemberProgressModal(member._id)")
| {{ member.profile.name }} | {{ member.profile.name }}
span(v-if='isLeader') span(v-if='isLeader || isAdmin')
b-dropdown.create-dropdown(:text="$t('addTaskToChallenge')", :variant="'success'") b-dropdown.create-dropdown(:text="$t('addTaskToChallenge')", :variant="'success'")
b-dropdown-item(v-for="type in columns", :key="type", @click="createTask(type)") b-dropdown-item(v-for="type in columns", :key="type", @click="createTask(type)")
| {{$t(type)}} | {{$t(type)}}
@@ -64,13 +64,13 @@
button.btn.btn-success(v-once, @click='joinChallenge()') {{$t('joinChallenge')}} button.btn.btn-success(v-once, @click='joinChallenge()') {{$t('joinChallenge')}}
div(v-if='isMember') div(v-if='isMember')
button.btn.btn-danger(v-once, @click='leaveChallenge()') {{$t('leaveChallenge')}} button.btn.btn-danger(v-once, @click='leaveChallenge()') {{$t('leaveChallenge')}}
div(v-if='isLeader') div(v-if='isLeader || isAdmin')
button.btn.btn-secondary(v-once, @click='edit()') {{$t('editChallenge')}} button.btn.btn-secondary(v-once, @click='edit()') {{$t('editChallenge')}}
div(v-if='isLeader') div(v-if='isLeader || isAdmin')
button.btn.btn-danger(v-once, @click='closeChallenge()') {{$t('endChallenge')}} button.btn.btn-danger(v-once, @click='closeChallenge()') {{$t('endChallenge')}}
div(v-if='isLeader || isAdmin') div(v-if='isLeader || isAdmin')
button.btn.btn-secondary(v-once, @click='exportChallengeCsv()') {{$t('exportChallengeCsv')}} button.btn.btn-secondary(v-once, @click='exportChallengeCsv()') {{$t('exportChallengeCsv')}}
div(v-if='isLeader') div(v-if='isLeader || isAdmin')
button.btn.btn-secondary(v-once, @click='cloneChallenge()') {{$t('clone')}} button.btn.btn-secondary(v-once, @click='cloneChallenge()') {{$t('clone')}}
.description-section .description-section
h2 {{$t('challengeSummary')}} h2 {{$t('challengeSummary')}}

View File

@@ -17,7 +17,7 @@
.form-group .form-group
label label
strong(v-once) {{$t('challengeDescription')}} * strong(v-once) {{$t('challengeDescription')}} *
a.float-right {{ $t('markdownFormattingHelp') }} a.float-right(v-markdown='$t("markdownFormattingHelp")')
textarea.description-textarea.form-control(:placeholder="$t('challengeDescriptionPlaceholder')", v-model="workingChallenge.description") textarea.description-textarea.form-control(:placeholder="$t('challengeDescriptionPlaceholder')", v-model="workingChallenge.description")
.form-group(v-if='creating') .form-group(v-if='creating')
label label
@@ -136,6 +136,8 @@ import bDropdown from 'bootstrap-vue/lib/components/dropdown';
import bDropdownItem from 'bootstrap-vue/lib/components/dropdown-item'; import bDropdownItem from 'bootstrap-vue/lib/components/dropdown-item';
import bFormInput from 'bootstrap-vue/lib/components/form-input'; import bFormInput from 'bootstrap-vue/lib/components/form-input';
import markdownDirective from 'client/directives/markdown';
import { TAVERN_ID, MIN_SHORTNAME_SIZE_FOR_CHALLENGES, MAX_SUMMARY_SIZE_FOR_CHALLENGES } from '../../../common/script/constants'; import { TAVERN_ID, MIN_SHORTNAME_SIZE_FOR_CHALLENGES, MAX_SUMMARY_SIZE_FOR_CHALLENGES } from '../../../common/script/constants';
import { mapState } from 'client/libs/store'; import { mapState } from 'client/libs/store';
@@ -147,6 +149,9 @@ export default {
bDropdownItem, bDropdownItem,
bFormInput, bFormInput,
}, },
directives: {
markdown: markdownDirective,
},
data () { data () {
let categoryOptions = [ let categoryOptions = [
{ {

View File

@@ -56,15 +56,16 @@
button.btn.btn-success(class='btn-success', v-if='isLeader && !group.purchased.active', @click='upgradeGroup()') button.btn.btn-success(class='btn-success', v-if='isLeader && !group.purchased.active', @click='upgradeGroup()')
| {{ $t('upgrade') }} | {{ $t('upgrade') }}
.button-container .button-container
button.btn.btn-primary(b-btn, @click="updateGuild", v-once, v-if='isLeader') {{ $t('edit') }} button.btn.btn-primary(b-btn, @click="updateGuild", v-once, v-if='isLeader || isAdmin') {{ $t('edit') }}
.button-container .button-container
button.btn.btn-success(class='btn-success', v-if='!isMember', @click='join()') {{ $t('join') }} button.btn.btn-success(class='btn-success', v-if='!isMember', @click='join()') {{ $t('join') }}
.button-container .button-container
button.btn.btn-primary(v-once, @click='showInviteModal()') {{$t('invite')}} button.btn.btn-primary(v-once, @click='showInviteModal()') {{$t('invite')}}
// @TODO: hide the invitation button if there's an active group plan and the player is not the leader
.button-container .button-container
// @TODO: V2 button.btn.btn-primary(v-once, v-if='!isLeader') {{$t('messageGuildLeader')}} // @TODO: V2 button.btn.btn-primary(v-once, v-if='!isLeader') {{$t('messageGuildLeader')}} // Suggest making the button visible to the leader too - useful for them to test how the feature works or to send a note to themself. -- Alys
.button-container .button-container
// @TODO: V2 button.btn.btn-primary(v-once, v-if='isMember && !isParty') {{$t('donateGems')}} // @TODO: V2 button.btn.btn-primary(v-once, v-if='isMember && !isParty') {{$t('donateGems')}} // Suggest removing the isMember restriction - it's okay if non-members donate to a public guild. Also probably allow it for parties if parties can buy imagery. -- Alys
.section-header(v-if='isParty') .section-header(v-if='isParty')
.row .row
@@ -81,7 +82,7 @@
.svg-icon(v-html="icons.questIcon") .svg-icon(v-html="icons.questIcon")
h4(v-once) {{ $t('youAreNotOnQuest') }} h4(v-once) {{ $t('youAreNotOnQuest') }}
p(v-once) {{ $t('questDescription') }} p(v-once) {{ $t('questDescription') }}
button.btn.btn-secondary(v-once, @click="openStartQuestModal()", v-if='isLeader') {{ $t('startAQuest') }} button.btn.btn-secondary(v-once, @click="openStartQuestModal()") {{ $t('startAQuest') }}
.row.quest-active-section(v-if='isParty && onPendingQuest && !onActiveQuest') .row.quest-active-section(v-if='isParty && onPendingQuest && !onActiveQuest')
.col-2 .col-2
.quest(:class='`inventory_quest_scroll_${questData.key}`') .quest(:class='`inventory_quest_scroll_${questData.key}`')
@@ -129,7 +130,7 @@
.col-6 .col-6
span.float-left span.float-left
| Rage {{questData.boss.rage.value}} | Rage {{questData.boss.rage.value}}
button.btn.btn-secondary(v-once, @click="questAbort()", v-if='isLeader') {{ $t('abort') }} button.btn.btn-secondary(v-once, @click="questAbort()", v-if='canEditQuest') {{ $t('abort') }}
.section-header(v-if='!isParty') .section-header(v-if='!isParty')
.row .row
@@ -562,12 +563,17 @@ export default {
isLeader () { isLeader () {
return this.user._id === this.group.leader._id; return this.user._id === this.group.leader._id;
}, },
isAdmin () {
return Boolean(this.user.contributor.admin);
},
isMember () { isMember () {
return this.isMemberOfGroup(this.user, this.group); return this.isMemberOfGroup(this.user, this.group);
}, },
canEditQuest () { canEditQuest () {
let isQuestLeader = this.group.quest && this.group.quest.leader === this.user._id; if (!this.group.quest) return false;
return isQuestLeader; let isQuestLeader = this.group.quest.leader === this.user._id;
let isPartyLeader = this.group.leader._id === this.user._id;
return isQuestLeader || isPartyLeader;
}, },
isMemberOfPendingQuest () { isMemberOfPendingQuest () {
let userid = this.user._id; let userid = this.user._id;
@@ -710,6 +716,9 @@ export default {
fetchRecentMessages () { fetchRecentMessages () {
this.fetchGuild(); this.fetchGuild();
}, },
reverseChat () {
this.group.chat.reverse();
},
updateGuild () { updateGuild () {
this.$store.state.editingGroup = this.group; this.$store.state.editingGroup = this.group;
this.$root.$emit('show::modal', 'guild-form'); this.$root.$emit('show::modal', 'guild-form');

View File

@@ -63,7 +63,7 @@
.form-group .form-group
label label
strong(v-once) {{$t('groupDescription')}} * strong(v-once) {{$t('groupDescription')}} *
a.float-right {{ $t('markdownFormattingHelp') }} a.float-right(v-markdown='$t("markdownFormattingHelp")')
textarea.form-control.description-textarea(type="text", textarea, :placeholder="isParty ? $t('partyDescriptionPlaceholder') : $t('guildDescriptionPlaceholder')", v-model="workingGroup.description") textarea.form-control.description-textarea(type="text", textarea, :placeholder="isParty ? $t('partyDescriptionPlaceholder') : $t('guildDescriptionPlaceholder')", v-model="workingGroup.description")
.form-group(v-if='creatingParty && !workingGroup.id') .form-group(v-if='creatingParty && !workingGroup.id')
@@ -177,6 +177,7 @@ import bTooltip from 'bootstrap-vue/lib/components/tooltip';
import { mapState } from 'client/libs/store'; import { mapState } from 'client/libs/store';
import toggleSwitch from 'client/components/ui/toggleSwitch'; import toggleSwitch from 'client/components/ui/toggleSwitch';
import markdownDirective from 'client/directives/markdown';
import gemIcon from 'assets/svg/gem.svg'; import gemIcon from 'assets/svg/gem.svg';
import informationIcon from 'assets/svg/information.svg'; import informationIcon from 'assets/svg/information.svg';
@@ -198,6 +199,9 @@ export default {
bTooltip, bTooltip,
toggleSwitch, toggleSwitch,
}, },
directives: {
markdown: markdownDirective,
},
data () { data () {
let data = { let data = {
workingGroup: { workingGroup: {

View File

@@ -13,9 +13,10 @@
.pending.float-right(v-if='member.accepted === null') {{ $t('pending') }} .pending.float-right(v-if='member.accepted === null') {{ $t('pending') }}
div(v-if='questData') div(v-if='questData')
questDialogContent(:item="questData") questDialogContent(:item="questData")
div.text-center.actions div.text-center.actions(v-if='canEditQuest')
div div
button.btn.btn-secondary(v-once, @click="questForceStart()") {{ $t('begin') }} button.btn.btn-secondary(v-once, @click="questForceStart()") {{ $t('begin') }}
// @TODO don't allow the party leader to start the quest until the leader has accepted or rejected the invitation (users get confused and think "begin" means "join quest")
div div
.cancel(v-once, @click="questCancel()") {{ $t('cancel') }} .cancel(v-once, @click="questCancel()") {{ $t('cancel') }}
.side-panel(v-if='questData') .side-panel(v-if='questData')
@@ -189,6 +190,12 @@ export default {
}; };
}); });
}, },
canEditQuest () {
if (!this.group.quest) return false;
let isQuestLeader = this.group.quest.leader === this.user._id;
let isPartyLeader = this.group.leader._id === this.user._id;
return isQuestLeader || isPartyLeader;
},
}, },
methods: { methods: {
async questForceStart () { async questForceStart () {

View File

@@ -127,10 +127,7 @@
:count="context.item.quantity" :count="context.item.quantity"
) )
hatchedPetDialog( hatchedPetDialog()
:pet="hatchedPet",
@closed="closeHatchedPetDialog()"
)
div.hatchingPotionInfo(ref="draggingPotionInfo") div.hatchingPotionInfo(ref="draggingPotionInfo")
div(v-if="currentDraggingPotion != null") div(v-if="currentDraggingPotion != null")
@@ -251,7 +248,6 @@ export default {
currentDraggingPotion: null, currentDraggingPotion: null,
potionClickMode: false, potionClickMode: false,
hatchedPet: null,
cardOptions: { cardOptions: {
cardType: '', cardType: '',
messageOptions: 0, messageOptions: 0,
@@ -345,7 +341,7 @@ export default {
}, },
hatchPet (potion, egg) { hatchPet (potion, egg) {
this.$store.dispatch('common:hatch', {egg: egg.key, hatchingPotion: potion.key}); this.$store.dispatch('common:hatch', {egg: egg.key, hatchingPotion: potion.key});
this.hatchedPet = createAnimal(egg, potion, 'pet', this.content, this.user.items); this.$root.$emit('hatchedPet::open', createAnimal(egg, potion, 'pet', this.content, this.user.items));
}, },
onDragEnd () { onDragEnd () {
this.currentDraggingPotion = null; this.currentDraggingPotion = null;
@@ -405,9 +401,6 @@ export default {
this.potionClickMode = false; this.potionClickMode = false;
} }
}, },
closeHatchedPetDialog () {
this.hatchedPet = null;
},
async itemClicked (groupKey, item) { async itemClicked (groupKey, item) {
if (item.type && item.type === 'card') { if (item.type && item.type === 'card') {

View File

@@ -1,12 +1,10 @@
<template lang="pug"> <template lang="pug">
b-modal#hatchedPet-modal( b-modal#hatchedPet-modal(
:visible="true",
v-if="pet != null",
:hide-header="true" :hide-header="true"
) )
div.content div.content(v-if="pet != null")
div.dialog-header.title You hatched a new pet! div.dialog-header.title(v-once) {{ $t('hatchedPetGeneric') }}
div.inner-content div.inner-content
@@ -45,7 +43,7 @@
} }
.inner-content { .inner-content {
margin: 33px auto auto; margin: 24px auto auto;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
@@ -77,16 +75,33 @@
components: { components: {
bModal, bModal,
}, },
data () {
return {
pet: null,
};
},
created () {
},
mounted () {
this.$root.$on('hatchedPet::open', this.openDialog);
},
destroyed () {
this.$root.$off('hatchedPet::open', this.openDialog);
},
methods: { methods: {
openDialog (item) {
this.pet = item;
this.$root.$emit('show::modal', 'hatchedPet-modal');
},
close () { close () {
this.$emit('closed', this.item); this.$emit('closed', this.item);
this.$root.$emit('hide::modal', 'hatchedPet-modal'); this.$root.$emit('hide::modal', 'hatchedPet-modal');
this.pet = null;
}, },
}, },
props: { props: {
pet: {
type: Object,
},
hideText: { hideText: {
type: Boolean, type: Boolean,
}, },

View File

@@ -210,7 +210,6 @@
div.content-text(v-once) {{ $t('welcomeStableText') }} div.content-text(v-once) {{ $t('welcomeStableText') }}
b-modal#hatching-modal( b-modal#hatching-modal(
:visible="hatchablePet != null",
@change="resetHatchablePet($event)" @change="resetHatchablePet($event)"
) )
div.content(v-if="hatchablePet") div.content(v-if="hatchablePet")
@@ -230,9 +229,7 @@
button.btn.btn-secondary.btn-flat(@click="closeHatchPetDialog()") {{ $t('cancel') }} button.btn.btn-secondary.btn-flat(@click="closeHatchPetDialog()") {{ $t('cancel') }}
hatchedPetDialog( hatchedPetDialog(
:pet="hatchedPet",
:hideText="true", :hideText="true",
@closed="closeHatchedPetDialog()"
) )
div.foodInfo(ref="dragginFoodInfo") div.foodInfo(ref="dragginFoodInfo")
@@ -337,11 +334,6 @@
padding-right:0; padding-right:0;
} }
.drawer-container {
// 3% padding + 252px sidebar width
left: calc(3% + 252px) !important;
}
.svg-icon.inline.icon-16 { .svg-icon.inline.icon-16 {
vertical-align: bottom; vertical-align: bottom;
} }
@@ -392,7 +384,6 @@
} }
.title { .title {
height: 24px;
margin-top: 24px; margin-top: 24px;
font-family: Roboto; font-family: Roboto;
font-size: 20px; font-size: 20px;
@@ -588,7 +579,6 @@
highlightPet: '', highlightPet: '',
hatchablePet: null, hatchablePet: null,
hatchedPet: null,
foodClickMode: false, foodClickMode: false,
currentDraggingFood: null, currentDraggingFood: null,
@@ -918,7 +908,8 @@
hatchPet (pet) { hatchPet (pet) {
this.$store.dispatch('common:hatch', {egg: pet.eggKey, hatchingPotion: pet.potionKey}); this.$store.dispatch('common:hatch', {egg: pet.eggKey, hatchingPotion: pet.potionKey});
this.hatchedPet = pet;
this.$root.$emit('hatchedPet::open', pet);
this.closeHatchPetDialog(); this.closeHatchPetDialog();
}, },
@@ -974,6 +965,8 @@
} }
// opens the hatch dialog // opens the hatch dialog
this.hatchablePet = pet; this.hatchablePet = pet;
this.$root.$emit('show::modal', 'hatching-modal');
} }
}, },
@@ -988,9 +981,6 @@
closeHatchPetDialog () { closeHatchPetDialog () {
this.$root.$emit('hide::modal', 'hatching-modal'); this.$root.$emit('hide::modal', 'hatching-modal');
}, },
closeHatchedPetDialog () {
this.hatchedPet = null;
},
resetHatchablePet ($event) { resetHatchablePet ($event) {
if (!$event) { if (!$event) {

View File

@@ -158,6 +158,7 @@ export default {
return { return {
yesterDailies: [], yesterDailies: [],
levelBeforeYesterdailies: 0,
notificationData: {}, notificationData: {},
unlockLevels, unlockLevels,
lastShownNotifications, lastShownNotifications,
@@ -256,12 +257,8 @@ export default {
this.mp(mana); this.mp(mana);
}, },
userLvl (after, before) { userLvl (after, before) {
if (after <= before) return; if (after <= before || this.isRunningYesterdailies) return;
this.lvl(); this.showLevelUpNotifications(after);
this.playSound('Level_Up');
if (this.user._tmp && this.user._tmp.drop && this.user._tmp.drop.type === 'Quest') return;
if (this.unlockLevels[`${after}`]) return;
if (!this.user.preferences.suppressModals.levelUp) this.$root.$emit('show::modal', 'level-up');
}, },
userClassSelect (after) { userClassSelect (after) {
if (!after) return; if (!after) return;
@@ -334,6 +331,13 @@ export default {
}); });
}, },
methods: { methods: {
showLevelUpNotifications (newlevel) {
this.lvl();
this.playSound('Level_Up');
if (this.user._tmp && this.user._tmp.drop && this.user._tmp.drop.type === 'Quest') return;
if (this.unlockLevels[`${newlevel}`]) return;
if (!this.user.preferences.suppressModals.levelUp) this.$root.$emit('show::modal', 'level-up');
},
playSound (sound) { playSound (sound) {
this.$root.$emit('playSound', sound); this.$root.$emit('playSound', sound);
}, },
@@ -390,6 +394,7 @@ export default {
return; return;
} }
this.levelBeforeYesterdailies = this.user.stats.lvl;
this.$root.$emit('show::modal', 'yesterdaily'); this.$root.$emit('show::modal', 'yesterdaily');
}, },
async runYesterDailiesAction () { async runYesterDailiesAction () {
@@ -405,6 +410,10 @@ export default {
this.$store.dispatch('tasks:fetchUserTasks', {forceLoad: true}), this.$store.dispatch('tasks:fetchUserTasks', {forceLoad: true}),
]); ]);
if (this.levelBeforeYesterdailies < this.user.stats.lvl) {
this.showLevelUpNotifications(this.user.stats.lvl);
}
this.handleUserNotifications(this.user.notifications); this.handleUserNotifications(this.user.notifications);
this.scheduleNextCron(); this.scheduleNextCron();
}, },

View File

@@ -209,6 +209,7 @@
<script> <script>
import bModal from 'bootstrap-vue/lib/components/modal'; import bModal from 'bootstrap-vue/lib/components/modal';
import * as Analytics from 'client/libs/analytics'; import * as Analytics from 'client/libs/analytics';
import spellsMixin from 'client/mixins/spells';
import svgClose from 'assets/svg/close.svg'; import svgClose from 'assets/svg/close.svg';
import svgGold from 'assets/svg/gold.svg'; import svgGold from 'assets/svg/gold.svg';
@@ -220,6 +221,7 @@
import BalanceInfo from './balanceInfo.vue'; import BalanceInfo from './balanceInfo.vue';
import currencyMixin from './_currencyMixin'; import currencyMixin from './_currencyMixin';
import notifications from 'client/mixins/notifications'; import notifications from 'client/mixins/notifications';
import buyMixin from 'client/mixins/buy';
import { mapState } from 'client/libs/store'; import { mapState } from 'client/libs/store';
@@ -233,7 +235,7 @@
import moment from 'moment'; import moment from 'moment';
export default { export default {
mixins: [currencyMixin, notifications], mixins: [currencyMixin, notifications, spellsMixin, buyMixin],
components: { components: {
bModal, bModal,
BalanceInfo, BalanceInfo,
@@ -301,17 +303,11 @@
this.$emit('change', $event); this.$emit('change', $event);
}, },
buyItem () { buyItem () {
if (this.genericPurchase) { if (this.item.cast) {
this.$store.dispatch('shops:genericPurchase', { this.castStart(this.item);
pinType: this.item.pinType, } else if (this.genericPurchase) {
type: this.item.purchaseType, this.makeGenericPurchase(this.item);
key: this.item.key,
currency: this.item.currency,
});
this.purchased(this.item.text); this.purchased(this.item.text);
this.$root.$emit('buyModal::boughtItem', this.item);
this.$root.$emit('playSound', 'Reward');
} }
this.$emit('buyPressed', this.item); this.$emit('buyPressed', this.item);

View File

@@ -71,9 +71,6 @@
p.span p.span
span {{ $t('marketing4Lead3-2') }} span {{ $t('marketing4Lead3-2') }}
a.btn.btn-primary(href='/static/plans',target='_blank') {{ $t('contactUs') }} a.btn.btn-primary(href='/static/plans',target='_blank') {{ $t('contactUs') }}
p.span
span {{ $t('marketing4Lead3-3') }}
a.btn.btn-primary(href='/static/videos') {{ $t('watchVideos') }}
</template> </template>
<style lang='scss' scoped> <style lang='scss' scoped>

View File

@@ -46,6 +46,11 @@
min-height: 556px; min-height: 556px;
} }
.task-wrapper {
position: relative;
z-index: 2;
}
.task-wrapper + .reward-items { .task-wrapper + .reward-items {
margin-top: 16px; margin-top: 16px;
} }
@@ -104,7 +109,7 @@
.column-background { .column-background {
position: absolute; position: absolute;
bottom: 32px; bottom: 32px;
z-index: 7; z-index: 1;
&.initial-description { &.initial-description {
top: 30%; top: 30%;
@@ -160,19 +165,25 @@
<script> <script>
import Task from './task'; import Task from './task';
import sortBy from 'lodash/sortBy'; import sortBy from 'lodash/sortBy';
import throttle from 'lodash/throttle';
import bModal from 'bootstrap-vue/lib/components/modal';
import sortable from 'client/directives/sortable.directive';
import buyMixin from 'client/mixins/buy';
import { mapState, mapActions } from 'client/libs/store'; import { mapState, mapActions } from 'client/libs/store';
import shopItem from '../shops/shopItem';
import { shouldDo } from 'common/script/cron'; import { shouldDo } from 'common/script/cron';
import inAppRewards from 'common/script/libs/inAppRewards'; import inAppRewards from 'common/script/libs/inAppRewards';
import spells from 'common/script/content/spells';
import habitIcon from 'assets/svg/habit.svg'; import habitIcon from 'assets/svg/habit.svg';
import dailyIcon from 'assets/svg/daily.svg'; import dailyIcon from 'assets/svg/daily.svg';
import todoIcon from 'assets/svg/todo.svg'; import todoIcon from 'assets/svg/todo.svg';
import rewardIcon from 'assets/svg/reward.svg'; import rewardIcon from 'assets/svg/reward.svg';
import bModal from 'bootstrap-vue/lib/components/modal';
import shopItem from '../shops/shopItem';
import throttle from 'lodash/throttle';
import sortable from 'client/directives/sortable.directive';
export default { export default {
mixins: [buyMixin],
components: { components: {
Task, Task,
bModal, bModal,
@@ -252,8 +263,30 @@ export default {
}, },
inAppRewards () { inAppRewards () {
let watchRefresh = this.forceRefresh; // eslint-disable-line let watchRefresh = this.forceRefresh; // eslint-disable-line
let rewards = inAppRewards(this.user);
return inAppRewards(this.user);
// Add season rewards if user is affected
// @TODO: Add buff coniditional
const seasonalSkills = {
snowball: 'salt',
spookySparkles: 'opaquePotion',
shinySeed: 'petalFreePotion',
seafoam: 'sand',
};
for (let key in seasonalSkills) {
if (this.user.stats.buffs[key]) {
let debuff = seasonalSkills[key];
let item = Object.assign({}, spells.special[debuff]);
item.text = item.text();
item.notes = item.notes();
item.class = `shop_${key}`;
rewards.push(item);
}
}
return rewards;
}, },
hasRewardsList () { hasRewardsList () {
return this.isUser === true && this.type === 'reward' && this.activeFilters[this.type].label !== 'custom'; return this.isUser === true && this.type === 'reward' && this.activeFilters[this.type].label !== 'custom';
@@ -378,6 +411,14 @@ export default {
} }
}, },
openBuyDialog (rewardItem) { openBuyDialog (rewardItem) {
// Buy armoire and health potions immediately
let itemsToPurchaseImmediately = ['potion', 'armoire'];
if (itemsToPurchaseImmediately.indexOf(rewardItem.key) !== -1) {
this.makeGenericPurchase(rewardItem);
this.$emit('buyPressed', rewardItem);
return;
}
if (rewardItem.purchaseType !== 'gear' || !rewardItem.locked) { if (rewardItem.purchaseType !== 'gear' || !rewardItem.locked) {
this.$emit('openBuyDialog', rewardItem); this.$emit('openBuyDialog', rewardItem);
} }

View File

@@ -148,14 +148,13 @@ div(v-if='user.stats.lvl > 10')
</style> </style>
<script> <script>
import axios from 'axios';
import isArray from 'lodash/isArray';
import bPopover from 'bootstrap-vue/lib/directives/popover'; import bPopover from 'bootstrap-vue/lib/directives/popover';
import spells from '../../../common/script/content/spells'; import spells from '../../../common/script/content/spells';
import { mapState } from 'client/libs/store'; import { mapState } from 'client/libs/store';
import notifications from 'client/mixins/notifications'; import notifications from 'client/mixins/notifications';
import spellsMixin from 'client/mixins/spells';
import Drawer from 'client/components/ui/drawer'; import Drawer from 'client/components/ui/drawer';
import MouseMoveDirective from 'client/directives/mouseposition.directive'; import MouseMoveDirective from 'client/directives/mouseposition.directive';
@@ -164,7 +163,7 @@ import quests from 'common/script/content/quests';
import { CONSTANTS, setLocalSetting, getLocalSetting } from 'client/libs/userlocalManager'; import { CONSTANTS, setLocalSetting, getLocalSetting } from 'client/libs/userlocalManager';
export default { export default {
mixins: [notifications], mixins: [notifications, spellsMixin],
components: { components: {
Drawer, Drawer,
}, },
@@ -245,122 +244,6 @@ export default {
return notes; return notes;
}, },
async castStart (spell) {
if (this.$store.state.spellOptions.castingSpell) {
this.castCancel();
return;
}
if (this.user.stats.mp < spell.mana) return this.text(this.$t('notEnoughMana'));
if (spell.immediateUse && this.user.stats.gp < spell.value) {
return this.text('Not enough gold.');
}
this.potionClickMode = true;
this.applyingAction = true;
this.$store.state.spellOptions.castingSpell = true;
this.spell = spell;
if (spell.target === 'self') {
this.castEnd(null, spell.target);
} else if (spell.target === 'party') {
if (!this.user.party._id) {
let party = [this.user];
this.castEnd(party, spell.target);
return;
}
let party = this.$store.state.party.members;
party = isArray(party) ? party : [];
party = party.concat(this.user);
this.castEnd(party, spell.target);
} else if (spell.target === 'tasks') {
let userTasks = this.$store.state.tasks.data;
// exclude rewards
let tasks = userTasks.habits
.concat(userTasks.dailys)
.concat(userTasks.todos);
// exclude challenge and group plan tasks
tasks = tasks.filter((task) => {
if (task.challenge && task.challenge.id && !task.challenge.broken) return false;
if (task.group && task.group.id && !task.group.broken) return false;
return true;
});
this.castEnd(tasks, spell.target);
}
},
async castEnd (target, type) {
if (!this.$store.state.spellOptions.castingSpell) return;
let beforeQuestProgress = this.questProgress();
if (!this.applyingAction) return 'No applying action';
if (this.spell.target !== type) return this.text(this.$t('invalidTarget'));
if (target && target.type && target.type === 'reward') return this.text(this.$t('invalidTarget'));
if (target && target.challenge && target.challenge.id) return this.text(this.$t('invalidTarget'));
if (target && target.group && target.group.id) return this.text(this.$t('invalidTarget'));
// @TODO: just call castCancel?
this.$store.state.spellOptions.castingSpell = false;
this.potionClickMode = false;
this.spell.cast(this.user, target);
// User.save(); // @TODO:
let spell = this.spell;
let targetId = target ? target._id : null;
this.spell = null;
this.applyingAction = false;
let spellUrl = `/api/v3/user/class/cast/${spell.key}`;
if (targetId) spellUrl += `?targetId=${targetId}`;
await axios.post(spellUrl);
let msg = this.$t('youCast', {
spell: spell.text(),
});
switch (type) {
case 'task':
msg = this.$t('youCastTarget', {
spell: spell.text(),
target: target.text,
});
break;
case 'user':
msg = this.$t('youCastTarget', {
spell: spell.text(),
target: target.profile.name,
});
break;
case 'party':
msg = this.$t('youCastParty', {
spell: spell.text(),
});
break;
}
this.markdown(msg); // @TODO: mardown directive?
// @TODO:
let questProgress = this.questProgress() - beforeQuestProgress;
if (questProgress > 0) {
let userQuest = this.quests[this.user.party.quest.key];
if (userQuest.boss) {
this.quest('questDamage', questProgress.toFixed(1));
} else if (userQuest.collection && userQuest.collect) {
this.quest('questCollection', questProgress);
}
}
// @TOOD: User.sync();
},
castCancel () {
this.potionClickMode = false;
this.applyingAction = false;
this.spell = null;
document.querySelector('body').style.cursor = 'initial';
this.$store.state.spellOptions.castingSpell = false;
},
questProgress () { questProgress () {
let user = this.user; let user = this.user;
if (!user.party.quest) return 0; if (!user.party.quest) return 0;

View File

@@ -18,6 +18,7 @@
.task-notes.small-text(v-markdown="task.notes") .task-notes.small-text(v-markdown="task.notes")
.checklist(v-if="canViewchecklist") .checklist(v-if="canViewchecklist")
label.custom-control.custom-checkbox.checklist-item( label.custom-control.custom-checkbox.checklist-item(
v-if='!castingSpell',
v-for="item in task.checklist", :class="{'checklist-item-done': item.completed}", v-for="item in task.checklist", :class="{'checklist-item-done': item.completed}",
) )
input.custom-control-input(type="checkbox", :checked="item.completed", @change="toggleChecklistItem(item)") input.custom-control-input(type="checkbox", :checked="item.completed", @change="toggleChecklistItem(item)")
@@ -339,7 +340,10 @@ export default {
}; };
}, },
computed: { computed: {
...mapState({user: 'user.data'}), ...mapState({
user: 'user.data',
castingSpell: 'spellOptions.castingSpell',
}),
...mapGetters({ ...mapGetters({
getTagsFor: 'tasks:getTagsFor', getTagsFor: 'tasks:getTagsFor',
getTaskClasses: 'tasks:getTaskClasses', getTaskClasses: 'tasks:getTaskClasses',
@@ -390,6 +394,7 @@ export default {
methods: { methods: {
...mapActions({scoreChecklistItem: 'tasks:scoreChecklistItem'}), ...mapActions({scoreChecklistItem: 'tasks:scoreChecklistItem'}),
toggleChecklistItem (item) { toggleChecklistItem (item) {
if (this.castingSpell) return;
item.completed = !item.completed; item.completed = !item.completed;
this.scoreChecklistItem({taskId: this.task._id, itemId: item.id}); this.scoreChecklistItem({taskId: this.task._id, itemId: item.id});
}, },
@@ -407,6 +412,8 @@ export default {
this.$root.$emit('castEnd', task, 'task', e); this.$root.$emit('castEnd', task, 'task', e);
}, },
async score (direction) { async score (direction) {
if (this.castingSpell) return;
// TODO move to an action // TODO move to an action
const Content = this.$store.state.content; const Content = this.$store.state.content;
const user = this.user; const user = this.user;

View File

@@ -26,9 +26,9 @@
max-width: 80%; max-width: 80%;
@media screen and (min-width: 1241px) { @media screen and (min-width: 1241px) {
max-width: 968px; max-width: 978px;
// 16.67% is the width of the .col-2 sidebar // 16.67% is the width of the .col-2 sidebar
left: calc((100% + 16.67% - 968px) / 2); left: calc((100% + 16.67% - 978px) / 2);
right: 0%; right: 0%;
} }
} }

View File

@@ -23,6 +23,10 @@
.fill-height { .fill-height {
height: 38px; // button + margin + padding height: 38px; // button + margin + padding
} }
.item-rows {
margin-right: -24px;
}
</style> </style>
<script> <script>

View File

@@ -0,0 +1,14 @@
export default {
methods: {
makeGenericPurchase (item) {
this.$store.dispatch('shops:genericPurchase', {
pinType: item.pinType,
type: item.purchaseType,
key: item.key,
currency: item.currency,
});
this.$root.$emit('buyModal::boughtItem', item);
this.$root.$emit('playSound', 'Reward');
},
},
};

View File

@@ -0,0 +1,128 @@
import axios from 'axios';
import isArray from 'lodash/isArray';
// @TODO: Let's separate some of the business logic out of Vue if possible
export default {
methods: {
async castStart (spell) {
if (this.$store.state.spellOptions.castingSpell) {
this.castCancel();
return;
}
if (this.user.stats.mp < spell.mana) return this.text(this.$t('notEnoughMana'));
if (spell.immediateUse && this.user.stats.gp < spell.value) {
return this.text('Not enough gold.');
}
this.potionClickMode = true;
this.applyingAction = true;
this.$store.state.spellOptions.castingSpell = true;
this.spell = spell;
if (spell.target === 'self') {
this.castEnd(null, spell.target);
} else if (spell.target === 'party') {
if (!this.user.party._id) {
let party = [this.user];
this.castEnd(party, spell.target);
return;
}
let party = this.$store.state.party.members;
party = isArray(party) ? party : [];
party = party.concat(this.user);
this.castEnd(party, spell.target);
} else if (spell.target === 'tasks') {
let userTasks = this.$store.state.tasks.data;
// exclude rewards
let tasks = userTasks.habits
.concat(userTasks.dailys)
.concat(userTasks.todos);
// exclude challenge and group plan tasks
tasks = tasks.filter((task) => {
if (task.challenge && task.challenge.id && !task.challenge.broken) return false;
if (task.group && task.group.id && !task.group.broken) return false;
return true;
});
this.castEnd(tasks, spell.target);
}
},
async castEnd (target, type) {
if (!this.$store.state.spellOptions.castingSpell) return;
let beforeQuestProgress;
if (this.spell.target === 'party') beforeQuestProgress = this.questProgress();
if (!this.applyingAction) return 'No applying action';
if (this.spell.target !== type) return this.text(this.$t('invalidTarget'));
if (target && target.type && target.type === 'reward') return this.text(this.$t('invalidTarget'));
if (target && target.challenge && target.challenge.id) return this.text(this.$t('invalidTarget'));
if (target && target.group && target.group.id) return this.text(this.$t('invalidTarget'));
// @TODO: just call castCancel?
this.$store.state.spellOptions.castingSpell = false;
this.potionClickMode = false;
this.spell.cast(this.user, target);
// User.save(); // @TODO:
let spell = this.spell;
let targetId = target ? target._id : null;
this.spell = null;
this.applyingAction = false;
let spellUrl = `/api/v3/user/class/cast/${spell.key}`;
if (targetId) spellUrl += `?targetId=${targetId}`;
let spellText = typeof spell.text === 'function' ? spell.text() : spell.text;
await axios.post(spellUrl);
let msg = this.$t('youCast', {
spell: spellText,
});
switch (type) {
case 'task':
msg = this.$t('youCastTarget', {
spell: spellText,
target: target.text,
});
break;
case 'user':
msg = this.$t('youCastTarget', {
spell: spellText,
target: target.profile.name,
});
break;
case 'party':
msg = this.$t('youCastParty', {
spell: spellText,
});
break;
}
this.markdown(msg); // @TODO: mardown directive?
// @TODO:
if (!beforeQuestProgress) return;
let questProgress = this.questProgress() - beforeQuestProgress;
if (questProgress > 0) {
let userQuest = this.quests[this.user.party.quest.key];
if (userQuest.boss) {
this.quest('questDamage', questProgress.toFixed(1));
} else if (userQuest.collection && userQuest.collect) {
this.quest('questCollection', questProgress);
}
}
// @TOOD: User.sync();
},
castCancel () {
this.potionClickMode = false;
this.applyingAction = false;
this.spell = null;
document.querySelector('body').style.cursor = 'initial';
this.$store.state.spellOptions.castingSpell = false;
},
},
};

View File

@@ -9,14 +9,15 @@ import * as Analytics from 'client/libs/analytics';
import ParentPage from './components/parentPage'; import ParentPage from './components/parentPage';
// Static Pages // Static Pages
const StaticWrapper = () => import(/* webpackChunkName: "static" */'./components/static/staticWrapper'); const StaticWrapper = () => import(/* webpackChunkName: "entry" */'./components/static/staticWrapper');
const HomePage = () => import(/* webpackChunkName: "entry" */'./components/static/home');
const AppPage = () => import(/* webpackChunkName: "static" */'./components/static/app'); const AppPage = () => import(/* webpackChunkName: "static" */'./components/static/app');
const ClearBrowserDataPage = () => import(/* webpackChunkName: "static" */'./components/static/clearBrowserData'); const ClearBrowserDataPage = () => import(/* webpackChunkName: "static" */'./components/static/clearBrowserData');
const CommunityGuidelinesPage = () => import(/* webpackChunkName: "static" */'./components/static/communityGuidelines'); const CommunityGuidelinesPage = () => import(/* webpackChunkName: "static" */'./components/static/communityGuidelines');
const ContactPage = () => import(/* webpackChunkName: "static" */'./components/static/contact'); const ContactPage = () => import(/* webpackChunkName: "static" */'./components/static/contact');
const FAQPage = () => import(/* webpackChunkName: "static" */'./components/static/faq'); const FAQPage = () => import(/* webpackChunkName: "static" */'./components/static/faq');
const FeaturesPage = () => import(/* webpackChunkName: "static" */'./components/static/features'); const FeaturesPage = () => import(/* webpackChunkName: "static" */'./components/static/features');
const HomePage = () => import(/* webpackChunkName: "static" */'./components/static/home');
const GroupPlansPage = () => import(/* webpackChunkName: "static" */'./components/static/groupPlans'); const GroupPlansPage = () => import(/* webpackChunkName: "static" */'./components/static/groupPlans');
const MerchPage = () => import(/* webpackChunkName: "static" */'./components/static/merch'); const MerchPage = () => import(/* webpackChunkName: "static" */'./components/static/merch');
const NewsPage = () => import(/* webpackChunkName: "static" */'./components/static/newStuff'); const NewsPage = () => import(/* webpackChunkName: "static" */'./components/static/newStuff');

View File

@@ -116,7 +116,7 @@
"shortName": "Short Name", "shortName": "Short Name",
"shortNamePlaceholder": "What short tag should be used to identify your Challenge?", "shortNamePlaceholder": "What short tag should be used to identify your Challenge?",
"updateChallenge": "Update Challenge", "updateChallenge": "Update Challenge",
"haveNoChallenges": "You don't have any Challenges", "haveNoChallenges": "This group has no Challenges",
"loadMore": "Load More", "loadMore": "Load More",
"exportChallengeCsv": "Export Challenge", "exportChallengeCsv": "Export Challenge",
"editingChallenge": "Editing Challenge", "editingChallenge": "Editing Challenge",

View File

@@ -149,7 +149,7 @@
"optOutOfPMs": "Opt Out", "optOutOfPMs": "Opt Out",
"chooseClass": "Choose your Class", "chooseClass": "Choose your Class",
"chooseClassLearnMarkdown": "[Learn more about Habitica's class system](http://habitica.wikia.com/wiki/Class_System)", "chooseClassLearnMarkdown": "[Learn more about Habitica's class system](http://habitica.wikia.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 -> Stats.", "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.",
"selectClass": "Select <%= heroClass %>", "selectClass": "Select <%= heroClass %>",
"select": "Select", "select": "Select",
"stealth": "Stealth", "stealth": "Stealth",
@@ -196,7 +196,7 @@
"int": "INT", "int": "INT",
"showQuickAllocation": "Show stat allocation", "showQuickAllocation": "Show stat allocation",
"hideQuickAllocation": "Hide stat allocation", "hideQuickAllocation": "Hide stat allocation",
"quickAllocationLevelPopover": "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 found in User -> Stats.", "quickAllocationLevelPopover": "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 found in User Icon > Stats.",
"invalidAttribute": "\"<%= attr %>\" is not a valid attribute.", "invalidAttribute": "\"<%= attr %>\" is not a valid attribute.",
"notEnoughAttrPoints": "You don't have enough attribute points.", "notEnoughAttrPoints": "You don't have enough attribute points.",
"style": "Style", "style": "Style",

View File

@@ -125,7 +125,7 @@
"error": "Error", "error": "Error",
"menu": "Menu", "menu": "Menu",
"notifications": "Notifications", "notifications": "Notifications",
"noNotifications": "You have no new messages.", "noNotifications": "You have no notifications.",
"clear": "Clear", "clear": "Clear",
"endTour": "End Tour", "endTour": "End Tour",
"audioTheme": "Audio Theme", "audioTheme": "Audio Theme",

View File

@@ -358,7 +358,7 @@
"guildSummaryPlaceholder": "Write a short description advertising your Guild to other Habiticans. What is the main purpose of your Guild and why should people join it? Try to include useful keywords in the summary so that Habiticans can easily find it when they search!", "guildSummaryPlaceholder": "Write a short description advertising your Guild to other Habiticans. What is the main purpose of your Guild and why should people join it? Try to include useful keywords in the summary so that Habiticans can easily find it when they search!",
"groupDescription": "Description", "groupDescription": "Description",
"guildDescriptionPlaceholder": "Use this section to go into more detail about everything that Guild members should know about your Guild. Useful tips, helpful links, and encouraging statements all go here!", "guildDescriptionPlaceholder": "Use this section to go into more detail about everything that Guild members should know about your Guild. Useful tips, helpful links, and encouraging statements all go here!",
"markdownFormattingHelp": "Markdown formatting help", "markdownFormattingHelp": "[Markdown formatting help](http://habitica.wikia.com/wiki/Markdown_Cheat_Sheet)",
"partyDescriptionPlaceholder": "This is our party's description. It describes what we do in this party. If you want to learn more about what we do in this party, read the description. Party on.", "partyDescriptionPlaceholder": "This is our party's description. It describes what we do in this party. If you want to learn more about what we do in this party, read the description. Party on.",
"guildGemCostInfo": "A Gem cost promotes high quality Guilds and is transferred into your Guild's bank.", "guildGemCostInfo": "A Gem cost promotes high quality Guilds and is transferred into your Guild's bank.",
"noGuildsTitle": "You aren't a member of any Guilds.", "noGuildsTitle": "You aren't a member of any Guilds.",
@@ -391,7 +391,7 @@
"reverseChat": "Reverse Chat", "reverseChat": "Reverse Chat",
"invites": "Invites", "invites": "Invites",
"details": "Details", "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.", "participantDesc": "Once all members have either accepted or declined, the Quest begins. Only those who clicked 'accept' will be able to participate in the Quest and receive the rewards.",
"groupGems": "Group Gems", "groupGems": "Group Gems",
"groupGemsDesc": "Guild Gems can be spent to make Challenges! In the future, you will be able to add more Guild Gems." "groupGemsDesc": "Guild Gems can be spent to make Challenges! In the future, you will be able to add more Guild Gems."
} }

View File

@@ -73,6 +73,7 @@
"useGems": "If you've got your eye on a pet, but can't wait any longer for it to drop, use Gems in <strong>Inventory > Market</strong> to buy one!", "useGems": "If you've got your eye on a pet, but can't wait any longer for it to drop, use Gems in <strong>Inventory > Market</strong> to buy one!",
"hatchAPot": "Hatch a new <%= potion %> <%= egg %>?", "hatchAPot": "Hatch a new <%= potion %> <%= egg %>?",
"hatchedPet": "You hatched a new <%= potion %> <%= egg %>!", "hatchedPet": "You hatched a new <%= potion %> <%= egg %>!",
"hatchedPetGeneric": "You hatched a new pet!",
"displayNow": "Display Now", "displayNow": "Display Now",
"displayLater": "Display Later", "displayLater": "Display Later",
"petNotOwned": "You do not own this pet.", "petNotOwned": "You do not own this pet.",

View File

@@ -1,10 +1,10 @@
{ {
"unlockedReward": "You have received <%= reward %>", "unlockedReward": "You have received <%= reward %>",
"earnedRewardForDevotion": "You have earned <%= reward %> for being committed to improving your life.", "earnedRewardForDevotion": "You have earned <%= reward %> for being committed to improving your life.",
"nextRewardUnlocksIn": "Check-ins until your next prize: <%= numberOfCheckinsLeft %>", "nextRewardUnlocksIn": "Check-Ins until your next prize: <%= numberOfCheckinsLeft %>",
"awesome": "Awesome!", "awesome": "Awesome!",
"totalCount": "<%= count %> total count", "totalCount": "<%= count %> total count",
"countLeft": "Check-ins until next reward: <%= count %>", "countLeft": "Check-Ins until next reward: <%= count %>",
"incentivesDescription": "When it comes to building habits, consistency is key. Each day you check-in, you get closer to a prize.", "incentivesDescription": "When it comes to building habits, consistency is key. Each day you check-in, you get closer to a prize.",
"totalCheckins": "<%= count %> Check-Ins", "totalCheckins": "<%= count %> Check-Ins",
"checkinEarned": "Your Check-In Counter went up!", "checkinEarned": "Your Check-In Counter went up!",

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB