You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

231 lines
6.7 KiB

<template>
<section id="Content">
<div v-if="claimData">
<canvas ref="claimCanvas"></canvas>
</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 * as endorserServer from "../libs/endorserServer";
import {
GenericCredWrapper,
GenericVerifiableCredential,
} from "../interfaces/common";
import { logger } from "../utils/logger";
import { Contact } from "@/db/tables/contacts";
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
import { NOTIFY_ERROR_LOADING_CLAIM } from "@/constants/notifications";
@Component({
mixins: [PlatformServiceMixin],
})
export default class ClaimReportCertificateView extends Vue {
$notify!: (notification: NotificationIface, timeout?: number) => void;
// Notification helper
notify!: ReturnType<typeof createNotifyHelpers>;
activeDid = "";
allMyDids: Array<string> = [];
apiServer = "";
claimId = "";
claimData = null;
allContacts: Contact[] = [];
endorserServer = endorserServer;
// Computed properties for canvas optimization
get CANVAS_WIDTH() {
return 1100;
}
get CANVAS_HEIGHT() {
return 850;
}
async created() {
// Initialize notification helper
this.notify = createNotifyHelpers(this.$notify);
const settings = await this.$settings();
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 response = await fetch(
`${this.apiServer}/api/claim/${this.claimId}`,
);
if (response.ok) {
this.claimData = await response.json();
await nextTick(); // Wait for the DOM to update
if (this.claimData) {
this.drawCanvas(this.claimData);
}
} else {
throw new Error(`Error fetching claim: ${response.statusText}`);
}
} catch (error) {
logger.error("Failed to load claim:", error);
this.notify.error(NOTIFY_ERROR_LOADING_CLAIM.message, TIMEOUTS.LONG);
}
}
async drawCanvas(claimData: GenericCredWrapper<GenericVerifiableCredential>) {
if (!claimData || !claimData.claim) {
return;
}
// Load contacts if not already loaded
if (this.allContacts.length === 0) {
this.allContacts = await this.$getAllContacts();
}
const canvas = this.$refs.claimCanvas as HTMLCanvasElement;
if (canvas) {
// size to approximate portrait of 8.5"x11"
canvas.width = this.CANVAS_WIDTH;
canvas.height = this.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,
this.CANVAS_WIDTH,
this.CANVAS_HEIGHT,
);
// Set font and styles
ctx.fillStyle = "black";
// Draw claim type
ctx.font = "bold 20px Arial";
const claimTypeText =
this.endorserServer.capitalizeAndInsertSpacesBeforeCaps(
claimData.claimType || "",
);
const claimTypeWidth = ctx.measureText(claimTypeText).width;
ctx.fillText(
claimTypeText,
(this.CANVAS_WIDTH - claimTypeWidth) / 2, // Center horizontally
this.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,
(this.CANVAS_WIDTH - presentedWidth) / 2, // Center horizontally
this.CANVAS_HEIGHT * 0.37,
);
const agentText = endorserServer.didInfoForCertificate(
typeof claimData.claim.agent === "string"
? claimData.claim.agent
: claimData.claim.agent?.identifier,
this.allContacts,
);
ctx.font = "bold 20px Arial";
const agentWidth = ctx.measureText(agentText).width;
ctx.fillText(
agentText,
(this.CANVAS_WIDTH - agentWidth) / 2, // Center horizontally
this.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,
(this.CANVAS_WIDTH - descriptionWidth) / 2,
this.CANVAS_HEIGHT * 0.45,
);
}
// Draw claim issuer & recipient
if (claimData.issuer) {
ctx.font = "14px Arial";
const issuerText =
"Issued by " +
endorserServer.didInfoForCertificate(
claimData.issuer,
this.allContacts,
);
ctx.fillText(
issuerText,
this.CANVAS_WIDTH * 0.3,
this.CANVAS_HEIGHT * 0.6,
);
}
// Draw claim ID
ctx.font = "14px Arial";
ctx.fillText(
this.claimId,
this.CANVAS_WIDTH * 0.3,
this.CANVAS_HEIGHT * 0.7,
);
ctx.fillText(
"via EndorserSearch.com",
this.CANVAS_WIDTH * 0.3,
this.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,
this.CANVAS_WIDTH * 0.6,
this.CANVAS_HEIGHT * 0.55,
);
};
}
}
}
}
</script>
<style scoped>
canvas {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
</style>