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 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: true },
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 request_uri = `https://issuance.wallet-connect.eu/disclosure/${props.clientId}/request_uri?session_type=${session_type}`;
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(data);
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