mirror of
https://github.com/sudoxnym/habitica.git
synced 2026-04-14 19:56:23 +00:00
Merge Develop onto Release (#9123)
* Some random quick (#9111) * Switch group button directions * Allowed admins to export challenges * Added scoping to some stable styles * Fixed challenge cloning * Tasks tags (#9112) * Added auto apply and exit * Add challenge tag editing * Fixed lint * Skill fixes (#9113) * Added local storage setting for spell drawer * Added new spell styles * Fixed typo * Reset local creds if access is denied (#9114) * various fixes: group leader's name at top of edit drop-down; Members List; etc (#9117) * fix text describing location of subscription/gem gift box * disable Copy As To-Do in Tavern, guilds, party because it's not working * change members label on group pages to Member List * remove outdated info about seeing number of Gems available to buy * allow Danger Zone to be seen by players without local authentication Also add an hr because the Danger Zone heading was crammed up against the button above it. * put current group leader's name at top of Leader change drop-down * Client Fixes (#9120) * unduplicate logout code * re-enable debug menu * fix pets badge and equipping mounts * close gift modal after sending gems * armoire notifications * Oct 1 fixes (#9121) * Added default tags to task * Added seasonal gear check and show spooky * Disabled spooky sparkles * Fixed challenge remove tasks modal * Hid checklist * Added group gems modal * Purchase with amazon * Added check for user health * Added missing notification file
This commit is contained in:
parent
02f8ba1638
commit
39d7581c6c
30 changed files with 226 additions and 109 deletions
|
|
@ -147,11 +147,7 @@ export default {
|
|||
// Check for conditions to reset the user auth
|
||||
const invalidUserMessage = [this.$t('invalidCredentials'), 'Missing authentication headers.'];
|
||||
if (invalidUserMessage.indexOf(error.response.data.message) !== -1) {
|
||||
localStorage.removeItem('habit-mobile-settings');
|
||||
localStorage.removeItem('hello');
|
||||
this.$store.state.isUserLoggedIn = false;
|
||||
window.location.href = '/static/home';
|
||||
return Promise.reject(error);
|
||||
this.$store.dispatch('auth:logout');
|
||||
}
|
||||
|
||||
// Don't show errors from getting user details. These users have delete their account,
|
||||
|
|
|
|||
|
|
@ -49,7 +49,6 @@ import bModal from 'bootstrap-vue/lib/components/modal';
|
|||
|
||||
import quests from 'common/script/content/quests';
|
||||
import { mapState } from 'client/libs/store';
|
||||
import revive from '../../../common/script/ops/revive';
|
||||
import percent from '../../../common/script/libs/percent';
|
||||
import {maxHealth} from '../../../common/script/index';
|
||||
|
||||
|
|
@ -73,11 +72,7 @@ export default {
|
|||
},
|
||||
methods: {
|
||||
close () {
|
||||
this.$root.$emit('hide::modal', 'death');
|
||||
},
|
||||
revive () {
|
||||
// @TODO: Post
|
||||
revive(this.user);
|
||||
this.$root.$emit('hide::modal', 'quest-invitation');
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
h1(v-markdown='challenge.name')
|
||||
div
|
||||
strong(v-once) {{$t('createdBy')}}:
|
||||
span {{challenge.leader.profile.name}}
|
||||
span(v-if='challenge.leader && challenge.leader.profile') {{challenge.leader.profile.name}}
|
||||
// @TODO: make challenge.author a variable inside the createdBy string (helps with RTL languages)
|
||||
// @TODO: Implement in V2 strong.margin-left(v-once)
|
||||
.svg-icon.calendar-icon(v-html="icons.calendarIcon")
|
||||
|
|
|
|||
|
|
@ -36,10 +36,11 @@
|
|||
.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(v-if='!inbox', @click='copyAsTodo(msg)')
|
||||
.svg-icon(v-html="icons.copy")
|
||||
| {{$t('copyAsTodo')}}
|
||||
// @TODO make copyAsTodo work in the inbox
|
||||
// @TODO make copyAsTodo work in Tavern, guilds, party (inbox can be done later)
|
||||
span.action(v-if='!inbox', @click='copyAsTodo(msg)')
|
||||
.svg-icon(v-html="icons.copy")
|
||||
| {{$t('copyAsTodo')}}
|
||||
// @TODO make copyAsTodo work in the inbox
|
||||
span.action(v-if='!inbox && user.flags.communityGuidelinesAccepted', @click='report(msg)')
|
||||
.svg-icon(v-html="icons.report")
|
||||
| {{$t('report')}}
|
||||
|
|
@ -72,10 +73,11 @@
|
|||
.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(v-if='!inbox', @click='copyAsTodo(msg)')
|
||||
.svg-icon(v-html="icons.copy")
|
||||
| {{$t('copyAsTodo')}}
|
||||
// @TODO make copyAsTodo work in the inbox
|
||||
// @TODO make copyAsTodo work in Tavern, guilds, party (inbox can be done later)
|
||||
span.action(v-if='!inbox', @click='copyAsTodo(msg)')
|
||||
.svg-icon(v-html="icons.copy")
|
||||
| {{$t('copyAsTodo')}}
|
||||
// @TODO make copyAsTodo work in the inbox
|
||||
span.action(v-if='user.flags.communityGuidelinesAccepted', @click='report(msg)')
|
||||
span.action(v-if='!inbox && user.flags.communityGuidelinesAccepted', @click='report(msg)')
|
||||
.svg-icon(v-html="icons.report")
|
||||
|
|
|
|||
|
|
@ -1350,7 +1350,15 @@ export default {
|
|||
cost = fullSet ? 1.25 : 0.5; // (Hair, skin, etc) 5G per set, 2G per individual
|
||||
}
|
||||
|
||||
let loginIncentives = ['background.blue', 'background.green', 'background.red', 'background.purple', 'background.yellow', 'background.violet'];
|
||||
let loginIncentives = [
|
||||
'background.blue',
|
||||
'background.green',
|
||||
'background.red',
|
||||
'background.purple',
|
||||
'background.yellow',
|
||||
'background.violet',
|
||||
];
|
||||
|
||||
if (loginIncentives.indexOf(path) === -1) {
|
||||
if (fullSet) {
|
||||
if (confirm(this.$t('purchaseFor', {cost: cost * 4})) !== true) return;
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
invite-modal(:group='this.group')
|
||||
start-quest-modal(:group='this.group')
|
||||
quest-details-modal(:group='this.group')
|
||||
group-gems-modal
|
||||
.col-12.col-sm-8.standard-page
|
||||
.row
|
||||
.col-6.title-details
|
||||
|
|
@ -18,9 +19,9 @@
|
|||
.svg-icon.shield(v-html="icons.silverGuildBadgeIcon", v-if='group.memberCount > 100 && group.memberCount < 999')
|
||||
.svg-icon.shield(v-html="icons.bronzeGuildBadgeIcon", v-if='group.memberCount < 100')
|
||||
span.number {{ group.memberCount | abbrNum }}
|
||||
div(v-once) {{ $t('members') }}
|
||||
div(v-once) {{ $t('memberList') }}
|
||||
.col-4(v-if='!isParty')
|
||||
.item-with-icon
|
||||
.item-with-icon(@click='showGroupGems()')
|
||||
.svg-icon.gem(v-html="icons.gem")
|
||||
span.number {{group.balance * 4}}
|
||||
div(v-once) {{ $t('guildBank') }}
|
||||
|
|
@ -47,7 +48,6 @@
|
|||
.row
|
||||
.col-12.hr
|
||||
chat-message(:chat.sync='group.chat', :group-id='group._id', group-name='group.name')
|
||||
|
||||
.col-12.col-sm-4.sidebar
|
||||
.row(:class='{"guild-background": !isParty}')
|
||||
.col-6
|
||||
|
|
@ -453,6 +453,7 @@ import inviteModal from './inviteModal';
|
|||
import chatMessage from '../chat/chatMessages';
|
||||
import autocomplete from '../chat/autoComplete';
|
||||
import groupChallenges from '../challenges/groupChallenges';
|
||||
import groupGemsModal from 'client/components/groups/groupGemsModal';
|
||||
import markdownDirective from 'client/directives/markdown';
|
||||
|
||||
import bCollapse from 'bootstrap-vue/lib/components/collapse';
|
||||
|
|
@ -490,6 +491,7 @@ export default {
|
|||
groupChallenges,
|
||||
autocomplete,
|
||||
questDetailsModal,
|
||||
groupGemsModal,
|
||||
},
|
||||
directives: {
|
||||
bToggle,
|
||||
|
|
@ -857,6 +859,9 @@ export default {
|
|||
let quest = await this.$store.dispatch('quests:sendAction', {groupId: this.group._id, action: 'quests/reject'});
|
||||
this.group.quest = quest;
|
||||
},
|
||||
showGroupGems () {
|
||||
this.$root.$emit('show::modal', 'group-gems-modal');
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
label
|
||||
strong(v-once) {{$t('guildOrPartyLeader')}} *
|
||||
select.form-control(v-model="workingGroup.newLeader")
|
||||
option(v-for='member in members', :value="member._id") {{ member.profile.name }}
|
||||
option(v-for='potentialLeader in potentialLeaders', :value="potentialLeader._id") {{ potentialLeader.name }}
|
||||
|
||||
.form-group(v-if='!this.workingGroup.id')
|
||||
label
|
||||
|
|
@ -316,6 +316,17 @@ export default {
|
|||
isParty () {
|
||||
return this.workingGroup.type === 'party';
|
||||
},
|
||||
potentialLeaders () {
|
||||
let leaders = [{ _id: this.user._id, name: this.user.profile.name }];
|
||||
// @TODO consider pushing all recent posters to the top of the list if they are guild members - more likely to be the ones the leader wants to see (and then ignore them in the while below)
|
||||
let i = 0;
|
||||
while (this.members[i]) {
|
||||
let memb = this.members[i];
|
||||
i++;
|
||||
if (memb._id !== this.user._id) leaders.push({_id: memb._id, name: memb.profile.name});
|
||||
}
|
||||
return leaders;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
editingGroup () {
|
||||
|
|
|
|||
32
website/client/components/groups/groupGemsModal.vue
Normal file
32
website/client/components/groups/groupGemsModal.vue
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
<template lang="pug">
|
||||
b-modal#group-gems-modal(:title="$t('groupGems')", size='md', :hide-footer="true")
|
||||
.modal-body
|
||||
.row
|
||||
.col-6.offset-3
|
||||
h3 {{ $t('groupGemsDesc') }}
|
||||
.modal-footer
|
||||
.col-12.text-center
|
||||
button.btn.btn-primary(@click='close()') {{$t('close')}}
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.modal-body {
|
||||
margin-top: 1em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import bModal from 'bootstrap-vue/lib/components/modal';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
bModal,
|
||||
},
|
||||
methods: {
|
||||
close () {
|
||||
this.$root.$emit('hide::modal', 'group-gems-modal');
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
@ -113,7 +113,7 @@
|
|||
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)")
|
||||
starBadge(:selected="item.key === currentPet", :show="item.isOwned()", @click="selectPet(item)")
|
||||
|
||||
.btn.btn-flat.btn-show-more(@click="setShowMore(petGroup.key)", v-if='petGroup.key !== "specialPets"')
|
||||
| {{ showMore === petGroup.key ? $t('showLess') : $t('showMore') }}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<template lang="pug">
|
||||
div
|
||||
.item-wrapper(:id="itemId")
|
||||
.item-wrapper(@click="click()", :id="itemId")
|
||||
.item(
|
||||
:class="{'item-empty': emptyItem}",
|
||||
)
|
||||
|
|
@ -48,5 +48,10 @@ div
|
|||
itemId: uuid.v4(),
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
click () {
|
||||
this.$emit('click', {});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -303,6 +303,11 @@ export default {
|
|||
this.$root.$emit('show::modal', 'avatar-modal');
|
||||
}
|
||||
|
||||
if (this.user.stats.hp <= 0) {
|
||||
this.playSound('Death');
|
||||
this.$root.$emit('show::modal', 'death');
|
||||
}
|
||||
|
||||
if (this.questCompleted) {
|
||||
this.$root.$emit('show::modal', 'quest-completed');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,22 @@
|
|||
<template lang="pug">
|
||||
b-modal#amazon-payment(title="Amazon", :hide-footer="true", size='lg')
|
||||
b-modal#amazon-payment(title="Amazon", :hide-footer="true", size='md')
|
||||
h2.text-center Continue with Amazon
|
||||
#AmazonPayButton
|
||||
#AmazonPayWallet(v-if="amazonPayments.loggedIn", style="width: 400px; height: 228px;")
|
||||
#AmazonPayRecurring(v-if="amazonPayments.loggedIn && amazonPayments.type === 'subscription'",
|
||||
style="width: 400px; height: 140px;")
|
||||
.modal-footer
|
||||
.btn.btn-primary(:disabled="amazonPaymentsCanCheckout() || !amazonButtonEnabled",
|
||||
@click="amazonCheckOut()") {{ $t('checkout') }}
|
||||
.text-center
|
||||
.btn.btn-primary(v-if="amazonPaymentsCanCheckout",
|
||||
@click="amazonCheckOut()") {{ $t('checkout') }}
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
#AmazonPayButton {
|
||||
margin: 0 auto;
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
#AmazonPayRecurring {
|
||||
height: 200px;
|
||||
width: 500px;
|
||||
|
|
@ -41,6 +48,17 @@ export default {
|
|||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
...mapState(['isAmazonReady']),
|
||||
amazonPaymentsCanCheckout () {
|
||||
if (this.amazonPayments.type === 'single') {
|
||||
return this.amazonPaymentspaymentSelected === true;
|
||||
} else if (this.amazonPayments.type === 'subscription') {
|
||||
return this.amazonPaymentspaymentSelected === true &&
|
||||
// Mah.. one is a boolean the other a string...
|
||||
this.amazonPaymentsrecurringConsent === 'true';
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
},
|
||||
mounted () {
|
||||
if (this.isAmazonReady) return this.setupAmazon();
|
||||
|
|
@ -79,11 +97,16 @@ export default {
|
|||
billingAgreementId: this.amazonPaymentsbillingAgreementId,
|
||||
});
|
||||
|
||||
// @TODO: Success
|
||||
this.amazonPayments.loggedIn = true;
|
||||
this.amazonPaymentsorderReferenceId = response.data.orderReferenceId;
|
||||
this.amazonPaymentsinitWidgets();
|
||||
// @TODO: error
|
||||
if (response.status === 200) {
|
||||
this.amazonPayments.loggedIn = true;
|
||||
this.amazonPayments.orderReferenceId = response.data.data.orderReferenceId;
|
||||
|
||||
// @TODO: Clarify the deifference of these functions by renaming
|
||||
this.amazonPaymentsinitWidgets();
|
||||
this.amazonInitWidgets();
|
||||
return;
|
||||
}
|
||||
|
||||
alert(response.message);
|
||||
}
|
||||
},
|
||||
|
|
@ -106,17 +129,6 @@ export default {
|
|||
onError: this.amazonOnError,
|
||||
});
|
||||
},
|
||||
amazonPaymentsCanCheckout () {
|
||||
if (this.amazonPayments.type === 'single') {
|
||||
return this.amazonPaymentspaymentSelected === true;
|
||||
} else if (this.amazonPayments.type === 'subscription') {
|
||||
return this.amazonPaymentspaymentSelected === true &&
|
||||
// Mah.. one is a boolean the other a string...
|
||||
this.amazonPaymentsrecurringConsent === 'true';
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
amazonInitWidgets () {
|
||||
let walletParams = {
|
||||
sellerId: AMAZON_PAYMENTS.SELLER_ID, // @TODO: Import
|
||||
|
|
@ -131,6 +143,7 @@ export default {
|
|||
onError: this.amazonOnError,
|
||||
};
|
||||
|
||||
// @TODO: Check if this is duplicated below
|
||||
if (this.amazonPayments.type === 'subscription') {
|
||||
walletParams.agreementType = 'BillingAgreement';
|
||||
|
||||
|
|
@ -158,7 +171,7 @@ export default {
|
|||
}).bind('AmazonPayRecurring');
|
||||
};
|
||||
} else {
|
||||
walletParams.amazonOrderReferenceId = this.amazonPaymentsorderReferenceId;
|
||||
walletParams.amazonOrderReferenceId = this.amazonPayments.orderReferenceId;
|
||||
}
|
||||
|
||||
new this.OffAmazonPayments.Widgets.Wallet(walletParams).bind('AmazonPayWallet');
|
||||
|
|
@ -166,18 +179,22 @@ export default {
|
|||
async amazonCheckOut () {
|
||||
this.amazonButtonEnabled = false;
|
||||
|
||||
// @TODO: Create factory functions
|
||||
// @TODO: A gift should not read the same as buying gems for yourself.
|
||||
if (this.amazonPayments.type === 'single') {
|
||||
let url = '/amazon/checkout';
|
||||
let response = await axios.post(url, {
|
||||
orderReferenceId: this.amazonPaymentsorderReferenceId,
|
||||
orderReferenceId: this.amazonPayments.orderReferenceId,
|
||||
gift: this.amazonPaymentsgift,
|
||||
});
|
||||
|
||||
// Success
|
||||
this.amazonPaymentsreset();
|
||||
window.location.reload(true);
|
||||
if (response.status < 400) {
|
||||
this.reset();
|
||||
// @TODO: What are we syncing?
|
||||
window.location.reload(true);
|
||||
return;
|
||||
}
|
||||
|
||||
// Failure
|
||||
alert(response.message);
|
||||
this.amazonPaymentsreset();
|
||||
} else if (this.amazonPayments.type === 'subscription') {
|
||||
|
|
@ -212,7 +229,7 @@ export default {
|
|||
}
|
||||
|
||||
window.location.reload(true);
|
||||
this.amazonPaymentsreset();
|
||||
this.reset();
|
||||
}
|
||||
},
|
||||
amazonPaymentsinitWidgets () {
|
||||
|
|
@ -263,16 +280,15 @@ export default {
|
|||
},
|
||||
amazonOnError (error) {
|
||||
alert(error.getErrorMessage());
|
||||
// @TODO: this.amazonPaymentsreset();
|
||||
this.reset();
|
||||
},
|
||||
reset () {
|
||||
this.amazonPaymentsmodal.close(); // @TODO: this.$root.$emit('hide::modal', 'guild-form');
|
||||
this.amazonPaymentsmodal = null;
|
||||
this.amazonPayments.type = null;
|
||||
this.amazonPayments.loggedIn = false;
|
||||
this.amazonPaymentsgift = null;
|
||||
this.amazonPaymentsbillingAgreementId = null;
|
||||
this.amazonPaymentsorderReferenceId = null;
|
||||
this.amazonPayments.orderReferenceId = null;
|
||||
this.amazonPaymentspaymentSelected = false;
|
||||
this.amazonPaymentsrecurringConsent = false;
|
||||
this.amazonPaymentssubscription = null;
|
||||
|
|
|
|||
|
|
@ -39,14 +39,14 @@
|
|||
.gem-text {{ $t('gems') }}
|
||||
.divider
|
||||
button.btn.btn-primary(@click='gemAmount = 4') {{gemAmount === 4 ? $t('selected') : '$1.00'}}
|
||||
.card.text-center.col-3(:class="{active: gemAmount === 21 }")
|
||||
.card.text-center.col-3(:class="{active: gemAmount === 20 }")
|
||||
.card-img-top
|
||||
.mx-auto(v-html='icons.twentyOneGems', style='"height: 55px; width: 47.5px; margin-top: 1.85em;"')
|
||||
.card-body
|
||||
.gem-count 20
|
||||
.gem-text {{ $t('gems') }}
|
||||
.divider
|
||||
button.btn.btn-primary(@click='gemAmount === 21 ? gemAmount = 0 : gemAmount = 21') {{gemAmount === 21 ? $t('selected') : '$5.00'}}
|
||||
button.btn.btn-primary(@click='gemAmount === 20 ? gemAmount = 0 : gemAmount = 20') {{gemAmount === 20 ? $t('selected') : '$5.00'}}
|
||||
//.card.text-center(:class="{active: gemAmount === 42}")
|
||||
.card-img-top
|
||||
.mx-auto(v-html='icons.fortyTwoGems', style='"height: 49.5px; width: 51px; margin-top: 1.9em;"')
|
||||
|
|
|
|||
|
|
@ -141,6 +141,7 @@ export default {
|
|||
gemAmount: this.gift.gems.amount,
|
||||
});
|
||||
this.text(this.$t('sentGems'));
|
||||
this.close();
|
||||
},
|
||||
close () {
|
||||
this.$root.$emit('hide::modal', 'send-gems');
|
||||
|
|
|
|||
|
|
@ -169,14 +169,15 @@
|
|||
.form-group
|
||||
input.form-control(type='password', :placeholder="$t('confirmPass')", v-model='passwordUpdates.confirmPassword')
|
||||
button.btn.btn-primary(type='submit', @click='changeUser("password", passwordUpdates)') {{ $t('submit') }}
|
||||
hr
|
||||
|
||||
div
|
||||
h5 {{ $t('dangerZone') }}
|
||||
div
|
||||
h5 {{ $t('dangerZone') }}
|
||||
div
|
||||
button.btn.btn-danger(@click='openResetModal()',
|
||||
popover-trigger='mouseenter', popover-placement='right', v-b-popover.hover.auto="$t('resetAccPop')") {{ $t('resetAccount') }}
|
||||
button.btn.btn-danger(@click='openDeleteModal()',
|
||||
popover-trigger='mouseenter', v-b-popover.hover.auto="$t('deleteAccPop')") {{ $t('deleteAccount') }}
|
||||
button.btn.btn-danger(@click='openResetModal()',
|
||||
popover-trigger='mouseenter', popover-placement='right', v-b-popover.hover.auto="$t('resetAccPop')") {{ $t('resetAccount') }}
|
||||
button.btn.btn-danger(@click='openDeleteModal()',
|
||||
popover-trigger='mouseenter', v-b-popover.hover.auto="$t('deleteAccPop')") {{ $t('deleteAccount') }}
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@
|
|||
span.text(v-once, v-html="seasonal.notes")
|
||||
span.rectangle
|
||||
div.content(v-else-if="seasonal.featured.items.length !== 0")
|
||||
div.featured-label.with-border
|
||||
div.featured-label.with-border(v-if='!featuredGearBought')
|
||||
span.rectangle
|
||||
span.text(v-once) {{ $t('featuredset', { name: seasonal.featured.text }) }}
|
||||
span.rectangle
|
||||
|
|
@ -349,6 +349,7 @@
|
|||
selectedSortItemsBy: 'AZ',
|
||||
|
||||
hidePinned: false,
|
||||
featuredGearBought: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
|
@ -363,7 +364,22 @@
|
|||
},
|
||||
|
||||
seasonal () {
|
||||
return shops.getSeasonalShop(this.user);
|
||||
let seasonal = shops.getSeasonalShop(this.user);
|
||||
|
||||
let itemsNotOwned = seasonal.featured.items.filter(item => {
|
||||
return !this.user.items.gear.owned[item.key];
|
||||
});
|
||||
seasonal.featured.items = itemsNotOwned;
|
||||
|
||||
// If we are out of gear, show the spells
|
||||
// @TODO: Open this up when they are available.
|
||||
// @TODO: add dates to check instead?
|
||||
// if (seasonal.featured.items.length === 0) {
|
||||
// this.featuredGearBought = true;
|
||||
// seasonal.featured.items = seasonal.featured.items.concat(seasonal.categories[0].items);
|
||||
// }
|
||||
|
||||
return seasonal;
|
||||
},
|
||||
seasonalCategories () {
|
||||
return this.seasonal.categories;
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ div
|
|||
.item(:class="getItemClasses()")
|
||||
slot(name="itemBadge", :item="item", :emptyItem="emptyItem")
|
||||
|
||||
|
||||
span.badge.badge-pill.badge-item.badge-clock(
|
||||
v-if="item.event && showEventBadge",
|
||||
)
|
||||
|
|
@ -19,7 +18,6 @@ div
|
|||
slot(name="itemImage", :item="item")
|
||||
span.svg-icon.inline.icon-48(v-if="item.key == 'gem'", v-html="icons.gems")
|
||||
|
||||
|
||||
div.price
|
||||
span.svg-icon.inline.icon-16(v-html="icons[currencyClass]")
|
||||
|
||||
|
|
|
|||
|
|
@ -123,6 +123,7 @@ export default {
|
|||
};
|
||||
},
|
||||
created () {
|
||||
// @TODO the notifications always close even if timeout is false
|
||||
let timeout = this.notification.hasOwnProperty('timeout') ? this.notification.timeout : true;
|
||||
if (timeout) {
|
||||
let delay = this.notification.delay || 1000;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<template lang='pug'>
|
||||
b-modal#broken-task-modal(title="Broken Challenge", size='sm', :hide-footer="true", v-if='brokenChallengeTask.challenge')
|
||||
b-modal#broken-task-modal(title="Broken Challenge", size='sm', :hide-footer="true", v-if='brokenChallengeTask && brokenChallengeTask.challenge')
|
||||
.modal-body
|
||||
div(v-if='brokenChallengeTask.challenge.broken === "TASK_DELETED" || brokenChallengeTask.challenge.broken === "CHALLENGE_TASK_NOT_FOUND"')
|
||||
h2 {{ $t('brokenTask') }}
|
||||
|
|
@ -38,10 +38,14 @@ import notifications from 'client/mixins/notifications';
|
|||
|
||||
export default {
|
||||
mixins: [notifications],
|
||||
props: ['brokenChallengeTask'],
|
||||
components: {
|
||||
bModal,
|
||||
},
|
||||
computed: {
|
||||
brokenChallengeTask () {
|
||||
return this.$store.state.brokenChallengeTask;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...mapActions({
|
||||
destroyTask: 'tasks:destroy',
|
||||
|
|
@ -70,6 +74,7 @@ export default {
|
|||
this.destroyTask(this.brokenChallengeTask);
|
||||
},
|
||||
close () {
|
||||
this.$store.state.brokenChallengeTask = {};
|
||||
this.$root.$emit('hide::modal', 'broken-task-modal');
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
<template lang="pug">
|
||||
.task-wrapper
|
||||
broken-task-modal(:brokenChallengeTask='brokenChallengeTask')
|
||||
.task(@click='castEnd($event, task)')
|
||||
approval-header(:task='task', v-if='this.task.group.id', :group='group')
|
||||
.d-flex(:class="{'task-not-scoreable': isUser !== true}")
|
||||
|
|
@ -17,7 +16,7 @@
|
|||
.task-clickable-area(@click="edit($event, task)")
|
||||
h3.task-title(:class="{ 'has-notes': task.notes }", v-markdown="task.text")
|
||||
.task-notes.small-text(v-markdown="task.notes")
|
||||
.checklist(v-if="task.checklist && task.checklist.length > 0")
|
||||
.checklist(v-if="canViewchecklist")
|
||||
label.custom-control.custom-checkbox.checklist-item(
|
||||
v-for="item in task.checklist", :class="{'checklist-item-done': item.completed}",
|
||||
)
|
||||
|
|
@ -311,7 +310,6 @@ import checkIcon from 'assets/svg/check.svg';
|
|||
import bPopover from 'bootstrap-vue/lib/components/popover';
|
||||
import markdownDirective from 'client/directives/markdown';
|
||||
import notifications from 'client/mixins/notifications';
|
||||
import brokenTaskModal from './brokenTaskModal';
|
||||
import approvalHeader from './approvalHeader';
|
||||
import approvalFooter from './approvalFooter';
|
||||
|
||||
|
|
@ -321,7 +319,6 @@ export default {
|
|||
bPopover,
|
||||
approvalFooter,
|
||||
approvalHeader,
|
||||
brokenTaskModal,
|
||||
},
|
||||
directives: {
|
||||
markdown: markdownDirective,
|
||||
|
|
@ -339,7 +336,6 @@ export default {
|
|||
tags: tagsIcon,
|
||||
check: checkIcon,
|
||||
}),
|
||||
brokenChallengeTask: {},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
|
@ -348,6 +344,11 @@ export default {
|
|||
getTagsFor: 'tasks:getTagsFor',
|
||||
getTaskClasses: 'tasks:getTaskClasses',
|
||||
}),
|
||||
canViewchecklist () {
|
||||
let hasChecklist = this.task.checklist && this.task.checklist.length > 0;
|
||||
let userIsTaskUser = this.task.userId ? this.task.userId === this.user._id : true;
|
||||
return hasChecklist && userIsTaskUser;
|
||||
},
|
||||
leftControl () {
|
||||
const task = this.task;
|
||||
if (task.type === 'reward') return false;
|
||||
|
|
@ -505,7 +506,7 @@ export default {
|
|||
}
|
||||
},
|
||||
handleBrokenTask (task) {
|
||||
this.brokenChallengeTask = task;
|
||||
this.$store.state.brokenChallengeTask = task;
|
||||
this.$root.$emit('show::modal', 'broken-task-modal');
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
<template lang="pug">
|
||||
.row.user-tasks-page
|
||||
broken-task-modal
|
||||
task-modal(
|
||||
:task="editingTask || creatingTask",
|
||||
:purpose="creatingTask !== null ? 'create' : 'edit'",
|
||||
|
|
@ -289,6 +290,7 @@ import throttle from 'lodash/throttle';
|
|||
import cloneDeep from 'lodash/cloneDeep';
|
||||
import { mapState, mapActions } from 'client/libs/store';
|
||||
import taskDefaults from 'common/script/libs/taskDefaults';
|
||||
import brokenTaskModal from './brokenTaskModal';
|
||||
|
||||
import Item from 'client/components/inventory/item.vue';
|
||||
|
||||
|
|
@ -300,6 +302,7 @@ export default {
|
|||
bDropdownItem,
|
||||
Item,
|
||||
spells,
|
||||
brokenTaskModal,
|
||||
},
|
||||
directives: {
|
||||
markdown,
|
||||
|
|
@ -412,6 +415,8 @@ export default {
|
|||
},
|
||||
createTask (type) {
|
||||
this.creatingTask = taskDefaults({type, text: ''});
|
||||
this.creatingTask.tags = this.selectedTags;
|
||||
|
||||
// Necessary otherwise the first time the modal is not rendered
|
||||
Vue.nextTick(() => {
|
||||
this.$root.$emit('show::modal', 'task-modal');
|
||||
|
|
|
|||
29
website/client/libs/notifications.js
Normal file
29
website/client/libs/notifications.js
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
export function getDropClass (item) {
|
||||
let dropClass = '';
|
||||
if (item) {
|
||||
switch (item.type) {
|
||||
case 'Egg':
|
||||
dropClass = `Pet_Egg_${item.key}`;
|
||||
break;
|
||||
case 'HatchingPotion':
|
||||
dropClass = `Pet_HatchingPotion_${item.key}`;
|
||||
break;
|
||||
case 'Food':
|
||||
dropClass = `Pet_Food_${item.key}`;
|
||||
break;
|
||||
case 'armor':
|
||||
case 'back':
|
||||
case 'body':
|
||||
case 'eyewear':
|
||||
case 'head':
|
||||
case 'headAccessory':
|
||||
case 'shield':
|
||||
case 'weapon':
|
||||
dropClass = `shop_${item.key}`;
|
||||
break;
|
||||
default:
|
||||
dropClass = 'glyphicon glyphicon-gift';
|
||||
}
|
||||
}
|
||||
return dropClass;
|
||||
}
|
||||
|
|
@ -29,4 +29,4 @@ export function setup () {
|
|||
stripeScript.async = true;
|
||||
stripeScript.src = '//checkout.stripe.com/v2/checkout.js';
|
||||
firstScript.parentNode.insertBefore(stripeScript, firstScript);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import habiticaMarkdown from 'habitica-markdown';
|
||||
import { mapState } from 'client/libs/store';
|
||||
import { getDropClass } from 'client/libs/notifications';
|
||||
|
||||
export default {
|
||||
computed: {
|
||||
|
|
@ -14,32 +15,7 @@ export default {
|
|||
this.notify(message, 'crit', 'glyphicon glyphicon-certificate');
|
||||
},
|
||||
drop (val, item) {
|
||||
let dropClass = '';
|
||||
if (item) {
|
||||
switch (item.type) {
|
||||
case 'Egg':
|
||||
dropClass = `Pet_Egg_${item.key}`;
|
||||
break;
|
||||
case 'HatchingPotion':
|
||||
dropClass = `Pet_HatchingPotion_${item.key}`;
|
||||
break;
|
||||
case 'Food':
|
||||
dropClass = `Pet_Food_${item.key}`;
|
||||
break;
|
||||
case 'armor':
|
||||
case 'back':
|
||||
case 'body':
|
||||
case 'eyewear':
|
||||
case 'head':
|
||||
case 'headAccessory':
|
||||
case 'shield':
|
||||
case 'weapon':
|
||||
dropClass = `shop_${item.key}`;
|
||||
break;
|
||||
default:
|
||||
dropClass = 'glyphicon glyphicon-gift';
|
||||
}
|
||||
}
|
||||
let dropClass = getDropClass({key: item.key, type: item.type});
|
||||
this.notify(val, 'drop', dropClass);
|
||||
},
|
||||
quest (type, val) {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import sellOp from 'common/script/ops/sell';
|
|||
import unlockOp from 'common/script/ops/unlock';
|
||||
import buyArmoire from 'common/script/ops/buyArmoire';
|
||||
import rerollOp from 'common/script/ops/reroll';
|
||||
import { getDropClass } from 'client/libs/notifications';
|
||||
|
||||
export function buyItem (store, params) {
|
||||
const user = store.state.user.data;
|
||||
|
|
@ -78,10 +79,14 @@ export function genericPurchase (store, params) {
|
|||
|
||||
// @TODO: We might need to abstract notifications to library rather than mixin
|
||||
if (buyResult[1]) {
|
||||
const resData = buyResult[0];
|
||||
const item = resData.armoire;
|
||||
|
||||
store.state.notificationStore.push({
|
||||
title: '',
|
||||
text: buyResult[1],
|
||||
type: 'drop',
|
||||
type: item.type === 'experience' ? 'xp' : 'drop',
|
||||
icon: item.type === 'experience' ? null : getDropClass({type: item.type, key: item.dropKey}),
|
||||
timeout: true,
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -133,6 +133,7 @@ export default function () {
|
|||
notificationStore: [],
|
||||
modalStack: [],
|
||||
userIdToMessage: '',
|
||||
brokenChallengeTask: {},
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -80,6 +80,7 @@
|
|||
"logoUrl": "Logo URL",
|
||||
"assignLeader": "Assign Group Leader",
|
||||
"members": "Members",
|
||||
"memberList": "Member List",
|
||||
"partyList": "Order for party members in header",
|
||||
"banTip": "Boot Member",
|
||||
"moreMembers": "more members",
|
||||
|
|
@ -390,5 +391,7 @@
|
|||
"reverseChat": "Reverse Chat",
|
||||
"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."
|
||||
"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.",
|
||||
"groupGems": "Group Gems",
|
||||
"groupGemsDesc": "Guild Gems can be spent to make Challenges! In the future, you will be able to add more Guild Gems."
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
"exclusiveJackalopePetText": "Get the Royal Purple Jackalope pet, available only to subscribers!",
|
||||
"giftSubscription": "Want to gift a subscription to someone?",
|
||||
"giftSubscriptionText1": "Open their profile! You can do this by clicking on their avatar in your party header or by clicking on their name in chat.",
|
||||
"giftSubscriptionText2": "Click on the gift icon in the bottom left of their profile.",
|
||||
"giftSubscriptionText2": "Click on the gift icon in the top right of their profile.",
|
||||
"giftSubscriptionText3": "Select \"subscription\" and enter your payment information.",
|
||||
"giftSubscriptionText4": "Thanks for supporting Habitica!",
|
||||
"monthUSD": "USD / Month",
|
||||
|
|
@ -70,7 +70,7 @@
|
|||
"subCanceled": "Subscription will become inactive on",
|
||||
"buyGemsGoldTitle": "To Buy Gems with Gold",
|
||||
"becomeSubscriber": "Become a Subscriber",
|
||||
"subGemPop": "Because you subscribe to Habitica, you can purchase a number of Gems each month using Gold. You can see how many Gems are available to buy at the corner of the Gem icon.",
|
||||
"subGemPop": "Because you subscribe to Habitica, you can purchase a number of Gems each month using Gold.",
|
||||
"subGemName": "Subscriber Gems",
|
||||
"freeGemsTitle": "Obtain Gems for Free",
|
||||
"maxBuyGems": "You have bought all the Gems you can this month. More become available within the first three days of each month. Thanks for subscribing!",
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ module.exports = {
|
|||
},
|
||||
|
||||
availableSpells: [
|
||||
// 'spookySparkles',
|
||||
'spookySparkles',
|
||||
],
|
||||
|
||||
availableQuests: [
|
||||
|
|
|
|||
Loading…
Reference in a new issue