wallet-connect-button-vue 1.0.11 → 1.0.13

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,6 +40,7 @@ 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',
@@ -48,13 +49,16 @@ const _sfc_main = {
48
49
  clientId: { type: String, required: true },
49
50
  onSuccess: { type: Function, required: false },
50
51
  apiKey: { type: String, required: false },
51
- walletConnectHost: { type: String, required: false },
52
+ useLocalWcServer: { type: Boolean, required: false, default: false },
53
+ business: { type: Boolean, required: false, default: false },
52
54
  lang: { type: String, required: false, default: 'nl' },
53
55
  helpBaseUrl: { type: String, required: false },
54
56
  issuance: { type: Boolean, required: false }
55
57
  },
56
58
  setup(__props) {
57
59
 
60
+ const credentialsCache = new Map();
61
+
58
62
  const props = __props;
59
63
 
60
64
  const { searchParams, setSearchParams, removeSearchParam } = useSearchParams();
@@ -62,23 +66,183 @@ const loading = ref(false);
62
66
  const error = ref(null);
63
67
  const buttonRef = ref(null);
64
68
 
69
+ const getDefaultHost = () => {
70
+ // If useLocalWcServer is set, use local server
71
+ if (props.useLocalWcServer) {
72
+ if (props.business) {
73
+ return props.issuance ? 'http://localhost:4007' : 'http://bw.localhost:3021';
74
+ }
75
+
76
+ return props.issuance ? 'http://localhost:3007' : 'http://localhost:3021';
77
+ }
78
+
79
+ // Otherwise use remote servers
80
+ if (props.business) {
81
+ return props.issuance ? 'https://bw.issuance.wallet-connect.eu' : 'https://bw.wallet-connect.eu';
82
+ }
83
+
84
+ return props.issuance ? 'https://issuance.wallet-connect.eu' : 'https://wallet-connect.eu';
85
+ };
86
+
65
87
  const startUrl = computed(() => {
66
- const baseUrl = props.walletConnectHost || 'https://wallet-connect.eu';
88
+ const baseUrl = getDefaultHost();
67
89
  const returnUrl = typeof window !== 'undefined' ? window.location.href : '';
68
90
  return `${baseUrl}/api/create-session?lang=en&return_url=${encodeURIComponent(returnUrl)}`;
69
91
  });
70
92
 
71
93
  const constructURI = (session_type) => {
72
- const request_uri = `https://issuance.wallet-connect.eu/disclosure/${props.clientId}/request_uri?session_type=${session_type}`;
94
+ const request_uri = `${getDefaultHost()}/disclosure/${props.clientId}/request_uri?session_type=${session_type}`;
73
95
  const request_uri_method = "post";
74
96
  const client_id_uri = `${props.clientId}.example.com`;
75
-
76
- return `walletdebuginteraction://wallet.edi.rijksoverheid.nl/disclosure_based_issuance?request_uri=${encodeURIComponent(request_uri)}&request_uri_method=${request_uri_method}&client_id=${client_id_uri}`;
97
+
98
+ const deepLinkScheme = props.business
99
+ ? 'businesswalletdebuginteraction://wallet.kvk.rijksoverheid.nl'
100
+ : 'walletdebuginteraction://wallet.edi.rijksoverheid.nl';
101
+
102
+ return `${deepLinkScheme}/disclosure_based_issuance?request_uri=${encodeURIComponent(
103
+ request_uri
104
+ )}&request_uri_method=${request_uri_method}&client_id=${client_id_uri}`;
77
105
  };
78
106
 
79
107
  const sameDeviceUl = computed(() => constructURI("same_device"));
80
108
  const crossDeviceUl = computed(() => constructURI("cross_device"));
81
109
 
110
+ const fetchRequestedCredentials = async () => {
111
+ if (!props.apiKey || !props.clientId) return [];
112
+
113
+ const cacheKey = `${props.clientId}-${getDefaultHost()}`;
114
+
115
+ // Check if we already have data in cache
116
+ const cached = credentialsCache.get(cacheKey);
117
+ if (cached?.data) {
118
+ return cached.data;
119
+ }
120
+
121
+ // Check if there's already a request in progress
122
+ if (cached?.promise) {
123
+ return await cached.promise;
124
+ }
125
+
126
+ const fetchPromise = (async () => {
127
+ try {
128
+ const baseUrl = getDefaultHost();
129
+ const url = `${baseUrl}/api/client/${props.clientId}/requested-credentials`;
130
+ const headers = { 'Authorization': `Bearer ${props.apiKey}` };
131
+
132
+ const response = await axios.get(url, { headers });
133
+
134
+ // Extract credentials from the response
135
+ const credentials = response.data?.data?.requestedCredentials || [];
136
+
137
+ // Cache the result
138
+ credentialsCache.set(cacheKey, { data: credentials });
139
+ return credentials;
140
+ } catch (error) {
141
+ // Remove failed request from cache
142
+ credentialsCache.delete(cacheKey);
143
+ throw error;
144
+ }
145
+ })();
146
+
147
+ // Cache the promise to prevent duplicate requests
148
+ credentialsCache.set(cacheKey, { promise: fetchPromise });
149
+
150
+ return await fetchPromise;
151
+ };
152
+
153
+ const injectCredentialsIntoShadowDOM = (credentials, retryCount = 0) => {
154
+ const maxRetries = 10;
155
+ const walletButton = buttonRef.value;
156
+
157
+ if (!walletButton || !walletButton.shadowRoot) {
158
+ return;
159
+ }
160
+
161
+ // Remove any existing credential info
162
+ const existingCredentials = walletButton.shadowRoot.querySelector('.required-credentials');
163
+ if (existingCredentials) {
164
+ existingCredentials.remove();
165
+ }
166
+
167
+ if (credentials.length === 0) return;
168
+
169
+ // Look for the modal and website section
170
+ const modal = walletButton.shadowRoot.querySelector('.modal');
171
+ if (!modal) {
172
+ // Retry if modal not found yet
173
+ if (retryCount < maxRetries) {
174
+ setTimeout(() => {
175
+ injectCredentialsIntoShadowDOM(credentials, retryCount + 1);
176
+ }, 200);
177
+ return;
178
+ }
179
+ return;
180
+ }
181
+
182
+ const websiteSection = modal.querySelector('.website');
183
+
184
+ // Determine language and translations
185
+ const isNL = props.lang === 'nl';
186
+ const headerText = isNL ? 'Benodigde Attestaties:' : 'Required Credentials:';
187
+ const getLinkText = isNL ? '→ Verkrijg attestatie' : '→ Get credential';
188
+
189
+ // Create credential info element
190
+ const credentialsDiv = document.createElement('div');
191
+ credentialsDiv.className = 'required-credentials';
192
+ credentialsDiv.innerHTML = `
193
+ <div style="
194
+ background: #f8f9fa;
195
+ border: 1px solid #e9ecef;
196
+ border-radius: 6px;
197
+ padding: 12px;
198
+ font-family: inherit;
199
+ font-size: 13px;
200
+ line-height: 1.4;
201
+ ">
202
+ <div style="margin: 0 0 8px 0; color: #212529; font-size: 14px; font-weight: 600;">${headerText}</div>
203
+ ${credentials.map(credential => {
204
+ const credentialName = isNL ? credential.credentialName.nl : credential.credentialName.en;
205
+ return `
206
+ <div style="margin-bottom: 6px; display: flex; align-items: center; flex-wrap: wrap; gap: 8px;">
207
+ <span style="color: #495057; font-weight: 500;">${credentialName}</span>
208
+ ${credential.websiteUrl ? `
209
+ <a href="${credential.websiteUrl}" target="_blank" rel="noopener noreferrer" style="
210
+ color: #0066cc;
211
+ text-decoration: none;
212
+ font-size: 12px;
213
+ white-space: nowrap;
214
+ ">${getLinkText}</a>
215
+ ` : ''}
216
+ </div>
217
+ `;
218
+ }).join('')}
219
+ </div>
220
+ `;
221
+
222
+ // Insert the credentials div after the website section
223
+ if (websiteSection) {
224
+ websiteSection.insertAdjacentElement('afterend', credentialsDiv);
225
+ } else {
226
+ // Fallback: insert at the beginning of modal
227
+ modal.insertBefore(credentialsDiv, modal.firstChild);
228
+ }
229
+ };
230
+
231
+ const handleButtonClick = async (_event) => {
232
+ try {
233
+ const credentials = await fetchRequestedCredentials();
234
+
235
+ if (credentials && credentials.length > 0) {
236
+ // Inject credentials into the shadow DOM with multiple attempts
237
+ setTimeout(() => {
238
+ injectCredentialsIntoShadowDOM(credentials);
239
+ }, 100);
240
+ }
241
+ } catch (error) {
242
+ console.error('Failed to fetch credentials:', error);
243
+ }
244
+ };
245
+
82
246
  const handleSuccess = (e) => {
83
247
  const customEvent = e;
84
248
  if (customEvent.detail && customEvent.detail.length > 1) {
@@ -105,6 +269,7 @@ const loadWebComponent = async () => {
105
269
  if (button) {
106
270
  button.addEventListener("success", handleSuccess);
107
271
  button.addEventListener("failed", handleFailed);
272
+ button.addEventListener("click", handleButtonClick);
108
273
  }
109
274
  } catch (error) {
110
275
  console.warn('Could not load nl-wallet-web.js:', error);
@@ -118,12 +283,12 @@ const fetchDisclosedAttributes = async () => {
118
283
  if (!session_token) return;
119
284
 
120
285
  loading.value = true;
121
- const baseUrl = props.apiKey ? (props.walletConnectHost || "https://wallet-connect.eu") : "";
286
+ const baseUrl = props.apiKey ? getDefaultHost() : "";
122
287
  let url = baseUrl + `/api/disclosed-attributes?session_token=${session_token}&client_id=${props.clientId}`;
123
288
  if (nonce) url = `${url}&nonce=${nonce}`;
124
289
 
125
290
  const headers = props.apiKey ? { 'Authorization': `Bearer ${props.apiKey}` } : {};
126
-
291
+
127
292
  try {
128
293
  const { data } = await axios.get(url, { headers });
129
294
  console.log("Disclosed attributes:", data);
@@ -152,6 +317,7 @@ onUnmounted(() => {
152
317
  if (button) {
153
318
  button.removeEventListener("success", handleSuccess);
154
319
  button.removeEventListener("failed", handleFailed);
320
+ button.removeEventListener("click", handleButtonClick);
155
321
  }
156
322
  });
157
323