Skip to content

Commit

Permalink
Add wallet connect auth (#17)
Browse files Browse the repository at this point in the history
Co-authored-by: zlayine <[email protected]>
Co-authored-by: zlayine <[email protected]>
  • Loading branch information
3 people authored Aug 7, 2023
1 parent 5dff93f commit f75ced5
Show file tree
Hide file tree
Showing 17 changed files with 11,312 additions and 15,574 deletions.
26,310 changes: 10,750 additions & 15,560 deletions package-lock.json

Large diffs are not rendered by default.

11 changes: 10 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,24 +27,33 @@
"sass": "^1.63.6",
"tailwindcss": "^3.3.2",
"ts-loader": "^9.4.3",
"typescript": "^4.9.5",
"typescript": "^5.0.4",
"vite": "^4.1.5"
},
"dependencies": {
"@headlessui/vue": "^1.7.14",
"@heroicons/vue": "^2.0.18",
"@polkadot/api": "^10.9.1",
"@polkadot/keyring": "^12.3.2",
"@polkadot/types": "^10.9.1",
"@polkadot/ui-shared": "^3.5.1",
"@polkadot/util": "^12.3.2",
"@polkadot/util-crypto": "^10.4.2",
"@tailwindcss/forms": "^0.5.3",
"@talismn/connect-wallets": "^1.2.3",
"@vitejs/plugin-vue": "^4.2.3",
"@vue/eslint-config-typescript": "^11.0.3",
"@vuepic/vue-datepicker": "^5.3.0",
"@vueuse/core": "^10.2.0",
"@walletconnect/modal-sign-html": "^2.6.0",
"@web3modal/html": "^2.7.0",
"eslint-plugin-import": "^2.27.5",
"eventemitter3": "^5.0.0",
"pinia": "^2.1.4",
"pinia-plugin-persistedstate": "^3.1.0",
"prettier": "^2.8.4",
"qrcode.vue": "^3.3.4",
"scale-ts": "^1.3.0",
"vee-validate": "^4.7.4",
"vite-plugin-dynamic-base": "^0.4.9",
"vite-plugin-dynamic-import": "^1.2.7",
Expand Down
2 changes: 2 additions & 0 deletions resources/js/api/mutations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import SetTokenAttribute from '~/graphql/mutation/token/SetTokenAttribute';
import RemoveAllAttributes from '~/graphql/mutation/RemoveAllAttributes';
import Freeze from '~/graphql/mutation/Freeze';
import Thaw from '~/graphql/mutation/Thaw';
import UpdateTransaction from '~/graphql/mutation/UpdateTransaction';

import BatchMint from '~/graphql/mutation/batch/BatchMint';
import BatchTransfer from '~/graphql/mutation/batch/BatchTransfer';
Expand Down Expand Up @@ -83,6 +84,7 @@ export default {
RemoveAllAttributes,
Freeze,
Thaw,
UpdateTransaction,

BatchMint,
BatchTransfer,
Expand Down
16 changes: 16 additions & 0 deletions resources/js/api/transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,20 @@ export class TransactionApi {

return ApiService.sendPlatfromRequest(data);
}

static async updateTransaction(updateTransactionData: Record<string, unknown>) {
const data = {
query: mutations.UpdateTransaction,
variables: {
id: updateTransactionData.id,
state: updateTransactionData.state,
transactionId: updateTransactionData.transactionId,
transactionHash: updateTransactionData.transactionHash,
signingAccount: updateTransactionData.signingAccount,
signedAtBlock: updateTransactionData.signedAtBlock,
},
};

return ApiService.sendPlatfromRequest(data);
}
}
30 changes: 30 additions & 0 deletions resources/js/components/Identicon.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<template>
<div class="relative">
<svg :height="44" viewBox="0 0 64 64" :width="44">
<circle
v-for="(circle, index) in circles"
:key="index"
:cx="circle.cx"
:cy="circle.cy"
:fill="circle.fill"
:r="circle.r"
/>
</svg>
</div>
</template>

<script setup>
import { polkadotIcon } from '@polkadot/ui-shared';
import { ref, h } from 'vue';
const props = defineProps({
address: {
type: String,
required: true,
},
});
const circles = polkadotIcon(props.address, {
isAlternative: false,
}).map(({ cx, cy, fill, r }) => ({ cx, cy, fill, r }));
</script>
73 changes: 73 additions & 0 deletions resources/js/components/SignTransaction.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<template>
<Btn primary @click="signTransaction" class="px-8" :disabled="isLoading">
<LoadingCircle v-if="isLoading" class="h-5 w-5 mx-1 ml-0.5 text-white" />
<span v-else> Sign </span>
</Btn>
<Modal :is-open="showAccountsModal" :close="closeModal" width="max-w-lg">
<DialogTitle as="h3" class="text-lg font-medium leading-6 text-gray-900 text-center">
Select an account to sign
</DialogTitle>
<div class="flex flex-col space-y-2 mt-4">
<div
v-for="account in useAppStore().accounts"
:key="account.address"
class="px-4 py-3 border border-gray-300 rounded-md cursor-pointer hover:bg-primary/20 transition-all flex items-center space-x-4"
@click="selectAccount(account)"
>
<Identicon :address="account.address" />
<div class="flex flex-col">
<span class="font-medium">{{ account.name }} </span>
<span class="text-sm">
{{ addressShortHex(account.address) }}
</span>
</div>
</div>
</div>
</Modal>
</template>

<script setup lang="ts">
import { DialogTitle } from '@headlessui/vue';
import Btn from './Btn.vue';
import Modal from './Modal.vue';
import { addressShortHex } from '~/util/address';
import { useAppStore } from '~/store';
import { ref } from 'vue';
import LoadingCircle from './LoadingCircle.vue';
import snackbar from '~/util/snackbar';
import Identicon from './Identicon.vue';
const props = defineProps<{
transaction: any;
}>();
const emit = defineEmits(['success']);
const isLoading = ref(false);
const showAccountsModal = ref(false);
const signTransaction = async () => {
useAppStore().getAccounts();
showAccountsModal.value = true;
};
const closeModal = () => {
showAccountsModal.value = false;
};
const selectAccount = async (account) => {
try {
isLoading.value = true;
useAppStore().setAccount(account);
showAccountsModal.value = false;
const res = await useAppStore().signTransaction(props.transaction);
if (res) {
emit('success');
}
} catch (e) {
snackbar.error({ title: 'Failed to sign transaction' });
} finally {
isLoading.value = false;
}
};
</script>
9 changes: 7 additions & 2 deletions resources/js/components/Slideover.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,12 @@
>
<DialogPanel class="pointer-events-auto w-screen max-w-xs md:max-w-lg transition-all">
<div ref="initialFocusRef"></div>
<component :is="component" @close="emit('close')" :item="item" />
<component
:is="component"
:item="item"
@close="emit('close')"
@update="($event) => emit('update', $event)"
/>
</DialogPanel>
</TransitionChild>
</div>
Expand All @@ -30,7 +35,7 @@
import { Dialog, DialogPanel, TransitionChild, TransitionRoot } from '@headlessui/vue';
import { defineAsyncComponent, shallowRef, watch } from 'vue';
const emit = defineEmits(['close']);
const emit = defineEmits(['close', 'update']);
const component = shallowRef();
const initialFocusRef = shallowRef();
Expand Down
2 changes: 2 additions & 0 deletions resources/js/components/UserNavbar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
</div>
</div>
<div class="flex items-center space-x-4" v-if="appStore.loggedIn">
<WalletConnectButton />
<InformationCircleIcon class="h-6 w-6 text-gray-400 cursor-pointer" @click="openHelp" />
<NotificationsList />
<ProfileMenu />
Expand Down Expand Up @@ -52,6 +53,7 @@ import ProfileMenu from '~/components/ProfileMenu.vue';
import DisclosureMenu from '~/components/DisclosureMenu.vue';
import NotificationsList from '~/components/NotificationsList.vue';
import Handbook from '~/components/Handbook.vue';
import WalletConnectButton from '~/components/WalletConnectButton.vue';
const open = ref(false);
const help = ref(false);
Expand Down
108 changes: 108 additions & 0 deletions resources/js/components/WalletConnectButton.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
<template>
<Menu as="div" class="relative">
<div>
<MenuButton
class="flex items-center space-x-2 rounded-md bg-primary py-2 px-3 text-sm font-semibold shadow-sm focus:outline-none focus:ring-2 focus:ring-primary-light focus:ring-offset-2 text-white transition-all"
>
<WalletIcon class="h-6 w-6" />
<LoadingCircle v-if="loading" class="!text-white" />
<span v-else>{{ walletSession ? 'Wallet connected' : 'Connect wallet' }}</span>
</MenuButton>
</div>
<ScaleTransition>
<MenuItems
class="absolute right-0 z-10 mt-2 w-44 origin-top-right divide-y divide-gray-100 rounded-md bg-white shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none pt-1"
>
<template v-if="!walletSession">
<MenuItem v-slot="{ active }">
<button
:class="[
active ? 'bg-gray-100 text-gray-900' : 'text-gray-700',
'block px-4 py-2 text-sm w-full text-center transition-all',
]"
@click="connectWallet('wc')"
>
WalletConnect
</button>
</MenuItem>
<MenuItem v-slot="{ active }">
<button
:class="[
active ? 'bg-gray-100 text-gray-900' : 'text-gray-700',
'block px-4 py-2 text-sm w-full text-center transition-all',
]"
@click="connectWallet('polkadot.js')"
>
Polkadot.JS
</button>
</MenuItem>
</template>
<template v-else>
<MenuItem v-slot="{ active }">
<button
:class="[
active ? 'bg-gray-100 text-gray-900' : 'text-gray-700',
'block px-4 py-2 text-sm w-full text-center transition-all',
]"
@click="disconnectWallet"
>
Disconnect
</button>
</MenuItem>
</template>
</MenuItems>
</ScaleTransition>
</Menu>
</template>

<script setup lang="ts">
import { WalletIcon } from '@heroicons/vue/24/outline';
import { useAppStore } from '~/store';
import LoadingCircle from './LoadingCircle.vue';
import { computed, ref } from 'vue';
import { Menu, MenuButton, MenuItem, MenuItems } from '@headlessui/vue';
import ScaleTransition from './ScaleTransition.vue';
import snackbar from '~/util/snackbar';
const appStore = useAppStore();
const loading = ref(true);
const showAccountsModal = ref(false);
const walletSession = computed(() => appStore.wallet);
const connectWallet = async (provider: string) => {
try {
loading.value = true;
await appStore.connectWallet(provider);
if (appStore.accounts) {
showAccountsModal.value = true;
}
} catch {
snackbar.error({ title: 'Failed to connect wallet' });
} finally {
loading.value = false;
}
};
const getSession = async () => {
try {
loading.value = true;
await appStore.getSession();
} catch (e) {
snackbar.error({ title: 'Failed to get session' });
} finally {
loading.value = false;
}
};
const disconnectWallet = async () => {
loading.value = true;
await appStore.disconnectWallet();
loading.value = false;
};
(async () => {
getSession();
})();
</script>
2 changes: 1 addition & 1 deletion resources/js/components/pages/Setup.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<div class="bg-white px-4 py-8 shadow sm:rounded-lg sm:px-10">
<Form ref="formRef" class="space-y-6" :validation-schema="validation" @submit="setupAccount">
<FormInput
v-model="url.origin"
v-model="url"
label="Enjin Platform URL"
name="url"
input-class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-primary sm:text-sm sm:leading-6"
Expand Down
Loading

0 comments on commit f75ced5

Please sign in to comment.