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

Add authority

parent fe19db71
No related branches found
No related tags found
No related merge requests found
Showing
with 371 additions and 127 deletions
......@@ -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
......
......@@ -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",
......
......@@ -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>
......@@ -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>
......
<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>
......@@ -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">
......
<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>
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>
<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>
export { default as Switch } from './Switch.vue'
<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>
export { default as Textarea } from './Textarea.vue'
<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
<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
<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
<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
<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>
import { createHiveChain, type IHiveChainInterface } from "@hiveio/wax/vite";
let chain: IHiveChainInterface;
export const getWax = async(): Promise<IHiveChainInterface> => {
if (!chain)
chain = await createHiveChain();
return chain;
};
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