Compare commits
14 Commits
android-sa
...
contact-gi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ec53452220 | ||
|
|
ec326495b2 | ||
|
|
cc50c38d13 | ||
| 3969167d92 | |||
|
|
eb44e7b51e | ||
|
|
ca8d72e1c9 | ||
|
|
a4528c5703 | ||
|
|
6acebb66ef | ||
| b3f7026afe | |||
|
|
ec1a725832 | ||
|
|
6d316c2b3f | ||
|
|
24f6730572 | ||
|
|
0fc44b31bf | ||
|
|
bed2c7106a |
@@ -22,7 +22,7 @@ projects, and special entities with selection. * * @author Matthew Raymer */
|
|||||||
<!-- "Unnamed" entity -->
|
<!-- "Unnamed" entity -->
|
||||||
<SpecialEntityCard
|
<SpecialEntityCard
|
||||||
entity-type="unnamed"
|
entity-type="unnamed"
|
||||||
label="Unnamed"
|
:label="unnamedEntityName"
|
||||||
icon="circle-question"
|
icon="circle-question"
|
||||||
:entity-data="unnamedEntityData"
|
:entity-data="unnamedEntityData"
|
||||||
:notify="notify"
|
:notify="notify"
|
||||||
@@ -83,6 +83,7 @@ import ShowAllCard from "./ShowAllCard.vue";
|
|||||||
import { Contact } from "../db/tables/contacts";
|
import { Contact } from "../db/tables/contacts";
|
||||||
import { PlanData } from "../interfaces/records";
|
import { PlanData } from "../interfaces/records";
|
||||||
import { NotificationIface } from "../constants/app";
|
import { NotificationIface } from "../constants/app";
|
||||||
|
import { UNNAMED_ENTITY_NAME } from "@/constants/entities";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* EntityGrid - Unified grid layout for displaying people or projects
|
* EntityGrid - Unified grid layout for displaying people or projects
|
||||||
@@ -159,6 +160,10 @@ export default class EntityGrid extends Vue {
|
|||||||
@Prop({ default: "other party" })
|
@Prop({ default: "other party" })
|
||||||
conflictContext!: string;
|
conflictContext!: string;
|
||||||
|
|
||||||
|
/** Whether to hide the "Show All" navigation */
|
||||||
|
@Prop({ default: false })
|
||||||
|
hideShowAll!: boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function to determine which entities to display (allows parent control)
|
* Function to determine which entities to display (allows parent control)
|
||||||
*
|
*
|
||||||
@@ -245,7 +250,9 @@ export default class EntityGrid extends Vue {
|
|||||||
* Whether to show the "Show All" navigation
|
* Whether to show the "Show All" navigation
|
||||||
*/
|
*/
|
||||||
get shouldShowAll(): boolean {
|
get shouldShowAll(): boolean {
|
||||||
return this.entities.length > 0 && this.showAllRoute !== "";
|
return (
|
||||||
|
!this.hideShowAll && this.entities.length > 0 && this.showAllRoute !== ""
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -271,10 +278,17 @@ export default class EntityGrid extends Vue {
|
|||||||
get unnamedEntityData(): { did: string; name: string } {
|
get unnamedEntityData(): { did: string; name: string } {
|
||||||
return {
|
return {
|
||||||
did: "",
|
did: "",
|
||||||
name: "Unnamed",
|
name: UNNAMED_ENTITY_NAME,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the unnamed entity name constant
|
||||||
|
*/
|
||||||
|
get unnamedEntityName(): string {
|
||||||
|
return UNNAMED_ENTITY_NAME;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if a person DID is conflicted
|
* Check if a person DID is conflicted
|
||||||
*/
|
*/
|
||||||
@@ -304,16 +318,13 @@ export default class EntityGrid extends Vue {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle special entity selection from SpecialEntityCard
|
* Handle special entity selection from SpecialEntityCard
|
||||||
|
* Treat "You" and "Unnamed" as person entities
|
||||||
*/
|
*/
|
||||||
handleEntitySelected(event: {
|
handleEntitySelected(event: { data: { did?: string; name: string } }): void {
|
||||||
type: string;
|
// Convert special entities to person entities since they represent people
|
||||||
entityType: string;
|
|
||||||
data: { did?: string; name: string };
|
|
||||||
}): void {
|
|
||||||
this.emitEntitySelected({
|
this.emitEntitySelected({
|
||||||
type: "special",
|
type: "person",
|
||||||
entityType: event.entityType,
|
data: event.data as Contact,
|
||||||
data: event.data,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -321,13 +332,11 @@ export default class EntityGrid extends Vue {
|
|||||||
|
|
||||||
@Emit("entity-selected")
|
@Emit("entity-selected")
|
||||||
emitEntitySelected(data: {
|
emitEntitySelected(data: {
|
||||||
type: "person" | "project" | "special";
|
type: "person" | "project";
|
||||||
entityType?: string;
|
data: Contact | PlanData;
|
||||||
data: Contact | PlanData | { did?: string; name: string };
|
|
||||||
}): {
|
}): {
|
||||||
type: "person" | "project" | "special";
|
type: "person" | "project";
|
||||||
entityType?: string;
|
data: Contact | PlanData;
|
||||||
data: Contact | PlanData | { did?: string; name: string };
|
|
||||||
} {
|
} {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ Matthew Raymer */
|
|||||||
:show-all-query-params="showAllQueryParams"
|
:show-all-query-params="showAllQueryParams"
|
||||||
:notify="notify"
|
:notify="notify"
|
||||||
:conflict-context="conflictContext"
|
:conflict-context="conflictContext"
|
||||||
|
:hide-show-all="hideShowAll"
|
||||||
@entity-selected="handleEntitySelected"
|
@entity-selected="handleEntitySelected"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -55,9 +56,8 @@ interface EntityData {
|
|||||||
* Entity selection event data structure
|
* Entity selection event data structure
|
||||||
*/
|
*/
|
||||||
interface EntitySelectionEvent {
|
interface EntitySelectionEvent {
|
||||||
type: "person" | "project" | "special";
|
type: "person" | "project";
|
||||||
entityType?: string;
|
data: Contact | PlanData;
|
||||||
data: Contact | PlanData | EntityData;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -154,6 +154,10 @@ export default class EntitySelectionStep extends Vue {
|
|||||||
@Prop()
|
@Prop()
|
||||||
notify?: (notification: NotificationIface, timeout?: number) => void;
|
notify?: (notification: NotificationIface, timeout?: number) => void;
|
||||||
|
|
||||||
|
/** Whether to hide the "Show All" navigation */
|
||||||
|
@Prop({ default: false })
|
||||||
|
hideShowAll!: boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CSS classes for the cancel button
|
* CSS classes for the cancel button
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -42,8 +42,8 @@ computed CSS properties * * @author Matthew Raymer */
|
|||||||
<p class="text-xs text-slate-500 leading-1 -mb-1 uppercase">
|
<p class="text-xs text-slate-500 leading-1 -mb-1 uppercase">
|
||||||
{{ label }}
|
{{ label }}
|
||||||
</p>
|
</p>
|
||||||
<h3 class="font-semibold truncate">
|
<h3 :class="nameClasses">
|
||||||
{{ entity?.name || "Unnamed" }}
|
{{ displayName }}
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -62,6 +62,7 @@ import { Component, Prop, Vue } from "vue-facing-decorator";
|
|||||||
import EntityIcon from "./EntityIcon.vue";
|
import EntityIcon from "./EntityIcon.vue";
|
||||||
import ProjectIcon from "./ProjectIcon.vue";
|
import ProjectIcon from "./ProjectIcon.vue";
|
||||||
import { Contact } from "../db/tables/contacts";
|
import { Contact } from "../db/tables/contacts";
|
||||||
|
import { UNNAMED_ENTITY_NAME } from "@/constants/entities";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Entity interface for both person and project entities
|
* Entity interface for both person and project entities
|
||||||
@@ -138,6 +139,38 @@ export default class EntitySummaryButton extends Vue {
|
|||||||
return this.editable ? "text-blue-500" : "text-slate-400";
|
return this.editable ? "text-blue-500" : "text-slate-400";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computed CSS classes for the entity name
|
||||||
|
*/
|
||||||
|
get nameClasses(): string {
|
||||||
|
const baseClasses = "font-semibold truncate";
|
||||||
|
|
||||||
|
// Add italic styling for special "Unnamed" or entities without set names
|
||||||
|
if (!this.entity?.name || this.entity?.did === "") {
|
||||||
|
return `${baseClasses} italic text-slate-500`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return baseClasses;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computed display name for the entity
|
||||||
|
*/
|
||||||
|
get displayName(): string {
|
||||||
|
// If the entity has a set name, use that name
|
||||||
|
if (this.entity?.name) {
|
||||||
|
return this.entity.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the entity is the special "Unnamed", use "Unnamed"
|
||||||
|
if (this.entity?.did === "") {
|
||||||
|
return UNNAMED_ENTITY_NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the entity does not have a set name, but is not the special "Unnamed", use their DID
|
||||||
|
return this.entity?.did;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle click event - only call function prop if editable
|
* Handle click event - only call function prop if editable
|
||||||
* Allows parent to control edit behavior and validation
|
* Allows parent to control edit behavior and validation
|
||||||
|
|||||||
@@ -29,6 +29,7 @@
|
|||||||
:unit-code="unitCode"
|
:unit-code="unitCode"
|
||||||
:offer-id="offerId"
|
:offer-id="offerId"
|
||||||
:notify="$notify"
|
:notify="$notify"
|
||||||
|
:hide-show-all="hideShowAll"
|
||||||
@entity-selected="handleEntitySelected"
|
@entity-selected="handleEntitySelected"
|
||||||
@cancel="cancel"
|
@cancel="cancel"
|
||||||
/>
|
/>
|
||||||
@@ -87,6 +88,7 @@ import {
|
|||||||
NOTIFY_GIFTED_DETAILS_NO_IDENTIFIER,
|
NOTIFY_GIFTED_DETAILS_NO_IDENTIFIER,
|
||||||
NOTIFY_GIFTED_DETAILS_RECORDING_GIVE,
|
NOTIFY_GIFTED_DETAILS_RECORDING_GIVE,
|
||||||
} from "@/constants/notifications";
|
} from "@/constants/notifications";
|
||||||
|
import { UNNAMED_ENTITY_NAME } from "@/constants/entities";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
components: {
|
components: {
|
||||||
@@ -114,6 +116,7 @@ export default class GiftedDialog extends Vue {
|
|||||||
@Prop() fromProjectId = "";
|
@Prop() fromProjectId = "";
|
||||||
@Prop() toProjectId = "";
|
@Prop() toProjectId = "";
|
||||||
@Prop() isFromProjectView = false;
|
@Prop() isFromProjectView = false;
|
||||||
|
@Prop() hideShowAll = false;
|
||||||
@Prop({ default: "person" }) giverEntityType = "person" as
|
@Prop({ default: "person" }) giverEntityType = "person" as
|
||||||
| "person"
|
| "person"
|
||||||
| "project";
|
| "project";
|
||||||
@@ -224,15 +227,6 @@ export default class GiftedDialog extends Vue {
|
|||||||
|
|
||||||
this.allMyDids = await retrieveAccountDids();
|
this.allMyDids = await retrieveAccountDids();
|
||||||
|
|
||||||
if (this.giver && !this.giver.name) {
|
|
||||||
this.giver.name = didInfo(
|
|
||||||
this.giver.did,
|
|
||||||
this.activeDid,
|
|
||||||
this.allMyDids,
|
|
||||||
this.allContacts,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
this.giverEntityType === "project" ||
|
this.giverEntityType === "project" ||
|
||||||
this.recipientEntityType === "project"
|
this.recipientEntityType === "project"
|
||||||
@@ -455,14 +449,14 @@ export default class GiftedDialog extends Vue {
|
|||||||
if (contact) {
|
if (contact) {
|
||||||
this.giver = {
|
this.giver = {
|
||||||
did: contact.did,
|
did: contact.did,
|
||||||
name: contact.name || contact.did,
|
name: contact.name,
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
// Only set to "Unnamed" if no giver is currently set
|
// Only set to "Unnamed" if no giver is currently set
|
||||||
if (!this.giver || !this.giver.did) {
|
if (!this.giver || !this.giver.did) {
|
||||||
this.giver = {
|
this.giver = {
|
||||||
did: "",
|
did: "",
|
||||||
name: "Unnamed",
|
name: UNNAMED_ENTITY_NAME,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -517,14 +511,14 @@ export default class GiftedDialog extends Vue {
|
|||||||
if (contact) {
|
if (contact) {
|
||||||
this.receiver = {
|
this.receiver = {
|
||||||
did: contact.did,
|
did: contact.did,
|
||||||
name: contact.name || contact.did,
|
name: contact.name,
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
// Only set to "Unnamed" if no receiver is currently set
|
// Only set to "Unnamed" if no receiver is currently set
|
||||||
if (!this.receiver || !this.receiver.did) {
|
if (!this.receiver || !this.receiver.did) {
|
||||||
this.receiver = {
|
this.receiver = {
|
||||||
did: "",
|
did: "",
|
||||||
name: "Unnamed",
|
name: UNNAMED_ENTITY_NAME,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -566,20 +560,21 @@ export default class GiftedDialog extends Vue {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle entity selection from EntitySelectionStep
|
* Handle entity selection from EntitySelectionStep
|
||||||
* @param entity - The selected entity (person, project, or special) with stepType
|
* @param entity - The selected entity (person or project) with stepType
|
||||||
*/
|
*/
|
||||||
handleEntitySelected(entity: {
|
handleEntitySelected(entity: {
|
||||||
type: "person" | "project" | "special";
|
type: "person" | "project";
|
||||||
entityType?: string;
|
data: Contact | PlanData;
|
||||||
data: Contact | PlanData | { did?: string; name: string };
|
|
||||||
stepType: string;
|
stepType: string;
|
||||||
}) {
|
}) {
|
||||||
if (entity.type === "person") {
|
if (entity.type === "person") {
|
||||||
const contact = entity.data as Contact;
|
const contact = entity.data as Contact;
|
||||||
|
// Apply DID-based logic for person entities
|
||||||
|
const processedContact = this.processPersonEntity(contact);
|
||||||
if (entity.stepType === "giver") {
|
if (entity.stepType === "giver") {
|
||||||
this.selectGiver(contact);
|
this.selectGiver(processedContact);
|
||||||
} else {
|
} else {
|
||||||
this.selectRecipient(contact);
|
this.selectRecipient(processedContact);
|
||||||
}
|
}
|
||||||
} else if (entity.type === "project") {
|
} else if (entity.type === "project") {
|
||||||
const project = entity.data as PlanData;
|
const project = entity.data as PlanData;
|
||||||
@@ -588,33 +583,22 @@ export default class GiftedDialog extends Vue {
|
|||||||
} else {
|
} else {
|
||||||
this.selectRecipientProject(project);
|
this.selectRecipientProject(project);
|
||||||
}
|
}
|
||||||
} else if (entity.type === "special") {
|
}
|
||||||
// Handle special entities like "You" and "Unnamed"
|
}
|
||||||
if (entity.entityType === "you") {
|
|
||||||
// "You" entity selected
|
/**
|
||||||
const youEntity = {
|
* Processes person entities using DID-based logic for "You" and "Unnamed"
|
||||||
did: this.activeDid,
|
*/
|
||||||
name: "You",
|
private processPersonEntity(contact: Contact): Contact {
|
||||||
};
|
if (contact.did === this.activeDid) {
|
||||||
if (entity.stepType === "giver") {
|
// If DID matches active DID, create "You" entity
|
||||||
this.giver = youEntity;
|
return { ...contact, name: "You" };
|
||||||
} else {
|
} else if (!contact.did || contact.did === "") {
|
||||||
this.receiver = youEntity;
|
// If DID is empty/null, create "Unnamed" entity
|
||||||
}
|
return { ...contact, name: UNNAMED_ENTITY_NAME };
|
||||||
this.firstStep = false;
|
} else {
|
||||||
} else if (entity.entityType === "unnamed") {
|
// Return the contact as-is
|
||||||
// "Unnamed" entity selected
|
return contact;
|
||||||
const unnamedEntity = {
|
|
||||||
did: "",
|
|
||||||
name: "Unnamed",
|
|
||||||
};
|
|
||||||
if (entity.stepType === "giver") {
|
|
||||||
this.giver = unnamedEntity;
|
|
||||||
} else {
|
|
||||||
this.receiver = unnamedEntity;
|
|
||||||
}
|
|
||||||
this.firstStep = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -81,7 +81,7 @@
|
|||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<h3 class="text-lg font-medium">
|
<h3 class="text-lg font-medium">
|
||||||
{{ member.name || "Unnamed Member" }}
|
{{ member.name || unnamedMember }}
|
||||||
</h3>
|
</h3>
|
||||||
<div
|
<div
|
||||||
v-if="!getContactFor(member.did) && member.did !== activeDid"
|
v-if="!getContactFor(member.did) && member.did !== activeDid"
|
||||||
@@ -177,6 +177,7 @@ import {
|
|||||||
NOTIFY_ADD_CONTACT_FIRST,
|
NOTIFY_ADD_CONTACT_FIRST,
|
||||||
NOTIFY_CONTINUE_WITHOUT_ADDING,
|
NOTIFY_CONTINUE_WITHOUT_ADDING,
|
||||||
} from "@/constants/notifications";
|
} from "@/constants/notifications";
|
||||||
|
import { SOMEONE_UNNAMED } from "@/constants/entities";
|
||||||
|
|
||||||
interface Member {
|
interface Member {
|
||||||
admitted: boolean;
|
admitted: boolean;
|
||||||
@@ -220,6 +221,13 @@ export default class MembersList extends Vue {
|
|||||||
apiServer = "";
|
apiServer = "";
|
||||||
contacts: Array<Contact> = [];
|
contacts: Array<Contact> = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the unnamed member constant
|
||||||
|
*/
|
||||||
|
get unnamedMember(): string {
|
||||||
|
return SOMEONE_UNNAMED;
|
||||||
|
}
|
||||||
|
|
||||||
async created() {
|
async created() {
|
||||||
this.notify = createNotifyHelpers(this.$notify);
|
this.notify = createNotifyHelpers(this.$notify);
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ conflict detection. * * @author Matthew Raymer */
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h3 :class="nameClasses">
|
<h3 :class="nameClasses">
|
||||||
{{ person.name || person.did || "Unnamed" }}
|
{{ displayName }}
|
||||||
</h3>
|
</h3>
|
||||||
</li>
|
</li>
|
||||||
</template>
|
</template>
|
||||||
@@ -98,9 +98,27 @@ export default class PersonCard extends Vue {
|
|||||||
return `${baseClasses} text-slate-400`;
|
return `${baseClasses} text-slate-400`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add italic styling for entities without set names
|
||||||
|
if (!this.person.name) {
|
||||||
|
return `${baseClasses} italic text-slate-500`;
|
||||||
|
}
|
||||||
|
|
||||||
return baseClasses;
|
return baseClasses;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computed display name for the person
|
||||||
|
*/
|
||||||
|
get displayName(): string {
|
||||||
|
// If the entity has a set name, use that name
|
||||||
|
if (this.person.name) {
|
||||||
|
return this.person.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the entity does not have a set name
|
||||||
|
return this.person.did;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle card click - emit if selectable and not conflicted, show warning if conflicted
|
* Handle card click - emit if selectable and not conflicted, show warning if conflicted
|
||||||
*/
|
*/
|
||||||
@@ -114,7 +132,7 @@ export default class PersonCard extends Vue {
|
|||||||
group: "alert",
|
group: "alert",
|
||||||
type: "warning",
|
type: "warning",
|
||||||
title: "Cannot Select",
|
title: "Cannot Select",
|
||||||
text: `You cannot select "${this.person.name || this.person.did || "Unnamed"}" because they are already selected as the ${this.conflictContext}.`,
|
text: `You cannot select "${this.displayName}" because they are already selected as the ${this.conflictContext}.`,
|
||||||
},
|
},
|
||||||
3000,
|
3000,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ issuer information. * * @author Matthew Raymer */
|
|||||||
<h3
|
<h3
|
||||||
class="text-xs font-medium text-ellipsis whitespace-nowrap overflow-hidden"
|
class="text-xs font-medium text-ellipsis whitespace-nowrap overflow-hidden"
|
||||||
>
|
>
|
||||||
{{ project.name || "Unnamed Project" }}
|
{{ project.name || unnamedProject }}
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<div class="text-xs text-slate-500 truncate">
|
<div class="text-xs text-slate-500 truncate">
|
||||||
@@ -31,6 +31,7 @@ import ProjectIcon from "./ProjectIcon.vue";
|
|||||||
import { PlanData } from "../interfaces/records";
|
import { PlanData } from "../interfaces/records";
|
||||||
import { Contact } from "../db/tables/contacts";
|
import { Contact } from "../db/tables/contacts";
|
||||||
import { didInfo } from "../libs/endorserServer";
|
import { didInfo } from "../libs/endorserServer";
|
||||||
|
import { UNNAMED_PROJECT } from "@/constants/entities";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ProjectCard - Displays a project entity with selection capability
|
* ProjectCard - Displays a project entity with selection capability
|
||||||
@@ -63,6 +64,13 @@ export default class ProjectCard extends Vue {
|
|||||||
@Prop({ required: true })
|
@Prop({ required: true })
|
||||||
allContacts!: Contact[];
|
allContacts!: Contact[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the unnamed project constant
|
||||||
|
*/
|
||||||
|
get unnamedProject(): string {
|
||||||
|
return UNNAMED_PROJECT;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Computed display name for the project issuer
|
* Computed display name for the project issuer
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -115,6 +115,7 @@ import { urlBase64ToUint8Array } from "../libs/crypto/vc/util";
|
|||||||
import * as libsUtil from "../libs/util";
|
import * as libsUtil from "../libs/util";
|
||||||
import { logger } from "../utils/logger";
|
import { logger } from "../utils/logger";
|
||||||
import { PlatformServiceMixin } from "../utils/PlatformServiceMixin";
|
import { PlatformServiceMixin } from "../utils/PlatformServiceMixin";
|
||||||
|
import { UNNAMED_ENTITY_NAME } from "@/constants/entities";
|
||||||
|
|
||||||
// Example interface for error
|
// Example interface for error
|
||||||
interface ErrorResponse {
|
interface ErrorResponse {
|
||||||
@@ -602,7 +603,7 @@ export default class PushNotificationPermission extends Vue {
|
|||||||
* Returns the default message for direct push
|
* Returns the default message for direct push
|
||||||
*/
|
*/
|
||||||
get notificationMessagePlaceholder(): string {
|
get notificationMessagePlaceholder(): string {
|
||||||
return "Click to share some gratitude with the world -- even if they're unnamed.";
|
return `Click to share some gratitude with the world -- even if they're ${UNNAMED_ENTITY_NAME.toLowerCase()}.`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -124,8 +124,6 @@ export default class SpecialEntityCard extends Vue {
|
|||||||
handleClick(): void {
|
handleClick(): void {
|
||||||
if (this.selectable && !this.conflicted) {
|
if (this.selectable && !this.conflicted) {
|
||||||
this.emitEntitySelected({
|
this.emitEntitySelected({
|
||||||
type: "special",
|
|
||||||
entityType: this.entityType,
|
|
||||||
data: this.entityData,
|
data: this.entityData,
|
||||||
});
|
});
|
||||||
} else if (this.conflicted && this.notify) {
|
} else if (this.conflicted && this.notify) {
|
||||||
@@ -145,13 +143,7 @@ export default class SpecialEntityCard extends Vue {
|
|||||||
// Emit methods using @Emit decorator
|
// Emit methods using @Emit decorator
|
||||||
|
|
||||||
@Emit("entity-selected")
|
@Emit("entity-selected")
|
||||||
emitEntitySelected(data: {
|
emitEntitySelected(data: { data: { did?: string; name: string } }): {
|
||||||
type: string;
|
|
||||||
entityType: string;
|
|
||||||
data: { did?: string; name: string };
|
|
||||||
}): {
|
|
||||||
type: string;
|
|
||||||
entityType: string;
|
|
||||||
data: { did?: string; name: string };
|
data: { did?: string; name: string };
|
||||||
} {
|
} {
|
||||||
return data;
|
return data;
|
||||||
|
|||||||
14
src/constants/entities.ts
Normal file
14
src/constants/entities.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
/**
|
||||||
|
* Constants for entity-related strings, particularly for unnamed/unknown person entities
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Core unnamed entity names
|
||||||
|
export const UNNAMED_ENTITY_NAME = "Unnamed";
|
||||||
|
|
||||||
|
// Descriptive phrases for unnamed entities
|
||||||
|
export const SOMEONE_UNNAMED = "Someone Unnamed";
|
||||||
|
export const THAT_UNNAMED_PERSON = "That unnamed person";
|
||||||
|
export const UNNAMED_PERSON = "unnamed person";
|
||||||
|
|
||||||
|
// Project-related unnamed entities
|
||||||
|
export const UNNAMED_PROJECT = "Unnamed Project";
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
import { THAT_UNNAMED_PERSON } from "./entities";
|
||||||
|
|
||||||
// Notification message constants for user-facing notifications
|
// Notification message constants for user-facing notifications
|
||||||
// Add new notification messages here as needed
|
// Add new notification messages here as needed
|
||||||
@@ -873,7 +874,7 @@ export const NOTIFY_CONTACT_LINK_COPIED = {
|
|||||||
// Template for registration success message
|
// Template for registration success message
|
||||||
// Used in: ContactsView.vue (register method - registration success with contact name)
|
// Used in: ContactsView.vue (register method - registration success with contact name)
|
||||||
export const getRegisterPersonSuccessMessage = (name?: string): string =>
|
export const getRegisterPersonSuccessMessage = (name?: string): string =>
|
||||||
`${name || "That unnamed person"} ${NOTIFY_REGISTER_PERSON_SUCCESS.message}`;
|
`${name || THAT_UNNAMED_PERSON} ${NOTIFY_REGISTER_PERSON_SUCCESS.message}`;
|
||||||
|
|
||||||
// Template for visibility success message
|
// Template for visibility success message
|
||||||
// Used in: ContactsView.vue (setVisibility method - visibility success with contact name)
|
// Used in: ContactsView.vue (setVisibility method - visibility success with contact name)
|
||||||
@@ -1378,7 +1379,7 @@ export function createQRContactAddedMessage(hasVisibility: boolean): string {
|
|||||||
export function createQRRegistrationSuccessMessage(
|
export function createQRRegistrationSuccessMessage(
|
||||||
contactName: string,
|
contactName: string,
|
||||||
): string {
|
): string {
|
||||||
return `${contactName || "That unnamed person"}${NOTIFY_QR_REGISTRATION_SUCCESS.message}`;
|
return `${contactName || THAT_UNNAMED_PERSON}${NOTIFY_QR_REGISTRATION_SUCCESS.message}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ContactQRScanShowView.vue timeout constants
|
// ContactQRScanShowView.vue timeout constants
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ import { PlanSummaryRecord } from "../interfaces/records";
|
|||||||
import { logger } from "../utils/logger";
|
import { logger } from "../utils/logger";
|
||||||
import { PlatformServiceFactory } from "@/services/PlatformServiceFactory";
|
import { PlatformServiceFactory } from "@/services/PlatformServiceFactory";
|
||||||
import { APP_SERVER } from "@/constants/app";
|
import { APP_SERVER } from "@/constants/app";
|
||||||
|
import { SOMEONE_UNNAMED } from "@/constants/entities";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Standard context for schema.org data
|
* Standard context for schema.org data
|
||||||
@@ -309,7 +310,7 @@ export function didInfoForContact(
|
|||||||
showDidForVisible: boolean = false,
|
showDidForVisible: boolean = false,
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
): { known: boolean; displayName: string; profileImageUrl?: string } {
|
): { known: boolean; displayName: string; profileImageUrl?: string } {
|
||||||
if (!did) return { displayName: "Someone Not Named", known: false };
|
if (!did) return { displayName: SOMEONE_UNNAMED, known: false };
|
||||||
if (did === activeDid) {
|
if (did === activeDid) {
|
||||||
return { displayName: "You", known: true };
|
return { displayName: "You", known: true };
|
||||||
} else if (contact) {
|
} else if (contact) {
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import { logger } from "../utils/logger";
|
|||||||
import { PlatformServiceFactory } from "../services/PlatformServiceFactory";
|
import { PlatformServiceFactory } from "../services/PlatformServiceFactory";
|
||||||
import { IIdentifier } from "@veramo/core";
|
import { IIdentifier } from "@veramo/core";
|
||||||
import { DEFAULT_ROOT_DERIVATION_PATH } from "./crypto";
|
import { DEFAULT_ROOT_DERIVATION_PATH } from "./crypto";
|
||||||
|
import { UNNAMED_PERSON } from "@/constants/entities";
|
||||||
|
|
||||||
// Consolidate this with src/utils/PlatformServiceMixin.mapQueryResultToValues
|
// Consolidate this with src/utils/PlatformServiceMixin.mapQueryResultToValues
|
||||||
function mapQueryResultToValues(
|
function mapQueryResultToValues(
|
||||||
@@ -192,7 +193,7 @@ export const nameForContact = (
|
|||||||
): string => {
|
): string => {
|
||||||
return (
|
return (
|
||||||
(contact?.name as string) ||
|
(contact?.name as string) ||
|
||||||
(capitalize ? "This" : "this") + " unnamed user"
|
(capitalize ? "This" : "this") + " " + UNNAMED_PERSON
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -17,20 +17,40 @@
|
|||||||
|
|
||||||
<!-- Results List -->
|
<!-- Results List -->
|
||||||
<ul class="border-t border-slate-300">
|
<ul class="border-t border-slate-300">
|
||||||
<li class="border-b border-slate-300 py-3">
|
<!-- "You" entity -->
|
||||||
|
<li v-if="shouldShowYouEntity" class="border-b border-slate-300 py-3">
|
||||||
<h2 class="text-base flex gap-4 items-center">
|
<h2 class="text-base flex gap-4 items-center">
|
||||||
<span class="grow flex gap-2 items-center font-medium">
|
<span class="grow flex gap-2 items-center font-medium">
|
||||||
<font-awesome
|
<font-awesome icon="hand" class="text-blue-500 text-4xl shrink-0" />
|
||||||
icon="circle-question"
|
<span class="text-ellipsis overflow-hidden text-blue-500">You</span>
|
||||||
class="text-slate-400 text-4xl"
|
|
||||||
/>
|
|
||||||
<span class="italic text-slate-400">(Not Named)</span>
|
|
||||||
</span>
|
</span>
|
||||||
<span class="text-right">
|
<span class="text-right">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="block w-full text-center text-sm uppercase 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-3 py-1.5 rounded-md"
|
class="block w-full text-center text-sm uppercase 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-3 py-1.5 rounded-md"
|
||||||
@click="openDialog('Unnamed')"
|
@click="openDialog({ did: activeDid, name: 'You' })"
|
||||||
|
>
|
||||||
|
<font-awesome icon="gift" class="fa-fw"></font-awesome>
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
</h2>
|
||||||
|
</li>
|
||||||
|
<li class="border-b border-slate-300 py-3">
|
||||||
|
<h2 class="text-base flex gap-4 items-center">
|
||||||
|
<span class="grow flex gap-2 items-center font-medium">
|
||||||
|
<font-awesome
|
||||||
|
icon="circle-question"
|
||||||
|
class="text-slate-400 text-4xl shrink-0"
|
||||||
|
/>
|
||||||
|
<span class="text-ellipsis overflow-hidden italic text-slate-500">{{
|
||||||
|
unnamedEntityName
|
||||||
|
}}</span>
|
||||||
|
</span>
|
||||||
|
<span class="text-right">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="block w-full text-center text-sm uppercase 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-3 py-1.5 rounded-md"
|
||||||
|
@click="openDialog({ did: '', name: unnamedEntityName })"
|
||||||
>
|
>
|
||||||
<font-awesome icon="gift" class="fa-fw"></font-awesome>
|
<font-awesome icon="gift" class="fa-fw"></font-awesome>
|
||||||
</button>
|
</button>
|
||||||
@@ -43,14 +63,22 @@
|
|||||||
class="border-b border-slate-300 py-3"
|
class="border-b border-slate-300 py-3"
|
||||||
>
|
>
|
||||||
<h2 class="text-base flex gap-4 items-center">
|
<h2 class="text-base flex gap-4 items-center">
|
||||||
<span class="grow flex gap-2 items-center font-medium">
|
<span
|
||||||
|
class="grow flex gap-2 items-center font-medium overflow-hidden"
|
||||||
|
>
|
||||||
<EntityIcon
|
<EntityIcon
|
||||||
:contact="contact"
|
:contact="contact"
|
||||||
:icon-size="34"
|
:icon-size="34"
|
||||||
class="inline-block align-middle border border-slate-300 rounded-full overflow-hidden"
|
class="inline-block align-middle border border-slate-300 rounded-full overflow-hidden shrink-0"
|
||||||
/>
|
/>
|
||||||
<span v-if="contact.name">{{ contact.name }}</span>
|
<span v-if="contact.name" class="text-ellipsis overflow-hidden">{{
|
||||||
<span v-else class="italic text-slate-400">(No name)</span>
|
contact.name
|
||||||
|
}}</span>
|
||||||
|
<span
|
||||||
|
v-else
|
||||||
|
class="text-ellipsis overflow-hidden italic text-slate-500"
|
||||||
|
>{{ contact.did }}</span
|
||||||
|
>
|
||||||
</span>
|
</span>
|
||||||
<span class="text-right">
|
<span class="text-right">
|
||||||
<button
|
<button
|
||||||
@@ -72,6 +100,7 @@
|
|||||||
:from-project-id="fromProjectId"
|
:from-project-id="fromProjectId"
|
||||||
:to-project-id="toProjectId"
|
:to-project-id="toProjectId"
|
||||||
:is-from-project-view="isFromProjectView"
|
:is-from-project-view="isFromProjectView"
|
||||||
|
:hide-show-all="true"
|
||||||
/>
|
/>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
@@ -89,6 +118,7 @@ import { GiverReceiverInputInfo } from "../libs/util";
|
|||||||
import { logger } from "../utils/logger";
|
import { logger } from "../utils/logger";
|
||||||
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
|
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
|
||||||
import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
|
import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
|
||||||
|
import { UNNAMED_ENTITY_NAME } from "@/constants/entities";
|
||||||
@Component({
|
@Component({
|
||||||
components: { GiftedDialog, QuickNav, EntityIcon },
|
components: { GiftedDialog, QuickNav, EntityIcon },
|
||||||
mixins: [PlatformServiceMixin],
|
mixins: [PlatformServiceMixin],
|
||||||
@@ -188,147 +218,151 @@ export default class ContactGiftingView extends Vue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
openDialog(contact?: GiverReceiverInputInfo | "Unnamed") {
|
openDialog(contact?: GiverReceiverInputInfo) {
|
||||||
if (contact === "Unnamed") {
|
// Determine the selected entity based on contact type
|
||||||
// Special case: Handle "Unnamed" contacts for both givers and recipients
|
const selectedEntity = this.createEntityFromContact(contact);
|
||||||
let recipient: GiverReceiverInputInfo;
|
|
||||||
let giver: GiverReceiverInputInfo | undefined;
|
|
||||||
|
|
||||||
if (this.stepType === "giver") {
|
// Create giver and recipient based on step type and selected entity
|
||||||
// We're selecting a giver, so preserve the existing recipient from context
|
const { giver, recipient } = this.createGiverAndRecipient(selectedEntity);
|
||||||
if (this.recipientEntityType === "project") {
|
|
||||||
recipient = {
|
|
||||||
did: this.recipientProjectHandleId,
|
|
||||||
name: this.recipientProjectName,
|
|
||||||
image: this.recipientProjectImage,
|
|
||||||
handleId: this.recipientProjectHandleId,
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
// Preserve the existing recipient from context
|
|
||||||
if (this.recipientDid === this.activeDid) {
|
|
||||||
// Recipient was "You"
|
|
||||||
recipient = { did: this.activeDid, name: "You" };
|
|
||||||
} else if (this.recipientDid) {
|
|
||||||
// Recipient was a regular contact
|
|
||||||
recipient = {
|
|
||||||
did: this.recipientDid,
|
|
||||||
name: this.recipientProjectName || "Someone",
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
// Fallback to "You" if no recipient was previously selected
|
|
||||||
recipient = { did: this.activeDid, name: "You" };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
giver = undefined; // Will be set to "Unnamed" in GiftedDialog
|
|
||||||
} else {
|
|
||||||
// We're selecting a recipient, so recipient is "Unnamed" and giver is preserved from context
|
|
||||||
recipient = { did: "", name: "Unnamed" };
|
|
||||||
|
|
||||||
// Preserve the existing giver from the context
|
// Open the dialog
|
||||||
if (this.giverEntityType === "project") {
|
(this.$refs.giftedDialog as GiftedDialog).open(
|
||||||
giver = {
|
giver,
|
||||||
did: this.giverProjectHandleId,
|
recipient,
|
||||||
name: this.giverProjectName,
|
this.offerId,
|
||||||
image: this.giverProjectImage,
|
this.prompt,
|
||||||
handleId: this.giverProjectHandleId,
|
this.description,
|
||||||
};
|
this.amountInput,
|
||||||
} else if (this.giverDid) {
|
this.unitCode,
|
||||||
giver = {
|
);
|
||||||
did: this.giverDid,
|
|
||||||
name: this.giverProjectName || "Someone",
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
giver = { did: this.activeDid, name: "You" };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
(this.$refs.giftedDialog as GiftedDialog).open(
|
// Move to Step 2 - entities are already set by the open() call
|
||||||
giver,
|
(this.$refs.giftedDialog as GiftedDialog).moveToStep2();
|
||||||
recipient,
|
}
|
||||||
this.offerId,
|
|
||||||
this.prompt,
|
|
||||||
this.description,
|
|
||||||
this.amountInput,
|
|
||||||
this.unitCode,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Move to Step 2 - entities are already set by the open() call
|
/**
|
||||||
(this.$refs.giftedDialog as GiftedDialog).moveToStep2();
|
* Creates an entity object from the contact parameter
|
||||||
|
* Uses DID-based logic to determine "You" and "Unnamed" entities
|
||||||
|
*/
|
||||||
|
private createEntityFromContact(
|
||||||
|
contact?: GiverReceiverInputInfo,
|
||||||
|
): GiverReceiverInputInfo | undefined {
|
||||||
|
if (!contact) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle GiverReceiverInputInfo object
|
||||||
|
if (contact.did === this.activeDid) {
|
||||||
|
// If DID matches active DID, create "You" entity
|
||||||
|
return { did: this.activeDid, name: "You" };
|
||||||
|
} else if (!contact.did || contact.did === "") {
|
||||||
|
// If DID is empty/null, create "Unnamed" entity
|
||||||
|
return { did: "", name: UNNAMED_ENTITY_NAME };
|
||||||
} else {
|
} else {
|
||||||
// Regular case: contact is a GiverReceiverInputInfo
|
// Create a copy of the contact to avoid modifying the original
|
||||||
let giver: GiverReceiverInputInfo;
|
return { ...contact };
|
||||||
let recipient: GiverReceiverInputInfo;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (this.stepType === "giver") {
|
/**
|
||||||
// We're selecting a giver, so the contact becomes the giver
|
* Creates giver and recipient objects based on step type and selected entity
|
||||||
giver = contact as GiverReceiverInputInfo; // Safe because we know contact is not "Unnamed" or undefined
|
*/
|
||||||
|
private createGiverAndRecipient(selectedEntity?: GiverReceiverInputInfo): {
|
||||||
|
giver: GiverReceiverInputInfo | undefined;
|
||||||
|
recipient: GiverReceiverInputInfo;
|
||||||
|
} {
|
||||||
|
if (this.stepType === "giver") {
|
||||||
|
// We're selecting a giver, so the selected entity becomes the giver
|
||||||
|
const giver = selectedEntity;
|
||||||
|
const recipient = this.createRecipientFromContext();
|
||||||
|
return { giver, recipient };
|
||||||
|
} else {
|
||||||
|
// We're selecting a recipient, so the selected entity becomes the recipient
|
||||||
|
const recipient = selectedEntity || {
|
||||||
|
did: "",
|
||||||
|
name: UNNAMED_ENTITY_NAME,
|
||||||
|
};
|
||||||
|
const giver = this.createGiverFromContext();
|
||||||
|
return { giver, recipient };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Preserve the existing recipient from the context
|
/**
|
||||||
if (this.recipientEntityType === "project") {
|
* Creates recipient object from context (preserves existing recipient)
|
||||||
recipient = {
|
*/
|
||||||
did: this.recipientProjectHandleId,
|
private createRecipientFromContext(): GiverReceiverInputInfo {
|
||||||
name: this.recipientProjectName,
|
if (this.recipientEntityType === "project") {
|
||||||
image: this.recipientProjectImage,
|
return {
|
||||||
handleId: this.recipientProjectHandleId,
|
name: this.recipientProjectName,
|
||||||
};
|
image: this.recipientProjectImage,
|
||||||
} else {
|
handleId: this.recipientProjectHandleId,
|
||||||
// Check if the preserved recipient was "You" or a regular contact
|
};
|
||||||
if (this.recipientDid === this.activeDid) {
|
} else {
|
||||||
// Recipient was "You"
|
if (this.recipientDid === this.activeDid) {
|
||||||
recipient = { did: this.activeDid, name: "You" };
|
return { did: this.activeDid, name: "You" };
|
||||||
} else if (this.recipientDid) {
|
} else if (this.recipientDid) {
|
||||||
// Recipient was a regular contact
|
return {
|
||||||
recipient = {
|
did: this.recipientDid,
|
||||||
did: this.recipientDid,
|
name: this.recipientProjectName,
|
||||||
name: this.recipientProjectName || "Someone",
|
};
|
||||||
};
|
|
||||||
} else {
|
|
||||||
// Fallback to "Unnamed"
|
|
||||||
recipient = { did: "", name: "Unnamed" };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// We're selecting a recipient, so the contact becomes the recipient
|
return { did: "", name: UNNAMED_ENTITY_NAME };
|
||||||
recipient = contact as GiverReceiverInputInfo; // Safe because we know contact is not "Unnamed" or undefined
|
|
||||||
|
|
||||||
// Preserve the existing giver from the context
|
|
||||||
if (this.giverEntityType === "project") {
|
|
||||||
giver = {
|
|
||||||
did: this.giverProjectHandleId,
|
|
||||||
name: this.giverProjectName,
|
|
||||||
image: this.giverProjectImage,
|
|
||||||
handleId: this.giverProjectHandleId,
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
// Check if the preserved giver was "You" or a regular contact
|
|
||||||
if (this.giverDid === this.activeDid) {
|
|
||||||
// Giver was "You"
|
|
||||||
giver = { did: this.activeDid, name: "You" };
|
|
||||||
} else if (this.giverDid) {
|
|
||||||
// Giver was a regular contact
|
|
||||||
giver = {
|
|
||||||
did: this.giverDid,
|
|
||||||
name: this.giverProjectName || "Someone",
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
// Fallback to "Unnamed"
|
|
||||||
giver = { did: "", name: "Unnamed" };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
(this.$refs.giftedDialog as GiftedDialog).open(
|
/**
|
||||||
giver,
|
* Creates giver object from context (preserves existing giver)
|
||||||
recipient,
|
*/
|
||||||
this.offerId,
|
private createGiverFromContext(): GiverReceiverInputInfo {
|
||||||
this.prompt,
|
if (this.giverEntityType === "project") {
|
||||||
this.description,
|
return {
|
||||||
this.amountInput,
|
name: this.giverProjectName,
|
||||||
this.unitCode,
|
image: this.giverProjectImage,
|
||||||
);
|
handleId: this.giverProjectHandleId,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
if (this.giverDid === this.activeDid) {
|
||||||
|
return { did: this.activeDid, name: "You" };
|
||||||
|
} else if (this.giverDid) {
|
||||||
|
return {
|
||||||
|
did: this.giverDid,
|
||||||
|
name: this.giverProjectName,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return { did: "", name: UNNAMED_ENTITY_NAME };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Move to Step 2 - entities are already set by the open() call
|
/**
|
||||||
(this.$refs.giftedDialog as GiftedDialog).moveToStep2();
|
* Get the unnamed entity name constant
|
||||||
|
*/
|
||||||
|
get unnamedEntityName(): string {
|
||||||
|
return UNNAMED_ENTITY_NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
get shouldShowYouEntity(): boolean {
|
||||||
|
if (this.stepType === "giver") {
|
||||||
|
// When selecting a giver, show "You" if the current recipient is not "You"
|
||||||
|
// This prevents selecting yourself as both giver and recipient
|
||||||
|
if (this.recipientEntityType === "project") {
|
||||||
|
// If recipient is a project, we can select "You" as giver
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
// If recipient is a person, check if it's not "You"
|
||||||
|
return this.recipientDid !== this.activeDid;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// When selecting a recipient, show "You" if the current giver is not "You"
|
||||||
|
// This prevents selecting yourself as both giver and recipient
|
||||||
|
if (this.giverEntityType === "project") {
|
||||||
|
// If giver is a project, we can select "You" as recipient
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
// If giver is a person, check if it's not "You"
|
||||||
|
return this.giverDid !== this.activeDid;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -290,6 +290,7 @@ import {
|
|||||||
NOTIFY_SERVER_ACCESS_ERROR,
|
NOTIFY_SERVER_ACCESS_ERROR,
|
||||||
NOTIFY_NO_IDENTITY_ERROR,
|
NOTIFY_NO_IDENTITY_ERROR,
|
||||||
} from "@/constants/notifications";
|
} from "@/constants/notifications";
|
||||||
|
import { THAT_UNNAMED_PERSON } from "@/constants/entities";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DIDView Component
|
* DIDView Component
|
||||||
@@ -551,7 +552,7 @@ export default class DIDView extends Vue {
|
|||||||
contact.registered = true;
|
contact.registered = true;
|
||||||
await this.$updateContact(contact.did, { registered: true });
|
await this.$updateContact(contact.did, { registered: true });
|
||||||
|
|
||||||
const name = contact.name || "That unnamed person";
|
const name = contact.name || THAT_UNNAMED_PERSON;
|
||||||
this.notify.success(
|
this.notify.success(
|
||||||
`${name} ${NOTIFY_REGISTRATION_SUCCESS.message}`,
|
`${name} ${NOTIFY_REGISTRATION_SUCCESS.message}`,
|
||||||
TIMEOUTS.LONG,
|
TIMEOUTS.LONG,
|
||||||
|
|||||||
@@ -233,7 +233,7 @@
|
|||||||
|
|
||||||
<div class="grow">
|
<div class="grow">
|
||||||
<h2 class="text-base font-semibold">
|
<h2 class="text-base font-semibold">
|
||||||
{{ project.name || "Unnamed Project" }}
|
{{ project.name || unnamedProject }}
|
||||||
</h2>
|
</h2>
|
||||||
<div class="text-sm">
|
<div class="text-sm">
|
||||||
<font-awesome
|
<font-awesome
|
||||||
@@ -340,6 +340,7 @@ import {
|
|||||||
NOTIFY_DISCOVER_LOCAL_SEARCH_ERROR,
|
NOTIFY_DISCOVER_LOCAL_SEARCH_ERROR,
|
||||||
NOTIFY_DISCOVER_MAP_SEARCH_ERROR,
|
NOTIFY_DISCOVER_MAP_SEARCH_ERROR,
|
||||||
} from "@/constants/notifications";
|
} from "@/constants/notifications";
|
||||||
|
import { UNNAMED_PROJECT } from "@/constants/entities";
|
||||||
interface Tile {
|
interface Tile {
|
||||||
indexLat: number;
|
indexLat: number;
|
||||||
indexLon: number;
|
indexLon: number;
|
||||||
@@ -370,6 +371,13 @@ export default class DiscoverView extends Vue {
|
|||||||
|
|
||||||
notify!: ReturnType<typeof createNotifyHelpers>;
|
notify!: ReturnType<typeof createNotifyHelpers>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the unnamed project constant
|
||||||
|
*/
|
||||||
|
get unnamedProject(): string {
|
||||||
|
return UNNAMED_PROJECT;
|
||||||
|
}
|
||||||
|
|
||||||
activeDid = "";
|
activeDid = "";
|
||||||
allContacts: Array<Contact> = [];
|
allContacts: Array<Contact> = [];
|
||||||
allMyDids: Array<string> = [];
|
allMyDids: Array<string> = [];
|
||||||
|
|||||||
@@ -200,7 +200,7 @@
|
|||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Then you can record your appreciation for... whatever: select any contact on the home page
|
Then you can record your appreciation for... whatever: select any contact on the home page
|
||||||
(or "Unnamed") and send it. The main goal is to record what people
|
(or "{{ unnamedEntityName }}") and send it. The main goal is to record what people
|
||||||
have given you, to grow giving economies. You can also record your own
|
have given you, to grow giving economies. You can also record your own
|
||||||
ideas for projects. Each claim is recorded on a
|
ideas for projects. Each claim is recorded on a
|
||||||
custom ledger.
|
custom ledger.
|
||||||
@@ -600,6 +600,7 @@ import QuickNav from "../components/QuickNav.vue";
|
|||||||
import { APP_SERVER } from "../constants/app";
|
import { APP_SERVER } from "../constants/app";
|
||||||
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
|
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
|
||||||
import { QRNavigationService } from "@/services/QRNavigationService";
|
import { QRNavigationService } from "@/services/QRNavigationService";
|
||||||
|
import { UNNAMED_ENTITY_NAME } from "@/constants/entities";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* HelpView.vue - Comprehensive Help System Component
|
* HelpView.vue - Comprehensive Help System Component
|
||||||
@@ -647,6 +648,13 @@ export default class HelpView extends Vue {
|
|||||||
APP_SERVER = APP_SERVER;
|
APP_SERVER = APP_SERVER;
|
||||||
// Capacitor reference removed - using QRNavigationService instead
|
// Capacitor reference removed - using QRNavigationService instead
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the unnamed entity name constant
|
||||||
|
*/
|
||||||
|
get unnamedEntityName(): string {
|
||||||
|
return UNNAMED_ENTITY_NAME;
|
||||||
|
}
|
||||||
|
|
||||||
// Ideally, we put no functionality in here, especially in the setup,
|
// Ideally, we put no functionality in here, especially in the setup,
|
||||||
// because we never want this page to have a chance of throwing an error.
|
// because we never want this page to have a chance of throwing an error.
|
||||||
|
|
||||||
|
|||||||
@@ -282,6 +282,7 @@ import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
|
|||||||
import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
|
import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
|
||||||
import { NOTIFY_CONTACT_LOADING_ISSUE } from "@/constants/notifications";
|
import { NOTIFY_CONTACT_LOADING_ISSUE } from "@/constants/notifications";
|
||||||
import * as Package from "../../package.json";
|
import * as Package from "../../package.json";
|
||||||
|
import { UNNAMED_ENTITY_NAME } from "@/constants/entities";
|
||||||
|
|
||||||
// consolidate this with GiveActionClaim in src/interfaces/claims.ts
|
// consolidate this with GiveActionClaim in src/interfaces/claims.ts
|
||||||
interface Claim {
|
interface Claim {
|
||||||
@@ -1546,30 +1547,41 @@ export default class HomeView extends Vue {
|
|||||||
* @param giver Optional contact info for giver
|
* @param giver Optional contact info for giver
|
||||||
* @param description Optional gift description
|
* @param description Optional gift description
|
||||||
*/
|
*/
|
||||||
openDialog(giver?: GiverReceiverInputInfo | "Unnamed", prompt?: string) {
|
openDialog(giver?: GiverReceiverInputInfo, prompt?: string) {
|
||||||
if (giver === "Unnamed") {
|
// Determine the giver entity based on DID logic
|
||||||
// Special case: Pass undefined to trigger Step 1, but with "Unnamed" pre-selected
|
const giverEntity = this.createGiverEntity(giver);
|
||||||
(this.$refs.giftedDialog as GiftedDialog).open(
|
|
||||||
undefined,
|
(this.$refs.giftedDialog as GiftedDialog).open(
|
||||||
{
|
giverEntity,
|
||||||
did: this.activeDid,
|
{
|
||||||
name: "You",
|
did: this.activeDid,
|
||||||
} as GiverReceiverInputInfo,
|
name: "You", // In HomeView, we always use "You" as the giver
|
||||||
undefined,
|
} as GiverReceiverInputInfo,
|
||||||
prompt,
|
undefined,
|
||||||
);
|
prompt,
|
||||||
// Immediately select "Unnamed" and move to Step 2
|
);
|
||||||
(this.$refs.giftedDialog as GiftedDialog).selectGiver();
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates giver entity using DID-based logic
|
||||||
|
*/
|
||||||
|
private createGiverEntity(
|
||||||
|
giver?: GiverReceiverInputInfo,
|
||||||
|
): GiverReceiverInputInfo | undefined {
|
||||||
|
if (!giver) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle GiverReceiverInputInfo object
|
||||||
|
if (giver.did === this.activeDid) {
|
||||||
|
// If DID matches active DID, create "You" entity
|
||||||
|
return { did: this.activeDid, name: "You" };
|
||||||
|
} else if (!giver.did || giver.did === "") {
|
||||||
|
// If DID is empty/null, create "Unnamed" entity
|
||||||
|
return { did: "", name: UNNAMED_ENTITY_NAME };
|
||||||
} else {
|
} else {
|
||||||
(this.$refs.giftedDialog as GiftedDialog).open(
|
// Return the giver as-is
|
||||||
giver,
|
return giver;
|
||||||
{
|
|
||||||
did: this.activeDid,
|
|
||||||
name: "You",
|
|
||||||
} as GiverReceiverInputInfo,
|
|
||||||
undefined,
|
|
||||||
prompt,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1632,10 +1644,15 @@ export default class HomeView extends Vue {
|
|||||||
this.isImageViewerOpen = true;
|
this.isImageViewerOpen = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
openPersonDialog(
|
private handleQRCodeClick() {
|
||||||
giver?: GiverReceiverInputInfo | "Unnamed",
|
if (Capacitor.isNativePlatform()) {
|
||||||
prompt?: string,
|
this.$router.push({ name: "contact-qr-scan-full" });
|
||||||
) {
|
} else {
|
||||||
|
this.$router.push({ name: "contact-qr" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
openPersonDialog(giver?: GiverReceiverInputInfo, prompt?: string) {
|
||||||
this.showProjectsDialog = false;
|
this.showProjectsDialog = false;
|
||||||
this.openDialog(giver, prompt);
|
this.openDialog(giver, prompt);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -183,7 +183,7 @@
|
|||||||
class="text-blue-500"
|
class="text-blue-500"
|
||||||
@click="onClickLoadProject(plan.handleId)"
|
@click="onClickLoadProject(plan.handleId)"
|
||||||
>
|
>
|
||||||
{{ plan.name || "Unnamed Project" }}
|
{{ plan.name || unnamedProject }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="fulfillersToHitLimit" class="text-center">
|
<div v-if="fulfillersToHitLimit" class="text-center">
|
||||||
@@ -207,7 +207,7 @@
|
|||||||
class="text-blue-500"
|
class="text-blue-500"
|
||||||
@click="onClickLoadProject(fulfilledByThis.handleId)"
|
@click="onClickLoadProject(fulfilledByThis.handleId)"
|
||||||
>
|
>
|
||||||
{{ fulfilledByThis.name || "Unnamed Project" }}
|
{{ fulfilledByThis.name || unnamedProject }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -611,6 +611,7 @@ import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
|
|||||||
import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
|
import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
|
||||||
import { NOTIFY_CONFIRM_CLAIM } from "@/constants/notifications";
|
import { NOTIFY_CONFIRM_CLAIM } from "@/constants/notifications";
|
||||||
import { APP_SERVER } from "@/constants/app";
|
import { APP_SERVER } from "@/constants/app";
|
||||||
|
import { UNNAMED_PROJECT } from "@/constants/entities";
|
||||||
/**
|
/**
|
||||||
* Project View Component
|
* Project View Component
|
||||||
* @author Matthew Raymer
|
* @author Matthew Raymer
|
||||||
@@ -664,6 +665,13 @@ export default class ProjectViewView extends Vue {
|
|||||||
/** Notification helpers instance */
|
/** Notification helpers instance */
|
||||||
notify!: ReturnType<typeof createNotifyHelpers>;
|
notify!: ReturnType<typeof createNotifyHelpers>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the unnamed project constant
|
||||||
|
*/
|
||||||
|
get unnamedProject(): string {
|
||||||
|
return UNNAMED_PROJECT;
|
||||||
|
}
|
||||||
|
|
||||||
// Account and Settings State
|
// Account and Settings State
|
||||||
/** Currently active DID */
|
/** Currently active DID */
|
||||||
activeDid = "";
|
activeDid = "";
|
||||||
|
|||||||
@@ -244,7 +244,7 @@
|
|||||||
|
|
||||||
<div class="grow overflow-hidden">
|
<div class="grow overflow-hidden">
|
||||||
<h2 class="text-base font-semibold">
|
<h2 class="text-base font-semibold">
|
||||||
{{ project.name || "Unnamed Project" }}
|
{{ project.name || unnamedProject }}
|
||||||
</h2>
|
</h2>
|
||||||
<div class="text-sm truncate">
|
<div class="text-sm truncate">
|
||||||
{{ project.description }}
|
{{ project.description }}
|
||||||
@@ -286,6 +286,7 @@ import {
|
|||||||
NOTIFY_OFFERS_LOAD_ERROR,
|
NOTIFY_OFFERS_LOAD_ERROR,
|
||||||
NOTIFY_OFFERS_FETCH_ERROR,
|
NOTIFY_OFFERS_FETCH_ERROR,
|
||||||
} from "@/constants/notifications";
|
} from "@/constants/notifications";
|
||||||
|
import { UNNAMED_PROJECT } from "@/constants/entities";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Projects View Component
|
* Projects View Component
|
||||||
@@ -324,6 +325,13 @@ export default class ProjectsView extends Vue {
|
|||||||
|
|
||||||
notify!: ReturnType<typeof createNotifyHelpers>;
|
notify!: ReturnType<typeof createNotifyHelpers>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the unnamed project constant
|
||||||
|
*/
|
||||||
|
get unnamedProject(): string {
|
||||||
|
return UNNAMED_PROJECT;
|
||||||
|
}
|
||||||
|
|
||||||
// User account state
|
// User account state
|
||||||
activeDid = "";
|
activeDid = "";
|
||||||
allContacts: Array<Contact> = [];
|
allContacts: Array<Contact> = [];
|
||||||
|
|||||||
@@ -69,6 +69,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { test, expect } from '@playwright/test';
|
import { test, expect } from '@playwright/test';
|
||||||
|
import { UNNAMED_ENTITY_NAME } from '../src/constants/entities';
|
||||||
import { deleteContact, generateAndRegisterEthrUser, importUser } from './testUtils';
|
import { deleteContact, generateAndRegisterEthrUser, importUser } from './testUtils';
|
||||||
|
|
||||||
test('Check activity feed - check that server is running', async ({ page }) => {
|
test('Check activity feed - check that server is running', async ({ page }) => {
|
||||||
@@ -177,7 +178,7 @@ test('Check User 0 can register a random person', async ({ page }) => {
|
|||||||
await page.goto('./');
|
await page.goto('./');
|
||||||
await page.getByTestId('closeOnboardingAndFinish').click();
|
await page.getByTestId('closeOnboardingAndFinish').click();
|
||||||
await page.getByRole('button', { name: 'Person' }).click();
|
await page.getByRole('button', { name: 'Person' }).click();
|
||||||
await page.getByRole('listitem').filter({ hasText: 'Unnamed' }).locator('svg').click();
|
await page.getByRole('listitem').filter({ hasText: UNNAMED_ENTITY_NAME }).locator('svg').click();
|
||||||
await page.getByPlaceholder('What was given').fill('Gave me access!');
|
await page.getByPlaceholder('What was given').fill('Gave me access!');
|
||||||
await page.getByRole('button', { name: 'Sign & Send' }).click();
|
await page.getByRole('button', { name: 'Sign & Send' }).click();
|
||||||
await expect(page.getByText('That gift was recorded.')).toBeVisible();
|
await expect(page.getByText('That gift was recorded.')).toBeVisible();
|
||||||
|
|||||||
@@ -79,6 +79,7 @@
|
|||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
import { test, expect } from '@playwright/test';
|
import { test, expect } from '@playwright/test';
|
||||||
|
import { UNNAMED_ENTITY_NAME } from '../src/constants/entities';
|
||||||
import { importUser } from './testUtils';
|
import { importUser } from './testUtils';
|
||||||
|
|
||||||
test('Record something given', async ({ page }) => {
|
test('Record something given', async ({ page }) => {
|
||||||
@@ -101,7 +102,7 @@ test('Record something given', async ({ page }) => {
|
|||||||
await page.goto('./');
|
await page.goto('./');
|
||||||
await page.getByTestId('closeOnboardingAndFinish').click();
|
await page.getByTestId('closeOnboardingAndFinish').click();
|
||||||
await page.getByRole('button', { name: 'Person' }).click();
|
await page.getByRole('button', { name: 'Person' }).click();
|
||||||
await page.getByRole('listitem').filter({ hasText: 'Unnamed' }).locator('svg').click();
|
await page.getByRole('listitem').filter({ hasText: UNNAMED_ENTITY_NAME }).locator('svg').click();
|
||||||
await page.getByPlaceholder('What was given').fill(finalTitle);
|
await page.getByPlaceholder('What was given').fill(finalTitle);
|
||||||
await page.getByRole('spinbutton').fill(randomNonZeroNumber.toString());
|
await page.getByRole('spinbutton').fill(randomNonZeroNumber.toString());
|
||||||
await page.getByRole('button', { name: 'Sign & Send' }).click();
|
await page.getByRole('button', { name: 'Sign & Send' }).click();
|
||||||
|
|||||||
@@ -85,6 +85,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { test, expect } from '@playwright/test';
|
import { test, expect } from '@playwright/test';
|
||||||
|
import { UNNAMED_ENTITY_NAME } from '../src/constants/entities';
|
||||||
import { importUser, createUniqueStringsArray, createRandomNumbersArray } from './testUtils';
|
import { importUser, createUniqueStringsArray, createRandomNumbersArray } from './testUtils';
|
||||||
|
|
||||||
test('Record 9 new gifts', async ({ page }) => {
|
test('Record 9 new gifts', async ({ page }) => {
|
||||||
@@ -116,7 +117,7 @@ test('Record 9 new gifts', async ({ page }) => {
|
|||||||
await page.getByTestId('closeOnboardingAndFinish').click();
|
await page.getByTestId('closeOnboardingAndFinish').click();
|
||||||
}
|
}
|
||||||
await page.getByRole('button', { name: 'Person' }).click();
|
await page.getByRole('button', { name: 'Person' }).click();
|
||||||
await page.getByRole('listitem').filter({ hasText: 'Unnamed' }).locator('svg').click();
|
await page.getByRole('listitem').filter({ hasText: UNNAMED_ENTITY_NAME }).locator('svg').click();
|
||||||
await page.getByPlaceholder('What was given').fill(finalTitles[i]);
|
await page.getByPlaceholder('What was given').fill(finalTitles[i]);
|
||||||
await page.getByRole('spinbutton').fill(finalNumbers[i].toString());
|
await page.getByRole('spinbutton').fill(finalNumbers[i].toString());
|
||||||
await page.getByRole('button', { name: 'Sign & Send' }).click();
|
await page.getByRole('button', { name: 'Sign & Send' }).click();
|
||||||
|
|||||||
Reference in New Issue
Block a user