Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add wallet connect auth #17

Merged
merged 22 commits into from
Aug 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading