Add Gift Messaging to Success Modal (#14270)

* initial commit: based on group-tracking-modal branch

* chore: merge group-tracking-modal

* update: create functions for each success condition

* chore: merge develop

* chore: work on successModal.vue & remove redundant code on groupPlan.vue

* update: remove `giftSubscriptionText4` from footer

* fix: correct groupPlan.vue file

* update: add messaging placeholder, clean up logic in a few places, update/add strings

* update: rearrange modal in order of display & test existence of 'gift-subscription' paymentType

* update: added props for receiverName so 'gift-subscription' works

* update: add close.svg & function
style: refactor CSS

* update: work on gift messaging

* update: work on gift messaging

* update: work on gift messaging

* update: let's make messages GO

* update: messages are a GO, we have LIFT OFF!

* fix: remove console log (oops)

Co-authored-by: SabreCat <sabe@habitica.com>
This commit is contained in:
Natalie L
2022-11-01 21:55:40 -04:00
committed by GitHub
parent 53fb28cc48
commit c16207c9ba
5 changed files with 330 additions and 207 deletions

View File

@@ -75,6 +75,7 @@
v-show="selectedPage === 'subscription'" v-show="selectedPage === 'subscription'"
class="subscribe-option" class="subscribe-option"
:userReceivingGift="userReceivingGift" :userReceivingGift="userReceivingGift"
:receiverName="receiverName"
/> />
<!-- gem block --> <!-- gem block -->
@@ -648,6 +649,7 @@ export default {
}, },
}, },
giftReceiver: this.receiverName, giftReceiver: this.receiverName,
toUserId: this.userReceivingGift._id,
}); });
}, 500); }, 500);
}, },

View File

@@ -1,11 +1,22 @@
<template> <template>
<b-modal <b-modal
id="payments-success-modal" id="payments-success-modal"
:title="$t('accountSuspendedTitle')" :hide-footer="isNewGroup || isGems || isSubscription"
:hide-footer="isFromBalance || paymentData.newGroup" :modal-class="isNewGroup || isGems || isSubscription
:modal-class="isFromBalance || paymentData.newGroup ? ['modal-hidden-footer'] : []" ? ['modal-hidden-footer'] : []"
> >
<!-- HEADER -->
<div slot="modal-header"> <div slot="modal-header">
<div
class="modal-close"
@click="close()"
>
<div
class="icon-close"
v-html="icons.close"
>
</div>
</div>
<div class="check-container d-flex align-items-center justify-content-center"> <div class="check-container d-flex align-items-center justify-content-center">
<div <div
v-once v-once
@@ -13,19 +24,127 @@
v-html="icons.check" v-html="icons.check"
></div> ></div>
</div> </div>
<h2>{{ $t(isFromBalance ? 'success' : 'paymentSuccessful') }}</h2> <h2>{{ $t(isGemsBalance ? 'success' : 'paymentSuccessful') }}</h2>
</div> </div>
<!-- BODY -->
<div class="row">
<div class="col-12 modal-body-col">
<!-- buy gems for self -->
<template v-if="isGems">
<strong v-once>{{ $t('paymentYouReceived') }}</strong>
<div class="details-block gems">
<div
v-once
class="svg-icon"
v-html="icons.gem"
></div>
<span>{{ paymentData.gemsBlock.gems }}</span>
</div>
</template>
<!-- buy gems to someone else OR send gems from balance-->
<template
v-if="isGiftGems || isGemsBalance"
>
<span v-html="$t('paymentYouSentGems', {name: paymentData.giftReceiver})"></span>
<div class="details-block gems">
<div
v-once
class="svg-icon"
v-html="icons.gem"
></div>
<span>{{ paymentData.gift.gems.amount }}</span>
</div>
</template>
<!-- give gift subscription (non-recurring)-->
<template v-if="paymentData.paymentType === 'gift-subscription'">
<div>
<span
v-html="$t('paymentYouSentSubscription', {
name: paymentData.giftReceiver, months: paymentData.subscription.months})"
></span>
</div>
</template>
<!-- buy self subscription (recurring) -->
<template v-if="isSubscription">
<strong v-once>{{ $t('nowSubscribed') }}</strong>
<div class="details-block">
<span
v-html="$t('paymentSubBilling', {
amount: paymentData.subscription.price, months: paymentData.subscription.months})"
></span>
</div>
</template>
<!-- group plan new or upgraded -->
<template v-if="isGroupPlan">
<span
v-html="$t(isNewGroup
? 'groupPlanCreated' : 'groupPlanUpgraded', {groupName: paymentData.group.name})"
></span>
<div
v-if="isGroupPlan"
class=""
>
<div class="details-block group-billing-date">
<span
v-html="$t('groupsPaymentSubBilling', { renewalDate })"
>
</span>
</div>
<div class="small-text group-auto-renew">
<span
v-once
>{{ $t('groupsPaymentAutoRenew') }}
</span>
</div>
</div>
</template>
<!-- buy self subscription auto renew -->
<template
v-if="isSubscription"
>
<span
v-once
class="small-text auto-renew"
>{{ $t('paymentAutoRenew') }}</span>
</template>
<!-- buttons for subscriptions / new Group / buy Gems for self -->
<button
v-if="isNewGroup || isGems || isSubscription"
v-once
class="btn btn-primary"
@click="submit()"
>
{{ $t('onwards') }}
</button>
</div>
</div>
<!-- FOOTER -->
<div slot="modal-footer"> <div slot="modal-footer">
<!-- everyone else --> <!-- gift gems balance & buy, gift subscription -->
<div <div
v-if="paymentData.paymentType !== 'groupPlan' || paymentData.newGroup" v-if="isGemsBalance || isGiftGems || isGiftSubscription"
class="small-text" class="message mx-auto"
> >
{{ $t('giftSubscriptionText4') }} <lockable-label
:text="$t('sendGiftLabel')"
class="mx-auto label-text"
/>
<textarea
v-model="gift.message"
class="form-control mx-auto"
:placeholder="$t('sendGiftMessagePlaceholder')"
></textarea>
<button
:disabled="!gift.message || sendingInProgress"
class="btn btn-primary mx-auto"
@click="sendMessage()"
>
{{ $t('sendMessage') }}
</button>
</div> </div>
<!-- upgradedGroup --> <!-- upgradedGroup -->
<div <div
v-else v-else-if="isUpgradedGroup"
class="demographics d-flex flex-column justify-content-center" class="demographics d-flex flex-column justify-content-center"
> >
<lockable-label <lockable-label
@@ -56,108 +175,137 @@
</button> </button>
</div> </div>
</div> </div>
<div class="row">
<div class="col-12 modal-body-col">
<!-- buy gems for self -->
<template v-if="paymentData.paymentType === 'gems'">
<strong v-once>{{ $t('paymentYouReceived') }}</strong>
<div class="details-block gems">
<div
v-once
class="svg-icon"
v-html="icons.gem"
></div>
<span>{{ paymentData.gemsBlock.gems }}</span>
</div>
</template>
<!-- buy or gift gems to someone else -->
<template
v-if="paymentData.paymentType === 'gift-gems'
|| paymentData.paymentType === 'gift-gems-balance'"
>
<span v-html="$t('paymentYouSentGems', {name: paymentData.giftReceiver})"></span>
<div class="details-block gems">
<div
v-once
class="svg-icon"
v-html="icons.gem"
></div>
<span>{{ paymentData.gift.gems.amount }}</span>
</div>
</template>
<!-- give gift subscription (non-recurring)-->
<template v-if="paymentData.paymentType === 'gift-subscription'">
<span
v-html="$t('paymentYouSentSubscription', {
name: paymentData.giftReceiver, months: paymentData.subscription.months})"
></span>
</template>
<!-- buy self subscription (recurring) -->
<template v-if="paymentData.paymentType === 'subscription'">
<strong v-once>{{ $t('nowSubscribed') }}</strong>
<div class="details-block">
<span
v-html="$t('paymentSubBilling', {
amount: paymentData.subscription.price, months: paymentData.subscription.months})"
></span>
</div>
</template>
<!-- group plan new or upgraded -->
<template v-if="paymentData.paymentType === 'groupPlan'">
<span
v-html="$t(paymentData.newGroup
? 'groupPlanCreated' : 'groupPlanUpgraded', {groupName: paymentData.group.name})"
></span>
<div
v-if="!paymentData.newGroup || paymentData.newGroup"
class=""
>
<div class="details-block group-billing-date">
<span
v-html="$t('groupsPaymentSubBilling', { renewalDate })"
>
</span>
</div>
<div class="small-text group-auto-renew">
<span
v-once
>{{ $t('groupsPaymentAutoRenew') }}
</span>
</div>
</div>
</template>
<!-- buy self subscription auto renew -->
<template
v-if="paymentData.paymentType === 'subscription'"
>
<span
v-once
class="small-text auto-renew"
>{{ $t('paymentAutoRenew') }}</span>
</template>
<!-- buttons for subscriptions -->
<button
v-if="paymentData.paymentType !== 'groupPlan'"
v-once
class="btn btn-primary"
@click="submit()"
>
{{ $t('onwards') }}
</button>
</div>
</div>
</b-modal> </b-modal>
</template> </template>
<style lang="scss"> <style lang="scss">
@import '~@/assets/scss/colors.scss'; @import '~@/assets/scss/colors.scss';
#payments-success-modal .modal-md { #payments-success-modal {
max-width: 448px; .modal-md {
} max-width: 448px;
min-width: 330px;
#payments-success-modal .modal-content { .modal-close {
background: transparent; position: absolute;
right: 16px;
top: 16px;
cursor: pointer;
.icon-close {
width: 18px;
height: 18px;
vertical-align: middle;
& svg path {
fill: $green-1;
}
& :hover {
fill: $green-1;
}
}
}
.modal-content {
background: transparent;
}
.modal-header {
justify-content: center;
padding-top: 24px;
padding-bottom: 0px;
background: $green-100;
border-top-right-radius: 8px;
border-top-left-radius: 8px;
border-bottom: none;
h2 {
color: $green-1;
}
.check-container {
width: 64px;
height: 64px;
border-radius: 50%;
background: $green-1;
margin: 0 auto;
margin-bottom: 16px;
}
.check {
width: 35.1px;
height: 28px;
color: $white;
}
}
.modal-body {
padding: 16px 32px 24px 32px;
background: $white;
.modal-body-col {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
.btn.btn-primary {
margin-top: 24px;
}
}
.details-block {
background: $gray-700;
border-radius: 4px;
padding: 8px 16px;
margin-top: 16px;
display: inline-flex;
flex-direction: row;
text-align: center;
&.gems {
padding: 12px 16px 12px 20px;
color: $green-10;
font-size: 24px;
font-weight: bold;
line-height: 1.33;
.svg-icon {
margin-right: 8px;
width: 32px;
height: 32px;
}
}
}
.auto-renew {
margin-top: 16px;
color: $orange-10;
font-style: normal;
}
.group-auto-renew {
margin: 12px 20px -8px 20px;
color: $yellow-5;
font-style: normal;
}
.group-billing-date {
width: 269px;
}
}
.modal-footer {
background: $gray-700;
border-bottom-right-radius: 8px;
border-bottom-left-radius: 8px;
justify-content: center;
border-top: none;
.small-text {
font-style: normal;
}
}
}
} }
#payments-success-modal.modal-hidden-footer .modal-body { #payments-success-modal.modal-hidden-footer .modal-body {
@@ -165,102 +313,6 @@
border-bottom-left-radius: 8px; border-bottom-left-radius: 8px;
} }
#payments-success-modal .modal-header {
justify-content: center;
padding-top: 24px;
padding-bottom: 0px;
background: $green-100;
border-top-right-radius: 8px;
border-top-left-radius: 8px;
border-bottom: none;
h2 {
color: $green-1;
}
.check-container {
width: 64px;
height: 64px;
border-radius: 50%;
background: $green-1;
margin: 0 auto;
margin-bottom: 16px;
}
.check {
width: 35.1px;
height: 28px;
color: $white;
}
}
#payments-success-modal .modal-body {
padding: 16px 32px 24px 32px;
background: $white;
.modal-body-col {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
.btn.btn-primary {
margin-top: 24px;
}
}
.details-block {
background: $gray-700;
border-radius: 4px;
padding: 8px 16px;
margin-top: 16px;
display: inline-flex;
flex-direction: row;
text-align: center;
&.gems {
padding: 12px 16px 12px 20px;
color: $green-10;
font-size: 24px;
font-weight: bold;
line-height: 1.33;
.svg-icon {
margin-right: 8px;
width: 32px;
height: 32px;
}
}
}
.auto-renew {
margin-top: 16px;
color: $orange-10;
font-style: normal;
}
.group-auto-renew {
margin: 12px 20px -8px 20px;
color: $yellow-5;
font-style: normal;
}
.group-billing-date {
width: 269px;
}
}
#payments-success-modal .modal-footer {
background: $gray-700;
border-bottom-right-radius: 8px;
border-bottom-left-radius: 8px;
justify-content: center;
border-top: none;
.small-text {
font-style: normal;
}
}
.demographics { .demographics {
background-color: $gray-700; background-color: $gray-700;
@@ -278,23 +330,42 @@
margin-bottom: 20px; margin-bottom: 20px;
} }
} }
.message {
margin-bottom: 8px;
width: 378px;
display: flex;
flex-direction: column;
textarea.form-control {
height: 56px;
margin: 0 24px 24px 24px;
font-size: 0.875rem;
}
}
</style> </style>
<style lang="scss"> <style lang="scss">
@import '~@/assets/scss/mixins.scss'; @import '~@/assets/scss/mixins.scss';
</style> </style>
<script> <script>
// icons
import checkIcon from '@/assets/svg/check.svg'; import checkIcon from '@/assets/svg/check.svg';
import gemIcon from '@/assets/svg/gem.svg'; import gemIcon from '@/assets/svg/gem.svg';
import closeIcon from '@/assets/svg/close.svg';
// components
import { mapState } from '@/libs/store'; import { mapState } from '@/libs/store';
import subscriptionBlocks from '@/../../common/script/content/subscriptionBlocks'; import subscriptionBlocks from '@/../../common/script/content/subscriptionBlocks';
import selectTranslatedArray from '@/components/tasks/modal-controls/selectTranslatedArray'; import selectTranslatedArray from '@/components/tasks/modal-controls/selectTranslatedArray';
import lockableLabel from '@/components/tasks/modal-controls/lockableLabel'; import lockableLabel from '@/components/tasks/modal-controls/lockableLabel';
// mixins
import notificationsMixin from '@/mixins/notifications';
import paymentsMixin from '@/mixins/payments'; import paymentsMixin from '@/mixins/payments';
// analytics
import * as Analytics from '@/libs/analytics'; import * as Analytics from '@/libs/analytics';
export default { export default {
@@ -302,18 +373,30 @@ export default {
selectTranslatedArray, selectTranslatedArray,
lockableLabel, lockableLabel,
}, },
mixins: [paymentsMixin], mixins: [
paymentsMixin,
notificationsMixin,
],
data () { data () {
return { return {
icons: Object.freeze({ icons: Object.freeze({
check: checkIcon, check: checkIcon,
gem: gemIcon, gem: gemIcon,
close: closeIcon,
}), }),
paymentData: {}, paymentData: {},
upgradedGroup: { upgradedGroup: {
name: '', name: '',
demographics: null, demographics: null,
}, },
sendingInProgress: false,
gift: {
message: '',
},
receiverName: {
name: null,
uuid: null,
},
}; };
}, },
computed: { computed: {
@@ -323,9 +406,30 @@ export default {
const memberCount = this.paymentData.group.memberCount || 1; const memberCount = this.paymentData.group.memberCount || 1;
return sub.price + 3 * (memberCount - 1); return sub.price + 3 * (memberCount - 1);
}, },
isFromBalance () { isGemsBalance () {
return this.paymentData.paymentType === 'gift-gems-balance'; return this.paymentData.paymentType === 'gift-gems-balance';
}, },
isGems () {
return this.paymentData.paymentType === 'gems';
},
isGiftGems () {
return this.paymentData.paymentType === 'gift-gems';
},
isGiftSubscription () {
return this.paymentData.paymentType === 'gift-subscription';
},
isSubscription () {
return this.paymentData.paymentType === 'subscription';
},
isGroupPlan () {
return this.paymentData.paymentType === 'groupPlan';
},
isUpgradedGroup () {
return this.paymentData.paymentType === 'groupPlan' && !this.paymentData.newGroup;
},
isNewGroup () {
return this.paymentData.paymentType === 'groupPlan' && this.paymentData.newGroup;
},
}, },
mounted () { mounted () {
this.$root.$on('habitica:payment-success', data => { this.$root.$on('habitica:payment-success', data => {
@@ -341,6 +445,19 @@ export default {
this.$root.$off('habitica:payments-success'); this.$root.$off('habitica:payments-success');
}, },
methods: { methods: {
async sendMessage () {
this.sendingInProgress = true;
await this.$store.dispatch('members:sendPrivateMessage', {
message: this.gift.message,
toUserId: this.paymentData.gift.uuid || this.paymentData.toUserId,
});
this.close();
},
close () {
this.gift.message = '';
this.sendingInProgress = false;
this.$root.$emit('bv::hide::modal', 'payments-success-modal');
},
submit () { submit () {
if (this.paymentData.group && !this.paymentData.newGroup) { if (this.paymentData.group && !this.paymentData.newGroup) {
Analytics.track({ Analytics.track({

View File

@@ -125,6 +125,10 @@ export default {
type: Object, type: Object,
default () {}, default () {},
}, },
receiverName: {
type: String,
default: '',
},
}, },
data () { data () {
return { return {
@@ -135,7 +139,6 @@ export default {
type: 'subscription', type: 'subscription',
subscription: { key: 'basic_earned' }, subscription: { key: 'basic_earned' },
}, },
receiverName: '',
}; };
}, },
computed: { computed: {

View File

@@ -132,7 +132,8 @@
"sendGiftTotal": "Total:", "sendGiftTotal": "Total:",
"sendGiftFromBalance": "From Balance", "sendGiftFromBalance": "From Balance",
"sendGiftPurchase": "Purchase", "sendGiftPurchase": "Purchase",
"sendGiftMessagePlaceholder": "Personal message (optional)", "sendGiftLabel": "Would you like to send a gift message?",
"sendGiftMessagePlaceholder": "Add a gift message",
"sendGiftSubscription": "<%= months %> Month(s): $<%= price %> USD", "sendGiftSubscription": "<%= months %> Month(s): $<%= price %> USD",
"gemGiftsAreOptional": "Please note that Habitica will never require you to gift gems to other players. Begging people for gems is a <strong>violation of the Community Guidelines</strong>, and all such instances should be reported to <%= hrefTechAssistanceEmail %>.", "gemGiftsAreOptional": "Please note that Habitica will never require you to gift gems to other players. Begging people for gems is a <strong>violation of the Community Guidelines</strong>, and all such instances should be reported to <%= hrefTechAssistanceEmail %>.",
"giftMessageTooLong": "The maximum length for gift messages is <%= maxGiftMessageLength %>.", "giftMessageTooLong": "The maximum length for gift messages is <%= maxGiftMessageLength %>.",

View File

@@ -92,7 +92,7 @@
"paymentSuccessful": "Your payment was successful!", "paymentSuccessful": "Your payment was successful!",
"paymentYouReceived": "You received:", "paymentYouReceived": "You received:",
"paymentYouSentGems": "You sent <strong><%- name %></strong>:", "paymentYouSentGems": "You sent <strong><%- name %></strong>:",
"paymentYouSentSubscription": "You sent <strong><%- name %></strong> a <%= months %>-months Habitica subscription.", "paymentYouSentSubscription": "You sent <strong><%- name %></strong><br> a <%= months %> month(s) Habitica subscription.",
"paymentSubBilling": "Your subscription will be billed <strong>$<%= amount %></strong> every <strong><%= months %> months</strong>.", "paymentSubBilling": "Your subscription will be billed <strong>$<%= amount %></strong> every <strong><%= months %> months</strong>.",
"groupsPaymentSubBilling": "Your next billing date is <strong><%= renewalDate %></strong>.", "groupsPaymentSubBilling": "Your next billing date is <strong><%= renewalDate %></strong>.",
"paymentSubBillingWithMethod": "Your subscription will be billed <strong>$<%= amount %></strong> every <strong><%= months %> months</strong> via <strong><%= paymentMethod %></strong>.", "paymentSubBillingWithMethod": "Your subscription will be billed <strong>$<%= amount %></strong> every <strong><%= months %> months</strong> via <strong><%= paymentMethod %></strong>.",