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:
Keith Holliday 2017-10-01 20:42:02 -05:00 committed by GitHub
parent 02f8ba1638
commit 39d7581c6c
30 changed files with 226 additions and 109 deletions

View file

@ -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,

View file

@ -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');
},
},
};

View file

@ -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

View file

@ -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")

View file

@ -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")

View file

@ -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;

View file

@ -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>

View file

@ -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 () {

View 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>

View file

@ -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') }}

View file

@ -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>

View file

@ -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');
}

View file

@ -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;

View file

@ -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;"')

View file

@ -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');

View file

@ -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>

View file

@ -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;

View file

@ -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]")

View file

@ -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;

View file

@ -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');
},
},

View file

@ -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');
},
},

View file

@ -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');

View 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;
}

View file

@ -29,4 +29,4 @@ export function setup () {
stripeScript.async = true;
stripeScript.src = '//checkout.stripe.com/v2/checkout.js';
firstScript.parentNode.insertBefore(stripeScript, firstScript);
}
}

View file

@ -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) {

View file

@ -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,
});
}

View file

@ -133,6 +133,7 @@ export default function () {
notificationStore: [],
modalStack: [],
userIdToMessage: '',
brokenChallengeTask: {},
},
});

View file

@ -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."
}

View file

@ -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!",

View file

@ -22,7 +22,7 @@ module.exports = {
},
availableSpells: [
// 'spookySparkles',
'spookySparkles',
],
availableQuests: [