feat: Integrate step components into GiftedDialog
- Replace Step 1 template with EntitySelectionStep component - Replace Step 2 template with GiftDetailsStep component - Add event handlers for component integration - Fix ProjectCard import issues (didInfo, PlanData, Contact) - Maintain full backward compatibility with existing functionality - Reduce main component template from ~200 lines to ~20 lines Integration complete - ready for testing and further refactoring
This commit is contained in:
@@ -1,464 +1,51 @@
|
||||
<template>
|
||||
<div v-if="visible" class="dialog-overlay">
|
||||
<div class="dialog">
|
||||
<!-- Step 1: Giver -->
|
||||
<div v-show="currentStep === 1" id="sectionGiftedGiver">
|
||||
<label class="block font-bold mb-4">
|
||||
{{
|
||||
stepType === "recipient"
|
||||
? "Choose who received the gift:"
|
||||
: showProjects
|
||||
? "Choose a project benefitted from:"
|
||||
: "Choose a person received from:"
|
||||
}}
|
||||
</label>
|
||||
<!-- Step 1: Entity Selection -->
|
||||
<EntitySelectionStep
|
||||
v-show="currentStep === 1"
|
||||
:step-type="stepType"
|
||||
:giver-entity-type="giverEntityType"
|
||||
:recipient-entity-type="recipientEntityType"
|
||||
:show-projects="showProjects"
|
||||
:is-from-project-view="isFromProjectView"
|
||||
:projects="projects"
|
||||
:all-contacts="allContacts"
|
||||
:active-did="activeDid"
|
||||
:all-my-dids="allMyDids"
|
||||
:conflict-checker="wouldCreateConflict"
|
||||
:from-project-id="fromProjectId"
|
||||
:to-project-id="toProjectId"
|
||||
:giver="giver"
|
||||
:receiver="receiver"
|
||||
@entity-selected="handleEntitySelected"
|
||||
@cancel="cancel"
|
||||
/>
|
||||
|
||||
<!-- Unified Quick-pick grid for People and Projects -->
|
||||
<ul
|
||||
:class="
|
||||
shouldShowProjects
|
||||
? 'grid grid-cols-3 md:grid-cols-4 gap-x-2 gap-y-4 text-center mb-4'
|
||||
: 'grid grid-cols-4 sm:grid-cols-5 md:grid-cols-6 gap-x-2 gap-y-4 text-center mb-4'
|
||||
"
|
||||
>
|
||||
<template v-if="shouldShowProjects">
|
||||
<!-- show projects -->
|
||||
<li
|
||||
v-for="project in projects.slice(0, 7)"
|
||||
:key="project.handleId"
|
||||
class="cursor-pointer"
|
||||
@click="
|
||||
stepType === 'recipient'
|
||||
? selectRecipientProject(project)
|
||||
: selectProject(project)
|
||||
"
|
||||
>
|
||||
<div class="relative w-fit mx-auto">
|
||||
<ProjectIcon
|
||||
:entity-id="project.handleId"
|
||||
:icon-size="48"
|
||||
:image-url="project.image"
|
||||
class="!size-[3rem] mx-auto border border-slate-300 bg-white overflow-hidden rounded-full mb-1"
|
||||
/>
|
||||
</div>
|
||||
<h3
|
||||
class="text-xs font-medium text-ellipsis whitespace-nowrap overflow-hidden"
|
||||
>
|
||||
{{ project.name }}
|
||||
</h3>
|
||||
<div class="text-xs text-slate-500 truncate">
|
||||
<font-awesome icon="user" class="fa-fw text-slate-400" />
|
||||
{{
|
||||
didInfo(project.issuerDid, activeDid, allMyDids, allContacts)
|
||||
}}
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
v-if="projects.length === 0"
|
||||
class="text-xs text-slate-500 italic col-span-full"
|
||||
>
|
||||
(No projects found.)
|
||||
</li>
|
||||
<li v-if="projects.length > 0">
|
||||
<router-link :to="{ name: 'discover' }" class="cursor-pointer">
|
||||
<font-awesome
|
||||
icon="circle-right"
|
||||
class="text-blue-500 text-5xl mb-1"
|
||||
/>
|
||||
<h3
|
||||
class="text-xs text-slate-500 font-medium italic text-ellipsis whitespace-nowrap overflow-hidden"
|
||||
>
|
||||
Show All
|
||||
</h3>
|
||||
</router-link>
|
||||
</li>
|
||||
</template>
|
||||
<template v-else>
|
||||
<!-- show people (contacts) -->
|
||||
<li
|
||||
v-if="
|
||||
stepType === 'recipient' ||
|
||||
(stepType === 'giver' && isFromProjectView)
|
||||
"
|
||||
:class="{
|
||||
'cursor-pointer': !wouldCreateConflict(activeDid),
|
||||
'cursor-not-allowed opacity-50': wouldCreateConflict(activeDid)
|
||||
}"
|
||||
@click="
|
||||
!wouldCreateConflict(activeDid) &&
|
||||
(stepType === 'recipient'
|
||||
? selectRecipient({ did: activeDid, name: 'You' })
|
||||
: selectGiver({ did: activeDid, name: 'You' }))
|
||||
"
|
||||
>
|
||||
<font-awesome
|
||||
:class="{
|
||||
'text-blue-500 text-5xl mb-1': !wouldCreateConflict(activeDid),
|
||||
'text-slate-400 text-5xl mb-1': wouldCreateConflict(activeDid)
|
||||
}"
|
||||
icon="hand"
|
||||
/>
|
||||
<h3
|
||||
:class="{
|
||||
'text-xs text-blue-500 font-medium text-ellipsis whitespace-nowrap overflow-hidden': !wouldCreateConflict(activeDid),
|
||||
'text-xs text-slate-400 font-medium text-ellipsis whitespace-nowrap overflow-hidden': wouldCreateConflict(activeDid)
|
||||
}"
|
||||
>
|
||||
You
|
||||
</h3>
|
||||
</li>
|
||||
<li
|
||||
class="cursor-pointer"
|
||||
@click="
|
||||
stepType === 'recipient' ? selectRecipient() : selectGiver()
|
||||
"
|
||||
>
|
||||
<font-awesome
|
||||
icon="circle-question"
|
||||
class="text-slate-400 text-5xl mb-1"
|
||||
/>
|
||||
<h3
|
||||
class="text-xs text-slate-500 font-medium italic text-ellipsis whitespace-nowrap overflow-hidden"
|
||||
>
|
||||
Unnamed
|
||||
</h3>
|
||||
</li>
|
||||
<li
|
||||
v-if="allContacts.length === 0"
|
||||
class="text-xs text-slate-500 italic col-span-full"
|
||||
>
|
||||
(Add friends to see more people worthy of recognition.)
|
||||
</li>
|
||||
<li
|
||||
v-for="contact in allContacts.slice(0, 10)"
|
||||
:key="contact.did"
|
||||
:class="{
|
||||
'cursor-pointer': !wouldCreateConflict(contact.did),
|
||||
'cursor-not-allowed opacity-50': wouldCreateConflict(contact.did)
|
||||
}"
|
||||
@click="
|
||||
!wouldCreateConflict(contact.did) &&
|
||||
(stepType === 'recipient'
|
||||
? selectRecipient(contact)
|
||||
: selectGiver(contact))
|
||||
"
|
||||
>
|
||||
<div class="relative w-fit mx-auto">
|
||||
<EntityIcon
|
||||
:contact="contact"
|
||||
class="!size-[3rem] mx-auto border border-slate-300 bg-white overflow-hidden rounded-full mb-1"
|
||||
/>
|
||||
<div
|
||||
class="rounded-full bg-slate-400 absolute bottom-0 right-0 p-1 translate-x-1/3"
|
||||
>
|
||||
<font-awesome
|
||||
icon="clock"
|
||||
class="block text-white text-xs w-[1em]"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<h3
|
||||
:class="{
|
||||
'text-xs font-medium text-ellipsis whitespace-nowrap overflow-hidden': !wouldCreateConflict(contact.did),
|
||||
'text-xs font-medium text-ellipsis whitespace-nowrap overflow-hidden text-slate-400': wouldCreateConflict(contact.did)
|
||||
}"
|
||||
>
|
||||
{{ contact.name || contact.did }}
|
||||
</h3>
|
||||
</li>
|
||||
<li v-if="allContacts.length > 0" class="cursor-pointer">
|
||||
<router-link
|
||||
:to="{
|
||||
name: 'contact-gift',
|
||||
query: {
|
||||
stepType: stepType,
|
||||
giverEntityType: giverEntityType,
|
||||
recipientEntityType: recipientEntityType,
|
||||
...(stepType === 'giver'
|
||||
? {
|
||||
recipientProjectId: toProjectId,
|
||||
recipientProjectName: receiver?.name,
|
||||
recipientProjectImage: receiver?.image,
|
||||
recipientProjectHandleId: receiver?.handleId,
|
||||
recipientDid: receiver?.did,
|
||||
}
|
||||
: {
|
||||
giverProjectId: fromProjectId,
|
||||
giverProjectName: giver?.name,
|
||||
giverProjectImage: giver?.image,
|
||||
giverProjectHandleId: giver?.handleId,
|
||||
giverDid: giver?.did,
|
||||
}),
|
||||
fromProjectId: fromProjectId,
|
||||
toProjectId: toProjectId,
|
||||
showProjects: (showProjects || false).toString(),
|
||||
isFromProjectView: (isFromProjectView || false).toString(),
|
||||
},
|
||||
}"
|
||||
>
|
||||
<font-awesome
|
||||
icon="circle-right"
|
||||
class="text-blue-500 text-5xl mb-1"
|
||||
/>
|
||||
<h3
|
||||
class="text-xs text-slate-500 font-medium italic text-ellipsis whitespace-nowrap overflow-hidden"
|
||||
>
|
||||
Show All
|
||||
</h3>
|
||||
</router-link>
|
||||
</li>
|
||||
</template>
|
||||
</ul>
|
||||
|
||||
<button
|
||||
class="block w-full text-center text-md uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-2 rounded-lg"
|
||||
@click="cancel"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Step 2: Gift -->
|
||||
<div v-show="currentStep === 2" id="sectionGiftedGift">
|
||||
<div class="grid grid-cols-2 gap-2 mb-4">
|
||||
<!-- Giver Button -->
|
||||
<button
|
||||
v-if="
|
||||
(giverEntityType === 'person' || giverEntityType === 'project') &&
|
||||
!(isFromProjectView && giverEntityType === 'project')
|
||||
"
|
||||
class="flex-1 flex items-center gap-2 bg-slate-100 border border-slate-300 rounded-md p-2"
|
||||
@click="goBackToStep1('giver')"
|
||||
>
|
||||
<div>
|
||||
<template v-if="giverEntityType === 'project'">
|
||||
<ProjectIcon
|
||||
v-if="giver?.handleId"
|
||||
:entity-id="giver.handleId"
|
||||
:icon-size="32"
|
||||
:image-url="giver.image"
|
||||
class="rounded-full bg-white overflow-hidden !size-[2rem] object-cover"
|
||||
/>
|
||||
</template>
|
||||
<template v-else>
|
||||
<EntityIcon
|
||||
v-if="giver?.did"
|
||||
:contact="giver"
|
||||
class="rounded-full bg-white overflow-hidden !size-[2rem] object-cover"
|
||||
/>
|
||||
<font-awesome
|
||||
v-else
|
||||
icon="circle-question"
|
||||
class="text-slate-400 text-3xl"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<div class="text-start min-w-0">
|
||||
<p class="text-xs text-slate-500 leading-1 -mb-1 uppercase">
|
||||
{{
|
||||
giverEntityType === "project"
|
||||
? "Benefited from:"
|
||||
: "Received from:"
|
||||
}}
|
||||
</p>
|
||||
<h3 class="font-semibold truncate">
|
||||
{{ giver?.name || "Unnamed" }}
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<p class="ms-auto text-sm text-blue-500 pe-1">
|
||||
<font-awesome icon="pen" title="Change" />
|
||||
</p>
|
||||
</button>
|
||||
<div
|
||||
v-else
|
||||
class="flex-1 flex items-center gap-2 bg-slate-100 border border-slate-300 rounded-md p-2"
|
||||
>
|
||||
<div>
|
||||
<template v-if="giverEntityType === 'project'">
|
||||
<ProjectIcon
|
||||
v-if="giver?.handleId"
|
||||
:entity-id="giver.handleId"
|
||||
:icon-size="32"
|
||||
:image-url="giver.image"
|
||||
class="rounded-full bg-white overflow-hidden !size-[2rem] object-cover"
|
||||
/>
|
||||
</template>
|
||||
<template v-else>
|
||||
<EntityIcon
|
||||
v-if="giver?.did"
|
||||
:contact="giver"
|
||||
class="rounded-full bg-white overflow-hidden !size-[2rem] object-cover"
|
||||
/>
|
||||
<font-awesome
|
||||
v-else
|
||||
icon="circle-question"
|
||||
class="text-slate-400 text-3xl"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<div class="text-start min-w-0">
|
||||
<p class="text-xs text-slate-500 leading-1 -mb-1 uppercase">
|
||||
{{
|
||||
giverEntityType === "project"
|
||||
? "Benefited from:"
|
||||
: "Received from:"
|
||||
}}
|
||||
</p>
|
||||
<h3 class="font-semibold truncate">
|
||||
{{ giver?.name || "Unnamed" }}
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<p class="ms-auto text-sm text-slate-400 pe-1">
|
||||
<font-awesome icon="lock" title="Can't be changed" />
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Recipient Button -->
|
||||
<button
|
||||
v-if="recipientEntityType === 'person'"
|
||||
class="flex-1 flex items-center gap-2 bg-slate-100 border border-slate-300 rounded-md p-2"
|
||||
@click="goBackToStep1('recipient')"
|
||||
>
|
||||
<div>
|
||||
<EntityIcon
|
||||
v-if="receiver?.did"
|
||||
:contact="receiver"
|
||||
class="rounded-full bg-white overflow-hidden !size-[2rem] object-cover"
|
||||
/>
|
||||
<font-awesome
|
||||
v-else
|
||||
icon="circle-question"
|
||||
class="text-slate-400 text-3xl"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="text-start min-w-0">
|
||||
<p class="text-xs text-slate-500 leading-1 -mb-1 uppercase">
|
||||
Given to:
|
||||
</p>
|
||||
<h3 class="font-semibold truncate">
|
||||
{{ receiver?.name || "Unnamed" }}
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<p class="ms-auto text-sm text-blue-500 pe-1">
|
||||
<font-awesome icon="pen" title="Change" />
|
||||
</p>
|
||||
</button>
|
||||
<div
|
||||
v-else-if="recipientEntityType === 'project'"
|
||||
class="flex-1 flex items-center gap-2 bg-slate-100 border border-slate-300 rounded-md p-2"
|
||||
>
|
||||
<div>
|
||||
<ProjectIcon
|
||||
v-if="receiver?.handleId"
|
||||
:entity-id="receiver.handleId"
|
||||
:icon-size="32"
|
||||
:image-url="receiver.image"
|
||||
class="rounded-full bg-white overflow-hidden !size-[2rem] object-cover"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="text-start min-w-0">
|
||||
<p class="text-xs text-slate-500 leading-1 -mb-1 uppercase">
|
||||
Given to project:
|
||||
</p>
|
||||
<h3 class="font-semibold truncate">
|
||||
{{ receiver?.name || "Unnamed" }}
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<p class="ms-auto text-sm text-slate-400 pe-1">
|
||||
<font-awesome icon="lock" title="Can't be changed" />
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<input
|
||||
v-model="description"
|
||||
type="text"
|
||||
class="block w-full rounded border border-slate-400 px-3 py-2 mb-4 placeholder:italic"
|
||||
:placeholder="prompt || 'What was given?'"
|
||||
/>
|
||||
<div class="flex mb-4">
|
||||
<button
|
||||
class="rounded-s border border-e-0 border-slate-400 bg-slate-200 px-4 py-2"
|
||||
@click="amountInput === '0' ? null : decrement()"
|
||||
>
|
||||
<font-awesome icon="chevron-left" />
|
||||
</button>
|
||||
<input
|
||||
id="inputGivenAmount"
|
||||
v-model="amountInput"
|
||||
type="number"
|
||||
class="flex-1 border border-e-0 border-slate-400 px-2 py-2 text-center w-[1px]"
|
||||
/>
|
||||
<button
|
||||
class="rounded-e border border-slate-400 bg-slate-200 px-4 py-2"
|
||||
@click="increment()"
|
||||
>
|
||||
<font-awesome icon="chevron-right" />
|
||||
</button>
|
||||
|
||||
<select
|
||||
v-model="unitCode"
|
||||
class="flex-1 rounded border border-slate-400 ms-2 px-3 py-2"
|
||||
>
|
||||
<option value="HUR">Hours</option>
|
||||
<option value="USD">US $</option>
|
||||
<option value="BTC">BTC</option>
|
||||
<option value="BX">BX</option>
|
||||
<option value="ETH">ETH</option>
|
||||
</select>
|
||||
</div>
|
||||
<router-link
|
||||
:to="{
|
||||
name: 'gifted-details',
|
||||
query: giftedDetailsQuery,
|
||||
}"
|
||||
class="block w-full text-center text-md uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-2 rounded-lg mb-4"
|
||||
>
|
||||
Photo & more options…
|
||||
</router-link>
|
||||
<p class="text-center text-sm mb-4">
|
||||
<b class="font-medium">Sign & Send</b> to publish to the world
|
||||
<font-awesome
|
||||
icon="circle-info"
|
||||
class="fa-fw text-blue-500 text-base cursor-pointer"
|
||||
@click="explainData()"
|
||||
/>
|
||||
</p>
|
||||
|
||||
<!-- Conflict warning -->
|
||||
<div v-if="hasPersonConflict" class="mb-4 p-3 bg-red-50 border border-red-200 rounded-md">
|
||||
<p class="text-red-700 text-sm text-center">
|
||||
<font-awesome icon="exclamation-triangle" class="fa-fw mr-1" />
|
||||
Cannot record: Same person selected as both giver and recipient
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-2">
|
||||
<button
|
||||
:disabled="hasPersonConflict"
|
||||
:class="{
|
||||
'block w-full text-center text-md uppercase font-bold bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-2 rounded-lg': !hasPersonConflict,
|
||||
'block w-full text-center text-md uppercase font-bold bg-gradient-to-b from-slate-300 to-slate-500 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-slate-400 px-1.5 py-2 rounded-lg cursor-not-allowed': hasPersonConflict
|
||||
}"
|
||||
@click="confirm"
|
||||
>
|
||||
Sign & Send
|
||||
</button>
|
||||
<button
|
||||
class="block w-full text-center text-md uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-2 rounded-lg"
|
||||
@click="cancel"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Step 2: Gift Details -->
|
||||
<GiftDetailsStep
|
||||
v-show="currentStep === 2"
|
||||
:giver="giver"
|
||||
:receiver="receiver"
|
||||
:giver-entity-type="giverEntityType"
|
||||
:recipient-entity-type="recipientEntityType"
|
||||
:description="description"
|
||||
:amount="parseFloat(amountInput)"
|
||||
:unit-code="unitCode"
|
||||
:prompt="prompt"
|
||||
:is-from-project-view="isFromProjectView"
|
||||
:has-conflict="hasPersonConflict"
|
||||
:offer-id="offerId"
|
||||
:from-project-id="fromProjectId"
|
||||
:to-project-id="toProjectId"
|
||||
@update:description="description = $event"
|
||||
@update:amount="amountInput = $event.toString()"
|
||||
@update:unit-code="unitCode = $event"
|
||||
@edit-entity="handleEditEntity"
|
||||
@explain-data="explainData"
|
||||
@submit="handleSubmit"
|
||||
@cancel="cancel"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -482,12 +69,16 @@ import { logger } from "../utils/logger";
|
||||
import { PlatformServiceFactory } from "@/services/PlatformServiceFactory";
|
||||
import EntityIcon from "../components/EntityIcon.vue";
|
||||
import ProjectIcon from "../components/ProjectIcon.vue";
|
||||
import EntitySelectionStep from "../components/EntitySelectionStep.vue";
|
||||
import GiftDetailsStep from "../components/GiftDetailsStep.vue";
|
||||
import { PlanData } from "../interfaces/records";
|
||||
|
||||
@Component({
|
||||
components: {
|
||||
EntityIcon,
|
||||
ProjectIcon,
|
||||
EntitySelectionStep,
|
||||
GiftDetailsStep,
|
||||
},
|
||||
})
|
||||
export default class GiftedDialog extends Vue {
|
||||
@@ -1031,6 +622,45 @@ export default class GiftedDialog extends Vue {
|
||||
unitCode: this.unitCode,
|
||||
};
|
||||
}
|
||||
|
||||
// New event handlers for component integration
|
||||
|
||||
/**
|
||||
* Handle entity selection from EntitySelectionStep
|
||||
* @param entity - The selected entity (person or project)
|
||||
*/
|
||||
handleEntitySelected(entity: { type: 'person' | 'project', data: Contact | PlanData }) {
|
||||
if (entity.type === 'person') {
|
||||
const contact = entity.data as Contact;
|
||||
if (this.stepType === 'giver') {
|
||||
this.selectGiver(contact);
|
||||
} else {
|
||||
this.selectRecipient(contact);
|
||||
}
|
||||
} else {
|
||||
const project = entity.data as PlanData;
|
||||
if (this.stepType === 'giver') {
|
||||
this.selectProject(project);
|
||||
} else {
|
||||
this.selectRecipientProject(project);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle edit entity request from GiftDetailsStep
|
||||
* @param entityType - 'giver' or 'recipient'
|
||||
*/
|
||||
handleEditEntity(entityType: 'giver' | 'recipient') {
|
||||
this.goBackToStep1(entityType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle form submission from GiftDetailsStep
|
||||
*/
|
||||
handleSubmit() {
|
||||
this.confirm();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -34,9 +34,9 @@
|
||||
<script lang="ts">
|
||||
import { Component, Prop, Vue } from "vue-facing-decorator";
|
||||
import ProjectIcon from "./ProjectIcon.vue";
|
||||
import { PlanData } from "@/interfaces/plan-data";
|
||||
import { Contact } from "@/interfaces/contact";
|
||||
import { didInfo } from "@/libs/util";
|
||||
import { PlanData } from "@/interfaces/records";
|
||||
import { Contact } from "@/db/tables/contacts";
|
||||
import { didInfo } from "@/libs/endorserServer";
|
||||
|
||||
/**
|
||||
* ProjectCard - Displays a project entity with selection capability
|
||||
|
||||
Reference in New Issue
Block a user