mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-17 14:47:53 +01:00
issue(11266) - Restyle level-up modal with sparkles (#12486)
* issue(11266) - Restyle level-up modal with sparkles * issue(11266) - Add reward display to level up modal At levels 15, 30, 40 and 60 the earned quests are now shown in the level-up modal. * issue(11266) - Simplify css and don't use custom footer * issue(11266) - Don't show pink bars and use colour variables
This commit is contained in:
@@ -206,5 +206,4 @@
|
||||
font-style: normal;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
7
website/client/src/assets/svg/sparkles-left.svg
Normal file
7
website/client/src/assets/svg/sparkles-left.svg
Normal file
@@ -0,0 +1,7 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="15" viewBox="0 0 32 15">
|
||||
<g fill="none" fill-rule="evenodd">
|
||||
<path fill="#FFA624" d="M9.074 8.485L13.333 6.364 9.074 4.242 6.944 0 4.815 4.242 0.556 6.364 4.815 8.485 6.944 12.727z" transform="rotate(90 16 16)"/>
|
||||
<path fill="#FFBE5D" d="M12.634 19.632L15.19 18.359 12.634 17.087 11.356 14.541 10.079 17.087 7.523 18.359 10.079 19.632 11.356 22.177z" transform="rotate(90 16 16) rotate(53 11.356 18.36)"/>
|
||||
<path fill="#FEDEAD" d="M7.111 29.091L9.667 27.818 7.111 26.545 5.833 24 4.556 26.545 2 27.818 4.556 29.091 5.833 31.636z" transform="rotate(90 16 16)"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 662 B |
9
website/client/src/assets/svg/star-group.svg
Normal file
9
website/client/src/assets/svg/star-group.svg
Normal file
@@ -0,0 +1,9 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="64" viewBox="0 0 40 64">
|
||||
<g fill="none" fill-rule="evenodd">
|
||||
<path fill="#77F4C7" d="M35.667 8.667L40 6.5 35.667 4.333 33.5 0 31.333 4.333 27 6.5 31.333 8.667 33.5 13z" transform="matrix(-1 0 0 1 40 0)"/>
|
||||
<path fill="#BDA8FF" d="M24.667 35.667L30 33 24.667 30.333 22 25 19.333 30.333 14 33 19.333 35.667 22 41z" transform="matrix(-1 0 0 1 40 0)"/>
|
||||
<path fill="#8EEDF6" d="M35.667 60.667L39 59 35.667 57.333 34 54 32.333 57.333 29 59 32.333 60.667 34 64z" transform="matrix(-1 0 0 1 40 0)"/>
|
||||
<path fill="#FFBE5D" d="M6.667 46.667L10 45 6.667 43.333 5 40 3.333 43.333 0 45 3.333 46.667 5 50z" transform="matrix(-1 0 0 1 40 0)"/>
|
||||
<path fill="#FFB6B8" d="M5.667 17.667L8 16.5 5.667 15.333 4.5 13 3.333 15.333 1 16.5 3.333 17.667 4.5 20z" transform="matrix(-1 0 0 1 40 0)"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 889 B |
@@ -1,109 +1,200 @@
|
||||
<template>
|
||||
<b-modal
|
||||
id="level-up"
|
||||
:title="$t('levelUpShare')"
|
||||
size="sm"
|
||||
:hide-footer="true"
|
||||
:hide-header="true"
|
||||
:title="title"
|
||||
ok-only
|
||||
:ok-title="$t('onwards')"
|
||||
v-bind:footer-class="{ greyed: displayRewardQuest }"
|
||||
>
|
||||
<div class="modal-body text-center">
|
||||
<h2>{{ $t('reachedLevel', {level: user.stats.lvl}) }}</h2>
|
||||
<section class="d-flex">
|
||||
<span
|
||||
class="star-group mirror"
|
||||
v-html="icons.starGroup"
|
||||
></span>
|
||||
<avatar
|
||||
class="avatar"
|
||||
:member="user"
|
||||
/>
|
||||
<p class="text">
|
||||
<span
|
||||
class="star-group"
|
||||
v-html="icons.starGroup"
|
||||
></span>
|
||||
</section>
|
||||
|
||||
<p
|
||||
class="text"
|
||||
v-once
|
||||
>
|
||||
{{ $t('levelup') }}
|
||||
</p>
|
||||
<button
|
||||
class="btn btn-primary"
|
||||
@click="close()"
|
||||
|
||||
<section
|
||||
v-if="displayRewardQuest"
|
||||
class="greyed"
|
||||
>
|
||||
{{ $t('onwards') }}
|
||||
</button>
|
||||
<br>
|
||||
<div
|
||||
class="your-rewards d-flex"
|
||||
v-once
|
||||
>
|
||||
<span
|
||||
class="sparkles"
|
||||
v-html="icons.sparkles"
|
||||
></span>
|
||||
<span class="text">{{ $t('yourRewards') }}</span>
|
||||
<span
|
||||
class="sparkles mirror"
|
||||
v-html="icons.sparkles"
|
||||
></span>
|
||||
</div>
|
||||
|
||||
<div :class="questClass"></div>
|
||||
</section>
|
||||
<!-- @TODO: Keep this? .checkboxinput(type='checkbox', v-model=
|
||||
'user.preferences.suppressModals.levelUp', @change='changeLevelupSuppress()')
|
||||
label(style='display:inline-block') {{ $t('dontShowAgain') }}
|
||||
-->
|
||||
</div>
|
||||
</b-modal>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@import '~@/assets/scss/colors.scss';
|
||||
|
||||
#level-up {
|
||||
h2 {
|
||||
color: #4f2a93;
|
||||
.modal-content {
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 14px 28px 0 rgba($black, 0.24), 0 10px 10px 0 rgba($black, 0.28);
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
min-width: 28em;
|
||||
@media (min-width: 576px) {
|
||||
.modal-sm {
|
||||
max-width: 330px;
|
||||
}
|
||||
}
|
||||
|
||||
header {
|
||||
padding: 0;
|
||||
border: none;
|
||||
|
||||
h5 {
|
||||
margin: 31px auto 16px;
|
||||
color: $purple-200;
|
||||
}
|
||||
|
||||
button {
|
||||
position: absolute;
|
||||
right: 18px;
|
||||
top: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
footer {
|
||||
padding: 0;
|
||||
border: none;
|
||||
|
||||
button {
|
||||
margin: 0 auto 32px auto;
|
||||
}
|
||||
}
|
||||
|
||||
.greyed {
|
||||
background-color: $gray-700;
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
padding-top: 1em;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
margin-top: 0;
|
||||
.mirror {
|
||||
transform: scaleX(-1);
|
||||
}
|
||||
|
||||
.herobox {
|
||||
margin: auto 8.3em;
|
||||
width: 6em;
|
||||
height: 9em;
|
||||
padding-top: 0;
|
||||
cursor: default;
|
||||
.star-group {
|
||||
margin: auto;
|
||||
|
||||
svg {
|
||||
height: 64px;
|
||||
width: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
.character-sprites {
|
||||
margin: 0;
|
||||
width: 0;
|
||||
.avatar {
|
||||
cursor: auto;
|
||||
}
|
||||
|
||||
.class-badge {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.text {
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
color: #686274;
|
||||
margin-top: 1em;
|
||||
min-height: 0px;
|
||||
margin: 25px 24px 24px;
|
||||
min-height: auto;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style scoped>
|
||||
.avatar {
|
||||
margin-left: 6.8em;
|
||||
.your-rewards {
|
||||
margin: 0 auto;
|
||||
width: fit-content;
|
||||
|
||||
.sparkles {
|
||||
width: 32px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.text {
|
||||
font-weight: bold;
|
||||
margin: 16px;
|
||||
color: $gray-50;
|
||||
}
|
||||
}
|
||||
|
||||
section.greyed {
|
||||
padding-bottom: 17px
|
||||
}
|
||||
|
||||
.scroll {
|
||||
margin: -11px auto 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import Avatar from '../avatar';
|
||||
import { mapState } from '@/libs/store';
|
||||
import { MAX_HEALTH as maxHealth } from '@/../../common/script/constants';
|
||||
import styleHelper from '@/mixins/styleHelper';
|
||||
import starGroup from '@/assets/svg/star-group.svg';
|
||||
import sparkles from '@/assets/svg/sparkles-left.svg';
|
||||
|
||||
const levelQuests = {
|
||||
15: 'atom1',
|
||||
30: 'vice1',
|
||||
40: 'goldenknight1',
|
||||
60: 'moonstone1',
|
||||
};
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Avatar,
|
||||
},
|
||||
mixins: [styleHelper],
|
||||
data () {
|
||||
return {
|
||||
statsAllocationBoxIsOpen: true,
|
||||
maxHealth,
|
||||
icons: Object.freeze({
|
||||
starGroup,
|
||||
sparkles,
|
||||
}),
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState({ user: 'user.data' }),
|
||||
showAllocation () {
|
||||
return this.$store.getters['members:hasClass'](this.user) && !this.user.preferences.automaticAllocation;
|
||||
title () {
|
||||
return this.$t('reachedLevel', { level: this.user.stats.lvl });
|
||||
},
|
||||
displayRewardQuest () {
|
||||
return this.user.stats.lvl in levelQuests;
|
||||
},
|
||||
questClass () {
|
||||
return `scroll inventory_quest_scroll_${levelQuests[this.user.stats.lvl]}`;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
close () {
|
||||
this.$root.$emit('bv::hide::modal', 'level-up');
|
||||
},
|
||||
changeLevelupSuppress () {
|
||||
// @TODO: dispatch set({"preferences.suppressModals.levelUp":
|
||||
// user.preferences.suppressModals.levelUp?true: false})
|
||||
|
||||
@@ -46,7 +46,6 @@
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@import '~@/assets/scss/colors.scss';
|
||||
@import '~@/assets/scss/modal.scss';
|
||||
|
||||
#hatchedPet-modal {
|
||||
|
||||
@@ -389,7 +389,7 @@ export default {
|
||||
data () {
|
||||
// Levels that already display modals and should not trigger generic Level Up
|
||||
const unlockLevels = {
|
||||
10: 'class system',
|
||||
10: 'Class system',
|
||||
50: 'Orb of Rebirth',
|
||||
};
|
||||
|
||||
@@ -661,13 +661,7 @@ export default {
|
||||
showLevelUpNotifications (newlevel) {
|
||||
this.lvl();
|
||||
this.playSound('Level_Up');
|
||||
// NOTE this code isn't actually used because no modal is shown when a quest is dropped
|
||||
// In case it's added again it should keep in mind that it will not work
|
||||
// when the user progress to the next level using the RYA modal
|
||||
// as it doesn't score the tasks on the client side and thus this.user._tmp is not filled
|
||||
// with any value
|
||||
// if (this.user._tmp && this.user._tmp.drop && this.user._tmp.drop.type === 'Quest') return;
|
||||
if (this.unlockLevels[`${newlevel}`]) return;
|
||||
if (this.unlockLevels[newlevel]) return;
|
||||
if (!this.user.preferences.suppressModals.levelUp) this.$root.$emit('bv::show::modal', 'level-up');
|
||||
},
|
||||
playSound (sound) {
|
||||
|
||||
@@ -696,12 +696,6 @@
|
||||
box-shadow: 0 14px 28px 0 rgba($black, 0.24), 0 10px 10px 0 rgba($black, 0.28);
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
// the body has a margin/padding that can't be found
|
||||
// if found please remove that padding and this style
|
||||
// margin-bottom: -2rem;
|
||||
}
|
||||
|
||||
.modal-header, .modal-body, .modal-footer {
|
||||
padding: 0px;
|
||||
border: none;
|
||||
@@ -711,10 +705,6 @@
|
||||
cursor: auto;
|
||||
}
|
||||
|
||||
.cursor-auto {
|
||||
cursor: auto;
|
||||
}
|
||||
|
||||
.task-modal-header {
|
||||
color: $white;
|
||||
width: 100%;
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
import LevelUp from '@/components/achievements/levelUp.vue';
|
||||
|
||||
/*
|
||||
// I couldn't get rendering to work for the modal, this is what I tried.
|
||||
// Now the testing is done by overriding `this` for the exported computed
|
||||
// functions directly. I intend to come back to this later to test it
|
||||
// properly in a rendered component.
|
||||
|
||||
import { mount, createLocalVue } from '@vue/test-utils';
|
||||
import BootstrapVue from 'bootstrap-vue';
|
||||
import Store from '@/libs/store';
|
||||
|
||||
const localVue = createLocalVue();
|
||||
localVue.use(Store);
|
||||
localVue.use(BootstrapVue);
|
||||
|
||||
function createContainer () {
|
||||
const container = document.createElement('div');
|
||||
document.body.appendChild(container);
|
||||
return container;
|
||||
}
|
||||
*/
|
||||
|
||||
describe('LevelUp', () => {
|
||||
function testFunction (name, level = 10) {
|
||||
return LevelUp.computed[name].bind({
|
||||
user: { stats: { lvl: level } },
|
||||
$t: (...args) => args.map(JSON.stringify).join(' '),
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
// More potential rendering code
|
||||
let wrapper;
|
||||
beforeEach(async () => {
|
||||
wrapper = mount(LevelUp, {
|
||||
store: new Store({
|
||||
state: { user: { data: createUser() } },
|
||||
getters: {},
|
||||
actions: {},
|
||||
}),
|
||||
propsData: {
|
||||
static: true,
|
||||
visible: true,
|
||||
},
|
||||
localVue,
|
||||
mocks: { $t: string => string },
|
||||
attachTo: createContainer(),
|
||||
});
|
||||
});
|
||||
*/
|
||||
|
||||
it('displays the right level in the title', () => {
|
||||
const title = testFunction('title', 12);
|
||||
|
||||
expect(title()).to.equal('"reachedLevel" {"level":12}');
|
||||
});
|
||||
|
||||
it('does not display rewards for level 10', () => {
|
||||
const displayRewardQuest = testFunction('displayRewardQuest', 10);
|
||||
|
||||
expect(displayRewardQuest()).to.be.false;
|
||||
});
|
||||
|
||||
[15, 30, 40, 60].forEach(level => {
|
||||
it(`does display rewards for level ${level}`, () => {
|
||||
const displayRewardQuest = testFunction('displayRewardQuest', level);
|
||||
|
||||
expect(displayRewardQuest()).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
it('generates the right test class for level 15', () => {
|
||||
const questClass = testFunction('questClass', 15);
|
||||
|
||||
expect(questClass()).to.equal('scroll inventory_quest_scroll_atom1');
|
||||
});
|
||||
});
|
||||
@@ -4,6 +4,7 @@
|
||||
"onwards": "Onwards!",
|
||||
"levelup": "By accomplishing your real life goals, you leveled up and are now fully healed!",
|
||||
"reachedLevel": "You Reached Level <%= level %>",
|
||||
"yourRewards": "Your Rewards",
|
||||
"gettingStartedDesc": "Complete these onboarding tasks and you’ll earn <strong>5 Achievements</strong> and <strong class=\"gold-amount\">100 Gold</strong> once you’re done!",
|
||||
"onboardingProgress": "<%= percentage %>% progress",
|
||||
"yourProgress": "Your Progress",
|
||||
|
||||
Reference in New Issue
Block a user