mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-18 15:17:25 +01:00
fix(event): address code comments, add banner gradient
This commit is contained in:
@@ -32,7 +32,7 @@ body.modal-open .habitica-top-banner {
|
|||||||
z-index: 1300;
|
z-index: 1300;
|
||||||
}
|
}
|
||||||
|
|
||||||
.svg-icon {
|
.close-icon.svg-icon {
|
||||||
position: relative;
|
position: relative;
|
||||||
top: 0;
|
top: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
banner-id="gift-promo"
|
banner-id="gift-promo"
|
||||||
class="gift-promo-banner"
|
class="gift-promo-banner"
|
||||||
:show="showGiftPromoBanner"
|
:show="showGiftPromoBanner"
|
||||||
height="3rem"
|
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
slot="content"
|
slot="content"
|
||||||
@@ -14,16 +13,19 @@
|
|||||||
<div
|
<div
|
||||||
class="svg-icon svg-gifts left-gift"
|
class="svg-icon svg-gifts left-gift"
|
||||||
v-html="icons.gifts"
|
v-html="icons.gifts"
|
||||||
|
v-once
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="announce-text"
|
class="announce-text"
|
||||||
|
v-html="$t('g1g1Announcement')"
|
||||||
|
v-once
|
||||||
>
|
>
|
||||||
{{ $t('g1g1Announcement') }}
|
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="svg-icon svg-gifts right-gift"
|
class="svg-icon svg-gifts right-gift"
|
||||||
v-html="icons.gifts"
|
v-html="icons.gifts"
|
||||||
|
v-once
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -39,8 +41,8 @@
|
|||||||
|
|
||||||
.gift-promo-banner {
|
.gift-promo-banner {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-height: 2.5rem;
|
min-height: 3rem;
|
||||||
background: $teal-50;
|
background-image: linear-gradient(90deg, $teal-50 0%, $purple-400 100%);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,12 +58,11 @@
|
|||||||
|
|
||||||
.svg-gifts {
|
.svg-gifts {
|
||||||
width: 4.6rem;
|
width: 4.6rem;
|
||||||
opacity: 1;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
// import * as Analytics from '@/libs/analytics';
|
import * as Analytics from '@/libs/analytics';
|
||||||
import { mapState } from '@/libs/store';
|
import { mapState } from '@/libs/store';
|
||||||
import BaseBanner from './base';
|
import BaseBanner from './base';
|
||||||
|
|
||||||
@@ -93,12 +94,12 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
showSelectUser () {
|
showSelectUser () {
|
||||||
/* Analytics.track({
|
Analytics.track({
|
||||||
hitType: 'event',
|
hitType: 'event',
|
||||||
eventCategory: 'button',
|
eventCategory: 'button',
|
||||||
eventAction: 'click',
|
eventAction: 'click',
|
||||||
eventLabel: 'Gift Promo Banner',
|
eventLabel: 'Gift Promo Banner',
|
||||||
}); */
|
});
|
||||||
|
|
||||||
this.$root.$emit('bv::show::modal', 'select-user-modal');
|
this.$root.$emit('bv::show::modal', 'select-user-modal');
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
<div slot="header">
|
<div slot="header">
|
||||||
<div
|
<div
|
||||||
id="npmMattStable"
|
id="npmMattStable"
|
||||||
:class="mattClass"
|
:class="seasonalNPC('matt')"
|
||||||
></div>
|
></div>
|
||||||
<b-popover
|
<b-popover
|
||||||
triggers="hover"
|
triggers="hover"
|
||||||
@@ -455,6 +455,7 @@ import svgInformation from '@/assets/svg/information.svg';
|
|||||||
import notifications from '@/mixins/notifications';
|
import notifications from '@/mixins/notifications';
|
||||||
import openedItemRowsMixin from '@/mixins/openedItemRows';
|
import openedItemRowsMixin from '@/mixins/openedItemRows';
|
||||||
import petMixin from '@/mixins/petMixin';
|
import petMixin from '@/mixins/petMixin';
|
||||||
|
import seasonalNPC from '@/mixins/seasonalNPC';
|
||||||
|
|
||||||
import { CONSTANTS, setLocalSetting, getLocalSetting } from '@/libs/userlocalManager';
|
import { CONSTANTS, setLocalSetting, getLocalSetting } from '@/libs/userlocalManager';
|
||||||
import { isOwned } from '../../../libs/createAnimal';
|
import { isOwned } from '../../../libs/createAnimal';
|
||||||
@@ -493,7 +494,7 @@ export default {
|
|||||||
drag: DragDropDirective,
|
drag: DragDropDirective,
|
||||||
mousePosition: MouseMoveDirective,
|
mousePosition: MouseMoveDirective,
|
||||||
},
|
},
|
||||||
mixins: [notifications, openedItemRowsMixin, petMixin],
|
mixins: [notifications, openedItemRowsMixin, petMixin, seasonalNPC],
|
||||||
data () {
|
data () {
|
||||||
const stableSortState = getLocalSetting(CONSTANTS.keyConstants.STABLE_SORT_STATE) || 'standard';
|
const stableSortState = getLocalSetting(CONSTANTS.keyConstants.STABLE_SORT_STATE) || 'standard';
|
||||||
|
|
||||||
@@ -530,7 +531,6 @@ export default {
|
|||||||
currentMount: 'user.data.items.currentMount',
|
currentMount: 'user.data.items.currentMount',
|
||||||
userItems: 'user.data.items',
|
userItems: 'user.data.items',
|
||||||
user: 'user.data',
|
user: 'user.data',
|
||||||
currentEvent: 'worldState.data.currentEvent',
|
|
||||||
}),
|
}),
|
||||||
petGroups () {
|
petGroups () {
|
||||||
const petGroups = [
|
const petGroups = [
|
||||||
@@ -645,10 +645,6 @@ export default {
|
|||||||
anyFilterSelected () {
|
anyFilterSelected () {
|
||||||
return Object.values(this.viewOptions).some(g => g.selected);
|
return Object.values(this.viewOptions).some(g => g.selected);
|
||||||
},
|
},
|
||||||
mattClass () {
|
|
||||||
if (!this.currentEvent || !this.currentEvent.season) return 'npc_matt';
|
|
||||||
return `npc_matt npc_matt_${this.currentEvent.season}`;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
searchText: _throttle(function throttleSearch () {
|
searchText: _throttle(function throttleSearch () {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
@hide="hideFlag()"
|
@hide="hideFlag()"
|
||||||
>
|
>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div :class="mattClass"></div><h1
|
<div :class="seasonalNPC('matt')"></div><h1
|
||||||
v-once
|
v-once
|
||||||
class="page-header"
|
class="page-header"
|
||||||
>
|
>
|
||||||
@@ -58,17 +58,14 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapState } from '@/libs/store';
|
import { mapState } from '@/libs/store';
|
||||||
|
import seasonalNPC from '@/mixins/seasonalNPC';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
mixins: [seasonalNPC],
|
||||||
computed: {
|
computed: {
|
||||||
...mapState({
|
...mapState({
|
||||||
hideDialog: 'user.data.flags.tutorial.common.mounts',
|
hideDialog: 'user.data.flags.tutorial.common.mounts',
|
||||||
currentEvent: 'worldState.data.currentEvent',
|
|
||||||
}),
|
}),
|
||||||
mattClass () {
|
|
||||||
if (!this.currentEvent || !this.currentEvent.season) return 'npc_matt';
|
|
||||||
return `npc_matt npc_matt_${this.currentEvent.season}`;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
hideFlag () {
|
hideFlag () {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<div class="bailey-header d-flex align-items-center mb-3">
|
<div class="bailey-header d-flex align-items-center mb-3">
|
||||||
<div
|
<div
|
||||||
class="mr-3"
|
class="mr-3"
|
||||||
:class="baileyClass"
|
:class="seasonalNPC('bailey')"
|
||||||
></div>
|
></div>
|
||||||
<h1 v-once>
|
<h1 v-once>
|
||||||
{{ $t('newStuff') }}
|
{{ $t('newStuff') }}
|
||||||
@@ -68,8 +68,10 @@ h1 {
|
|||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import habiticaMarkdown from 'habitica-markdown';
|
import habiticaMarkdown from 'habitica-markdown';
|
||||||
import { mapState } from '@/libs/store';
|
import { mapState } from '@/libs/store';
|
||||||
|
import seasonalNPC from '@/mixins/seasonalNPC';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
mixins: [seasonalNPC],
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
posts: [],
|
posts: [],
|
||||||
@@ -78,12 +80,7 @@ export default {
|
|||||||
computed: {
|
computed: {
|
||||||
...mapState({
|
...mapState({
|
||||||
user: 'user.data',
|
user: 'user.data',
|
||||||
currentEvent: 'worldState.data.currentEvent',
|
|
||||||
}),
|
}),
|
||||||
baileyClass () {
|
|
||||||
if (!this.currentEvent || !this.currentEvent.season) return 'npc_bailey';
|
|
||||||
return `npc_bailey npc_bailey_${this.currentEvent.season}`;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async getPosts () {
|
async getPosts () {
|
||||||
|
|||||||
@@ -113,6 +113,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-else
|
v-else
|
||||||
|
v-once
|
||||||
class="w-55 text-center"
|
class="w-55 text-center"
|
||||||
v-html="$t('paymentSubBillingWithMethod', {
|
v-html="$t('paymentSubBillingWithMethod', {
|
||||||
amount: purchasedPlanIdInfo.price,
|
amount: purchasedPlanIdInfo.price,
|
||||||
@@ -143,6 +144,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-else
|
v-else
|
||||||
|
v-once
|
||||||
class="svg-icon"
|
class="svg-icon"
|
||||||
:class="paymentMethodLogo.class"
|
:class="paymentMethodLogo.class"
|
||||||
v-html="paymentMethodLogo.icon"
|
v-html="paymentMethodLogo.icon"
|
||||||
@@ -150,6 +152,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="purchasedPlanExtraMonthsDetails.months > 0"
|
v-if="purchasedPlanExtraMonthsDetails.months > 0"
|
||||||
|
v-once
|
||||||
class="extra-months green-10 py-2 px-3 mt-4"
|
class="extra-months green-10 py-2 px-3 mt-4"
|
||||||
v-html="$t('purchasedPlanExtraMonths',
|
v-html="$t('purchasedPlanExtraMonths',
|
||||||
{months: purchasedPlanExtraMonthsDetails.months})"
|
{months: purchasedPlanExtraMonthsDetails.months})"
|
||||||
@@ -171,6 +174,7 @@
|
|||||||
{{ $t('subscriptionCanceled') }}
|
{{ $t('subscriptionCanceled') }}
|
||||||
</h2>
|
</h2>
|
||||||
<div
|
<div
|
||||||
|
v-once
|
||||||
class="w-75 text-center mb-4"
|
class="w-75 text-center mb-4"
|
||||||
v-html="$t('subscriptionInactiveDate', {date: subscriptionEndDate})"
|
v-html="$t('subscriptionInactiveDate', {date: subscriptionEndDate})"
|
||||||
>
|
>
|
||||||
@@ -265,11 +269,13 @@
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="!hasGroupPlan && !canCancelSubscription"
|
v-if="!hasGroupPlan && !canCancelSubscription"
|
||||||
|
v-once
|
||||||
v-html="$t(`cancelSubInfo${user.purchased.plan.paymentMethod}`)"
|
v-html="$t(`cancelSubInfo${user.purchased.plan.paymentMethod}`)"
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="canCancelSubscription"
|
v-if="canCancelSubscription"
|
||||||
|
v-once
|
||||||
v-html="$t('cancelSubAlternatives')"
|
v-html="$t('cancelSubAlternatives')"
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
13
website/client/src/mixins/seasonalNPC.js
Normal file
13
website/client/src/mixins/seasonalNPC.js
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { mapState } from '@/libs/store';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
computed: {
|
||||||
|
...mapState({
|
||||||
|
currentEvent: 'worldState.data.currentEvent',
|
||||||
|
}),
|
||||||
|
npcClass (name) {
|
||||||
|
if (!this.currentEvent || !this.currentEvent.season) return `npc_${name}`;
|
||||||
|
return `npc_${name} npc_${name}_${this.currentEvent.season}`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -187,6 +187,6 @@
|
|||||||
"winterPromoGiftDetails1": "Until January 6th only, when you gift somebody a subscription, you get the same subscription for yourself for free!",
|
"winterPromoGiftDetails1": "Until January 6th only, when you gift somebody a subscription, you get the same subscription for yourself for free!",
|
||||||
"winterPromoGiftDetails2": "Please note that if you or your gift recipient already have a recurring subscription, the gifted subscription will only start after that subscription is cancelled or has expired. Thanks so much for your support! <3",
|
"winterPromoGiftDetails2": "Please note that if you or your gift recipient already have a recurring subscription, the gifted subscription will only start after that subscription is cancelled or has expired. Thanks so much for your support! <3",
|
||||||
"discountBundle": "bundle",
|
"discountBundle": "bundle",
|
||||||
"g1g1Announcement": "Gift a Subscription, Get a Subscription Free event going on now!",
|
"g1g1Announcement": "<strong>Gift a subscription and get a subscription free</strong> event going on now!",
|
||||||
"g1g1Details": "Gift a sub to a friend from their profile and you’ll receive the same sub for free!"
|
"g1g1Details": "Gift a sub to a friend from their profile and you’ll receive the same sub for free!"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
import find from 'lodash/find';
|
||||||
import t from './translation';
|
import t from './translation';
|
||||||
|
import { EVENTS } from './constants';
|
||||||
|
|
||||||
|
const CURRENT_EVENT = find(EVENTS, event => {
|
||||||
|
moment().isBetween(event.start, event.end);
|
||||||
|
});
|
||||||
|
|
||||||
/*
|
/*
|
||||||
---------------------------------------------------------------
|
---------------------------------------------------------------
|
||||||
@@ -81,7 +87,7 @@ const bundles = {
|
|||||||
'penguin',
|
'penguin',
|
||||||
],
|
],
|
||||||
canBuy () {
|
canBuy () {
|
||||||
return moment().isBetween('2020-12-17T08:00-04:00', '2021-01-31T20:00-04:00');
|
return CURRENT_EVENT.season === 'winter';
|
||||||
},
|
},
|
||||||
type: 'quests',
|
type: 'quests',
|
||||||
value: 7,
|
value: 7,
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,6 @@
|
|||||||
import each from 'lodash/each';
|
|
||||||
import defaults from 'lodash/defaults';
|
import defaults from 'lodash/defaults';
|
||||||
|
import each from 'lodash/each';
|
||||||
|
import find from 'lodash/find';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import sortBy from 'lodash/sortBy';
|
import sortBy from 'lodash/sortBy';
|
||||||
import t from './translation';
|
import t from './translation';
|
||||||
@@ -8,6 +9,9 @@ import {
|
|||||||
USER_CAN_OWN_QUEST_CATEGORIES,
|
USER_CAN_OWN_QUEST_CATEGORIES,
|
||||||
} from './constants';
|
} from './constants';
|
||||||
|
|
||||||
|
const CURRENT_EVENT = find(EVENTS, event => {
|
||||||
|
moment().isBetween(event.start, event.end);
|
||||||
|
});
|
||||||
const userCanOwnQuestCategories = USER_CAN_OWN_QUEST_CATEGORIES;
|
const userCanOwnQuestCategories = USER_CAN_OWN_QUEST_CATEGORIES;
|
||||||
|
|
||||||
const quests = {
|
const quests = {
|
||||||
@@ -259,7 +263,7 @@ const quests = {
|
|||||||
},
|
},
|
||||||
evilsanta: {
|
evilsanta: {
|
||||||
canBuy () {
|
canBuy () {
|
||||||
return moment().isBetween('2020-12-17T08:00-04:00', '2021-01-31T20:00-04:00');
|
return CURRENT_EVENT.season === 'winter';
|
||||||
},
|
},
|
||||||
event: EVENTS.winter2021,
|
event: EVENTS.winter2021,
|
||||||
text: t('questEvilSantaText'),
|
text: t('questEvilSantaText'),
|
||||||
@@ -287,7 +291,7 @@ const quests = {
|
|||||||
},
|
},
|
||||||
evilsanta2: {
|
evilsanta2: {
|
||||||
canBuy () {
|
canBuy () {
|
||||||
return moment().isBetween('2020-12-17T08:00-04:00', '2021-01-31T20:00-04:00');
|
return CURRENT_EVENT.season === 'winter';
|
||||||
},
|
},
|
||||||
event: EVENTS.winter2021,
|
event: EVENTS.winter2021,
|
||||||
text: t('questEvilSanta2Text'),
|
text: t('questEvilSanta2Text'),
|
||||||
|
|||||||
@@ -1,12 +1,20 @@
|
|||||||
|
import find from 'lodash/find';
|
||||||
|
import upperFirst from 'lodash/upperFirst';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { SEASONAL_SETS } from '../content/constants';
|
import {
|
||||||
|
EVENTS,
|
||||||
|
SEASONAL_SETS,
|
||||||
|
} from '../content/constants';
|
||||||
|
|
||||||
const SHOP_OPEN = moment().isBetween('2020-12-17T08:00-04:00', '2021-01-31T20:00-04:00');
|
const CURRENT_EVENT = find(EVENTS, event => {
|
||||||
|
moment().isBetween(event.start, event.end);
|
||||||
|
});
|
||||||
|
const SHOP_OPEN = CURRENT_EVENT && ['winter', 'spring', 'summer', 'fall'].includes(CURRENT_EVENT.season);
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
opened: SHOP_OPEN,
|
opened: SHOP_OPEN,
|
||||||
|
|
||||||
currentSeason: SHOP_OPEN ? 'Winter' : 'Closed',
|
currentSeason: SHOP_OPEN ? upperFirst(CURRENT_EVENT.season) : 'Closed',
|
||||||
|
|
||||||
dateRange: { start: '2020-12-17', end: '2021-01-31' },
|
dateRange: { start: '2020-12-17', end: '2021-01-31' },
|
||||||
|
|
||||||
@@ -25,7 +33,7 @@ export default {
|
|||||||
}
|
}
|
||||||
: {},
|
: {},
|
||||||
|
|
||||||
availableSpells: moment().isBetween('2020-12-17T08:00-04:00', '2021-01-31T20:00-04:00')
|
availableSpells: moment().isBetween('2020-12-29T08:00-04:00', '2021-01-31T20:00-04:00')
|
||||||
? [
|
? [
|
||||||
'snowball',
|
'snowball',
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -190,7 +190,7 @@ function sendSubscriptionNotification ({
|
|||||||
const timestamp = new Date();
|
const timestamp = new Date();
|
||||||
if (recipient.id) {
|
if (recipient.id) {
|
||||||
const currentEvent = getCurrentEvent();
|
const currentEvent = getCurrentEvent();
|
||||||
const promoString = currentEvent && currentEvent.promo && currentEvent.promo ? ' and got a promo' : '';
|
const promoString = currentEvent && currentEvent.promo ? ' and got a promo' : '';
|
||||||
text = `${buyer.name} ${buyer.id} ${buyer.email} bought a ${months}-month gift subscription for ${recipient.name} ${recipient.id} ${recipient.email}${promoString} using ${paymentMethod} on ${timestamp}`;
|
text = `${buyer.name} ${buyer.id} ${buyer.email} bought a ${months}-month gift subscription for ${recipient.name} ${recipient.id} ${recipient.email}${promoString} using ${paymentMethod} on ${timestamp}`;
|
||||||
} else if (groupId) {
|
} else if (groupId) {
|
||||||
text = `${buyer.name} ${buyer.id} ${buyer.email} bought a 1-month recurring group-plan for ${groupId} using ${paymentMethod} on ${timestamp}`;
|
text = `${buyer.name} ${buyer.id} ${buyer.email} bought a 1-month recurring group-plan for ${groupId} using ${paymentMethod} on ${timestamp}`;
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ export default function staticMiddleware (expressApp) {
|
|||||||
expressApp.use('/static/icons', express.static(`${BASE_DIR}/website/client/dist/static/icons`, { maxAge: ONE_WEEK }));
|
expressApp.use('/static/icons', express.static(`${BASE_DIR}/website/client/dist/static/icons`, { maxAge: ONE_WEEK }));
|
||||||
expressApp.use('/static/merch', express.static(`${BASE_DIR}/website/client/dist/static/merch`, { maxAge: ONE_WEEK }));
|
expressApp.use('/static/merch', express.static(`${BASE_DIR}/website/client/dist/static/merch`, { maxAge: ONE_WEEK }));
|
||||||
expressApp.use('/static/presskit', express.static(`${BASE_DIR}/website/client/dist/static/presskit`, { maxAge: ONE_WEEK }));
|
expressApp.use('/static/presskit', express.static(`${BASE_DIR}/website/client/dist/static/presskit`, { maxAge: ONE_WEEK }));
|
||||||
|
expressApp.use('/static/npc', express.static(`${BASE_DIR}/website/client/dist/static/npc`, { maxAge: ONE_WEEK }));
|
||||||
|
|
||||||
/* The remaining files are not cached yet. */
|
/* The remaining files are not cached yet. */
|
||||||
expressApp.use('/static', express.static(`${BASE_DIR}/website/client/dist/static`));
|
expressApp.use('/static', express.static(`${BASE_DIR}/website/client/dist/static`));
|
||||||
|
|||||||
Reference in New Issue
Block a user