<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"> <div v-if="record.issuerDid"> <EntityIcon :entity-id="record.issuerDid" class="rounded-full bg-white overflow-hidden !size-[2rem] object-cover" /> </div> <div v-else> <font-awesome icon="person-circle-question" class="text-slate-300 text-[2rem]" /> </div> <div> <h3 class="font-semibold"> {{ record.issuer.known ? record.issuer.displayName : "" }} </h3> <p class="ms-auto text-xs text-slate-500 italic"> {{ friendlyDate }} </p> </div> </div> <a class="cursor-pointer" @click="$emit('loadClaim', 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-6 -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="$emit('viewImage', 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="$emit('cacheImage', record.image)" /> </a> </div> <div class="relative flex justify-between gap-4 max-w-lg mx-auto mb-5"> <!-- Source --> <div class="w-28 sm:w-40 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"> <ProjectIcon :entity-id="record.providerPlanName" :icon-size="48" class="rounded size-[3rem] sm:size-[4rem] *:w-full *:h-full" /> </div> <!-- Identicon for DIDs --> <div v-else-if="record.agentDid"> <EntityIcon :entity-id="record.agentDid" :profile-image-url="record.issuer.profileImageUrl" class="rounded-full bg-slate-100 overflow-hidden !size-[3rem] sm:!size-[4rem]" /> </div> <!-- Unknown Person --> <div v-else> <font-awesome icon="person-circle-question" class="text-slate-300 text-[3rem] sm:text-[4rem]" /> </div> </div> </div> <div v-if="record.providerPlanName || record.giver.known" class="text-xs mt-2 line-clamp-3 sm:line-clamp-2" > <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-28 sm:inset-x-40 mx-2 top-1/2 -translate-y-1/2" > <div class="text-sm text-center leading-none font-semibold"> {{ 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-28 sm:w-40 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"> <ProjectIcon :entity-id="record.recipientProjectName" :icon-size="48" class="rounded size-[3rem] sm:size-[4rem] *:w-full *:h-full" /> </div> <!-- Identicon for DIDs --> <div v-else-if="record.recipientDid"> <EntityIcon :entity-id="record.recipientDid" :profile-image-url="record.receiver.profileImageUrl" class="rounded-full bg-slate-100 overflow-hidden !size-[3rem] sm:!size-[4rem]" /> </div> <!-- Unknown Person --> <div v-else> <font-awesome icon="person-circle-question" class="text-slate-300 text-[3rem] sm:text-[4rem]" /> </div> </div> </div> <div v-if="record.recipientProjectName || record.receiver.known" class="text-xs mt-2 line-clamp-3 sm:line-clamp-2" > <font-awesome :icon="record.recipientProjectName ? 'users' : 'user'" class="fa-fw text-slate-400" /> {{ record.recipientProjectName || record.receiver.displayName }} </div> </div> </div> <!-- Description --> <p class="font-medium"> <a class="cursor-pointer" @click="$emit('loadClaim', record.jwtId)"> {{ description }} </a> </p> </div> </li> </template> <script lang="ts"> import { Component, Prop, Vue } from "vue-facing-decorator"; import { GiveRecordWithContactInfo } from "../types"; import EntityIcon from "./EntityIcon.vue"; import { isGiveClaimType, notifyWhyCannotConfirm } from "../libs/util"; import { containsHiddenDid } from "../libs/endorserServer"; import ProjectIcon from "./ProjectIcon.vue"; @Component({ components: { EntityIcon, ProjectIcon, }, }) export default class ActivityListItem extends Vue { @Prop() record!: GiveRecordWithContactInfo; @Prop() lastViewedClaimId?: string; @Prop() isRegistered!: boolean; @Prop() activeDid!: string; @Prop() confirmerIdList?: string[]; get fetchAmount(): string { const claim = (this.record.fullClaim as unknown).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 unknown).claim || this.record.fullClaim; if (!claim.description) { return "something not described"; } 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; } get canConfirm(): boolean { if (!this.isRegistered) return false; if (!isGiveClaimType(this.record.fullClaim?.["@type"])) return false; if (this.confirmerIdList?.includes(this.activeDid)) return false; if (this.record.issuerDid === this.activeDid) return false; if (containsHiddenDid(this.record.fullClaim)) return false; return true; } handleConfirmClick() { if (!this.canConfirm) { notifyWhyCannotConfirm( this.$notify, this.isRegistered, this.record.fullClaim?.["@type"], this.record, this.activeDid, this.confirmerIdList, ); return; } this.$emit("confirmClaim", this.record); } get friendlyDate(): string { const date = new Date(this.record.issuedAt); return date.toLocaleDateString(undefined, { year: "numeric", month: "short", day: "numeric", }); } } </script>