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
This commit is contained in:
Keith Holliday
2017-10-03 16:33:01 -05:00
committed by GitHub
parent 4fa3046104
commit 0aa16d7021
8 changed files with 246 additions and 191 deletions

View File

@@ -3,9 +3,10 @@ div
inbox-modal
creator-intro
profile
nav.navbar.navbar-inverse.fixed-top.navbar-toggleable-lg
nav.navbar.navbar-inverse.fixed-top.navbar-toggleable-md
.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)
ul.navbar-nav.mr-auto
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/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) {
.nav-link {
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 {
background: $purple-100 url(~assets/svg/for-css/bits.svg) right no-repeat;
padding-left: 25px;

View File

@@ -158,6 +158,7 @@ export default {
return {
yesterDailies: [],
levelBeforeYesterdailies: 0,
notificationData: {},
unlockLevels,
lastShownNotifications,
@@ -256,12 +257,8 @@ export default {
this.mp(mana);
},
userLvl (after, before) {
if (after <= before) return;
this.lvl();
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');
if (after <= before || this.isRunningYesterdailies) return;
this.showLevelUpNotifications(after);
},
userClassSelect (after) {
if (!after) return;
@@ -334,6 +331,13 @@ export default {
});
},
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) {
this.$root.$emit('playSound', sound);
},
@@ -390,6 +394,7 @@ export default {
return;
}
this.levelBeforeYesterdailies = this.user.stats.lvl;
this.$root.$emit('show::modal', 'yesterdaily');
},
async runYesterDailiesAction () {
@@ -405,6 +410,10 @@ export default {
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.scheduleNextCron();
},

View File

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

View File

@@ -46,6 +46,11 @@
min-height: 556px;
}
.task-wrapper {
position: relative;
z-index: 2;
}
.task-wrapper + .reward-items {
margin-top: 16px;
}
@@ -104,7 +109,7 @@
.column-background {
position: absolute;
bottom: 32px;
z-index: 7;
z-index: 1;
&.initial-description {
top: 30%;
@@ -160,19 +165,25 @@
<script>
import Task from './task';
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 shopItem from '../shops/shopItem';
import { shouldDo } from 'common/script/cron';
import inAppRewards from 'common/script/libs/inAppRewards';
import spells from 'common/script/content/spells';
import habitIcon from 'assets/svg/habit.svg';
import dailyIcon from 'assets/svg/daily.svg';
import todoIcon from 'assets/svg/todo.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 {
mixins: [buyMixin],
components: {
Task,
bModal,
@@ -252,8 +263,30 @@ export default {
},
inAppRewards () {
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 () {
return this.isUser === true && this.type === 'reward' && this.activeFilters[this.type].label !== 'custom';
@@ -378,6 +411,14 @@ export default {
}
},
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) {
this.$emit('openBuyDialog', rewardItem);
}

View File

@@ -148,14 +148,13 @@ div(v-if='user.stats.lvl > 10')
</style>
<script>
import axios from 'axios';
import isArray from 'lodash/isArray';
import bPopover from 'bootstrap-vue/lib/directives/popover';
import spells from '../../../common/script/content/spells';
import { mapState } from 'client/libs/store';
import notifications from 'client/mixins/notifications';
import spellsMixin from 'client/mixins/spells';
import Drawer from 'client/components/ui/drawer';
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';
export default {
mixins: [notifications],
mixins: [notifications, spellsMixin],
components: {
Drawer,
},
@@ -245,122 +244,6 @@ export default {
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 () {
let user = this.user;
if (!user.party.quest) return 0;

View File

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

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