wallet-connect-button-vue 1.0.10 → 1.0.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.esm.js +147 -3
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +147 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.esm.js
CHANGED
|
@@ -40,13 +40,14 @@ const _hoisted_2 = {
|
|
|
40
40
|
const _hoisted_3 = { class: "verification-card" };
|
|
41
41
|
const _hoisted_4 = ["text", "usecase", "start-url", "lang", "help-base-url", "same-device-ul", "cross-device-ul"];
|
|
42
42
|
|
|
43
|
+
// Types for credentials functionality
|
|
43
44
|
|
|
44
45
|
const _sfc_main = {
|
|
45
46
|
__name: 'WalletConnectButton',
|
|
46
47
|
props: {
|
|
47
48
|
label: { type: String, required: false, default: 'Inloggen met NL Wallet' },
|
|
48
49
|
clientId: { type: String, required: true },
|
|
49
|
-
onSuccess: { type: Function, required:
|
|
50
|
+
onSuccess: { type: Function, required: false },
|
|
50
51
|
apiKey: { type: String, required: false },
|
|
51
52
|
walletConnectHost: { type: String, required: false },
|
|
52
53
|
lang: { type: String, required: false, default: 'nl' },
|
|
@@ -55,6 +56,8 @@ const _sfc_main = {
|
|
|
55
56
|
},
|
|
56
57
|
setup(__props) {
|
|
57
58
|
|
|
59
|
+
const credentialsCache = new Map();
|
|
60
|
+
|
|
58
61
|
const props = __props;
|
|
59
62
|
|
|
60
63
|
const { searchParams, setSearchParams, removeSearchParam } = useSearchParams();
|
|
@@ -69,7 +72,8 @@ const startUrl = computed(() => {
|
|
|
69
72
|
});
|
|
70
73
|
|
|
71
74
|
const constructURI = (session_type) => {
|
|
72
|
-
const
|
|
75
|
+
const baseHost = props.walletConnectHost || "https://issuance.wallet-connect.eu";
|
|
76
|
+
const request_uri = `${baseHost}/disclosure/${props.clientId}/request_uri?session_type=${session_type}`;
|
|
73
77
|
const request_uri_method = "post";
|
|
74
78
|
const client_id_uri = `${props.clientId}.example.com`;
|
|
75
79
|
|
|
@@ -79,6 +83,142 @@ const constructURI = (session_type) => {
|
|
|
79
83
|
const sameDeviceUl = computed(() => constructURI("same_device"));
|
|
80
84
|
const crossDeviceUl = computed(() => constructURI("cross_device"));
|
|
81
85
|
|
|
86
|
+
const fetchRequestedCredentials = async () => {
|
|
87
|
+
if (!props.apiKey || !props.clientId) return [];
|
|
88
|
+
|
|
89
|
+
const cacheKey = `${props.clientId}-${props.walletConnectHost || "default"}`;
|
|
90
|
+
|
|
91
|
+
// Check if we already have data in cache
|
|
92
|
+
const cached = credentialsCache.get(cacheKey);
|
|
93
|
+
if (cached?.data) {
|
|
94
|
+
return cached.data;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Check if there's already a request in progress
|
|
98
|
+
if (cached?.promise) {
|
|
99
|
+
return await cached.promise;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const fetchPromise = (async () => {
|
|
103
|
+
try {
|
|
104
|
+
const baseUrl = props.walletConnectHost || "https://wallet-connect.eu";
|
|
105
|
+
const url = `${baseUrl}/api/client/${props.clientId}/requested-credentials`;
|
|
106
|
+
const headers = { 'Authorization': `Bearer ${props.apiKey}` };
|
|
107
|
+
|
|
108
|
+
const response = await axios.get(url, { headers });
|
|
109
|
+
|
|
110
|
+
// Extract credentials from the response
|
|
111
|
+
const credentials = response.data?.data?.requestedCredentials || [];
|
|
112
|
+
|
|
113
|
+
// Cache the result
|
|
114
|
+
credentialsCache.set(cacheKey, { data: credentials });
|
|
115
|
+
return credentials;
|
|
116
|
+
} catch (error) {
|
|
117
|
+
// Remove failed request from cache
|
|
118
|
+
credentialsCache.delete(cacheKey);
|
|
119
|
+
throw error;
|
|
120
|
+
}
|
|
121
|
+
})();
|
|
122
|
+
|
|
123
|
+
// Cache the promise to prevent duplicate requests
|
|
124
|
+
credentialsCache.set(cacheKey, { promise: fetchPromise });
|
|
125
|
+
|
|
126
|
+
return await fetchPromise;
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const injectCredentialsIntoShadowDOM = (credentials, retryCount = 0) => {
|
|
130
|
+
const maxRetries = 10;
|
|
131
|
+
const walletButton = buttonRef.value;
|
|
132
|
+
|
|
133
|
+
if (!walletButton || !walletButton.shadowRoot) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Remove any existing credential info
|
|
138
|
+
const existingCredentials = walletButton.shadowRoot.querySelector('.required-credentials');
|
|
139
|
+
if (existingCredentials) {
|
|
140
|
+
existingCredentials.remove();
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (credentials.length === 0) return;
|
|
144
|
+
|
|
145
|
+
// Look for the modal and website section
|
|
146
|
+
const modal = walletButton.shadowRoot.querySelector('.modal');
|
|
147
|
+
if (!modal) {
|
|
148
|
+
// Retry if modal not found yet
|
|
149
|
+
if (retryCount < maxRetries) {
|
|
150
|
+
setTimeout(() => {
|
|
151
|
+
injectCredentialsIntoShadowDOM(credentials, retryCount + 1);
|
|
152
|
+
}, 200);
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const websiteSection = modal.querySelector('.website');
|
|
159
|
+
|
|
160
|
+
// Determine language and translations
|
|
161
|
+
const isNL = props.lang === 'nl';
|
|
162
|
+
const headerText = isNL ? 'Benodigde Attestaties:' : 'Required Credentials:';
|
|
163
|
+
const getLinkText = isNL ? '→ Verkrijg attestatie' : '→ Get credential';
|
|
164
|
+
|
|
165
|
+
// Create credential info element
|
|
166
|
+
const credentialsDiv = document.createElement('div');
|
|
167
|
+
credentialsDiv.className = 'required-credentials';
|
|
168
|
+
credentialsDiv.innerHTML = `
|
|
169
|
+
<div style="
|
|
170
|
+
background: #f8f9fa;
|
|
171
|
+
border: 1px solid #e9ecef;
|
|
172
|
+
border-radius: 6px;
|
|
173
|
+
padding: 12px;
|
|
174
|
+
font-family: inherit;
|
|
175
|
+
font-size: 13px;
|
|
176
|
+
line-height: 1.4;
|
|
177
|
+
">
|
|
178
|
+
<div style="margin: 0 0 8px 0; color: #212529; font-size: 14px; font-weight: 600;">${headerText}</div>
|
|
179
|
+
${credentials.map(credential => {
|
|
180
|
+
const credentialName = isNL ? credential.credentialName.nl : credential.credentialName.en;
|
|
181
|
+
return `
|
|
182
|
+
<div style="margin-bottom: 6px; display: flex; align-items: center; flex-wrap: wrap; gap: 8px;">
|
|
183
|
+
<span style="color: #495057; font-weight: 500;">${credentialName}</span>
|
|
184
|
+
${credential.websiteUrl ? `
|
|
185
|
+
<a href="${credential.websiteUrl}" target="_blank" rel="noopener noreferrer" style="
|
|
186
|
+
color: #0066cc;
|
|
187
|
+
text-decoration: none;
|
|
188
|
+
font-size: 12px;
|
|
189
|
+
white-space: nowrap;
|
|
190
|
+
">${getLinkText}</a>
|
|
191
|
+
` : ''}
|
|
192
|
+
</div>
|
|
193
|
+
`;
|
|
194
|
+
}).join('')}
|
|
195
|
+
</div>
|
|
196
|
+
`;
|
|
197
|
+
|
|
198
|
+
// Insert the credentials div after the website section
|
|
199
|
+
if (websiteSection) {
|
|
200
|
+
websiteSection.insertAdjacentElement('afterend', credentialsDiv);
|
|
201
|
+
} else {
|
|
202
|
+
// Fallback: insert at the beginning of modal
|
|
203
|
+
modal.insertBefore(credentialsDiv, modal.firstChild);
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
const handleButtonClick = async (_event) => {
|
|
208
|
+
try {
|
|
209
|
+
const credentials = await fetchRequestedCredentials();
|
|
210
|
+
|
|
211
|
+
if (credentials && credentials.length > 0) {
|
|
212
|
+
// Inject credentials into the shadow DOM with multiple attempts
|
|
213
|
+
setTimeout(() => {
|
|
214
|
+
injectCredentialsIntoShadowDOM(credentials);
|
|
215
|
+
}, 100);
|
|
216
|
+
}
|
|
217
|
+
} catch (error) {
|
|
218
|
+
console.error('Failed to fetch credentials:', error);
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
|
|
82
222
|
const handleSuccess = (e) => {
|
|
83
223
|
const customEvent = e;
|
|
84
224
|
if (customEvent.detail && customEvent.detail.length > 1) {
|
|
@@ -105,6 +245,7 @@ const loadWebComponent = async () => {
|
|
|
105
245
|
if (button) {
|
|
106
246
|
button.addEventListener("success", handleSuccess);
|
|
107
247
|
button.addEventListener("failed", handleFailed);
|
|
248
|
+
button.addEventListener("click", handleButtonClick);
|
|
108
249
|
}
|
|
109
250
|
} catch (error) {
|
|
110
251
|
console.warn('Could not load nl-wallet-web.js:', error);
|
|
@@ -127,7 +268,9 @@ const fetchDisclosedAttributes = async () => {
|
|
|
127
268
|
try {
|
|
128
269
|
const { data } = await axios.get(url, { headers });
|
|
129
270
|
console.log("Disclosed attributes:", data);
|
|
130
|
-
props.onSuccess
|
|
271
|
+
if (props.onSuccess) {
|
|
272
|
+
props.onSuccess(data);
|
|
273
|
+
}
|
|
131
274
|
removeSearchParam('session_token');
|
|
132
275
|
if (nonce) removeSearchParam('nonce');
|
|
133
276
|
loading.value = false;
|
|
@@ -150,6 +293,7 @@ onUnmounted(() => {
|
|
|
150
293
|
if (button) {
|
|
151
294
|
button.removeEventListener("success", handleSuccess);
|
|
152
295
|
button.removeEventListener("failed", handleFailed);
|
|
296
|
+
button.removeEventListener("click", handleButtonClick);
|
|
153
297
|
}
|
|
154
298
|
});
|
|
155
299
|
|