diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index db8b55bafa0df1c7958c9248e067737a825f8cfd..f7e8164c090e6488b6c248f96d2699821d8174dc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -30,6 +30,9 @@ importers: site: dependencies: + '@hiveio/wax': + specifier: 1.27.6-rc7-250304235913 + version: 1.27.6-rc7-250304235913 '@mdi/js': specifier: ^7.4.47 version: 7.4.47 diff --git a/site/package.json b/site/package.json index b171a8c4ccb3e6fba92f12d29c20957c8388494a..64b2a3d6117c051fe9fc21426d414cab0035617d 100644 --- a/site/package.json +++ b/site/package.json @@ -9,6 +9,7 @@ "preview": "vite preview" }, "dependencies": { + "@hiveio/wax": "1.27.6-rc7-250304235913", "@mdi/js": "^7.4.47", "@metamask/providers": "^16.0.0", "@vueuse/core": "^12.8.2", diff --git a/site/src/App.vue b/site/src/App.vue index 04cbf9c77aaaee5af63b88e4c4b4484c92595635..c4e788b44f1daee511b53a1979ba21e556743369 100644 --- a/site/src/App.vue +++ b/site/src/App.vue @@ -49,6 +49,6 @@ const complete = (data: { account: string; wallet: UsedWallet }) => { height: 100vh; width: 100%; overflow: hidden; - font-family: Arial, sans-serif; + font-family: Verdana, sans-serif; } </style> diff --git a/site/src/components/AppHeader.vue b/site/src/components/AppHeader.vue index 6c8d1e2a3cd16153b9b5395b80e5d88fd9411c22..5a19c7f7d338eed67d4996ec85778729befe56dc 100644 --- a/site/src/components/AppHeader.vue +++ b/site/src/components/AppHeader.vue @@ -23,7 +23,7 @@ const logout = () => { </div> <div class="flex items-center space-x-4"> <img src="/icon.svg" class="h-8 w-8" /> - <h1 class="hidden sm:inline text-2xl font-bold">Hive Bridge</h1> + <h1 class="hidden sm:inline text-2xl font-bold tracking-widest">Hive Bridge</h1> <Button variant="outline" class="absolute top-[14px] left-[45px] inline-flex sm:hidden" v-if="settingsStore.isLoaded && hasUser"> <h1 v-if="hasUser" class="inline sm:hidden text-lg sm:text-xl font-bold">@{{ settingsStore.settings.account }}</h1> </Button> diff --git a/site/src/components/hive/PublicKey.vue b/site/src/components/hive/PublicKey.vue new file mode 100644 index 0000000000000000000000000000000000000000..71321bd733598016ca22ab95db67446e4b029873 --- /dev/null +++ b/site/src/components/hive/PublicKey.vue @@ -0,0 +1,23 @@ +<script setup lang="ts"> +import { Button } from '@/components/ui/copybutton'; + +const props = defineProps<{ + value: string; + afterValue?: string; + context?: number; + disableCopy?: boolean; +}>(); + +const context = props.context ?? 6; +</script> + +<template> + <div class="flex items-center"> + <span class="font-mono pt-[2px] mr-1"> + <span v-if="context > 0">{{ props.value.slice(0, context) }}...{{ props.value.slice(-context) }}</span> + <span v-else>{{ props.value }}</span> + <span class="ml-2" v-if="props.afterValue">{{ props.afterValue }}</span> + </span> + <Button v-if="!props.disableCopy" :value="props.value"/> + </div> +</template> diff --git a/site/src/components/onboarding/wallets/metamask/MetamaskConnect.vue b/site/src/components/onboarding/wallets/metamask/MetamaskConnect.vue index 4cd035183e9a35a4b3d60f2f39b0568a829b9e25..9fe0b3807efd6e1b7ec532555ee5d862d549ab8d 100644 --- a/site/src/components/onboarding/wallets/metamask/MetamaskConnect.vue +++ b/site/src/components/onboarding/wallets/metamask/MetamaskConnect.vue @@ -4,12 +4,13 @@ import { Button } from "@/components/ui/button"; import { ref, onMounted } from 'vue'; import step1 from "@/assets/icons/wallets/metamask/step1.webp"; import step2 from "@/assets/icons/wallets/metamask/step2.webp"; -import { mdiClose, mdiContentCopy, mdiCheck } from '@mdi/js'; +import { mdiClose } from '@mdi/js'; import { UsedWallet, getWalletIcon } from '@/stores/settings.store'; import { useMetamaskStore } from "@/stores/metamask.store"; import { Combobox, ComboboxAnchor, ComboboxTrigger, ComboboxEmpty, ComboboxGroup, ComboboxInput, ComboboxItem, ComboboxItemIndicator, ComboboxList } from '@/components/ui/combobox'; import { Check, Search } from 'lucide-vue-next'; import { Separator } from '@/components/ui/separator'; +import PublicKey from '@/components/hive/PublicKey.vue'; const emit = defineEmits(["setaccount", "close"]); @@ -125,19 +126,6 @@ const updateAccountName = (value: string | any) => { if (!value) return; accountName.value = value.startsWith("@") ? value.slice(1) : value; }; - -const copyBtn = (event: MouseEvent) => { - const target = event.target as HTMLElement; - const value = target.getAttribute("data-copy"); - if (!value) return; - navigator.clipboard.writeText(value); - - const oldAttribute = target.children[0].children[0].getAttribute('d'); - target.children[0].children[0].setAttribute('d', mdiCheck); - setTimeout(() => { - target.children[0].children[0].setAttribute('d', oldAttribute!); - }, 1000); -}; </script> <template> @@ -191,12 +179,7 @@ const copyBtn = (event: MouseEvent) => { <div class="flex items-center p-1"> <span class="font-bold">{{ key.role[0].toUpperCase() }}{{ key.role.slice(1) }}</span> <div class="mx-2 border flex-grow border-[hsl(var(--foreground))] opacity-[0.1]" /> - <div class="flex items-center"> - <span class="font-mono pt-[2px] mr-1">{{ key.publicKey.slice(0, 6) }}...{{ key.publicKey.slice(-6) }}</span> - <Button @click="copyBtn" :data-copy="key.publicKey" variant="ghost" size="sm" class="px-2"> - <svg width="20" height="20" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path style="fill: hsl(var(--foreground))" :d="mdiContentCopy"/></svg> - </Button> - </div> + <PublicKey :value="key.publicKey"/> </div> </div> <div class="flex justify-center mt-3"> diff --git a/site/src/components/ui/copybutton/Button.vue b/site/src/components/ui/copybutton/Button.vue new file mode 100644 index 0000000000000000000000000000000000000000..489345486b8e94ed41b8339561bfae79b88373ba --- /dev/null +++ b/site/src/components/ui/copybutton/Button.vue @@ -0,0 +1,41 @@ +<script setup lang="ts"> +import type { HTMLAttributes } from 'vue' +import { cn } from '@/lib/utils' +import { Primitive, type PrimitiveProps } from 'reka-ui' +import { buttonVariants } from '.' +import { mdiCheck, mdiContentCopy } from '@mdi/js' + +interface Props extends PrimitiveProps { + class?: HTMLAttributes['class']; + value?: string; +} + +const props = withDefaults(defineProps<Props>(), { + as: 'button', +}); + +const copyBtn = (event: MouseEvent) => { + const target = event.target as HTMLElement; + const value = target.getAttribute("data-copy"); + if (!value) return; + navigator.clipboard.writeText(value); + + const oldAttribute = target.children[0].children[0].getAttribute('d'); + target.children[0].children[0].setAttribute('d', mdiCheck); + setTimeout(() => { + target.children[0].children[0].setAttribute('d', oldAttribute!); + }, 1000); +}; +</script> + +<template> + <Primitive + :as="as" + :as-child="asChild" + :class="cn(buttonVariants(), props.class, 'px-2')" + @click="copyBtn" + :data-copy="props.value" + > + <svg width="20" height="20" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path style="fill: hsl(var(--foreground))" :d="mdiContentCopy"/></svg> + </Primitive> +</template> diff --git a/site/src/components/ui/copybutton/index.ts b/site/src/components/ui/copybutton/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..a775fe19ba12dbd0a845fea9b4f0d447504a6246 --- /dev/null +++ b/site/src/components/ui/copybutton/index.ts @@ -0,0 +1,35 @@ +import { cva, type VariantProps } from 'class-variance-authority' + +export { default as Button } from './Button.vue' + +export const buttonVariants = cva( + 'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0', + { + variants: { + variant: { + default: 'bg-primary text-primary-foreground shadow hover:bg-primary/90', + destructive: + 'bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90', + outline: + 'border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground', + secondary: + 'bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80', + ghost: 'hover:bg-accent hover:text-accent-foreground', + link: 'text-primary underline-offset-4 hover:underline', + }, + size: { + default: 'h-9 px-4 py-2', + xs: 'h-7 rounded px-2', + sm: 'h-8 rounded-md px-3 text-xs', + lg: 'h-10 rounded-md px-8', + icon: 'h-9 w-9', + }, + }, + defaultVariants: { + variant: 'ghost', + size: 'sm', + }, + }, +) + +export type ButtonVariants = VariantProps<typeof buttonVariants> diff --git a/site/src/components/ui/switch/Switch.vue b/site/src/components/ui/switch/Switch.vue new file mode 100644 index 0000000000000000000000000000000000000000..c672a6e960f7c708dd125071ec659dc9c6ab837f --- /dev/null +++ b/site/src/components/ui/switch/Switch.vue @@ -0,0 +1,39 @@ +<script setup lang="ts"> +import { cn } from '@/lib/utils' +import { + SwitchRoot, + type SwitchRootEmits, + type SwitchRootProps, + SwitchThumb, + useForwardPropsEmits, +} from 'reka-ui' +import { computed, type HTMLAttributes } from 'vue' + +const props = defineProps<SwitchRootProps & { class?: HTMLAttributes['class'] }>() + +const emits = defineEmits<SwitchRootEmits>() + +const delegatedProps = computed(() => { + const { class: _, ...delegated } = props + + return delegated +}) + +const forwarded = useForwardPropsEmits(delegatedProps, emits) +</script> + +<template> + <SwitchRoot + v-bind="forwarded" + :class="cn( + 'peer inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input', + props.class, + )" + > + <SwitchThumb + :class="cn('pointer-events-none block h-4 w-4 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0')" + > + <slot name="thumb" /> + </SwitchThumb> + </SwitchRoot> +</template> diff --git a/site/src/components/ui/switch/index.ts b/site/src/components/ui/switch/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..87b4b17da8cfbfa5d4d81c4d646fa0eaa15ac9f9 --- /dev/null +++ b/site/src/components/ui/switch/index.ts @@ -0,0 +1 @@ +export { default as Switch } from './Switch.vue' diff --git a/site/src/components/ui/textarea/Textarea.vue b/site/src/components/ui/textarea/Textarea.vue new file mode 100644 index 0000000000000000000000000000000000000000..329bb59255b3567a54011352f35731addb667fdd --- /dev/null +++ b/site/src/components/ui/textarea/Textarea.vue @@ -0,0 +1,32 @@ +<script setup lang="ts"> +import type { HTMLAttributes } from 'vue' +import { Button } from '@/components/ui/copybutton' +import { cn } from '@/lib/utils' +import { useVModel } from '@vueuse/core' + +const props = defineProps<{ + class?: HTMLAttributes['class'] + defaultValue?: string | number + modelValue?: string | number + copyEnabled?: boolean; + disabled?: boolean; + height?: string; + placeholder?: string; +}>() + +const emits = defineEmits<{ + (e: 'update:modelValue', payload: string | number): void +}>() + +const modelValue = useVModel(props, 'modelValue', emits, { + passive: true, + defaultValue: props.defaultValue, +}) +</script> + +<template> + <div :class="cn(props.class, 'relative')"> + <textarea :disabled="disabled" :placeholder="placeholder" :style="{ height: props.height ?? '100px' }" v-model="modelValue" :class="'flex min-h-[60px] w-full rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50'" /> + <Button class="absolute top-[10px] right-[10px]" :value="modelValue" v-if="copyEnabled" /> + </div> +</template> diff --git a/site/src/components/ui/textarea/index.ts b/site/src/components/ui/textarea/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..6a7ab2a76612ce265d9eefcf7f568ddd2fb4df3d --- /dev/null +++ b/site/src/components/ui/textarea/index.ts @@ -0,0 +1 @@ +export { default as Textarea } from './Textarea.vue' diff --git a/site/src/components/utilcards/AuthorityCard.vue b/site/src/components/utilcards/AuthorityCard.vue new file mode 100644 index 0000000000000000000000000000000000000000..a7c1f09940abf744ad8154d7774dfee33a89a801 --- /dev/null +++ b/site/src/components/utilcards/AuthorityCard.vue @@ -0,0 +1,79 @@ +<script setup lang="ts"> +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; +import { Skeleton } from '@/components/ui/skeleton'; +import { mdiAccountKeyOutline } from '@mdi/js'; +import { useSettingsStore } from '@/stores/settings.store'; +import { computed, onMounted, ref, watch } from 'vue'; +import { AccountAuthorityUpdateOperation, type authority } from '@hiveio/wax/vite'; +import { getWax } from '@/stores/wax.store'; +import PublicKey from '@/components/hive/PublicKey.vue'; + +const settingsStore = useSettingsStore(); +const hasUser = computed(() => settingsStore.settings.account !== undefined); + +const memoKey = ref<null | string>(null); +const activeAuthority = ref<null | authority>(null); +const postingAuthority = ref<null | authority>(null); +const ownerAuthority = ref<null | authority>(null); + +const retrieveAuthority = async() => { + try { + const wax = await getWax(); + + const op = await AccountAuthorityUpdateOperation.createFor(wax, settingsStore.settings.account!); + memoKey.value = op.role("memo").value; + postingAuthority.value = op.role("posting").value; + activeAuthority.value = op.role("active").value; + ownerAuthority.value = op.role("owner").value; + } catch(error) { + console.error(error); // TODO: Handle error - toast + } +}; +watch(hasUser, value => { + if(value) + void retrieveAuthority(); +}); +onMounted(() => { + if(hasUser.value) + void retrieveAuthority(); +}); +</script> + +<template> + <Card class="bg-black/40 backdrop-blur-sm"> + <CardHeader> + <CardTitle class="inline-flex items-center justify-between"> + <span>Authority info</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="mdiAccountKeyOutline"/></svg> + </CardTitle> + <CardDescription class="mr-4">Use this module to gather information about your Hive on-chain authorities</CardDescription> + </CardHeader> + <CardContent> + <div class="space-y-4" v-if="hasUser"> + <div class="flex flex-col"> + <h4>Memo</h4> + <PublicKey v-if="memoKey" :value="memoKey" /> + <Skeleton v-if="!memoKey" class="ml-2 h-[40px] w-full rounded-xl" /> + </div> + <div class="flex flex-col"> + <h4>Posting</h4> + <PublicKey v-if="postingAuthority" v-for="(key, val) in postingAuthority.key_auths" :key="key" :value="val" :after-value="`(${key}/${postingAuthority.weight_threshold})`" /> + <PublicKey :context="0" v-if="postingAuthority" v-for="(key, val) in postingAuthority.account_auths" :key="key" :value="val" :after-value="`(${key}/${postingAuthority.weight_threshold})`"/> + <Skeleton v-if="!postingAuthority" class="ml-2 h-[40px] w-full rounded-xl" /> + </div> + <div class="flex flex-col"> + <h4>Active</h4> + <PublicKey v-if="activeAuthority" v-for="(key, val) in activeAuthority.key_auths" :key="key" :value="val" :after-value="`(${key}/${activeAuthority.weight_threshold})`" /> + <PublicKey :context="0" v-if="activeAuthority" v-for="(key, val) in activeAuthority.account_auths" :key="key" :value="val" :after-value="`(${key}/${activeAuthority.weight_threshold})`"/> + <Skeleton v-if="!activeAuthority" class="ml-2 h-[40px] w-full rounded-xl" /> + </div> + <div class="flex flex-col"> + <h4>Owner</h4> + <PublicKey v-if="ownerAuthority" v-for="(key, val) in ownerAuthority.key_auths" :key="key" :value="val" :after-value="`(${key}/${ownerAuthority.weight_threshold})`" /> + <PublicKey :context="0" v-if="ownerAuthority" v-for="(key, val) in ownerAuthority.account_auths" :key="key" :value="val" :after-value="`(${key}/${ownerAuthority.weight_threshold})`"/> + <Skeleton v-if="!ownerAuthority" class="ml-2 h-[40px] w-full rounded-xl" /> + </div> + </div> + </CardContent> + </Card> +</template> \ No newline at end of file diff --git a/site/src/components/utilcards/ConfirmCreateAccountCard.vue b/site/src/components/utilcards/ConfirmCreateAccountCard.vue new file mode 100644 index 0000000000000000000000000000000000000000..40ff809d5f8d402c78039acf0ab9a49983f42ad3 --- /dev/null +++ b/site/src/components/utilcards/ConfirmCreateAccountCard.vue @@ -0,0 +1,30 @@ +<script setup lang="ts"> +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; +import { mdiAccountPlusOutline } from '@mdi/js'; +import { Button } from '@/components/ui/button'; +import { Textarea } from '@/components/ui/textarea'; +import { Input } from '@/components/ui/input'; +</script> + +<template> + <Card class="bg-black/40 backdrop-blur-sm"> + <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> + </CardTitle> + <CardDescription class="mr-4">Use this module to process account creation request sent by other users</CardDescription> + </CardHeader> + <CardContent> + <div class="my-4"> + <Input placeholder="Account username" class="my-2" disabled/> + <Input placeholder="Memo key" class="my-2" disabled/> + <Input placeholder="Posting key" class="my-2" disabled/> + <Input placeholder="Active key" class="my-2" disabled/> + <Input placeholder="Owner key" class="my-2" disabled/> + <Textarea placeholder="Posting metadata" class="my-2" disabled/> + <Button class="my-2">Create account</Button> + </div> + </CardContent> + </Card> +</template> \ No newline at end of file diff --git a/site/src/components/utilcards/MemoEncryptCard.vue b/site/src/components/utilcards/MemoEncryptCard.vue new file mode 100644 index 0000000000000000000000000000000000000000..a4717ef767fe0d2b011ec3c9850dd7bdf68c2a1f --- /dev/null +++ b/site/src/components/utilcards/MemoEncryptCard.vue @@ -0,0 +1,37 @@ +<script setup lang="ts"> +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; +import { mdiMessageLockOutline } from '@mdi/js'; +import { ref } from 'vue'; +import { Textarea } from '@/components/ui/textarea'; +import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input'; +import { Switch } from '@/components/ui/switch'; + +const isEncrypt = ref(false); +</script> + +<template> + <Card class="bg-black/40 backdrop-blur-sm"> + <CardHeader> + <CardTitle class="inline-flex items-center justify-between"> + <span>Memo encryption</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="mdiMessageLockOutline"/></svg> + </CardTitle> + <CardDescription class="mr-4">Use this module to encrypt / decrypt given message using your memo key for any purpose</CardDescription> + </CardHeader> + <CardContent> + <div class="my-4 space-x-4 flex"> + <span>Decrypt</span> + <Switch v-model="isEncrypt" /> + <span>Encrypt</span> + </div> + <Textarea placeholder="Input" class="my-4"/> + <Input v-if="isEncrypt" placeholder="Receiver account or public key" class="mt-4"/> + <div class="flex mb-4 underline text-sm" v-if="isEncrypt"> + <a class="ml-auto mr-1 cursor-pointer" style="color: hsla(var(--foreground) / 70%)" @click="">Use my memo key</a> + </div> + <Button>{{ isEncrypt ? "Encrypt" : "Decrypt" }}</Button> + <Textarea placeholder="Output" copy-enabled class="my-4" disabled/> + </CardContent> + </Card> +</template> \ No newline at end of file diff --git a/site/src/components/utilcards/SignTransactionCard.vue b/site/src/components/utilcards/SignTransactionCard.vue new file mode 100644 index 0000000000000000000000000000000000000000..9b5ae54b4b1099dedcd8657fc0b20e70c4e22b3f --- /dev/null +++ b/site/src/components/utilcards/SignTransactionCard.vue @@ -0,0 +1,25 @@ +<script setup lang="ts"> +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; +import { mdiFileSign } from '@mdi/js'; +import { Textarea } from '@/components/ui/textarea'; +import { Button } from '@/components/ui/button'; +</script> + +<template> + <Card class="bg-black/40 backdrop-blur-sm"> + <CardHeader> + <CardTitle class="inline-flex items-center justify-between"> + <span>Transaction signing</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="mdiFileSign"/></svg> + </CardTitle> + <CardDescription class="mr-4">Use this module to sign the provided transaction</CardDescription> + </CardHeader> + <CardContent> + <Textarea placeholder="Transaction in API JSON form" class="my-4"/> + <div class="my-4 space-x-4"> + <Button>Sign transaction</Button> + </div> + <Textarea placeholder="Signed Transaction output" copy-enabled class="my-4" disabled/> + </CardContent> + </Card> +</template> \ No newline at end of file diff --git a/site/src/pages/index.vue b/site/src/pages/index.vue index 828dce24a04091fe7b7ebb49a75281a9425626e7..effef12e1ba3d3b51ec9e1a60c8c2abdedfa6dde 100644 --- a/site/src/pages/index.vue +++ b/site/src/pages/index.vue @@ -1,111 +1,15 @@ <script setup lang="ts"> -import { - Card, - CardContent, - CardDescription, - CardFooter, - CardHeader, - CardTitle, -} from '@/components/ui/card' +import AuthorityCard from '@/components/utilcards/AuthorityCard.vue'; +import ConfirmCreateAccountCard from '@/components/utilcards/ConfirmCreateAccountCard.vue'; +import MemoEncryptCard from '@/components/utilcards/MemoEncryptCard.vue'; +import SignTransactionCard from '@/components/utilcards/SignTransactionCard.vue'; </script> <template> <div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4 p-8"> - <Card class="bg-black/40 backdrop-blur-sm"> - <CardHeader> - <CardTitle>Card Title</CardTitle> - <CardDescription>Card Description</CardDescription> - </CardHeader> - <CardContent> - Card Content - </CardContent> - <CardFooter> - Card Footer - </CardFooter> - </Card> - <Card class="bg-black/40 backdrop-blur-sm"> - <CardHeader> - <CardTitle>Card Title</CardTitle> - <CardDescription>Card Description</CardDescription> - </CardHeader> - <CardContent> - Card Content - </CardContent> - <CardFooter> - Card Footer - </CardFooter> - </Card> - <Card class="bg-black/40 backdrop-blur-sm"> - <CardHeader> - <CardTitle>Card Title</CardTitle> - <CardDescription>Card Description</CardDescription> - </CardHeader> - <CardContent> - Card Content - </CardContent> - <CardFooter> - Card Footer - </CardFooter> - </Card> - <Card class="bg-black/40 backdrop-blur-sm"> - <CardHeader> - <CardTitle>Card Title</CardTitle> - <CardDescription>Card Description</CardDescription> - </CardHeader> - <CardContent> - Card Content - </CardContent> - <CardFooter> - Card Footer - </CardFooter> - </Card> - <Card class="bg-black/40 backdrop-blur-sm"> - <CardHeader> - <CardTitle>Card Title</CardTitle> - <CardDescription>Card Description</CardDescription> - </CardHeader> - <CardContent> - Card Content - </CardContent> - <CardFooter> - Card Footer - </CardFooter> - </Card> - <Card class="bg-black/40 backdrop-blur-sm"> - <CardHeader> - <CardTitle>Card Title</CardTitle> - <CardDescription>Card Description</CardDescription> - </CardHeader> - <CardContent> - Card Content - </CardContent> - <CardFooter> - Card Footer - </CardFooter> - </Card> - <Card class="bg-black/40 backdrop-blur-sm"> - <CardHeader> - <CardTitle>Card Title</CardTitle> - <CardDescription>Card Description</CardDescription> - </CardHeader> - <CardContent> - Card Content - </CardContent> - <CardFooter> - Card Footer - </CardFooter> - </Card> - <Card class="bg-black/40 backdrop-blur-sm"> - <CardHeader> - <CardTitle>Card Title</CardTitle> - <CardDescription>Card Description</CardDescription> - </CardHeader> - <CardContent> - Card Content - </CardContent> - <CardFooter> - Card Footer - </CardFooter> - </Card> + <AuthorityCard /> + <MemoEncryptCard /> + <SignTransactionCard /> + <ConfirmCreateAccountCard /> </div> -</template> \ No newline at end of file +</template> diff --git a/site/src/stores/wax.store.ts b/site/src/stores/wax.store.ts new file mode 100644 index 0000000000000000000000000000000000000000..39366133affe27391e65d2b946bf16468138d441 --- /dev/null +++ b/site/src/stores/wax.store.ts @@ -0,0 +1,10 @@ +import { createHiveChain, type IHiveChainInterface } from "@hiveio/wax/vite"; + +let chain: IHiveChainInterface; + +export const getWax = async(): Promise<IHiveChainInterface> => { + if (!chain) + chain = await createHiveChain(); + + return chain; +};