Skip to content

Commit 0199ece

Browse files
committed
wip
1 parent c8b9077 commit 0199ece

File tree

9 files changed

+203
-11
lines changed

9 files changed

+203
-11
lines changed

components/CredentialCompactBundle.vue

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
:credential="credential">
1010
<credential-switch
1111
class="q-ma-xs col"
12+
dense
13+
single-row
1214
:expandable="true"
1315
:credential="credential" />
1416
</slot>
@@ -91,6 +93,7 @@ async function createCompactBundledCredentials({credentials}) {
9193
}
9294
credentialsList.push(credential);
9395
}
96+
console.log(credentialsList);
9497
return credentialsList;
9598
}
9699

components/CredentialDashboard.vue

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,16 @@
1616
size="sm"
1717
color="primary"
1818
class="q-mr-sm"
19-
icon="fas fa-barcode"
19+
icon="fas fa-camera"
2020
@click="openBarcodeDialog" />
21+
<q-btn
22+
round
23+
outline
24+
size="sm"
25+
color="primary"
26+
class="q-mr-sm"
27+
icon="fas fa-shopping-cart"
28+
@click="openInteractionQrDialog" />
2129
<q-btn
2230
round
2331
outline
@@ -39,6 +47,9 @@
3947
@delete-credential="$event.waitUntil(deleteCredential($event))" />
4048
</div>
4149
<ShowScannerModal v-model="showBarcodeDialog" />
50+
<ShowInteractionQrModal
51+
v-if="showInteractionQrDialog"
52+
v-model="showInteractionQrDialog" />
4253
</q-page>
4354
</template>
4455

@@ -52,13 +63,15 @@ import {config} from '@bedrock/web';
5263
import {createEmitExtendable} from '@digitalbazaar/vue-extendable-event';
5364
import CredentialsList from './CredentialsList.vue';
5465
import SearchBox from './SearchBox.vue';
66+
import ShowInteractionQrModal from './ShowInteractionQrModal.vue';
5567
import ShowScannerModal from './ShowScannerModal.vue';
5668
5769
export default {
5870
name: 'CredentialDashboard',
5971
components: {
6072
CredentialsList,
6173
SearchBox,
74+
ShowInteractionQrModal,
6275
ShowScannerModal
6376
},
6477
props: {
@@ -96,6 +109,7 @@ export default {
96109
const search = ref('');
97110
const filteredProfiles = ref([]);
98111
const showBarcodeDialog = ref(false);
112+
const showInteractionQrDialog = ref(false);
99113
const credentials = toRef(props, 'credentials');
100114
101115
// Credentials filtered by search term match
@@ -132,14 +146,18 @@ export default {
132146
showBarcodeDialog.value = true;
133147
};
134148
149+
const openInteractionQrDialog = () => {
150+
showInteractionQrDialog.value = true;
151+
};
152+
135153
// Pass delete-credential event up component chain
136154
const deleteCredential = async ({profileId, credentialId}) => {
137155
return emitExtendable('delete-credential', {profileId, credentialId});
138156
};
139157
140158
// Watchers
141159
watch(() => filteredProfiles, () => {
142-
return emit('filtered-profiles', filteredProfiles);
160+
return emit('filtered-profiles', filteredProfiles.value);
143161
}, {immediate: true});
144162
145163
// Get each credential title and subtitle overrides
@@ -181,6 +199,8 @@ export default {
181199
search,
182200
openBarcodeDialog,
183201
showBarcodeDialog,
202+
openInteractionQrDialog,
203+
showInteractionQrDialog,
184204
};
185205
}
186206
};

components/CredentialsList.vue

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
<credential-switch
1414
class="q-ma-xs col"
1515
:expandable="true"
16+
dense
17+
single-row
1618
:credential="switchProps.credential" />
1719
</component>
1820
</template>

components/ShareCredentials.vue

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616
style="overflow: auto"
1717
:style="$q.screen.lt.sm ? 'max-height: calc(100% - 67px)' :
1818
'max-height: calc(100vh - 102px)'">
19-
<div class="full-width">
19+
<div
20+
v-if="!accountHasOnlyOneProfile"
21+
class="full-width">
2022
<profile-chooser
2123
:loading="profilesUpdating"
2224
:profiles="profiles"
@@ -34,7 +36,7 @@
3436
<template #credentials-display="displayProps">
3537
<credentials-list
3638
:compact="true"
37-
:selectable="true"
39+
:selectable="false"
3840
:selected-credentials="selectedCredentials"
3941
:credentials="displayProps.credentials"
4042
@select-credentials="selectCredentials" />
@@ -124,6 +126,10 @@ export default {
124126
return [];
125127
}, [], profilesUpdating);
126128
129+
const accountHasOnlyOneProfile = computed(() => {
130+
return profiles.value.length === 1;
131+
});
132+
127133
const verifiablePresentationRequest = toRef(
128134
props, 'verifiablePresentationRequest');
129135
@@ -197,6 +203,7 @@ export default {
197203
sharing.value);
198204
199205
return {
206+
accountHasOnlyOneProfile,
200207
displayableCredentials,
201208
selectedCredentials,
202209
loading,
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
<template>
2+
<q-dialog v-model="show">
3+
<q-card
4+
flat
5+
class="q-pa-md"
6+
style="border-radius: 12px; width: 340px">
7+
<q-card-section class="row items-center">
8+
<div class="text-body1 q-my-md text-center">
9+
Show QR code
10+
</div>
11+
</q-card-section>
12+
<q-card-section style="max-height: 375px">
13+
<q-img
14+
fit="contain"
15+
:src="qrCodeImageUrl" />
16+
</q-card-section>
17+
<q-card-actions align="between">
18+
<q-btn
19+
v-close-popup
20+
flat
21+
no-caps
22+
rounded
23+
size="16px"
24+
color="dark"
25+
label="Close"
26+
padding="sm md" />
27+
<q-btn
28+
no-caps
29+
rounded
30+
size="16px"
31+
color="primary"
32+
label="Copy Code"
33+
padding="sm md"
34+
@click="handleCopyCodeClick" />
35+
</q-card-actions>
36+
</q-card>
37+
</q-dialog>
38+
</template>
39+
40+
<script>
41+
/*!
42+
* Copyright (c) 2019-2024 Digital Bazaar, Inc. All rights reserved.
43+
*/
44+
import * as walletInteraction from './wallet-interaction.js';
45+
import {computed, onMounted, ref} from 'vue';
46+
import {useQuasar} from 'quasar';
47+
48+
export default {
49+
name: 'ShowInteractionQrModal',
50+
props: {
51+
modelValue: {
52+
type: Boolean,
53+
required: true
54+
}
55+
},
56+
emits: ['update', 'update:modelValue'],
57+
setup(props, {emit}) {
58+
const $q = useQuasar();
59+
const qrCodeImageUrl = ref('');
60+
const qrCodeText = ref('');
61+
// on mounted
62+
onMounted(async () => {
63+
const interactionIdUrl = await walletInteraction.create();
64+
console.log('Interaction ID URL:\n', interactionIdUrl);
65+
const text = walletInteraction.toQrText({url: interactionIdUrl});
66+
qrCodeText.value = text;
67+
console.log('Encoded URL:\n', text);
68+
qrCodeImageUrl.value = await walletInteraction.toQrCode({text});
69+
70+
// FIXME: abort poll if dialog is closed
71+
const website = await walletInteraction.poll({url: interactionIdUrl});
72+
const {hostname} = new URL(website);
73+
74+
$q.dialog({
75+
title: 'Are you sure you want to leave Veres Wallet?',
76+
message: `This site would like you to complete checkout (${hostname}).`,
77+
cancel: true,
78+
persistent: true
79+
}).onOk(() => {
80+
location.assign(website);
81+
});
82+
});
83+
84+
// computed
85+
const show = computed({
86+
get: () => props.modelValue,
87+
set: value => emit('update:modelValue', value)
88+
});
89+
90+
async function handleCopyCodeClick() {
91+
await navigator.clipboard.writeText(qrCodeText.value);
92+
}
93+
94+
return {
95+
handleCopyCodeClick,
96+
show,
97+
qrCodeImageUrl
98+
};
99+
}
100+
};
101+
</script>
102+
103+
<style lang="scss" scoped>
104+
</style>

components/StoreCredentials.vue

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,13 @@
1717
'max-height: calc(100vh - 102px)'">
1818
<div class="column">
1919
<h6 class="text-center q-my-sm">
20-
Would you like to store these credentials?
20+
Would you like to store these credential(s)?
2121
</h6>
2222
</div>
2323
<q-separator class="s-separator" />
24-
<div class="full-width">
24+
<div
25+
v-if="!accountHasOnlyOneProfile"
26+
class="full-width">
2527
<profile-chooser
2628
:loading="profilesUpdating"
2729
:profiles="profiles"
@@ -103,13 +105,17 @@ export default {
103105
const profiles = computedAsync(
104106
async () => profileManager.getProfiles({useCache: true}),
105107
[], profilesUpdating);
106-
108+
const accountHasOnlyOneProfile = computed(() => {
109+
return profiles.value.length === 1;
110+
});
107111
const storing = ref(false);
108112
109113
const loading = computed(
110114
() => profilesUpdating.value || storing.value);
111115
112-
return {loading, profiles, profilesUpdating, storing};
116+
return {
117+
accountHasOnlyOneProfile, loading, profiles, profilesUpdating, storing
118+
};
113119
},
114120
data() {
115121
return {

components/wallet-interaction.js

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*!
2+
* Copyright (c) 2025 Digital Bazaar, Inc. All rights reserved.
3+
*/
4+
5+
import {httpClient} from '@digitalbazaar/http-client';
6+
import QRCode from 'qrcode';
7+
8+
export async function create() {
9+
const {data: {id}} = await httpClient.post('/mock-interactions');
10+
return `${window.location.origin}/mock-interactions/${id}`;
11+
}
12+
13+
export function poll({url, interval = 1000, timeout = 60000}) {
14+
const start = Date.now();
15+
return new Promise((resolve, reject) => {
16+
(async function tick() {
17+
if(Date.now() - start >= timeout) {
18+
return reject(new Error('timeout'));
19+
}
20+
try {
21+
const {data} = await httpClient.get(url);
22+
if(data?.protocols?.website) {
23+
const {website} = data.protocols;
24+
return resolve(website);
25+
}
26+
27+
setTimeout(tick, interval);
28+
} catch(e) {
29+
console.error(e);
30+
setTimeout(tick, interval);
31+
}
32+
})();
33+
});
34+
}
35+
36+
export function toQrCode({text}) {
37+
if(!text || text.length === 0) {
38+
return;
39+
}
40+
41+
return QRCode.toDataURL(text, {
42+
type: 'image/png',
43+
width: 800,
44+
});
45+
}
46+
47+
export function toQrText(obj) {
48+
return window.btoa(JSON.stringify(obj));
49+
}

lib/index.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* Copyright (c) 2022-2023 Digital Bazaar, Inc. All rights reserved.
33
*/
44
import * as brQuasar from '@bedrock/quasar';
5-
import {Loading, Notify, Quasar} from 'quasar';
5+
import {Dialog, Loading, Notify, Quasar} from 'quasar';
66
import {session, sessionDataRef} from './session.js';
77
import {config} from '@bedrock/web';
88
import {configureRouter} from './router.js';
@@ -55,7 +55,8 @@ export async function initialize({app, router, features, quasarOptions} = {}) {
5555
plugins: {
5656
// known plugins in use
5757
Loading,
58-
Notify
58+
Notify,
59+
Dialog,
5960
},
6061
config: {}
6162
};

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
"@digitalbazaar/web-app-manifest-utils": "^2.0.0",
2323
"html5-qrcode": "^2.3.8",
2424
"mustache": "^4.2.0",
25-
"qrcode": "^1.5.3",
25+
"qrcode": "^1.5.4",
2626
"randomcolor": "^0.6.2",
2727
"web-credential-handler": "^3.0.0"
2828
},

0 commit comments

Comments
 (0)