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.
 
 
 
 
 
 

86 lines
3.1 KiB

/** * EntityIcon.vue - Icon generation component for contacts and entities * *
Generates icons for contacts and entities using either profile images * or
DiceBear avatars. Handles CORS image transformation and fallback * to blank
square for missing identifiers. * * Features: * - Profile image display with
CORS handling * - DiceBear avatar generation for missing images * - Fallback to
blank square for missing identifiers * - Dynamic icon sizing * - Contact object
integration * * @author Matthew Raymer */
<template>
<!-- eslint-disable-next-line vue/no-v-html -->
<div class="w-fit" v-html="generateIcon()"></div>
</template>
<script lang="ts">
import { createAvatar, StyleOptions } from "@dicebear/core";
import { avataaars } from "@dicebear/collection";
import { Vue, Component, Prop } from "vue-facing-decorator";
import { Contact } from "../db/tables/contacts";
import blankSquareSvg from "../assets/blank-square.svg";
/**
* EntityIcon - Icon generation component for contacts and entities
*
* Generates icons using profile images or DiceBear avatars with
* proper CORS handling and fallback mechanisms.
*/
@Component({ name: "EntityIcon" })
export default class EntityIcon extends Vue {
/** Contact object containing profile information */
@Prop()
contact?: Contact;
/** Entity identifier (overridden by contact.did or profileImageUrl) */
@Prop({ default: "" })
entityId!: string;
/** Icon size in pixels */
@Prop({ default: 0 })
iconSize!: number;
/** Profile image URL (overridden by contact.profileImageUrl) */
@Prop({ default: "" })
profileImageUrl!: string;
/**
* Generate icon HTML based on available data
*
* Priority order:
* 1. Contact profile image URL
* 2. Direct profile image URL
* 3. DiceBear avatar using contact DID or entity ID
* 4. Blank square SVG as final fallback
*
* @returns HTML string for the icon
*/
generateIcon(): string {
// Check for profile image URL (highest priority)
const imageUrl = this.contact?.profileImageUrl || this.profileImageUrl;
if (imageUrl) {
return `<img src="${imageUrl}" class="rounded" width="${this.iconSize}" height="${this.iconSize}" />`;
}
// Check for identifier for avatar generation
const identifier = this.contact?.did || this.entityId;
if (!identifier) {
// Final fallback: blank square
return `<img src="${blankSquareSvg}" class="rounded" width="${this.iconSize}" height="${this.iconSize}" />`;
}
// https://api.dicebear.com/8.x/avataaars/svg?seed=
// ... does not render things with the same seed as this library.
// "did:ethr:0x222BB77E6Ff3774d34c751f3c1260866357B677b" yields a girl with flowers in her hair and a lightning earring
// ... which looks similar to '' at the dicebear site but which is different.
const options: StyleOptions<object> = {
seed: (identifier as string) || "",
size: this.iconSize,
};
const avatar = createAvatar(avataaars, options);
const svgString = avatar.toString();
return svgString;
}
}
</script>
<style scoped>
/* Component-specific styles if needed */
</style>