Merge branch 'build-improvement' into performance-optimizations-testing
This commit is contained in:
207
docs/refactoring/GiftedDialog-EntityTypes-Refactoring.md
Normal file
207
docs/refactoring/GiftedDialog-EntityTypes-Refactoring.md
Normal file
@@ -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
|
||||
@@ -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,26 +243,31 @@ 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 || "",
|
||||
}
|
||||
: {
|
||||
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(),
|
||||
};
|
||||
|
||||
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 || "",
|
||||
recipientProjectId: this.toProjectId || "",
|
||||
recipientProjectName: this.receiver?.name || "",
|
||||
recipientProjectImage: this.receiver?.image || "",
|
||||
recipientProjectHandleId: this.receiver?.handleId || "",
|
||||
recipientDid: this.receiver?.did || "",
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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)">
|
||||
|
||||
@@ -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,7 +263,20 @@ export default class ContactGiftingView extends Vue {
|
||||
handleId: this.recipientProjectHandleId,
|
||||
};
|
||||
} else {
|
||||
recipient = { did: this.activeDid, name: "You" };
|
||||
// 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
|
||||
@@ -276,13 +290,21 @@ export default class ContactGiftingView extends Vue {
|
||||
image: this.giverProjectImage,
|
||||
handleId: this.giverProjectHandleId,
|
||||
};
|
||||
} else if (this.giverDid) {
|
||||
giver = {
|
||||
did: this.giverDid,
|
||||
name: this.giverProjectName || "Someone",
|
||||
};
|
||||
} else {
|
||||
giver = { did: this.activeDid, name: "You" };
|
||||
// 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" };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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" />
|
||||
|
||||
|
||||
@@ -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" />
|
||||
|
||||
|
||||
@@ -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"
|
||||
/>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user