@@ -4,6 +4,8 @@ import { computed, onBeforeUnmount, onMounted, ref } from 'vue'
44import HeroCanvas from ' ./components/HeroCanvas.vue'
55import SurfaceActStream from ' ./components/SurfaceActStream.vue'
66import { useSurfaceActs } from ' ./composables/useSurfaceActs'
7+ import { useWalletConnection } from ' ./composables/useWalletConnection'
8+ import type { WalletProviderId } from ' ./domain/wallet-connection'
79import { networks , type Network } from ' ./networks'
810import { surfaceLaws } from ' ./surfaceLaws'
911
@@ -20,6 +22,8 @@ const activeLawId = ref(defaultLaw.id)
2022const activeActorIndex = ref (0 )
2123const selectedNetworkKey = ref <Network [' key' ]>(' testnet' )
2224const networkMenuOpen = ref (false )
25+ const walletMenuOpen = ref (false )
26+ const connectingProvider = ref <WalletProviderId | null >(null )
2327const surfaceActionsEl = ref <HTMLElement | null >(null )
2428const { acts, loading, error, polling } = useSurfaceActs ()
2529
@@ -38,6 +42,22 @@ const activeActorLine = computed(() => actorLines[activeActorIndex.value] ?? act
3842const selectedNetwork = computed (
3943 () => networks .find ((network ) => network .key === selectedNetworkKey .value ) ?? networks [0 ]! ,
4044)
45+ const {
46+ status : walletConnectionStatus,
47+ connection : walletConnection,
48+ errorMessage : walletErrorMessage,
49+ availableProviders : availableWalletProviders,
50+ connect : connectWalletClient,
51+ disconnect : disconnectWalletClient,
52+ } = useWalletConnection (selectedNetwork )
53+ const walletTriggerLabel = computed (() => {
54+ if (walletConnectionStatus .value === ' connecting' ) {
55+ return ' Connecting…'
56+ }
57+
58+ const address = walletConnection .value ?.address
59+ return address ? ` ${address .slice (0 , 10 )}…${address .slice (- 6 )} ` : ' Connect identity'
60+ })
4161
4262function updateReducedMotion(event ? : MediaQueryListEvent ) {
4363 prefersReducedMotion .value = event ?.matches ?? motionQuery ?.matches ?? false
@@ -55,10 +75,32 @@ function selectNetwork(networkKey: Network['key']) {
5575
5676function toggleNetworkMenu() {
5777 networkMenuOpen .value = ! networkMenuOpen .value
78+ walletMenuOpen .value = false
79+ }
80+
81+ function toggleWalletMenu() {
82+ walletMenuOpen .value = ! walletMenuOpen .value
83+ networkMenuOpen .value = false
5884}
5985
60- function closeNetworkMenu () {
86+ function closeMenus () {
6187 networkMenuOpen .value = false
88+ walletMenuOpen .value = false
89+ }
90+
91+ async function connectWallet(provider : WalletProviderId ) {
92+ connectingProvider .value = provider
93+ await connectWalletClient (provider )
94+ connectingProvider .value = null
95+ }
96+
97+ function disconnectWallet() {
98+ disconnectWalletClient ()
99+ closeMenus ()
100+ }
101+
102+ function walletProviderName(provider : WalletProviderId ) {
103+ return provider === ' keplr' ? ' Keplr' : ' Leap'
62104}
63105
64106function rotateActorLine() {
@@ -97,13 +139,18 @@ onMounted(() => {
97139 documentClickHandler = (event ) => {
98140 const target = event .target as Node | null
99141 const root = surfaceActionsEl .value
100- if (networkMenuOpen .value && root && target && ! root .contains (target )) {
101- closeNetworkMenu ()
142+ if (
143+ (networkMenuOpen .value || walletMenuOpen .value ) &&
144+ root &&
145+ target &&
146+ ! root .contains (target )
147+ ) {
148+ closeMenus ()
102149 }
103150 }
104151 documentKeydownHandler = (event ) => {
105152 if (event .key === ' Escape' ) {
106- closeNetworkMenu ()
153+ closeMenus ()
107154 }
108155 }
109156 document .addEventListener (' click' , documentClickHandler )
@@ -139,7 +186,84 @@ onBeforeUnmount(() => {
139186 <nav class =" surface-bar" aria-label =" Surface heading" >
140187 <p class =" surface-mark" >AXONE / SURFACE</p >
141188 <div ref =" surfaceActionsEl" class =" surface-actions" aria-label =" Surface actions" >
142- <button class =" top-connect" type =" button" >Connect identity</button >
189+ <button
190+ class =" top-connect"
191+ :class =" { 'is-pending': walletConnectionStatus === 'connecting' }"
192+ type =" button"
193+ aria-haspopup =" menu"
194+ :aria-expanded =" walletMenuOpen"
195+ aria-controls =" wallet-menu"
196+ :disabled =" walletConnectionStatus === 'connecting'"
197+ @click =" toggleWalletMenu"
198+ >
199+ {{ walletTriggerLabel }}
200+ </button >
201+ <div
202+ v-if =" walletMenuOpen"
203+ id =" wallet-menu"
204+ class =" network-menu wallet-menu"
205+ role =" menu"
206+ aria-label =" Wallet connection"
207+ >
208+ <p
209+ v-if =" walletConnectionStatus === 'connecting'"
210+ class =" wallet-menu-status"
211+ role =" status"
212+ aria-live =" polite"
213+ >
214+ Connecting to
215+ {{ connectingProvider ? walletProviderName(connectingProvider) : 'wallet' }}…
216+ </p >
217+ <template v-else-if =" walletConnection " >
218+ <p class =" wallet-menu-metadata" >
219+ {{ walletProviderName(walletConnection.provider) }} ·
220+ {{ selectedNetwork.displayName }}
221+ </p >
222+ <button
223+ class =" network-option wallet-option"
224+ type =" button"
225+ role =" menuitem"
226+ @click =" disconnectWallet"
227+ >
228+ Disconnect identity
229+ </button >
230+ </template >
231+ <template v-else >
232+ <button
233+ class =" network-option wallet-option"
234+ :class =" { 'is-disabled': !availableWalletProviders.includes('keplr') }"
235+ type =" button"
236+ role =" menuitem"
237+ :aria-disabled =" !availableWalletProviders.includes('keplr')"
238+ :disabled =" !availableWalletProviders.includes('keplr')"
239+ @click =" connectWallet('keplr')"
240+ >
241+ {{ availableWalletProviders.includes('keplr') ? 'Keplr' : 'Keplr unavailable' }}
242+ </button >
243+ <button
244+ class =" network-option wallet-option"
245+ :class =" { 'is-disabled': !availableWalletProviders.includes('leap') }"
246+ type =" button"
247+ role =" menuitem"
248+ :aria-disabled =" !availableWalletProviders.includes('leap')"
249+ :disabled =" !availableWalletProviders.includes('leap')"
250+ @click =" connectWallet('leap')"
251+ >
252+ {{ availableWalletProviders.includes('leap') ? 'Leap' : 'Leap unavailable' }}
253+ </button >
254+ <p
255+ v-if =" availableWalletProviders.length === 0"
256+ class =" wallet-menu-status"
257+ role =" status"
258+ aria-live =" polite"
259+ >
260+ Install Keplr or Leap to connect an Axone identity.
261+ </p >
262+ </template >
263+ <p v-if =" walletErrorMessage" class =" wallet-menu-error" role =" alert" >
264+ {{ walletErrorMessage }}
265+ </p >
266+ </div >
143267 <span class =" surface-actions-divider" aria-hidden =" true" >|</span >
144268 <button
145269 class =" network-trigger"
0 commit comments