Skip to content
Snippets Groups Projects
Verified Commit 4abab212 authored by Mateusz Tyszczak's avatar Mateusz Tyszczak :scroll:
Browse files

Add request account creation

parent 4fdf51b3
No related branches found
No related tags found
No related merge requests found
Pipeline #118308 passed
......@@ -6,6 +6,7 @@ const props = defineProps<{
afterValue?: string;
context?: number;
disableCopy?: boolean;
class?: string;
}>();
const context = props.context ?? 6;
......@@ -16,8 +17,8 @@ const value = String(props.value);
<template>
<div class="flex items-center">
<span class="font-mono pt-[2px] mr-1">
<span v-if="context > 0">{{ value.slice(0, context) }}...{{ value.slice(-context) }}</span>
<span v-else>{{ value }}</span>
<span v-if="context > 0" :class="props.class">{{ value.slice(0, context) }}...{{ value.slice(-context) }}</span>
<span v-else :class="props.class">{{ value }}</span>
<span class="ml-2" v-if="props.afterValue">{{ props.afterValue }}</span>
</span>
<Button v-if="!props.disableCopy" :value="value"/>
......
<script setup lang="ts">
import { mdiHomeOutline, mdiMessageLockOutline, mdiFileSign, mdiAccountPlusOutline, mdiAccountArrowUpOutline } from "@mdi/js"
import { mdiHomeOutline, mdiMessageLockOutline, mdiFileSign, mdiAccountPlusOutline, mdiAccountArrowUpOutline, mdiAccountReactivateOutline } from "@mdi/js"
import { Sidebar, SidebarContent, SidebarHeader, SidebarFooter, SidebarGroup, SidebarGroupContent, SidebarGroupLabel, SidebarMenu, SidebarMenuButton, SidebarMenuItem } from "@/components/ui/sidebar"
import { useSidebar } from "@/components/ui/sidebar";
import { useRouter } from "vue-router";
import { Button } from "@/components/ui/button";
import { useWalletStore } from "@/stores/wallet.store";
import { computed } from "vue";
const router = useRouter();
const { toggleSidebar, isMobile } = useSidebar();
const walletStore = useWalletStore();
const isDisabledMenuButton = computed(() => !walletStore.hasWallet);
const groups = [{
title: "Account management",
items: [
......@@ -16,15 +23,22 @@ const groups = [{
url: "/",
icon: mdiHomeOutline,
},
{
title: "Request Account Creation",
url: "/account/request",
icon: mdiAccountPlusOutline,
},
{
title: "Process Account Creation",
url: "/account/create",
icon: mdiAccountPlusOutline,
icon: mdiAccountReactivateOutline,
disabled: isDisabledMenuButton,
},
{
title: "Process Authority Update",
url: "/account/update",
icon: mdiAccountArrowUpOutline,
disabled: isDisabledMenuButton,
},
]
}, {
......@@ -42,6 +56,13 @@ const groups = [{
},
]
}];
const navigateTo = (url: string) => {
router.push(url);
if (isMobile.value)
toggleSidebar();
};
</script>
<template>
......@@ -59,10 +80,10 @@ const groups = [{
<SidebarMenu>
<SidebarMenuItem v-for="item in group.items" :key="item.title">
<SidebarMenuButton asChild :class="{ 'bg-primary/5': router.currentRoute.value.path === item.url }">
<RouterLink @click="isMobile && toggleSidebar()" :to="item.url">
<Button variant="ghost" :disabled="item.disabled?.value" @click="navigateTo(item.url)" class="flex justify-start">
<svg width="24" height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path style="fill: hsl(var(--foreground))" :d="item.icon"/></svg>
<span class="text-foreground/80">{{item.title}}</span>
</RouterLink>
</Button>
</SidebarMenuButton>
</SidebarMenuItem>
</SidebarMenu>
......
......@@ -154,8 +154,12 @@ const generateAccountUpdateTransaction = async(): Promise<string | void> => {
toastError("Failed to generate account update transaction", error);
}
};
const hasCopiedCreateSignLink = ref(false);
const getAccountCreateSigningLink = (): string => {
const accountName = createAccountNameOperation.value!.startsWith('@') ? createAccountNameOperation.value!.slice(1) : createAccountNameOperation.value!;
hasCopiedCreateSignLink.value = true;
return `${window.location.protocol}//${window.location.host}/account/create?acc=${accountName}&posting=${
metamaskPublicKeys.value!.find(node => node.role === "posting")!.publicKey
}&active=${
......@@ -166,6 +170,9 @@ const getAccountCreateSigningLink = (): string => {
metamaskPublicKeys.value!.find(node => node.role === "memo")!.publicKey
}`;
};
const hasCopiedUpdateSignLink = ref(false);
const getAuthorityUpdateSigningLink = (): string => {
const accountName = updateAccountNameOperation.value!.startsWith('@') ? updateAccountNameOperation.value!.slice(1) : updateAccountNameOperation.value!;
const url = new URL(`${window.location.protocol}//${window.location.host}/account/update?acc=${accountName}`);
......@@ -173,6 +180,8 @@ const getAuthorityUpdateSigningLink = (): string => {
if (updateAuthType[key as TRole])
url.searchParams.set(key, metamaskPublicKeys.value!.find(node => node.role === key)!.publicKey);
hasCopiedUpdateSignLink.value = true;
return url.toString();
};
......@@ -247,6 +256,9 @@ const updateAccountName = (value: string | any) => {
<Button :disabled="isLoading || !updateAccountNameOperation" :copy="getAuthorityUpdateSigningLink" variant="outline" size="lg" class="mt-4 px-8 py-4 border-[#FF5C16] border-[1px]">
<span class="text-md font-bold">Copy signing link</span>
</Button>
<p v-if="hasCopiedUpdateSignLink" class="mt-4">
Now send this link to someone who has an account to execute this operation in blockchain
</p>
<Separator label="Or" class="mt-8" />
<div class="flex justify-center mt-4">
<Button :disabled="isLoading" :copy="generateAccountUpdateTransaction" variant="outline" size="lg" class="px-8 opacity-[0.9] py-4 border-[#FF5C16] border-[1px]">
......@@ -256,7 +268,7 @@ const updateAccountName = (value: string | any) => {
</div>
</div>
<div v-else-if="showCreateAccountModal">
<p class="mb-4">Step 6: Fill in this form in order to prepare the operation to create an account with requested metadata. Then please copy the signing link, and send it to someone who already has an account to execute this operation in blockchain:</p>
<p class="mb-4">Step 6: Fill in this form in order to prepare the operation to request account creation</p>
<div class="grid mb-2 w-full max-w-sm items-center gap-1.5">
<Label for="metamask_createAuth_account">New account name</Label>
<Input v-model="createAccountNameOperation!" @update:model-value="validateAccountName()" id="metamask_createAuth_account" />
......@@ -273,6 +285,9 @@ const updateAccountName = (value: string | any) => {
<Button :copy="getAccountCreateSigningLink" :disabled="isLoading || !createAccountNameOperation || !accountNameValid" variant="outline" size="lg" class="mt-4 px-8 py-4 border-[#FF5C16] border-[1px]">
<span class="text-md font-bold">Copy signing link</span>
</Button>
<p v-if="hasCopiedCreateSignLink" class="mt-4">
Now send this link to someone who has an account to execute this operation in blockchain
</p>
</div>
</div>
<div v-else-if="accountsMatchingKeys.length === 0">
......
<script setup lang="ts">
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
import { mdiAccountPlusOutline } from '@mdi/js';
import { mdiAccountReactivateOutline } from '@mdi/js';
import { Button } from '@/components/ui/button';
import { Textarea } from '@/components/ui/textarea';
import { Input } from '@/components/ui/input'
......@@ -124,7 +124,7 @@ const createAccount = async() => {
<CardHeader>
<CardTitle class="inline-flex items-center justify-between">
<span>Process Account Creation</span>
<svg width="20" height="20" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path style="fill: hsla(var(--foreground) / 80%)" :d="mdiAccountPlusOutline"/></svg>
<svg width="20" height="20" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path style="fill: hsla(var(--foreground) / 80%)" :d="mdiAccountReactivateOutline"/></svg>
</CardTitle>
<CardDescription class="mr-8">Use this module to process account creation request sent by other users</CardDescription>
</CardHeader>
......
<script setup lang="ts">
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { mdiAccountPlusOutline } from '@mdi/js';
import { computed, ref } from 'vue';
import { toastError } from '@/utils/parse-error';
import { getWax } from '@/stores/wax.store';
import { useWalletStore } from '@/stores/wallet.store';
import { useMetamaskStore } from '@/stores/metamask.store';
import type { TRole } from '@hiveio/wax/vite';
import { toast } from 'vue-sonner';
const walletStore = useWalletStore();
const metamaskStore = useMetamaskStore();
const publicKeys = ref<Record<TRole, string>>({
owner: "",
active: "",
posting: "",
memo: ""
});
const accountNameValid = ref(false);
const createAccountNameOperation = ref('');
const isLoading = ref(false);
const hasCopiedCreateSignLink = ref(false);
const validateAccountName = async() => {
try {
if(!createAccountNameOperation.value)
return accountNameValid.value = false;
const accountName = createAccountNameOperation.value.startsWith("@") ? createAccountNameOperation.value.slice(1) : createAccountNameOperation.value;
if (!accountName)
return accountNameValid.value = false;
const wax = await getWax();
return accountNameValid.value = wax.isValidAccountName(accountName);
} catch (error) {
toastError("Failed to validate account name", error);
}
}
const parseMetamaskPublicKeys = async() => {
const toastToDismiss = toast.loading("Metamask detected. Parsing public keys...");
try {
isLoading.value = true;
try {
await metamaskStore.connect();
} catch {
toast.error("Metamask is not installed or not connected");
return;
}
const { publicKeys: metamaskPublicKeys } = await metamaskStore.call("hive_getPublicKeys", {
keys: [{
role: "owner"
},{
role: "active"
},{
role: "posting"
},{
role: "memo"
}]
}) as any;
for(const publicKey of metamaskPublicKeys)
publicKeys.value[publicKey.role as TRole] = publicKey.publicKey;
toast.success("Successfully parsed Metamask public keys");
} catch (error) {
toastError("Failed to parse Metamask public keys", error);
throw error; // Make sure this method throws to handle sonner toast properly
} finally {
isLoading.value = false;
toast.dismiss(toastToDismiss);
}
};
const hasMetamaskWithSnap = computed(() => walletStore.walletsStatus.metamask && metamaskStore.isInstalled);
if(hasMetamaskWithSnap)
void parseMetamaskPublicKeys();
const getAccountCreateSigningLink = (): string => {
const accountName = createAccountNameOperation.value!.startsWith('@') ? createAccountNameOperation.value!.slice(1) : createAccountNameOperation.value!;
hasCopiedCreateSignLink.value = true;
return `${window.location.protocol}//${window.location.host}/account/create?acc=${accountName}&${Object.values(publicKeys.value).map((key, index) => `key${index + 1}=${key}`).join('&')}`;
};
</script>
<template>
<Card class="w-full max-w-[600px]">
<CardHeader>
<CardTitle class="inline-flex items-center justify-between">
<span>Request account creation</span>
<svg width="20" height="20" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path style="fill: hsla(var(--foreground) / 80%)" :d="mdiAccountPlusOutline"/></svg>
</CardTitle>
<CardDescription class="mr-8">Fill in this form in order to prepare the operation to request account creation</CardDescription>
</CardHeader>
<CardContent>
<div class="space-y-4" v-if="hasMetamaskWithSnap">
<div class="grid mb-2 w-full items-center gap-1.5">
<Label for="metamask_createAuth_account_card">New account name</Label>
<Input class="w-full" v-model="createAccountNameOperation!" @update:model-value="validateAccountName()" id="metamask_createAuth_account_card" />
<span class="text-red-400" v-if="createAccountNameOperation && !accountNameValid">Invalid account name</span>
</div>
<div v-for="(_key, role) in publicKeys" :key="role" class="grid mb-2 w-full items-center gap-1.5">
<Label :for="`metamask_createAuth_account_key_${role}_card`">{{ role[0].toUpperCase() }}{{ role.slice(1) }} key</Label>
<Input class="w-full" v-model="publicKeys[role]" :id="`metamask_createAuth_account_key_${role}_card`" />
</div>
<Button :copy="getAccountCreateSigningLink" :disabled="isLoading || !createAccountNameOperation || !accountNameValid">
<span class="text-md font-bold">Copy signing link</span>
</Button>
<p v-if="hasCopiedCreateSignLink">
Now send this link to someone who has an account to execute this operation in blockchain
</p>
</div>
<div v-else class="space-y-4">
<div class="bg-orange-100 border-l-4 border-orange-500 text-orange-700 p-4" role="alert">
<p class="font-bold">Wallet required</p>
<p>You have to connect to Metamask wallet before continuing!</p>
</div>
<Button @click="walletStore.openWalletSelectModal()" class="w-full font-bold">
Connect to Metamask wallet
</Button>
</div>
</CardContent>
</Card>
</template>
\ No newline at end of file
<script setup lang="ts">
import RequestAccountCreate from '@/components/utilcards/RequestAccountCreate.vue';
</script>
<template>
<div class="flex p-8">
<RequestAccountCreate />
</div>
</template>
......@@ -2,6 +2,7 @@ import Index from "@/pages/index.vue";
import SignTransaction from "@/pages/sign/transaction.vue";
import SignMessage from "@/pages/sign/message.vue";
import AccountCreate from "@/pages/account/create.vue";
import RequestCreate from "@/pages/account/request.vue";
import AccountUpdate from "@/pages/account/update.vue";
export const routes = [
......@@ -9,5 +10,6 @@ export const routes = [
{ path: '/sign/transaction', component: SignTransaction },
{ path: '/sign/message', component: SignMessage },
{ path: '/account/create', component: AccountCreate },
{ path: '/account/request', component: RequestCreate },
{ path: '/account/update', component: AccountUpdate }
];
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment