@1001-digital/components 0.0.4 → 1.0.0
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/package.json +11 -2
- package/src/base/components/Button.vue +2 -2
- package/src/base/components/Calendar.vue +227 -0
- package/src/base/components/Combobox.vue +241 -0
- package/src/base/components/ConfirmDialog.vue +33 -0
- package/src/base/components/Dialog.vue +7 -1
- package/src/base/components/FormDateField.vue +111 -0
- package/src/base/components/FormDatePicker.vue +382 -0
- package/src/base/components/FormSlider.vue +142 -0
- package/src/base/components/FormSwitch.vue +103 -0
- package/src/base/components/Globals.vue +9 -0
- package/src/base/components/Opepicon.vue +45 -0
- package/src/base/components/PinInput.vue +105 -0
- package/src/base/components/Progress.vue +66 -0
- package/src/base/components/Toasts.vue +6 -1
- package/src/base/composables/confirm.ts +29 -0
- package/src/base/composables/toast.ts +1 -0
- package/src/base/icons.ts +3 -1
- package/src/evm/components/EvmAvatar.vue +62 -0
- package/src/evm/components/EvmConnect.vue +83 -32
- package/src/evm/components/EvmConnectorQR.vue +12 -42
- package/src/evm/components/EvmProfile.vue +183 -0
- package/src/evm/components/EvmSwitchNetwork.vue +130 -0
- package/src/evm/components/EvmTransactionFlow.vue +41 -11
- package/src/evm/components/EvmWalletConnectWallets.vue +199 -0
- package/src/evm/composables/chainId.ts +2 -2
- package/src/evm/composables/uri.ts +11 -0
- package/src/evm/composables/walletExplorer.ts +130 -0
- package/src/evm/config.ts +1 -0
- package/src/evm/index.ts +9 -0
- package/src/evm/utils/uri.ts +24 -0
- package/src/index.ts +18 -0
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<PinInputRoot
|
|
3
|
+
v-model="model"
|
|
4
|
+
class="pin-input"
|
|
5
|
+
:disabled="disabled"
|
|
6
|
+
:placeholder="placeholder"
|
|
7
|
+
:mask="mask"
|
|
8
|
+
:otp="otp"
|
|
9
|
+
:type="type"
|
|
10
|
+
:name="name"
|
|
11
|
+
:required="required"
|
|
12
|
+
:dir="dir"
|
|
13
|
+
@complete="$emit('complete', $event)"
|
|
14
|
+
>
|
|
15
|
+
<PinInputInput
|
|
16
|
+
v-for="(_, i) in length"
|
|
17
|
+
:key="i"
|
|
18
|
+
:index="i"
|
|
19
|
+
:disabled="disabled"
|
|
20
|
+
class="pin-input-field"
|
|
21
|
+
/>
|
|
22
|
+
</PinInputRoot>
|
|
23
|
+
</template>
|
|
24
|
+
|
|
25
|
+
<script setup lang="ts">
|
|
26
|
+
import { PinInputInput, PinInputRoot } from 'reka-ui'
|
|
27
|
+
|
|
28
|
+
const model = defineModel<string[]>()
|
|
29
|
+
|
|
30
|
+
withDefaults(
|
|
31
|
+
defineProps<{
|
|
32
|
+
length?: number
|
|
33
|
+
disabled?: boolean
|
|
34
|
+
placeholder?: string
|
|
35
|
+
mask?: boolean
|
|
36
|
+
otp?: boolean
|
|
37
|
+
type?: 'text' | 'number'
|
|
38
|
+
name?: string
|
|
39
|
+
required?: boolean
|
|
40
|
+
dir?: 'ltr' | 'rtl'
|
|
41
|
+
}>(),
|
|
42
|
+
{
|
|
43
|
+
length: 4,
|
|
44
|
+
placeholder: '',
|
|
45
|
+
type: 'text',
|
|
46
|
+
},
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
defineEmits<{
|
|
50
|
+
complete: [value: string[]]
|
|
51
|
+
}>()
|
|
52
|
+
</script>
|
|
53
|
+
|
|
54
|
+
<style scoped>
|
|
55
|
+
@layer components {
|
|
56
|
+
.pin-input {
|
|
57
|
+
display: flex;
|
|
58
|
+
gap: var(--pin-input-gap);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.pin-input-field {
|
|
62
|
+
all: unset;
|
|
63
|
+
display: flex;
|
|
64
|
+
align-items: center;
|
|
65
|
+
justify-content: center;
|
|
66
|
+
text-align: center;
|
|
67
|
+
inline-size: var(--pin-input-size);
|
|
68
|
+
block-size: var(--pin-input-size);
|
|
69
|
+
border-radius: var(--pin-input-border-radius);
|
|
70
|
+
box-shadow: var(--border-shadow);
|
|
71
|
+
background: var(--pin-input-background);
|
|
72
|
+
color: var(--pin-input-color);
|
|
73
|
+
font-family: var(--font-family);
|
|
74
|
+
font-size: var(--pin-input-font-size);
|
|
75
|
+
font-weight: var(--pin-input-font-weight);
|
|
76
|
+
text-transform: var(--ui-text-transform);
|
|
77
|
+
letter-spacing: var(--ui-letter-spacing);
|
|
78
|
+
transition:
|
|
79
|
+
box-shadow var(--speed),
|
|
80
|
+
background var(--speed);
|
|
81
|
+
|
|
82
|
+
&:is(:hover, :focus) {
|
|
83
|
+
box-shadow: var(--border-shadow-highlight);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
&:focus {
|
|
87
|
+
background: var(--pin-input-background-focus);
|
|
88
|
+
outline: none;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
&::placeholder {
|
|
92
|
+
color: var(--ui-placeholder-color);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
&[data-disabled] {
|
|
96
|
+
opacity: 0.5;
|
|
97
|
+
cursor: not-allowed;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
&[data-complete] {
|
|
101
|
+
box-shadow: 0 0 0 var(--border-width) var(--pin-input-complete-border-color);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
</style>
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<ProgressRoot
|
|
3
|
+
v-model="model"
|
|
4
|
+
class="progress"
|
|
5
|
+
:max="max"
|
|
6
|
+
>
|
|
7
|
+
<ProgressIndicator
|
|
8
|
+
class="progress-indicator"
|
|
9
|
+
:style="model != null ? { width: `${percentage}%` } : undefined"
|
|
10
|
+
/>
|
|
11
|
+
</ProgressRoot>
|
|
12
|
+
</template>
|
|
13
|
+
|
|
14
|
+
<script setup lang="ts">
|
|
15
|
+
import { computed } from 'vue'
|
|
16
|
+
import { ProgressIndicator, ProgressRoot } from 'reka-ui'
|
|
17
|
+
|
|
18
|
+
const model = defineModel<number | null>()
|
|
19
|
+
|
|
20
|
+
const props = withDefaults(
|
|
21
|
+
defineProps<{
|
|
22
|
+
max?: number
|
|
23
|
+
}>(),
|
|
24
|
+
{
|
|
25
|
+
max: 100,
|
|
26
|
+
},
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
const percentage = computed(() =>
|
|
30
|
+
model.value != null ? (model.value / props.max) * 100 : 0,
|
|
31
|
+
)
|
|
32
|
+
</script>
|
|
33
|
+
|
|
34
|
+
<style scoped>
|
|
35
|
+
@layer components {
|
|
36
|
+
.progress {
|
|
37
|
+
position: relative;
|
|
38
|
+
inline-size: 100%;
|
|
39
|
+
block-size: var(--progress-height);
|
|
40
|
+
background: var(--progress-track-background);
|
|
41
|
+
border-radius: var(--progress-radius);
|
|
42
|
+
overflow: hidden;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.progress-indicator {
|
|
46
|
+
block-size: 100%;
|
|
47
|
+
background: var(--progress-indicator-background);
|
|
48
|
+
border-radius: var(--progress-radius);
|
|
49
|
+
transition: width var(--speed);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
[data-state='indeterminate'] > .progress-indicator {
|
|
53
|
+
inline-size: 30%;
|
|
54
|
+
animation: progress-indeterminate 1.5s ease-in-out infinite;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
@keyframes progress-indeterminate {
|
|
58
|
+
0% {
|
|
59
|
+
translate: -100% 0;
|
|
60
|
+
}
|
|
61
|
+
100% {
|
|
62
|
+
translate: 400% 0;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
</style>
|
|
@@ -28,13 +28,17 @@
|
|
|
28
28
|
<Icon type="close" />
|
|
29
29
|
</ToastClose>
|
|
30
30
|
|
|
31
|
-
<section v-if="toast.description || toast.action">
|
|
31
|
+
<section v-if="toast.description || toast.action || toast.progress">
|
|
32
32
|
<ToastDescription
|
|
33
33
|
v-if="toast.description"
|
|
34
34
|
class="toast-description"
|
|
35
35
|
>
|
|
36
36
|
{{ toast.description }}
|
|
37
37
|
</ToastDescription>
|
|
38
|
+
<Progress
|
|
39
|
+
v-if="toast.progress"
|
|
40
|
+
:model-value="toast.progress === true ? null : toast.progress"
|
|
41
|
+
/>
|
|
38
42
|
<ToastAction
|
|
39
43
|
v-if="toast.action"
|
|
40
44
|
:alt-text="toast.action.label"
|
|
@@ -62,6 +66,7 @@
|
|
|
62
66
|
import Actions from './Actions.vue'
|
|
63
67
|
import Button from './Button.vue'
|
|
64
68
|
import Icon from './Icon.vue'
|
|
69
|
+
import Progress from './Progress.vue'
|
|
65
70
|
import { useToast } from '../composables/toast'
|
|
66
71
|
import {
|
|
67
72
|
ToastAction,
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { ref } from 'vue'
|
|
2
|
+
|
|
3
|
+
export interface ConfirmOptions {
|
|
4
|
+
title: string
|
|
5
|
+
description?: string
|
|
6
|
+
okText?: string
|
|
7
|
+
cancelText?: string
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface ConfirmState extends ConfirmOptions {
|
|
11
|
+
resolve: (value: boolean) => void
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const state = ref<ConfirmState | null>(null)
|
|
15
|
+
|
|
16
|
+
export const useConfirm = () => {
|
|
17
|
+
const confirm = (options: ConfirmOptions) => {
|
|
18
|
+
return new Promise<boolean>((resolve) => {
|
|
19
|
+
state.value = { ...options, resolve }
|
|
20
|
+
})
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const resolve = (value: boolean) => {
|
|
24
|
+
state.value?.resolve(value)
|
|
25
|
+
state.value = null
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return { state, confirm, resolve }
|
|
29
|
+
}
|
package/src/base/icons.ts
CHANGED
|
@@ -6,10 +6,12 @@ export const IconAliasesKey: InjectionKey<IconAliases> = Symbol('IconAliases')
|
|
|
6
6
|
|
|
7
7
|
export const defaultIconAliases: IconAliases = {
|
|
8
8
|
add: 'lucide:plus',
|
|
9
|
+
calendar: 'lucide:calendar',
|
|
9
10
|
check: 'lucide:check',
|
|
10
|
-
|
|
11
|
+
'chevron-left': 'lucide:chevron-left',
|
|
11
12
|
'chevron-down': 'lucide:chevron-down',
|
|
12
13
|
'chevron-right': 'lucide:chevron-right',
|
|
14
|
+
close: 'lucide:x',
|
|
13
15
|
copy: 'lucide:copy',
|
|
14
16
|
edit: 'lucide:pencil',
|
|
15
17
|
help: 'lucide:circle-question-mark',
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<slot
|
|
3
|
+
:src="src"
|
|
4
|
+
:ens="ens"
|
|
5
|
+
:is-current="isCurrent"
|
|
6
|
+
>
|
|
7
|
+
<img
|
|
8
|
+
v-if="src"
|
|
9
|
+
:src="src"
|
|
10
|
+
:alt="ens || 'Avatar'"
|
|
11
|
+
:class="['evm-avatar', { large }]"
|
|
12
|
+
/>
|
|
13
|
+
<Opepicon
|
|
14
|
+
v-else-if="address"
|
|
15
|
+
:seed="address"
|
|
16
|
+
:size="large ? 256 : 64"
|
|
17
|
+
:class="['evm-avatar', { large }]"
|
|
18
|
+
/>
|
|
19
|
+
</slot>
|
|
20
|
+
</template>
|
|
21
|
+
|
|
22
|
+
<script setup lang="ts">
|
|
23
|
+
import { computed } from 'vue'
|
|
24
|
+
import type { Address } from 'viem'
|
|
25
|
+
import { useConnection } from '@wagmi/vue'
|
|
26
|
+
import { useEnsWithAvatar } from '../composables/ens'
|
|
27
|
+
import { useResolveUri } from '../composables/uri'
|
|
28
|
+
import Opepicon from '../../base/components/Opepicon.vue'
|
|
29
|
+
|
|
30
|
+
const props = defineProps<{
|
|
31
|
+
address?: Address
|
|
32
|
+
large?: boolean
|
|
33
|
+
}>()
|
|
34
|
+
const address = computed(() => props.address)
|
|
35
|
+
|
|
36
|
+
const { address: currentAddress } = useConnection()
|
|
37
|
+
|
|
38
|
+
const isCurrent = computed<boolean>(
|
|
39
|
+
() => currentAddress.value?.toLowerCase() === address.value?.toLowerCase(),
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
const { data: ensData } = useEnsWithAvatar(address)
|
|
43
|
+
const resolve = useResolveUri()
|
|
44
|
+
|
|
45
|
+
const ens = computed(() => ensData.value?.ens || null)
|
|
46
|
+
const src = computed(() => resolve(ensData.value?.data?.avatar))
|
|
47
|
+
</script>
|
|
48
|
+
|
|
49
|
+
<style scoped>
|
|
50
|
+
.evm-avatar {
|
|
51
|
+
width: var(--size-5);
|
|
52
|
+
height: var(--size-5);
|
|
53
|
+
border-radius: 50%;
|
|
54
|
+
background-color: var(--background);
|
|
55
|
+
object-fit: cover;
|
|
56
|
+
|
|
57
|
+
&.large {
|
|
58
|
+
width: var(--size-9);
|
|
59
|
+
height: var(--size-9);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
</style>
|
|
@@ -26,17 +26,17 @@
|
|
|
26
26
|
>
|
|
27
27
|
{{ errorMessage }}
|
|
28
28
|
</Alert>
|
|
29
|
-
<EvmWalletConnectQR
|
|
30
|
-
v-if="walletConnectUri"
|
|
31
|
-
:uri="walletConnectUri"
|
|
32
|
-
/>
|
|
33
29
|
<EvmMetaMaskQR
|
|
34
30
|
v-else-if="metaMaskUri"
|
|
35
31
|
:uri="metaMaskUri"
|
|
36
32
|
/>
|
|
33
|
+
<EvmWalletConnectWallets
|
|
34
|
+
v-else-if="walletConnectUri"
|
|
35
|
+
:uri="walletConnectUri"
|
|
36
|
+
/>
|
|
37
37
|
<template v-else-if="isConnecting">
|
|
38
38
|
<Loading
|
|
39
|
-
txt="Waiting for
|
|
39
|
+
:txt="`Waiting for ${connectingWallet} confirmation...`"
|
|
40
40
|
spinner
|
|
41
41
|
stacked
|
|
42
42
|
/>
|
|
@@ -66,6 +66,17 @@
|
|
|
66
66
|
</div>
|
|
67
67
|
<span>{{ connector.name }}</span>
|
|
68
68
|
</Button>
|
|
69
|
+
<Button
|
|
70
|
+
v-if="wcConnector"
|
|
71
|
+
@click="loginWithSafe"
|
|
72
|
+
class="choose-connector"
|
|
73
|
+
>
|
|
74
|
+
<img
|
|
75
|
+
:src="`${base}icons/wallets/safe.png`"
|
|
76
|
+
alt="Safe"
|
|
77
|
+
/>
|
|
78
|
+
<span>Safe</span>
|
|
79
|
+
</Button>
|
|
69
80
|
<Button
|
|
70
81
|
to="https://ethereum.org/wallets/"
|
|
71
82
|
target="_blank"
|
|
@@ -93,22 +104,23 @@ import Icon from '../../base/components/Icon.vue'
|
|
|
93
104
|
import Alert from '../../base/components/Alert.vue'
|
|
94
105
|
import Loading from '../../base/components/Loading.vue'
|
|
95
106
|
import EvmAccount from './EvmAccount.vue'
|
|
96
|
-
import EvmWalletConnectQR from './EvmWalletConnectQR.vue'
|
|
97
107
|
import EvmMetaMaskQR from './EvmMetaMaskQR.vue'
|
|
108
|
+
import EvmWalletConnectWallets from './EvmWalletConnectWallets.vue'
|
|
98
109
|
import { useBaseURL } from '../composables/base'
|
|
99
110
|
|
|
100
111
|
const ICONS: Record<string, string> = {
|
|
101
|
-
'
|
|
112
|
+
'Base Account': 'coinbase.svg',
|
|
102
113
|
MetaMask: 'metamask.svg',
|
|
103
114
|
Phantom: 'phantom.svg',
|
|
104
115
|
'Rabby Wallet': 'rabby.svg',
|
|
105
116
|
Rainbow: 'rainbow.svg',
|
|
117
|
+
Safe: 'safe.png',
|
|
106
118
|
WalletConnect: 'walletconnect.svg',
|
|
107
119
|
}
|
|
108
120
|
|
|
109
121
|
const PRIORITY: Record<string, number> = {
|
|
110
122
|
WalletConnect: 20,
|
|
111
|
-
'
|
|
123
|
+
'Base Account': 10,
|
|
112
124
|
}
|
|
113
125
|
|
|
114
126
|
defineProps<{
|
|
@@ -134,7 +146,9 @@ const shownConnectors = computed(() => {
|
|
|
134
146
|
)
|
|
135
147
|
|
|
136
148
|
const filtered =
|
|
137
|
-
unique.length > 1
|
|
149
|
+
unique.length > 1
|
|
150
|
+
? unique.filter((c) => c.id !== 'injected' && c.id !== 'safe')
|
|
151
|
+
: unique
|
|
138
152
|
|
|
139
153
|
return filtered.sort((a, b) => {
|
|
140
154
|
const priorityA = PRIORITY[a.name] ?? 5
|
|
@@ -143,30 +157,55 @@ const shownConnectors = computed(() => {
|
|
|
143
157
|
})
|
|
144
158
|
})
|
|
145
159
|
|
|
160
|
+
const wcConnector = computed(() =>
|
|
161
|
+
connectors.value.find((c) => c.id === 'walletConnect'),
|
|
162
|
+
)
|
|
163
|
+
|
|
146
164
|
const chooseModalOpen = ref(false)
|
|
147
165
|
const errorMessage = ref('')
|
|
148
166
|
const isConnecting = ref(false)
|
|
149
|
-
const
|
|
167
|
+
const connectingWallet = ref('')
|
|
150
168
|
const metaMaskUri = ref('')
|
|
169
|
+
const walletConnectUri = ref('')
|
|
170
|
+
const safeDeepLink = ref(false)
|
|
171
|
+
|
|
172
|
+
const loginWithSafe = () => {
|
|
173
|
+
if (!wcConnector.value) return
|
|
174
|
+
safeDeepLink.value = true
|
|
175
|
+
login(wcConnector.value)
|
|
176
|
+
}
|
|
151
177
|
|
|
152
178
|
const login = async (connector: Connector) => {
|
|
153
179
|
errorMessage.value = ''
|
|
154
180
|
isConnecting.value = true
|
|
155
|
-
|
|
181
|
+
connectingWallet.value = safeDeepLink.value ? 'Safe' : connector.name
|
|
156
182
|
metaMaskUri.value = ''
|
|
183
|
+
walletConnectUri.value = ''
|
|
157
184
|
|
|
158
|
-
const
|
|
185
|
+
const handleMetaMaskMessage = (event: { type: string; data?: unknown }) => {
|
|
159
186
|
if (event.type === 'display_uri' && typeof event.data === 'string') {
|
|
160
|
-
|
|
187
|
+
metaMaskUri.value = event.data
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const handleWcMessage = (event: { type: string; data?: unknown }) => {
|
|
192
|
+
if (event.type === 'display_uri' && typeof event.data === 'string') {
|
|
193
|
+
if (safeDeepLink.value) {
|
|
194
|
+
window.open(
|
|
195
|
+
`https://app.safe.global/wc?uri=${encodeURIComponent(event.data)}`,
|
|
196
|
+
'_blank',
|
|
197
|
+
'noreferrer',
|
|
198
|
+
)
|
|
199
|
+
} else {
|
|
161
200
|
walletConnectUri.value = event.data
|
|
162
|
-
} else if (connector.id === 'metaMaskSDK') {
|
|
163
|
-
metaMaskUri.value = event.data
|
|
164
201
|
}
|
|
165
202
|
}
|
|
166
203
|
}
|
|
167
204
|
|
|
168
|
-
if (connector.id === '
|
|
169
|
-
connector.emitter.on('message',
|
|
205
|
+
if (connector.id === 'metaMaskSDK') {
|
|
206
|
+
connector.emitter.on('message', handleMetaMaskMessage)
|
|
207
|
+
} else if (connector.id === 'walletConnect') {
|
|
208
|
+
connector.emitter.on('message', handleWcMessage)
|
|
170
209
|
}
|
|
171
210
|
|
|
172
211
|
try {
|
|
@@ -175,28 +214,35 @@ const login = async (connector: Connector) => {
|
|
|
175
214
|
setTimeout(() => {
|
|
176
215
|
chooseModalOpen.value = false
|
|
177
216
|
isConnecting.value = false
|
|
178
|
-
walletConnectUri.value = ''
|
|
179
217
|
metaMaskUri.value = ''
|
|
218
|
+
walletConnectUri.value = ''
|
|
219
|
+
safeDeepLink.value = false
|
|
180
220
|
}, 100)
|
|
181
221
|
} catch (error: unknown) {
|
|
182
222
|
isConnecting.value = false
|
|
183
|
-
walletConnectUri.value = ''
|
|
184
223
|
metaMaskUri.value = ''
|
|
224
|
+
walletConnectUri.value = ''
|
|
225
|
+
safeDeepLink.value = false
|
|
185
226
|
|
|
186
|
-
|
|
187
|
-
if (
|
|
188
|
-
errorMsg.
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
227
|
+
// Only show errors if our dialog is still open
|
|
228
|
+
if (chooseModalOpen.value) {
|
|
229
|
+
const errorMsg = error instanceof Error ? error.message : ''
|
|
230
|
+
if (
|
|
231
|
+
errorMsg.includes('User rejected') ||
|
|
232
|
+
errorMsg.includes('rejected') ||
|
|
233
|
+
errorMsg.includes('denied')
|
|
234
|
+
) {
|
|
235
|
+
errorMessage.value = 'Connection cancelled. Please try again.'
|
|
236
|
+
} else {
|
|
237
|
+
errorMessage.value = 'Failed to connect. Please try again.'
|
|
238
|
+
}
|
|
195
239
|
}
|
|
196
240
|
console.error('Wallet connection error:', error)
|
|
197
241
|
} finally {
|
|
198
|
-
if (connector.id === '
|
|
199
|
-
connector.emitter.off('message',
|
|
242
|
+
if (connector.id === 'metaMaskSDK') {
|
|
243
|
+
connector.emitter.off('message', handleMetaMaskMessage)
|
|
244
|
+
} else if (connector.id === 'walletConnect') {
|
|
245
|
+
connector.emitter.off('message', handleWcMessage)
|
|
200
246
|
}
|
|
201
247
|
}
|
|
202
248
|
}
|
|
@@ -204,15 +250,20 @@ const login = async (connector: Connector) => {
|
|
|
204
250
|
const onModalClosed = () => {
|
|
205
251
|
errorMessage.value = ''
|
|
206
252
|
isConnecting.value = false
|
|
207
|
-
|
|
253
|
+
connectingWallet.value = ''
|
|
208
254
|
metaMaskUri.value = ''
|
|
255
|
+
walletConnectUri.value = ''
|
|
256
|
+
safeDeepLink.value = false
|
|
209
257
|
}
|
|
210
258
|
|
|
211
259
|
const check = () =>
|
|
212
260
|
isConnected.value
|
|
213
261
|
? emit('connected', { address: address.value })
|
|
214
262
|
: emit('disconnected')
|
|
215
|
-
watch(isConnected, () =>
|
|
263
|
+
watch(isConnected, () => {
|
|
264
|
+
check()
|
|
265
|
+
if (!isConnected.value) onModalClosed()
|
|
266
|
+
})
|
|
216
267
|
onMounted(() => check())
|
|
217
268
|
</script>
|
|
218
269
|
|
|
@@ -5,17 +5,15 @@
|
|
|
5
5
|
<div class="qr-frame">
|
|
6
6
|
<canvas ref="qrCanvas"></canvas>
|
|
7
7
|
</div>
|
|
8
|
-
<
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
>
|
|
16
|
-
|
|
17
|
-
</Button>
|
|
18
|
-
</div>
|
|
8
|
+
<Button
|
|
9
|
+
@click="copyUri"
|
|
10
|
+
class="copy-uri tertiary small"
|
|
11
|
+
>
|
|
12
|
+
<Icon :type="isCopied ? 'check' : 'copy'" />
|
|
13
|
+
<span>
|
|
14
|
+
{{ isCopied ? 'Copied' : 'Copy Link' }}
|
|
15
|
+
</span>
|
|
16
|
+
</Button>
|
|
19
17
|
</template>
|
|
20
18
|
|
|
21
19
|
<script setup lang="ts">
|
|
@@ -81,36 +79,8 @@ p {
|
|
|
81
79
|
}
|
|
82
80
|
}
|
|
83
81
|
|
|
84
|
-
.uri
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
gap: var(--spacer-xs);
|
|
88
|
-
background: var(--color-bg-secondary);
|
|
89
|
-
border: var(--border);
|
|
90
|
-
border-radius: var(--border-radius-sm);
|
|
91
|
-
overflow: hidden;
|
|
92
|
-
height: min-content;
|
|
93
|
-
padding: 0;
|
|
94
|
-
|
|
95
|
-
code {
|
|
96
|
-
flex: 1;
|
|
97
|
-
font-size: var(--font-xs);
|
|
98
|
-
font-family: monospace;
|
|
99
|
-
white-space: nowrap;
|
|
100
|
-
overflow: hidden;
|
|
101
|
-
padding: 0 var(--spacer-sm);
|
|
102
|
-
color: var(--muted);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
.copy-button {
|
|
106
|
-
flex-shrink: 0;
|
|
107
|
-
padding: var(--spacer-xs);
|
|
108
|
-
min-width: auto;
|
|
109
|
-
margin: -1px;
|
|
110
|
-
|
|
111
|
-
&.copied {
|
|
112
|
-
color: var(--color-success);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
82
|
+
.copy-uri {
|
|
83
|
+
width: fit-content;
|
|
84
|
+
margin: 0 auto;
|
|
115
85
|
}
|
|
116
86
|
</style>
|