Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# bedrock-vue-wallet ChangeLog

## 29.4.2 - 2024-10-xx

### Changed
- Refactor render method fetch to use Promise.all.

## 29.4.1 - 2024-10-15

### Fixed
Expand Down
87 changes: 46 additions & 41 deletions components/CredentialDetailsViews.vue
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,9 @@
/*!
* Copyright (c) 2015-2024 Digital Bazaar, Inc. All rights reserved.
*/
import {onBeforeMount, onMounted, reactive, ref} from 'vue';
import {onMounted, reactive, ref} from 'vue';
import {date} from 'quasar';
import {httpClient} from '@digitalbazaar/http-client';
import Mustache from 'mustache';

const {formatDate} = date;
Expand Down Expand Up @@ -181,6 +182,7 @@ export default {
} else if(showDisplays.value) {
tab.value = 'displays';
}
getDisplaysFromRenderMethod();
});

// Constants
Expand All @@ -197,37 +199,28 @@ export default {
backgroundColor: 'gray',
};

// Fetch style, overrides, & highlights before component mounts
onBeforeMount(() => {
getDisplaysFromRenderMethod();
});

// Extract and parse images from credential's render method property
async function getDisplaysFromRenderMethod() {
if(props.credential?.renderMethod?.length) {
props.credential.renderMethod.forEach(async rm => {
if(supportedRenderMethods.includes(rm.type)) {
if(rm.type === 'SvgRenderingTemplate2023') {
useRenderTemplate2023(rm.id);
} else if(rm.type === 'SvgRenderingTemplate2024') {
const {template, url} = rm;
const values = props.credential;
await useRenderTemplate2024(template, url, values);
const imageValues = await Promise.all(
props.credential.renderMethod.map(async rm => {
let imageValue;
if(supportedRenderMethods.includes(rm.type)) {
if(rm.type === 'SvgRenderingTemplate2023') {
imageValue = useRenderTemplate2023(rm.id);
} else if(rm.type === 'SvgRenderingTemplate2024') {
const {template, url} = rm;
const values = props.credential;
imageValue = await useRenderTemplate2024(template, url, values);
}
}
}
});
return imageValue;
})
);
imageValues.filter(Boolean).forEach(v => credentialImages.push(v));
Comment on lines 203 to +220
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A quick note that none of this is vue-specific code, so we should import a function from a vanilla JS lib and call that (probably from bedrock-web-wallet?). That other library should also be able to, with time use a proxy or use OHTTP to obtain these things -- and be able to interact with a cache to avoid retrieval more than once across components and so on.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dlongley
In looking to refactor as described above, I noticed this function calls useRenderTemplate2024 that uses a UI library (Mustache) and a Quasar date library (date) to aid in the UI rendering logic.

Additionally, with renderMethod continuing to change in the future (before its become a stable standard) I thought it'd be best to leave this logic in this vue file for the time being.

}
}

/**
* Uses id for src value in image.
*
* @param {string} srcValue - Data URI.
*/
function useRenderTemplate2023(srcValue) {
credentialImages.push(srcValue);
}

/*
* Functions used to format Mustache template values
* See: https://github.com/janl/mustache.js#functions
Expand All @@ -242,34 +235,46 @@ export default {
}
};

/**
* Uses id for src value in image.
*
* @param {string} srcValue - Data URI.
* @returns {string} Src value.
*/
function useRenderTemplate2023(srcValue) {
return srcValue;
}

/**
* Load svg from url or template then hydrate with credentialSubject values.
*
* @param {string} template - Svg.
* @param {string} url - Url.
* @param {object} values - Credential.credentialSubject.
* @returns {string} Src value.
*/
async function useRenderTemplate2024(template, url, values) {
// Example credential renderMethod property:
// "renderMethod": [
// {
// "name": "Landscape",
// "mediaQuery": "@media (orientation: landscape)",
// "type": "SvgRenderingTemplate2024",
// "template": "",
// "url": "https://credentialTemplates.dev/example.svg",
// "mediaType": "image/svg+xml",
// }
// ]
//
/*
* Example credential renderMethod property:
* "renderMethod": [
* {
* "name": "Landscape",
* "mediaQuery": "@media (orientation: landscape)",
* "type": "SvgRenderingTemplate2024",
* "template": "",
* "url": "https://credentialTemplates.dev/example.svg",
* "mediaType": "image/svg+xml",
* }
* ]
*/
if(!template || url) {
const resp = await fetch(url);
template = await resp.text();
const headers = {headers: {accept: 'application/json'}};
const {data} = await httpClient.get(url, {headers});
template = await data.text();
}

const rv = Mustache.render(template, {...values, ...formattingFunctions});
const image = `data:image/svg+xml;base64,${btoa(rv)}`;
credentialImages.push(image);
return image;
}

return {
Expand Down