<template> <section id="Content"> <div class="flex items-center justify-center h-screen"> <div v-if="claimData"> <router-link :to="'/claim/' + this.claimId"> <canvas class="w-full block mx-auto" ref="claimCanvas"></canvas> </router-link> </div> </div> </section> </template> <script lang="ts"> import { Component, Vue } from "vue-facing-decorator"; import { nextTick } from "vue"; import QRCode from "qrcode"; import { APP_SERVER, NotificationIface } from "../constants/app"; import { db, retrieveSettingsForActiveAccount } from "../db/index"; import * as serverUtil from "../libs/endorserServer"; @Component export default class ClaimCertificateView extends Vue { $notify!: (notification: NotificationIface, timeout?: number) => void; activeDid = ""; allMyDids: Array<string> = []; apiServer = ""; claimId = ""; claimData = null; serverUtil = serverUtil; async created() { const settings = await retrieveSettingsForActiveAccount(); this.activeDid = settings.activeDid || ""; this.apiServer = settings.apiServer || ""; const pathParams = window.location.pathname.substring( "/claim-cert/".length, ); this.claimId = pathParams; await this.fetchClaim(); } async fetchClaim() { try { const headers = await serverUtil.getHeaders(this.activeDid); const response = await this.axios.get( `${this.apiServer}/api/claim/${this.claimId}`, { headers }, ); if (response.status === 200) { this.claimData = await response.data; const claimEntryIds = [this.claimId]; const headers = await serverUtil.getHeaders(this.activeDid); const confirmerResponse = await this.axios.post( `${this.apiServer}/api/v2/report/confirmers/?claimEntryIds=${this.claimId}`, { claimEntryIds }, { headers }, ); let confirmerIds: Array<string> = []; if (confirmerResponse.status === 200) { confirmerIds = await confirmerResponse.data.data; } await nextTick(); // Wait for the DOM to update if (this.claimData) { this.drawCanvas(this.claimData, confirmerIds); } } else { throw new Error(`Error fetching claim: ${response.statusText}`); } } catch (error) { console.error("Failed to load claim:", error); this.$notify({ group: "alert", type: "danger", title: "Error", text: "There was a problem loading the claim.", }); } } async drawCanvas( claimData: serverUtil.GenericCredWrapper<serverUtil.GenericVerifiableCredential>, confirmerIds: Array<string>, ) { await db.open(); const allContacts = await db.contacts.toArray(); const canvas = this.$refs.claimCanvas as HTMLCanvasElement; if (canvas) { const CANVAS_WIDTH = 1100; const CANVAS_HEIGHT = 850; // size to approximate portrait of 8.5"x11" canvas.width = CANVAS_WIDTH; canvas.height = CANVAS_HEIGHT; const ctx = canvas.getContext("2d"); if (ctx) { // Load the background image const backgroundImage = new Image(); backgroundImage.src = "/img/background/cert-frame-2.jpg"; backgroundImage.onload = async () => { // Draw the background image ctx.drawImage(backgroundImage, 0, 0, CANVAS_WIDTH, CANVAS_HEIGHT); // Set font and styles ctx.fillStyle = "black"; // Draw claim type ctx.font = "bold 20px Arial"; const claimTypeText = claimData.claimType === "GiveAction" ? "Gift" : claimData.claimType === "PlanAction" ? "Project" : this.serverUtil.capitalizeAndInsertSpacesBeforeCaps( claimData.claimType || "", ); const claimTypeWidth = ctx.measureText(claimTypeText).width; ctx.fillText( claimTypeText, (CANVAS_WIDTH - claimTypeWidth) / 2, // Center horizontally CANVAS_HEIGHT * 0.33, ); if (claimData.claimType === "GiveAction" && claimData.claim.agent) { const presentedText = "Thanks To "; ctx.font = "14px Arial"; const presentedWidth = ctx.measureText(presentedText).width; ctx.fillText( presentedText, (CANVAS_WIDTH - presentedWidth) / 2, // Center horizontally CANVAS_HEIGHT * 0.37, ); const agentDid = claimData.claim.agent.identifier || claimData.claim.agent; const agentText = serverUtil.didInfoForCertificate( agentDid, allContacts, ); ctx.font = "bold 20px Arial"; const agentWidth = ctx.measureText(agentText).width; ctx.fillText( agentText, (CANVAS_WIDTH - agentWidth) / 2, // Center horizontally CANVAS_HEIGHT * 0.41, ); } const descriptionText = claimData.claim.name || claimData.claim.description; if (descriptionText) { const descriptionLine = descriptionText.length > 50 ? descriptionText.substring(0, 75) + "..." : descriptionText; ctx.font = "14px Arial"; const descriptionWidth = ctx.measureText(descriptionLine).width; ctx.fillText( descriptionLine, (CANVAS_WIDTH - descriptionWidth) / 2, CANVAS_HEIGHT * 0.495, ); } if ( claimData.claim.object?.amountOfThisGood && claimData.claim.object?.unitCode ) { const amount = claimData.claim.object.amountOfThisGood; const unit = claimData.claim.object.unitCode; const amountText = serverUtil.displayAmount(unit, amount); const amountWidth = ctx.measureText(amountText).width; // if there was no description then put this in that spot, otherwise put it below the description const yPos = descriptionText ? CANVAS_HEIGHT * 0.525 : CANVAS_HEIGHT * 0.495; ctx.font = "14px Arial"; ctx.fillText(amountText, (CANVAS_WIDTH - amountWidth) / 2, yPos); } // Draw claim issuer if ( claimData.issuer == null || serverUtil.isHiddenDid(claimData.issuer) || // don't show if issuer claimed for themselves // (The confirmations are the good stuff anyway, and self-issued certs shouldn't detract from that.) claimData.issuer !== claimData.claim.agent?.identifier ) { ctx.font = "14px Arial"; let fullIssuer = serverUtil.didInfoForCertificate( claimData.issuer, allContacts, ); if (fullIssuer.length > 30) { fullIssuer = fullIssuer.substring(0, 30) + "..."; } const issuerText = "Issued by " + fullIssuer; ctx.fillText(issuerText, CANVAS_WIDTH * 0.3, CANVAS_HEIGHT * 0.6); } // Draw number of claim confirmers if (confirmerIds.length > 0) { const confirmerText = "Confirmed by " + confirmerIds.length + (confirmerIds.length === 1 ? " person" : " people"); ctx.font = "14px Arial"; ctx.fillText( confirmerText, CANVAS_WIDTH * 0.3, CANVAS_HEIGHT * 0.63, ); } // Draw claim ID ctx.font = "14px Arial"; ctx.fillText(this.claimId, CANVAS_WIDTH * 0.3, CANVAS_HEIGHT * 0.7); ctx.fillText( "via EndorserSearch.com", CANVAS_WIDTH * 0.3, CANVAS_HEIGHT * 0.73, ); // Generate and draw QR code const qrCodeCanvas = document.createElement("canvas"); await QRCode.toCanvas( qrCodeCanvas, APP_SERVER + "/claim/" + this.claimId, { width: 150, color: { light: "#0000" /* Transparent background */ }, }, ); ctx.drawImage(qrCodeCanvas, CANVAS_WIDTH * 0.6, CANVAS_HEIGHT * 0.55); }; } } } } </script>