forked from trent_larson/crowd-funder-for-time-pwa
- Remove unused handleConfirmClick() and emitConfirmClaim() methods from ActivityListItem - Remove unused canConfirm computed property and confirmerIdList prop - Remove unused imports: isGiveClaimType, notifyWhyCannotConfirm, containsHiddenDid - Remove unused confirmClaim() method from HomeView - Remove unused @confirm-claim event binding and :confirmer-id-list prop - Remove unused imports: serverUtil, NOTIFY_CONFIRMATION_ERROR The confirmation functionality was not being used in ActivityListItem as there was no UI to trigger it. Confirmation is handled in other components like ClaimView and ConfirmGiftView.
353 lines
11 KiB
Vue
353 lines
11 KiB
Vue
<template>
|
|
<li>
|
|
<!-- Last viewed separator -->
|
|
<div
|
|
v-if="record.jwtId == lastViewedClaimId"
|
|
class="border-b border-dashed border-slate-300 text-orange-400 mt-4 mb-6 font-bold text-sm"
|
|
>
|
|
<span class="block w-fit mx-auto -mb-2.5 bg-white px-2">
|
|
You've already seen all the following
|
|
</span>
|
|
</div>
|
|
|
|
<div
|
|
class="flex items-center justify-between gap-2 text-lg bg-slate-200 border border-slate-300 border-b-0 rounded-t-md px-3 sm:px-4 py-1 sm:py-2"
|
|
>
|
|
<div class="flex items-center gap-2">
|
|
<router-link
|
|
v-if="record.issuerDid && !isHiddenDid(record.issuerDid)"
|
|
:to="{
|
|
path: '/did/' + encodeURIComponent(record.issuerDid),
|
|
}"
|
|
title="More details about this person"
|
|
>
|
|
<EntityIcon
|
|
:entity-id="record.issuerDid"
|
|
class="rounded-full bg-white overflow-hidden !size-[2rem] object-cover"
|
|
/>
|
|
</router-link>
|
|
<font-awesome
|
|
v-else-if="isHiddenDid(record.issuerDid)"
|
|
icon="eye-slash"
|
|
class="text-slate-400 !size-[2rem] cursor-pointer"
|
|
@click="notifyHiddenPerson"
|
|
/>
|
|
<font-awesome
|
|
v-else
|
|
icon="person-circle-question"
|
|
class="text-slate-400 !size-[2rem] cursor-pointer"
|
|
@click="notifyUnknownPerson"
|
|
/>
|
|
|
|
<div>
|
|
<h3 v-if="record.issuer.known" class="font-semibold leading-tight">
|
|
{{ record.issuer.displayName }}
|
|
</h3>
|
|
<p class="ms-auto text-xs text-slate-500 italic">
|
|
{{ friendlyDate }}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<a
|
|
class="cursor-pointer"
|
|
data-testid="circle-info-link"
|
|
@click="emitLoadClaim(record.jwtId)"
|
|
>
|
|
<font-awesome icon="circle-info" class="fa-fw text-slate-500" />
|
|
</a>
|
|
</div>
|
|
|
|
<div class="bg-slate-100 rounded-b-md border border-slate-300 p-3 sm:p-4">
|
|
<!-- Record Image -->
|
|
<div
|
|
v-if="record.image"
|
|
class="bg-cover mb-2 -mt-3 sm:-mt-4 -mx-3 sm:-mx-4"
|
|
:style="`background-image: url(${record.image});`"
|
|
>
|
|
<a
|
|
class="block bg-slate-100/50 backdrop-blur-md px-6 py-4 cursor-pointer"
|
|
@click="emitViewImage(record.image)"
|
|
>
|
|
<img
|
|
class="w-full h-auto max-w-lg max-h-96 object-contain mx-auto drop-shadow-md"
|
|
:src="record.image"
|
|
alt="Activity image"
|
|
@load="handleImageLoad(record.image)"
|
|
/>
|
|
</a>
|
|
</div>
|
|
|
|
<!-- Description -->
|
|
<p class="font-medium">
|
|
<a class="cursor-pointer" @click="emitLoadClaim(record.jwtId)">
|
|
{{ description }}
|
|
</a>
|
|
</p>
|
|
|
|
<div
|
|
class="relative flex justify-between gap-4 max-w-[40rem] mx-auto mt-4"
|
|
>
|
|
<!-- Source -->
|
|
<div
|
|
class="w-[7rem] sm:w-[12rem] text-center bg-white border border-slate-200 rounded p-2 sm:p-3"
|
|
>
|
|
<div class="relative w-fit mx-auto">
|
|
<div>
|
|
<!-- Project Icon -->
|
|
<div v-if="record.providerPlanName">
|
|
<router-link
|
|
:to="{
|
|
path:
|
|
'/project/' +
|
|
encodeURIComponent(record.providerPlanHandleId || ''),
|
|
}"
|
|
title="View project details"
|
|
>
|
|
<ProjectIcon
|
|
:entity-id="record.providerPlanHandleId || ''"
|
|
:icon-size="48"
|
|
class="rounded size-[3rem] sm:size-[4rem] *:w-full *:h-full"
|
|
/>
|
|
</router-link>
|
|
</div>
|
|
<!-- Identicon for DIDs -->
|
|
<div v-else-if="record.agentDid">
|
|
<router-link
|
|
v-if="!isHiddenDid(record.agentDid)"
|
|
:to="{
|
|
path: '/did/' + encodeURIComponent(record.agentDid),
|
|
}"
|
|
title="More details about this person"
|
|
>
|
|
<EntityIcon
|
|
:entity-id="record.agentDid"
|
|
:profile-image-url="record.issuer.profileImageUrl"
|
|
class="rounded-full bg-slate-100 overflow-hidden !size-[3rem] sm:!size-[4rem]"
|
|
/>
|
|
</router-link>
|
|
<font-awesome
|
|
v-else
|
|
icon="eye-slash"
|
|
class="text-slate-300 !size-[3rem] sm:!size-[4rem]"
|
|
@click="notifyHiddenPerson"
|
|
/>
|
|
</div>
|
|
<!-- Unknown Person -->
|
|
<div v-else>
|
|
<font-awesome
|
|
icon="person-circle-question"
|
|
class="text-slate-300 text-[3rem] sm:text-[4rem]"
|
|
@click="notifyUnknownPerson"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div
|
|
v-if="record.providerPlanName || record.giver.known"
|
|
class="text-xs mt-2 truncate"
|
|
>
|
|
<font-awesome
|
|
:icon="record.providerPlanName ? 'users' : 'user'"
|
|
class="fa-fw text-slate-400"
|
|
/>
|
|
{{ record.providerPlanName || record.giver.displayName }}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Arrow -->
|
|
<div
|
|
class="absolute inset-x-[7rem] sm:inset-x-[12rem] mx-2 top-1/2 -translate-y-1/2"
|
|
>
|
|
<div
|
|
class="text-sm text-center leading-none font-semibold pe-2 sm:pe-4"
|
|
>
|
|
{{ fetchAmount }}
|
|
</div>
|
|
|
|
<div class="flex items-center">
|
|
<hr
|
|
class="grow border-t-[18px] sm:border-t-[24px] border-slate-300"
|
|
/>
|
|
|
|
<div
|
|
class="shrink-0 w-0 h-0 border border-slate-300 border-t-[20px] sm:border-t-[25px] border-t-transparent border-b-[20px] sm:border-b-[25px] border-b-transparent border-s-[27px] sm:border-s-[34px] border-e-0"
|
|
></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Destination -->
|
|
<div
|
|
class="w-[7rem] sm:w-[12rem] text-center bg-white border border-slate-200 rounded p-2 sm:p-3"
|
|
>
|
|
<div class="relative w-fit mx-auto">
|
|
<div>
|
|
<!-- Project Icon -->
|
|
<div v-if="record.recipientProjectName">
|
|
<router-link
|
|
:to="{
|
|
path:
|
|
'/project/' +
|
|
encodeURIComponent(record.fulfillsPlanHandleId || ''),
|
|
}"
|
|
title="View project details"
|
|
>
|
|
<ProjectIcon
|
|
:entity-id="record.fulfillsPlanHandleId || ''"
|
|
:icon-size="48"
|
|
class="rounded size-[3rem] sm:size-[4rem] *:w-full *:h-full"
|
|
/>
|
|
</router-link>
|
|
</div>
|
|
<!-- Identicon for DIDs -->
|
|
<div v-else-if="record.recipientDid">
|
|
<router-link
|
|
v-if="!isHiddenDid(record.recipientDid)"
|
|
:to="{
|
|
path: '/did/' + encodeURIComponent(record.recipientDid),
|
|
}"
|
|
title="More details about this person"
|
|
>
|
|
<EntityIcon
|
|
:entity-id="record.recipientDid"
|
|
:profile-image-url="record.receiver.profileImageUrl"
|
|
class="rounded-full bg-slate-100 overflow-hidden !size-[3rem] sm:!size-[4rem]"
|
|
/>
|
|
</router-link>
|
|
<font-awesome
|
|
v-else
|
|
icon="eye-slash"
|
|
class="text-slate-300 !size-[3rem] sm:!size-[4rem]"
|
|
@click="notifyHiddenPerson"
|
|
/>
|
|
</div>
|
|
<!-- Unknown Person -->
|
|
<div v-else>
|
|
<font-awesome
|
|
icon="person-circle-question"
|
|
class="text-slate-300 text-[3rem] sm:text-[4rem]"
|
|
@click="notifyUnknownPerson"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div
|
|
v-if="record.recipientProjectName || record.receiver.known"
|
|
class="text-xs mt-2 truncate"
|
|
>
|
|
<font-awesome
|
|
:icon="record.recipientProjectName ? 'users' : 'user'"
|
|
class="fa-fw text-slate-400"
|
|
/>
|
|
{{ record.recipientProjectName || record.receiver.displayName }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</li>
|
|
</template>
|
|
|
|
<script lang="ts">
|
|
import { Component, Prop, Vue, Emit } from "vue-facing-decorator";
|
|
import { GiveRecordWithContactInfo } from "@/interfaces/give";
|
|
import EntityIcon from "./EntityIcon.vue";
|
|
import { isHiddenDid } from "../libs/endorserServer";
|
|
import ProjectIcon from "./ProjectIcon.vue";
|
|
import { createNotifyHelpers } from "@/utils/notify";
|
|
import {
|
|
NOTIFY_PERSON_HIDDEN,
|
|
NOTIFY_UNKNOWN_PERSON,
|
|
} from "@/constants/notifications";
|
|
import { TIMEOUTS } from "@/utils/notify";
|
|
|
|
@Component({
|
|
components: {
|
|
EntityIcon,
|
|
ProjectIcon,
|
|
},
|
|
})
|
|
export default class ActivityListItem extends Vue {
|
|
@Prop() record!: GiveRecordWithContactInfo;
|
|
@Prop() lastViewedClaimId?: string;
|
|
@Prop() isRegistered!: boolean;
|
|
@Prop() activeDid!: string;
|
|
|
|
/**
|
|
* Function prop for handling image caching
|
|
* Called when an image loads successfully, allowing parent to control caching behavior
|
|
*/
|
|
@Prop({ type: Function, default: () => {} })
|
|
onImageCache!: (imageUrl: string) => void | Promise<void>;
|
|
|
|
isHiddenDid = isHiddenDid;
|
|
notify!: ReturnType<typeof createNotifyHelpers>;
|
|
$notify!: (notification: any, timeout?: number) => void;
|
|
|
|
created() {
|
|
this.notify = createNotifyHelpers(this.$notify);
|
|
}
|
|
|
|
notifyHiddenPerson() {
|
|
this.notify.warning(NOTIFY_PERSON_HIDDEN.message, TIMEOUTS.STANDARD);
|
|
}
|
|
|
|
notifyUnknownPerson() {
|
|
this.notify.warning(NOTIFY_UNKNOWN_PERSON.message, TIMEOUTS.STANDARD);
|
|
}
|
|
|
|
/**
|
|
* Handle image load event - call function prop for caching
|
|
* Allows parent to control caching behavior and validation
|
|
*/
|
|
handleImageLoad(imageUrl: string): void {
|
|
this.onImageCache(imageUrl);
|
|
}
|
|
|
|
get fetchAmount(): string {
|
|
const claim =
|
|
(this.record.fullClaim as any)?.claim || this.record.fullClaim;
|
|
|
|
const amount = claim.object?.amountOfThisGood
|
|
? this.displayAmount(claim.object.unitCode, claim.object.amountOfThisGood)
|
|
: "";
|
|
|
|
return amount;
|
|
}
|
|
|
|
get description(): string {
|
|
const claim =
|
|
(this.record.fullClaim as any)?.claim || this.record.fullClaim;
|
|
|
|
return `${claim?.description || ""}`;
|
|
}
|
|
|
|
private displayAmount(code: string, amt: number) {
|
|
return `${amt} ${this.currencyShortWordForCode(code, amt === 1)}`;
|
|
}
|
|
|
|
private currencyShortWordForCode(unitCode: string, single: boolean) {
|
|
return unitCode === "HUR" ? (single ? "hour" : "hours") : unitCode;
|
|
}
|
|
|
|
// Emit methods using @Emit decorator
|
|
@Emit("viewImage")
|
|
emitViewImage(imageUrl: string) {
|
|
return imageUrl;
|
|
}
|
|
|
|
@Emit("loadClaim")
|
|
emitLoadClaim(jwtId: string) {
|
|
return jwtId;
|
|
}
|
|
|
|
get friendlyDate(): string {
|
|
const date = new Date(this.record.issuedAt);
|
|
return date.toLocaleDateString(undefined, {
|
|
year: "numeric",
|
|
month: "short",
|
|
day: "numeric",
|
|
});
|
|
}
|
|
}
|
|
</script>
|