Update Party / Group Sidebar / Quest states (#12793)

* move groups/sidebar to groupSidebar.vue

* lint files

* extract group/party sidebar to rightSidebar.vue

* wip stories with example data

* update stories - wip sidebar re-styling

* message party / group leader + move items to the menu

* update paddings /place for quest section

* invite to party / guild

* update labels (* Party / Guild )

* guild-background to group-background

* correct menu order + missing a label based on the group type

* no quest - styles / layout applied

* quest owner / not started - styles applied   + extracted questActions from questDetailsModal.vue to a mixin

* no challenge style

* hover with underlines

* quest-pending area layout / margins

* "Collection Quest/Quest Owner Participating" Styling Done

* group sidebar menu with icons / background

* remove most participate button styles

* fix quest-invite panel

* move "Start Quest" + add "Leave Quest"

* Not Participating + Boss + Rage Quests restyling

* party quest changes - invitedToQuest + button styles + no-items style + view details

* fix icons + rage value + colors

* fix duplicate key

* hide items label if 0 items found + hide pending damage if there is none + sidebar section margin + fix percent calculation 0 => 0%

* combine quest abandon / cancel to one call + hide begin if quest has already started + close modal if quest was canceled

* remove unused translate string

* allow leaving an accepted but inactive quest + disable leave when user is quest leader

* update "are you sure" questions - remove "doubleSureAbort" - add "sureLeaveInactive"

* sidebar margins + menu icon color

* refactored css rules

* improve some styles

* fix button spacing

* fix dropmenu with icon hover

* hide leave quest for leaders + fix quest buttons spacing

* add pending items label

* remove "X items found" label

* first round of fixes

* last v-once

* Update Quest Dialogs (#13112)

* new quest rewards panel + extract questPopover and itemWithLabel

* WIP: questInfo still not applying the row-height..

* split up start-quest-modal into select and detail modal - also rename the current quest-details to be the group-quest-details modal

* remove start-quest-modal from modal-scss

* update package-lock

* WIP before using the quest sidebar branch as a base

* move quest detail actions to the "new" details dialog

* quest details layout for owner / participant

* fix quest rewards - open details modal from sidebar

* apply quest-details dialog styles to the buyQuestModal one

* fix quest reward icons / popover / texts

* WIP back to quest selection

* fix lint

* merge selectQuestModal.vue with questDetailModal.vue + UI for the select quest

* fix margins / layout / labels

* fix quest detail + wip invitationListModal.vue / participantListModal.vue

* fix questmodal user label centered

* fix centered reward items + grouping items and adding a count-badge

* sort quests by AZ or quantity

* invitations modal

* remove console.info

* complete participantListModal.vue + extracted getClassName

* missed a file for getClassName extraction

* fix invitations

* select the actual quest on details

* fix margins on invite to party / start quest buttons

* replace buyQuestModal close button and title

* fix recursion due to the same name

* missing import

* sort quantity by highest first

* fix "Can't find a Quest to start" styles

* fix "your balance" padding

* fix quest collections / drop items

* fix member details in participants list

* fix quest info

* remove nullable because the build doesn't like it (on this file..)

* add questCompleted to the stories + fix getDropName

* replace quest-rewards in questCompleted.vue

* fix questCompleted.vue style

* delete obsolete components

* add missing spritesheets to storebook

* requested pr changes

* refactored fetchMember

* revert optional chaining

* fix merge conflicts

* fix rightSidebar hover colors - $scss var to css var

* overflow auto instead of scroll

* prevent wrapping of quest collections

* rollback to multi line quest items

* use min-width for the quest popover
This commit is contained in:
negue
2021-05-28 23:11:43 +02:00
committed by GitHub
parent 33e7a378f7
commit a8b58815b4
69 changed files with 3967 additions and 1327 deletions

View File

@@ -6,11 +6,12 @@
>
<div class="col-12 text-center">
<div
class="svg-icon"
class="svg-icon quest-icon color"
v-html="icons.questIcon"
v-once
></div>
<h4 v-once>
{{ $t('youAreNotOnQuest') }}
{{ $t('yourPartyIsNotOnQuest') }}
</h4>
<p v-once>
{{ $t('questDescription') }}
@@ -18,102 +19,78 @@
<button
v-once
class="btn btn-secondary"
@click="openStartQuestModal()"
@click="openSelectQuestModal()"
>
{{ $t('startAQuest') }}
</button>
</div>
</div>
<div
v-if="onPendingQuest && !onActiveQuest"
class="row quest-active-section"
>
<div class="col-2">
<div
class="quest"
:class="`inventory_quest_scroll_${questData.key}`"
></div>
</div>
<div class="col-6 titles">
<strong>{{ questData.text() }}</strong>
<p>{{ acceptedCount }} / {{ group.memberCount }}</p>
</div>
<div class="col-4">
<button
class="btn btn-secondary"
@click="openQuestDetails()"
>
{{ $t('details') }}
{{ $t('selectQuest') }}
</button>
</div>
</div>
<div
v-if="user.party.quest && user.party.quest.RSVPNeeded"
class="row quest-active-section quest-invite"
class="quest-active-section quest-invite"
>
<span>{{ $t('wouldYouParticipate') }}</span>
<button
class="btn btn-primary accept"
@click="questAccept(group._id)"
>
{{ $t('accept') }}
</button>
<button
class="btn btn-primary reject"
@click="questReject(group._id)"
>
{{ $t('reject') }}
</button>
<span class="participate">{{ $t('invitedToThisQuest') }}</span>
<div class="buttons">
<button
class="btn btn-success accept"
@click="questAccept(group._id)"
>
{{ $t('accept') }}
</button>
<button
class="btn btn-danger reject"
@click="questReject(group._id)"
>
{{ $t('reject') }}
</button>
</div>
</div>
<div
v-if="!onPendingQuest && onActiveQuest"
class="row quest-active-section"
:class="{'not-participating': !userIsOnQuest}"
>
<div class="col-12 text-center">
<div
class="quest-boss"
:class="'quest_' + questData.key"
></div>
<h3 v-once>
{{ questData.text() }}
</h3>
<div class="quest-box">
<div
v-if="questData.collect"
class="collect-info"
>
<div class="row">
<div class="col-12">
<a
class="float-right"
@click="openParticipantList()"
>{{ $t('participantsTitle') }}</a>
</div>
</div>
<div
v-for="(value, key) in questData.collect"
:key="key"
class="row"
class="quest-item-row"
>
<div class="col-2">
<div class="quest-item-icon">
<div :class="'quest_' + questData.key + '_' + key"></div>
</div>
<div class="col-10">
<strong>{{ value.text() }}</strong>
<div class="quest-item-info">
<span class="label quest-label">{{ value.text() }}</span>
<div class="grey-progress-bar">
<div
class="collect-progress-bar"
:style="{width: (group.quest.progress.collect[key] / value.count) * 100 + '%'}"
></div>
</div>
<strong>{{ group.quest.progress.collect[key] }} / {{ value.count }}</strong>
<div class="item-progress-row">
<span
class="label item-progress"
:class="{'no-items': group.quest.progress.collect[key] === 0}"
>
{{ group.quest.progress.collect[key] }} / {{ value.count }}
</span>
</div>
</div>
</div>
<div
v-if="userIsOnQuest"
class="text-right"
>
{{ parseFloat(user.party.quest.progress.collectedItems) || 0 }} items found
<div v-if="hasPendingQuestItems"
class="item-progress-pending mb-2">
<div class="pending-amount pt-2 pb-2">
{{ $t('questItemsPending', { amount: user.party.quest.progress.collectedItems }) }}
</div>
</div>
</div>
<div
@@ -121,20 +98,14 @@
class="boss-info"
>
<div class="row">
<div class="col-6">
<div class="col-12">
<h4
v-once
class="float-left"
class="float-left boss-name"
>
{{ questData.boss.name() }}
</h4>
</div>
<div class="col-6">
<a
class="float-right"
@click="openParticipantList()"
>{{ $t('participantsTitle') }}</a>
</div>
</div>
<div class="row">
<div class="col-12">
@@ -143,12 +114,22 @@
class="boss-health-bar"
:style="{width: bossHpPercent + '%'}"
></div>
<div
class="pending-health-bar"
:style="{width: pendingHpPercent + '%'}"
>
</div>
</div>
</div>
</div>
<div class="row boss-details">
<div class="col-6">
<span class="float-left">
<span class="float-left hp-value">
<div
class="svg-icon health-icon"
v-html="icons.healthNoPaddingIcon"
v-once
></div>
{{
(Math.ceil(parseFloat(group.quest.progress.hp) * 100) / 100)
| localizeNumber(user.preferences.language, { toFixed:2 })
@@ -156,22 +137,30 @@
parseFloat(questData.boss.hp)
| localizeNumber(user.preferences.language, { toFixed:2 })
}}
<strong>HP</strong>
<!-- current boss hp uses ceil so
you don't underestimate damage needed to end quest-->
</span>
</div>
<div
v-if="userIsOnQuest"
v-if="userIsOnQuest && user.party.quest.progress.up"
class="col-6"
>
<!-- @TODO: Why do we not sync quest
progress on the group doc? Each user could have different progress.-->
<span class="float-right">
<span class="float-right pending-value">
<div
class="svg-icon sword-icon"
v-html="icons.swordIcon"
v-once
></div>
{{
(user.party.quest.progress.up || 0)
| floor(10)
| localizeNumber(user.preferences.language, { toFixed:1 })
}} {{ $t('pendingDamageLabel') }}
}}
{{ $t('pendingDamageLabel') }}
</span>
<!-- player's pending damage uses floor so you
don't overestimate damage you've already done-->
@@ -196,29 +185,80 @@
class="row boss-details rage-details"
>
<div class="col-6">
<span
class="float-left"
>{{ $t('rage') }} {{
parseFloat(group.quest.progress.rage)
| localizeNumber(user.preferences.language, { toFixed: 2 })
}} / {{
questData.boss.rage.value
| localizeNumber(user.preferences.language)
}}</span>
<span class="float-left rage-value">
<div
class="svg-icon rage-icon icon-16"
v-html="icons.rageIcon"
v-once
>
</div>
<span
class="float-left"
>{{ $t('rage') }} {{
parseFloat(group.quest.progress.rage)
| localizeNumber(user.preferences.language, { toFixed: 2 })
}} / {{
questData.boss.rage.value
| localizeNumber(user.preferences.language)
}}</span>
<strong v-once>{{ $t('rage') }}</strong>
</span>
</div>
</div>
</div>
</div>
<button
v-if="canEditQuest"
v-once
class="btn btn-secondary"
@click="questAbort()"
>
{{ $t('abort') }}
</button>
</div>
</div>
<div
v-if="onPendingQuest || onActiveQuest"
class="quest-pending-section"
>
<div class="titles">
<strong>{{ questData.text() }} </strong>
<a
class="members-invited"
@click="openParticipantList()"
>
{{ $t('membersParticipating', {accepted: acceptedCount, invited: group.memberCount}) }}
</a>
</div>
<div class="quest-icon">
<div
class="quest"
:class="`inventory_quest_scroll_${questData.key}`"
></div>
</div>
</div>
<div v-if="onPendingQuest || onActiveQuest"
class="quest-buttons">
<button
class="btn btn-secondary w-100"
@click="openQuestDetails()"
>
{{ $t('viewDetails') }}
</button>
</div>
<div v-if="userIsQuestLeader && !onActiveQuest"
class="quest-buttons">
<button
class="btn btn-success w-100"
@click="startQuest()"
>
{{ $t('startQuest') }}
</button>
</div>
<div
v-if="userIsOnQuest && !userIsQuestLeader"
class="leave-quest-holder"
>
<a
v-once
class="leave-quest text-center"
@click="questLeave()"
>
{{ $t('leaveQuest') }}
</a>
</div>
</sidebar-section>
</template>
@@ -230,30 +270,49 @@
width: 25px;
}
.quest-buttons {
margin-bottom: 0.25rem;
&:nth-last-of-type(2) {
margin-bottom: 0;
}
}
.quest-buttons + .quest-buttons {
margin-top: 0.25rem;
}
.quest-boss {
margin: 0 auto;
margin: 0 auto 1.188rem;
}
.boss-health-bar {
width: 80%;
background-color: red;
height: 15px;
margin-bottom: .5em;
background-color: $red-50;
height: 0.75rem;
display: inline-block;
}
.pending-health-bar {
height: 0.75rem;
background-color: $yellow-50;
display: inline-block;
}
.rage-details {
margin-bottom: 1em;
}
.boss-health-bar.rage-bar {
margin-top: 1em;
background-color: orange;
background-color: $orange-50;
}
.grey-progress-bar {
width: 100%;
height: 15px;
height: 0.75rem;
background-color: #e1e0e3;
border-radius: 2px;
overflow: hidden;
display: flex;
}
.collect-progress-bar {
@@ -264,35 +323,84 @@
.no-quest-section {
padding: 2em;
color: $gray-300;
h4 {
color: $gray-300;
margin-bottom: 0;
}
p {
margin-bottom: 2em;
margin-bottom: 1em;
color: $gray-100;
font-size: 0.75rem;
line-height: 1.33;
}
.svg-icon {
height: 30px;
width: 30px;
margin: 0 auto;
margin-bottom: 2em;
.quest-icon {
width: 1.125rem;
height: 1.25rem;
margin: 0 auto 0.5em;
object-fit: contain;
border-radius: 2px;
color: $gray-200;
}
}
.quest-pending-section {
display: flex;
margin-bottom: 0.5rem;
.titles {
flex: 1;
margin-top: 1rem;
font-size: 0.75rem;
line-height: 1.33;
strong {
display: block;
min-height: 1rem;
font-weight: bold;
font-size: 0.75rem;
line-height: 1.33;
color: $gray-100;
margin-bottom: 0.25rem;
}
.members-invited {
min-height: 1rem;
color: $blue-10;
margin: 0;
&:hover, &:focus {
color: $blue-10;
text-decoration: underline;
}
}
}
.quest-icon {
width: 4.25rem;
height: 4.25rem;
}
}
.quest-active-section {
margin-bottom: 0.5rem;
.participate {
font-size: 0.75rem;
font-weight: bold;
line-height: 1.33;
color: $white;
}
.titles {
padding-top: .5em;
padding-top: .5em;
}
.quest-box {
background-image: url('~@/assets/svg/for-css/quest-border.svg');
background-size: 100% 100%;
width: 100%;
padding: .5em;
margin-bottom: 1em;
padding: 0.75rem 1rem;
border-radius: 4px;
background-color: $white;
a {
font-family: 'Roboto Condensed', sans-serif;
@@ -306,43 +414,219 @@
}
}
.boss-info, .collect-info {
width: 90%;
margin: 0 auto;
.boss-info {
text-align: left;
.boss-name {
font-size: 0.75rem;
font-weight: bold;
line-height: 1.33;
color: $gray-100;
margin-bottom: 0.25rem;
}
.boss-details {
margin-top: 0.5rem;
}
.hp-value {
font-size: 0.75rem;
line-height: 1.33;
color: $maroon-10;
display: flex;
}
.rage-value {
font-size: 0.75rem;
line-height: 1.33;
color: $orange-10;
display: flex;
height: 1rem;
.span {
align-self: center;
}
}
.pending-value {
font-size: 0.75rem;
line-height: 1.33;
text-align: right;
color: $yellow-5;
display: flex;
}
.health-icon {
width: 1rem;
height: 1rem;
object-fit: contain;
margin-right: 0.25rem;
}
.rage-icon {
width: 1rem;
height: 1rem;
object-fit: contain;
margin-right: 0.25rem;
::v-deep svg {
height: 1rem;
}
}
.sword-icon {
width: 1rem;
height: 1rem;
object-fit: contain;
margin-right: 0.25rem;
}
strong {
margin-left: 0.25rem;
}
}
}
.quest-invite {
background-color: #2995cd;
color: #fff;
padding: 1em;
background-color: $blue-10;
color: $white;
display: flex;
border-radius: 2px;
span {
margin-top: .3em;
font-size: 14px;
font-weight: bold;
.participate {
margin-top: 0.75rem;
margin-bottom: 0.75rem;
margin-left: 1rem;
flex: 1;
}
.buttons {
margin-top: 0.5rem;
margin-bottom: 0.5rem;
margin-right: 0.5rem;
}
.accept, .reject {
padding: .2em 1em;
font-size: 12px;
height: 24px;
font-size: 0.75rem;
font-weight: bold;
line-height: 1.33;
text-align: center;
color: $white;
border-radius: 2px;
box-shadow: 0 2px 2px 0 rgba(26, 24, 29, 0.16), 0 1px 4px 0 rgba(26, 24, 29, 0.12);
}
.accept {
background-color: #24cc8f;
margin-left: 4em;
margin-right: .5em;
margin: 0 0.5rem 0 0;
}
.reject {
border-radius: 2px;
background-color: #f74e52;
}
}
.leave-quest-holder {
display: flex;
justify-content: center;
}
.leave-quest {
font-size: 0.875rem;
line-height: 1.71;
color: $maroon-50;
display: block;
margin-top: 1rem;
&:hover, &:focus {
color: $maroon-50;
text-decoration: underline;
}
&.disabled {
color: $gray-200;
cursor: default;
pointer-events: none;
}
}
.quest-item-row {
display: flex;
margin-bottom: 0.25rem;
.quest-item-icon {
margin-right: 0.813rem;
width: 3.5rem;
height: 3.5rem;
display: flex;
align-items: center;
justify-content: center;
align-self: center;
}
.quest-item-info {
flex: 1;
text-align: left;
.label {
font-size: 0.75rem;
line-height: 1.33;
color: $gray-100;
&.quest-label {
font-weight: bold;
margin-bottom: 0.25rem;
display: block;
}
}
.item-progress-row {
margin-top: 0.5rem;
display: flex;
> * {
flex: 1;
}
}
.item-progress:not(.no-items) {
color: $green-10;
}
.item-progress-label {
text-align: right;
color: $gray-100;
}
}
}
.item-progress-pending {
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
margin-left: -1rem;
margin-right: -1rem;
margin-bottom: -1rem !important;
background-color: $gray-200;
.pending-amount {
font-size: 12px;
font-weight: bold;
line-height: 1.33;
text-align: center;
color: $white;
}
}
.not-participating {
opacity: 0.5;
}
.rage-bar-row {
margin-top: 0.875rem;
}
</style>
<script>
@@ -353,16 +637,24 @@ import percent from '@/../../common/script/libs/percent';
import sidebarSection from '../sidebarSection';
import questIcon from '@/assets/svg/quest.svg';
import swordIcon from '@/assets/svg/sword.svg';
import rageIcon from '@/assets/svg/rage.svg';
import healthNoPaddingIcon from '@/assets/svg/health_no_padding.svg';
import questActionsMixin from '@/components/groups/questActions.mixin';
export default {
components: {
sidebarSection,
},
mixins: [questActionsMixin],
props: ['group'],
data () {
return {
icons: Object.freeze({
questIcon,
healthNoPaddingIcon,
swordIcon,
rageIcon,
}),
};
},
@@ -372,24 +664,27 @@ export default {
if (!this.group.quest || !this.group.quest.members) return false;
return Boolean(this.group.quest.members[this.user._id]);
},
userIsQuestLeader () {
if (!this.group.quest) return false;
return this.group.quest.leader === this.user._id;
},
onPendingQuest () {
return Boolean(this.group.quest.key) && !this.group.quest.active;
},
onActiveQuest () {
return this.group.quest.active;
},
bossHpPercent () {
return percent(this.group.quest.progress.hp, this.questData.boss.hp);
},
pendingHpPercent () {
return percent(this.user.party.quest.progress.up, this.questData.boss.hp);
},
questData () {
if (!this.group.quest) return {};
return quests.quests[this.group.quest.key];
},
canEditQuest () {
if (!this.group.quest) return false;
const isQuestLeader = this.group.quest.leader === this.user._id;
const isPartyLeader = this.group.leader._id === this.user._id;
return isQuestLeader || isPartyLeader;
return this.userIsQuestLeader || isPartyLeader;
},
isMemberOfPendingQuest () {
const userid = this.user._id;
@@ -416,25 +711,32 @@ export default {
return count;
},
hasPendingQuestItems () {
return Boolean(this.user.party.quest?.progress?.collectedItems);
},
},
methods: {
openStartQuestModal () {
this.$root.$emit('bv::show::modal', 'start-quest-modal');
openSelectQuestModal () {
this.$root.$emit('bv::show::modal', 'quest-detail-modal');
},
openQuestDetails () {
this.$root.$emit('bv::show::modal', 'quest-details');
this.$root.$emit('bv::show::modal', 'quest-detail-modal', {
key: this.group.quest.key,
from: 'sidebar',
});
},
openParticipantList () {
this.$root.$emit('bv::show::modal', 'participant-list');
},
async questAbort () {
if (!window.confirm(this.$t('sureAbort'))) return; // eslint-disable-line no-alert
if (!window.confirm(this.$t('doubleSureAbort'))) return; // eslint-disable-line no-alert
const quest = await this.$store.dispatch('quests:sendAction', { groupId: this.group._id, action: 'quests/abort' });
this.group.quest = quest;
if (this.onPendingQuest) {
this.$root.$emit('bv::show::modal', 'invitation-list');
} else {
this.$root.$emit('bv::show::modal', 'participant-list');
}
},
async questLeave () {
if (!window.confirm(this.$t('sureLeave'))) return; // eslint-disable-line no-alert
if (!window.confirm(this.$t(this.group.quest.active ? 'sureLeave' : 'sureLeaveInactive'))) {
return;
}
const quest = await this.$store.dispatch('quests:sendAction', { groupId: this.group._id, action: 'quests/leave' });
this.group.quest = quest;
},
@@ -447,6 +749,9 @@ export default {
const quest = await this.$store.dispatch('quests:sendAction', { groupId: partyId, action: 'quests/reject' });
this.user.party.quest = quest;
},
startQuest () {
this.questActionsConfirmQuest();
},
},
};
</script>