Browse Source

Refactor: simplify GiftedDialog with explicit entity type props

Replace complex updateEntityTypes() method with explicit giverEntityType and
recipientEntityType props. This makes the component more declarative and
maintainable by removing hidden logic and making entity type relationships
clear at the call site.

- Remove updateEntityTypes() method and related watchers
- Add explicit giverEntityType and recipientEntityType props with defaults
- Update all views to use inline logic for entity type determination
- Fix entity type preservation in navigation flows
- Enhance query parameter passing for better context preservation
- Fix recipient reset issue in ContactGiftingView
- Resolve entity type mismatch in HomeView project button flow

Files changed:
- GiftedDialog.vue: Remove complex logic, add explicit props
- EntitySelectionStep.vue: Enhanced query parameter handling
- ContactGiftingView.vue: Improved context preservation
- HomeView.vue, ProjectViewView.vue, ClaimView.vue, ContactsView.vue:
  Updated to use explicit entity type props
- Add refactoring documentation
get-get-hash
Jose Olarte III 2 weeks ago
parent
commit
06f3a4c7c2
  1. 207
      docs/refactoring/GiftedDialog-EntityTypes-Refactoring.md
  2. 37
      src/components/EntitySelectionStep.vue
  3. 84
      src/components/GiftedDialog.vue
  4. 3
      src/views/ClaimView.vue
  5. 28
      src/views/ContactGiftingView.vue
  6. 6
      src/views/ContactsView.vue
  7. 6
      src/views/HomeView.vue
  8. 5
      src/views/ProjectViewView.vue

207
docs/refactoring/GiftedDialog-EntityTypes-Refactoring.md

@ -0,0 +1,207 @@
# GiftedDialog Entity Types Refactoring
## Overview
This refactoring simplifies the `GiftedDialog` component by replacing the complex `updateEntityTypes()` method with explicit props for entity types. This makes the component more declarative, reusable, and easier to understand.
## Problem
The original `updateEntityTypes()` method used multiple props (`showProjects`, `fromProjectId`, `toProjectId`, `recipientEntityTypeOverride`) to determine entity types through complex conditional logic:
```typescript
updateEntityTypes() {
// Reset and set entity types based on current context
this.giverEntityType = "person";
this.recipientEntityType = "person";
// If recipient entity type is explicitly overridden, use that
if (this.recipientEntityTypeOverride) {
this.recipientEntityType = this.recipientEntityTypeOverride;
}
// Determine entity types based on current context
if (this.showProjects) {
// HomeView "Project" button or ProjectViewView "Given by This"
this.giverEntityType = "project";
// Only override recipient if not already set by recipientEntityTypeOverride
if (!this.recipientEntityTypeOverride) {
this.recipientEntityType = "person";
}
} else if (this.fromProjectId) {
// ProjectViewView "Given by This" button (project is giver)
this.giverEntityType = "project";
// Only override recipient if not already set by recipientEntityTypeOverride
if (!this.recipientEntityTypeOverride) {
this.recipientEntityType = "person";
}
} else if (this.toProjectId) {
// ProjectViewView "Given to This" button (project is recipient)
this.giverEntityType = "person";
// Only override recipient if not already set by recipientEntityTypeOverride
if (!this.recipientEntityTypeOverride) {
this.recipientEntityType = "project";
}
} else {
// HomeView "Person" button
this.giverEntityType = "person";
// Only override recipient if not already set by recipientEntityTypeOverride
if (!this.recipientEntityTypeOverride) {
this.recipientEntityType = "person";
}
}
}
```
### Issues with the Original Approach
1. **Complex Logic**: Nested conditionals that were hard to follow
2. **Tight Coupling**: Views needed to understand internal logic to set the right props
3. **Inflexible**: Adding new entity type combinations required modifying the method
4. **Unclear Intent**: The relationship between props and entity types was not obvious
## Solution
### 1. Explicit Props
Replace the complex logic with explicit props:
```typescript
@Prop({ default: "person" }) giverEntityType = "person" as "person" | "project";
@Prop({ default: "person" }) recipientEntityType = "person" as "person" | "project";
```
### 2. Simple Inline Logic
Views now use simple inline logic to determine entity types:
```vue
<!-- HomeView -->
<GiftedDialog
ref="giftedDialog"
:giver-entity-type="showProjectsDialog ? 'project' : 'person'"
:recipient-entity-type="'person'"
/>
<!-- ProjectViewView -->
<GiftedDialog
ref="giveDialogToThis"
:giver-entity-type="'person'"
:recipient-entity-type="'project'"
:to-project-id="projectId"
:is-from-project-view="true"
/>
<!-- ClaimView -->
<GiftedDialog
ref="customGiveDialog"
:giver-entity-type="'person'"
:recipient-entity-type="projectInfo ? 'project' : 'person'"
:to-project-id="..."
/>
```
## Benefits
### 1. **Declarative**
- Entity types are explicitly declared in the template
- No hidden logic in watchers or complex methods
- Clear intent at the call site
### 2. **Reusable**
- Views can easily specify any combination of entity types
- No need to understand internal logic
- Simple inline logic is easy to understand
### 3. **Maintainable**
- Adding new entity type combinations is straightforward
- Logic is visible directly in the template
- No additional files to maintain
### 4. **Testable**
- Entity type logic is visible and predictable
- No complex state management to test
- Template logic can be easily verified
### 5. **Type Safe**
- TypeScript ensures correct entity type values
- Compile-time validation of entity type combinations
## Migration Guide
### For Views Using GiftedDialog
Simply update the template to use explicit entity type props:
```vue
<!-- Before -->
<GiftedDialog :show-projects="showProjects" />
<!-- After -->
<GiftedDialog
:giver-entity-type="showProjects ? 'project' : 'person'"
:recipient-entity-type="'person'"
/>
```
### Common Patterns
1. **Person-to-Person**: `giver-entity-type="'person'" recipient-entity-type="'person'"`
2. **Project-to-Person**: `giver-entity-type="'project'" recipient-entity-type="'person'"`
3. **Person-to-Project**: `giver-entity-type="'person'" recipient-entity-type="'project'"`
4. **Conditional Project**: `recipient-entity-type="hasProject ? 'project' : 'person'"`
## Files Changed
### Core Changes
- `src/components/GiftedDialog.vue` - Removed `updateEntityTypes()` method, added explicit props
### View Updates
- `src/views/HomeView.vue` - Updated to use inline logic
- `src/views/ProjectViewView.vue` - Updated to use inline logic
- `src/views/ClaimView.vue` - Updated to use inline logic
- `src/views/ContactGiftingView.vue` - Updated to use inline logic
- `src/views/ContactsView.vue` - Updated to use inline logic
## Backward Compatibility
The refactoring maintains backward compatibility by:
- Keeping all existing props that are still needed (`fromProjectId`, `toProjectId`, `isFromProjectView`)
- Preserving the same component API for the `open()` method
- Maintaining the same template structure
## Future Enhancements
1. **Validation**: Add runtime validation for entity type combinations
2. **Documentation**: Add JSDoc comments to the component props
3. **Testing**: Add unit tests for the component with different entity type combinations
## Conclusion
This refactoring transforms `GiftedDialog` from a component with complex internal logic to a declarative, reusable component. The explicit entity type props make the component's behavior clear and predictable, while the simple inline logic keeps the code straightforward and maintainable.
## Bug Fixes
### Issue 1: Entity Type Preservation in Navigation
**Problem**: When navigating from HomeView with `showProjects = true` to ContactGiftingView via "Show All", the entity type information was lost because `showAllQueryParams` returned an empty object for project contexts.
**Solution**: Modified `EntitySelectionStep.vue` to always pass entity type information in the query parameters, even for project contexts.
### Issue 2: Recipient Reset in ContactGiftingView
**Problem**: When selecting a giver in ContactGiftingView, the recipient was always reset to "You" instead of preserving the current recipient.
**Solution**: Updated ContactGiftingView to preserve the existing recipient from the context when selecting a giver, and enhanced the query parameter passing to include both giver and recipient information for better context preservation.
### Issue 3: HomeView Project Button Entity Type Mismatch
**Problem**: When navigating from HomeView Project button → change recipient → Show All → ContactGifting, the giver entity type was incorrectly set to "person" instead of "project".
**Root Cause**: ContactGiftingView was inferring entity types from `fromProjectId` and `toProjectId` instead of using the explicitly passed `giverEntityType` and `recipientEntityType` from the query parameters.
**Solution**: Updated ContactGiftingView to use the explicitly passed entity types from query parameters instead of inferring them from project IDs.
### Files Modified for Bug Fixes
- `src/components/EntitySelectionStep.vue` - Enhanced query parameter passing
- `src/views/ContactGiftingView.vue` - Improved context preservation logic and entity type handling

37
src/components/EntitySelectionStep.vue

@ -234,11 +234,7 @@ export default class EntitySelectionStep extends Vue {
* Query parameters for "Show All" navigation
*/
get showAllQueryParams(): Record<string, string> {
if (this.shouldShowProjects) {
return {};
}
return {
const baseParams = {
stepType: this.stepType,
giverEntityType: this.giverEntityType,
recipientEntityType: this.recipientEntityType,
@ -247,25 +243,30 @@ export default class EntitySelectionStep extends Vue {
amountInput: this.amountInput,
unitCode: this.unitCode,
offerId: this.offerId,
...(this.stepType === "giver"
? {
recipientProjectId: this.toProjectId || "",
recipientProjectName: this.receiver?.name || "",
recipientProjectImage: this.receiver?.image || "",
recipientProjectHandleId: this.receiver?.handleId || "",
recipientDid: this.receiver?.did || "",
fromProjectId: this.fromProjectId,
toProjectId: this.toProjectId,
showProjects: this.showProjects.toString(),
isFromProjectView: this.isFromProjectView.toString(),
};
if (this.shouldShowProjects) {
// For project contexts, still pass entity type information
return baseParams;
}
: {
return {
...baseParams,
// Always pass both giver and recipient info for context preservation
giverProjectId: this.fromProjectId || "",
giverProjectName: this.giver?.name || "",
giverProjectImage: this.giver?.image || "",
giverProjectHandleId: this.giver?.handleId || "",
giverDid: this.giver?.did || "",
}),
fromProjectId: this.fromProjectId,
toProjectId: this.toProjectId,
showProjects: this.showProjects.toString(),
isFromProjectView: this.isFromProjectView.toString(),
recipientProjectId: this.toProjectId || "",
recipientProjectName: this.receiver?.name || "",
recipientProjectImage: this.receiver?.image || "",
recipientProjectHandleId: this.receiver?.handleId || "",
recipientDid: this.receiver?.did || "",
};
}

84
src/components/GiftedDialog.vue

@ -3,7 +3,7 @@
<div
class="dialog"
data-testid="gifted-dialog"
:data-recipient-entity-type-override="recipientEntityTypeOverride"
:data-recipient-entity-type="recipientEntityType"
>
<!-- Step 1: Entity Selection -->
<EntitySelectionStep
@ -11,7 +11,9 @@
:step-type="stepType"
:giver-entity-type="giverEntityType"
:recipient-entity-type="recipientEntityType"
:show-projects="showProjects"
:show-projects="
giverEntityType === 'project' || recipientEntityType === 'project'
"
:is-from-project-view="isFromProjectView"
:projects="projects"
:all-contacts="allContacts"
@ -60,7 +62,7 @@
</template>
<script lang="ts">
import { Vue, Component, Prop, Watch } from "vue-facing-decorator";
import { Vue, Component, Prop } from "vue-facing-decorator";
import {
createAndSubmitGive,
@ -105,29 +107,13 @@ export default class GiftedDialog extends Vue {
@Prop() fromProjectId = "";
@Prop() toProjectId = "";
@Prop({ default: false }) showProjects = false;
@Prop() isFromProjectView = false;
@Prop() recipientEntityTypeOverride?: "person" | "project";
@Watch("showProjects")
onShowProjectsChange() {
this.updateEntityTypes();
}
@Watch("fromProjectId")
onFromProjectIdChange() {
this.updateEntityTypes();
}
@Watch("toProjectId")
onToProjectIdChange() {
this.updateEntityTypes();
}
@Watch("recipientEntityTypeOverride")
onRecipientEntityTypeOverrideChange() {
this.updateEntityTypes();
}
@Prop({ default: "person" }) giverEntityType = "person" as
| "person"
| "project";
@Prop({ default: "person" }) recipientEntityType = "person" as
| "person"
| "project";
activeDid = "";
allContacts: Array<Contact> = [];
@ -203,50 +189,6 @@ export default class GiftedDialog extends Vue {
}
stepType = "giver";
giverEntityType = "person" as "person" | "project";
recipientEntityType = "person" as "person" | "project";
updateEntityTypes() {
// Reset and set entity types based on current context
this.giverEntityType = "person";
this.recipientEntityType = "person";
// If recipient entity type is explicitly overridden, use that
if (this.recipientEntityTypeOverride) {
this.recipientEntityType = this.recipientEntityTypeOverride;
}
// Determine entity types based on current context
if (this.showProjects) {
// HomeView "Project" button or ProjectViewView "Given by This"
this.giverEntityType = "project";
// Only override recipient if not already set by recipientEntityTypeOverride
if (!this.recipientEntityTypeOverride) {
this.recipientEntityType = "person";
}
} else if (this.fromProjectId) {
// ProjectViewView "Given by This" button (project is giver)
this.giverEntityType = "project";
// Only override recipient if not already set by recipientEntityTypeOverride
if (!this.recipientEntityTypeOverride) {
this.recipientEntityType = "person";
}
} else if (this.toProjectId) {
// ProjectViewView "Given to This" button (project is recipient)
this.giverEntityType = "person";
// Only override recipient if not already set by recipientEntityTypeOverride
if (!this.recipientEntityTypeOverride) {
this.recipientEntityType = "project";
}
} else {
// HomeView "Person" button
this.giverEntityType = "person";
// Only override recipient if not already set by recipientEntityTypeOverride
if (!this.recipientEntityTypeOverride) {
this.recipientEntityType = "person";
}
}
}
async open(
giver?: libsUtil.GiverReceiverInputInfo,
@ -261,7 +203,6 @@ export default class GiftedDialog extends Vue {
this.giver = giver;
this.receiver = receiver;
this.offerId = offerId || "";
console.log("offerId", this.offerId);
this.prompt = prompt || "";
this.description = description || "";
this.amountInput = amountInput || "0";
@ -270,8 +211,7 @@ export default class GiftedDialog extends Vue {
this.firstStep = !giver;
this.stepType = "giver";
// Update entity types based on current props
this.updateEntityTypes();
// Entity types are now set via props, no need to call updateEntityTypes()
try {
const settings = await this.$settings();

3
src/views/ClaimView.vue

@ -201,12 +201,13 @@
</div>
<GiftedDialog
ref="customGiveDialog"
:giver-entity-type="'person'"
:recipient-entity-type="projectInfo ? 'project' : 'person'"
:to-project-id="
detailsForGive?.fulfillsPlanHandleId ||
detailsForOffer?.fulfillsPlanHandleId ||
''
"
:recipient-entity-type-override="projectInfo ? 'project' : 'person'"
/>
<div v-if="libsUtil.isGiveAction(veriClaim)">

28
src/views/ContactGiftingView.vue

@ -67,9 +67,10 @@
<GiftedDialog
ref="giftedDialog"
:giver-entity-type="giverEntityType"
:recipient-entity-type="recipientEntityType"
:from-project-id="fromProjectId"
:to-project-id="toProjectId"
:show-projects="showProjects"
:is-from-project-view="isFromProjectView"
/>
</section>
@ -213,7 +214,7 @@ export default class ContactGiftingView extends Vue {
// Preserve the existing giver from the context
if (this.giverEntityType === "project") {
giver = {
// no did, because it's a project
did: this.giverProjectHandleId,
name: this.giverProjectName,
image: this.giverProjectImage,
handleId: this.giverProjectHandleId,
@ -253,7 +254,7 @@ export default class ContactGiftingView extends Vue {
// We're selecting a giver, so the contact becomes the giver
giver = contact as GiverReceiverInputInfo; // Safe because we know contact is not "Unnamed" or undefined
// Recipient is either a project or the current user
// Preserve the existing recipient from the context
if (this.recipientEntityType === "project") {
recipient = {
did: this.recipientProjectHandleId,
@ -262,8 +263,21 @@ export default class ContactGiftingView extends Vue {
handleId: this.recipientProjectHandleId,
};
} else {
// Check if the preserved recipient was "Unnamed" (empty DID) or a regular contact
if (this.recipientDid === "") {
// Recipient was "Unnamed"
recipient = { did: "", name: "Unnamed" };
} else if (this.recipientDid) {
// Recipient was a regular contact
recipient = {
did: this.recipientDid,
name: this.recipientProjectName || "Someone",
};
} else {
// Fallback to current user
recipient = { did: this.activeDid, name: "You" };
}
}
} else {
// We're selecting a recipient, so the contact becomes the recipient
recipient = contact as GiverReceiverInputInfo; // Safe because we know contact is not "Unnamed" or undefined
@ -276,15 +290,23 @@ export default class ContactGiftingView extends Vue {
image: this.giverProjectImage,
handleId: this.giverProjectHandleId,
};
} else {
// Check if the preserved giver was "Unnamed" (empty DID) or a regular contact
if (this.giverDid === "") {
// Giver was "Unnamed"
giver = { did: "", name: "Unnamed" };
} else if (this.giverDid) {
// Giver was a regular contact
giver = {
did: this.giverDid,
name: this.giverProjectName || "Someone",
};
} else {
// Fallback to current user
giver = { did: this.activeDid, name: "You" };
}
}
}
(this.$refs.giftedDialog as GiftedDialog).open(
giver,

6
src/views/ContactsView.vue

@ -107,7 +107,11 @@
@copy-selected="copySelectedContacts"
/>
<GiftedDialog ref="customGivenDialog" />
<GiftedDialog
ref="customGivenDialog"
:giver-entity-type="'person'"
:recipient-entity-type="'person'"
/>
<OfferDialog ref="customOfferDialog" />
<ContactNameDialog ref="contactNameDialog" />

6
src/views/HomeView.vue

@ -151,7 +151,11 @@ Raymer * @version 1.0.0 */
</div>
</div>
<GiftedDialog ref="giftedDialog" :show-projects="showProjectsDialog" />
<GiftedDialog
ref="giftedDialog"
:giver-entity-type="showProjectsDialog ? 'project' : 'person'"
:recipient-entity-type="'person'"
/>
<GiftedPrompts ref="giftedPrompts" />
<FeedFilters ref="feedFilters" />

5
src/views/ProjectViewView.vue

@ -216,6 +216,8 @@
<GiftedDialog
ref="giveDialogToThis"
:giver-entity-type="'person'"
:recipient-entity-type="'project'"
:to-project-id="projectId"
:is-from-project-view="true"
/>
@ -486,8 +488,9 @@
</div>
<GiftedDialog
ref="giveDialogFromThis"
:giver-entity-type="'project'"
:recipient-entity-type="'person'"
:from-project-id="projectId"
:show-projects="true"
:is-from-project-view="true"
/>

Loading…
Cancel
Save