<template> <section id="Content"> <div v-if="claimData"> <canvas ref="claimCanvas"></canvas> </div> </section> </template> <style scoped> canvas { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } </style> <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 = 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.claim.agent) { const presentedText = "Presented 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.4, ); } 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.45, ); } // Draw claim issuer & recipient if (claimData.issuer) { ctx.font = "14px Arial"; const issuerText = "Issued by " + serverUtil.didInfoForCertificate(claimData.issuer, allContacts); ctx.fillText(issuerText, CANVAS_WIDTH * 0.3, CANVAS_HEIGHT * 0.6); } 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>