Skip to content
Draft
14 changes: 14 additions & 0 deletions _frontend/src/AppStorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,20 @@ export class AppStorage {
this.setLocalStorageItem(this.ACCOUNT_AIRDROP_TAB_KEY, newValue)
}

//
// preferred tabs in account hooks section
//

private static readonly ACCOUNT_HOOKS_TAB_KEY = 'accountHooksTab'

static getAccountHooksTab() {
return this.getLocalStorageItem(this.ACCOUNT_HOOKS_TAB_KEY)
}

static setAccountHooksTab(newValue: string | null) {
this.setLocalStorageItem(this.ACCOUNT_HOOKS_TAB_KEY, newValue)
}

//
// preferred tab in contract bytecode section
//
Expand Down
155 changes: 155 additions & 0 deletions _frontend/src/components/hooks/HieroHookEntry.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
// SPDX-License-Identifier: Apache-2.0

<!-- --------------------------------------------------------------------------------------------------------------- -->
<!-- TEMPLATE -->
<!-- --------------------------------------------------------------------------------------------------------------- -->

<template>

<div v-if="props.hook" class="card-root">

<div :class="{'split-content': isMediumScreen}">

<div class="left-content">
<Property id="hook-id">
<template #name>Hook ID</template>
<template #value>{{ props.hook.hook_id }}</template>
</Property>
<Property id="hook-contract-id">
<template #name>Contract</template>
<template #value>
<ContractLink :contract-id="props.hook.contract_id"/>
</template>
</Property>
<Property id="hook-owner-id">
<template #name>Owner</template>
<template #value>
<AccountLink :account-id="props.hook.owner_id"/>
</template>
</Property>
<Property id="hook-extension-point">
<template #name>Extension Point</template>
<template #value>{{ props.hook.extension_point }}</template>
</Property>
<Property id="hook-type">
<template #name>Hook Type</template>
<template #value>{{ props.hook.type }}</template>
</Property>
</div>

<div :class="{'split-separator': isMediumScreen}" class="right-content">
<Property id="hook-admin-key">
<template #name>Admin Key</template>
<template #value>
<KeyValue
:key-bytes="props.hook.admin_key?.key"
:key-type="props.hook.admin_key?._type"
:show-none="true"
/>
</template>
</Property>
<Property id="hook-created-at">
<template #name>Created</template>
<template #value>
<TimestampValue
:show-none="true"
:timestamp="props.hook.created_timestamp"
/>
</template>
</Property>
<Property id="hook-deleted">
<template #name>Deleted</template>
<template #value>{{ props.hook.deleted }}</template>
</Property>
</div>

</div>

</div>

</template>

<!-- --------------------------------------------------------------------------------------------------------------- -->
<!-- SCRIPT -->
<!-- --------------------------------------------------------------------------------------------------------------- -->

<script lang="ts" setup>

import {inject, PropType} from "vue";
import Property from "@/components/Property.vue";
import KeyValue from "@/components/values/KeyValue.vue";
import TimestampValue from "@/components/values/TimestampValue.vue";
import ContractLink from "@/components/values/link/ContractLink.vue";
import AccountLink from "@/components/values/link/AccountLink.vue";
import {Hook} from "@/schemas/MirrorNodeSchemas.ts";

const props = defineProps({
hook: {
type: Object as PropType<Hook | null>,
default: null
},
})

const isMediumScreen = inject('isMediumScreen', true)

</script>

<!-- --------------------------------------------------------------------------------------------------------------- -->
<!-- STYLE -->
<!-- --------------------------------------------------------------------------------------------------------------- -->

<style scoped>

div.card-root {
background-color: var(--background-primary);
border: 1px solid var(--table-border);
border-radius: 16px;
display: flex;
flex-direction: column;
flex: 1;
gap: 16px;
padding: 16px;
}

@media (min-width: 1080px) {
div.card-root {
gap: 32px;
padding: 32px;
}
}

@media (min-width: 1080px) {
div.card-header {
align-items: center;
display: flex;
gap: 12px;
justify-content: space-between;
}
}

div.split-content {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 24px;
}

div.left-content {
align-content: flex-start;
display: flex;
flex-direction: column;
gap: 16px;
}

div.right-content {
align-content: flex-start;
display: flex;
flex-direction: column;
gap: 16px;
}

div.split-separator {
border-left: 1px solid var(--border-secondary);
padding-left: 24px;
}

</style>
90 changes: 90 additions & 0 deletions _frontend/src/components/hooks/HieroHookStorage.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// SPDX-License-Identifier: Apache-2.0

<!-- --------------------------------------------------------------------------------------------------------------- -->
<!-- TEMPLATE -->
<!-- --------------------------------------------------------------------------------------------------------------- -->

<template>

<div class="" style="display: flex; flex-direction: column; gap: 8px;">

<div style="display: flex; align-items: center; justify-content: flex-start; gap: 8px; padding-left: 8px">
<div>Storage State for Hook ID</div>
<SelectView v-model="hookId" small style="font-size:12px; min-width: 70px;">
<option v-for="h in hooks" :key="h.hook_id" :value="h.hook_id">
{{ h.hook_id }}
</option>
</SelectView>
</div>

<table class="o-table">
<thead>
<tr style="text-align: left;">
<th class="o-table__th" scope="col">ADDRESS</th>
<th class="o-table__th" scope="col">VALUE</th>
<th class="o-table__th" scope="col">DATE</th>
</tr>
</thead>
<tbody>
<tr v-for="s in slots" :key="s.timestamp">
<td class="o-table__td" style="max-width: 300px;">
<HexaDumpValue :byte-string="s.key" :word-wrap-medium="8" :word-wrap-small="4"/>
</td>
<td class="o-table__td" style="max-width: 300px;">
<HexaDumpValue :byte-string="s.value" :word-wrap-medium="8" :word-wrap-small="4"/>
</td>
<td class="o-table__td" style="max-width: 250px;">
<TimestampValue :nano="true" :timestamp="s.timestamp"/>
</td>
</tr>
</tbody>
</table>
</div>

</template>

<!-- --------------------------------------------------------------------------------------------------------------- -->
<!-- SCRIPT -->
<!-- --------------------------------------------------------------------------------------------------------------- -->

<script lang="ts" setup>

import {computed, onBeforeUnmount, onMounted, ref} from "vue";
import {HieroHooksByAccountIdCache} from "@/utils/cache/HieroHooksByAccountIdCache.ts";
import SelectView from "@/elements/SelectView.vue";
import {HieroHookStorageByIdCache} from "@/utils/cache/HieroHookStorageByIdCache.ts";
import HexaDumpValue from "@/components/values/HexaDumpValue.vue";
import TimestampValue from "@/components/values/TimestampValue.vue";

const props = defineProps({
accountId: {
type: String
}
})

const accountId = computed(() => props.accountId ?? null)
const hooksLookup = HieroHooksByAccountIdCache.instance.makeLookup(accountId)
onMounted(() => hooksLookup.mount())
onBeforeUnmount(() => hooksLookup.unmount())
const hooks = computed(() => hooksLookup.entity.value || [])
const hookId = ref<number>(hooks.value[0]?.hook_id ?? 1)

const storageLookupKey = computed(() => {
return accountId.value
? HieroHookStorageByIdCache.makeKey(accountId.value, hookId.value)
: ''
})
const storageLookup = HieroHookStorageByIdCache.instance.makeLookup(storageLookupKey)
onMounted(() => storageLookup.mount())
onBeforeUnmount(() => storageLookup.unmount())
const slots = computed(() => storageLookup.entity.value || [])

</script>

<!-- --------------------------------------------------------------------------------------------------------------- -->
<!-- STYLE -->
<!-- --------------------------------------------------------------------------------------------------------------- -->

<style scoped>

</style>
52 changes: 52 additions & 0 deletions _frontend/src/components/hooks/HieroHooksList.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// SPDX-License-Identifier: Apache-2.0

<!-- --------------------------------------------------------------------------------------------------------------- -->
<!-- TEMPLATE -->
<!-- --------------------------------------------------------------------------------------------------------------- -->

<template>

<div style="display: flex; flex-direction: column; gap: 16px;">
<template v-for="h in hooks" :key="h.hook_id">
<HieroHookEntry :hook="h"/>
</template>
</div>

</template>

<!-- --------------------------------------------------------------------------------------------------------------- -->
<!-- SCRIPT -->
<!-- --------------------------------------------------------------------------------------------------------------- -->

<script lang="ts" setup>

import {computed, onBeforeUnmount, onMounted, watch} from "vue";
import {HieroHooksByAccountIdCache} from "@/utils/cache/HieroHooksByAccountIdCache.ts";
import HieroHookEntry from "@/components/hooks/HieroHookEntry.vue";

const props = defineProps({
accountId: String,
nbHooks: Number,
})

const emit = defineEmits(['update:nbHooks'])


const accountId = computed(() => props.accountId ?? null)
const hooksLookup = HieroHooksByAccountIdCache.instance.makeLookup(accountId)
onMounted(() => hooksLookup.mount())
onBeforeUnmount(() => hooksLookup.unmount())
const hooks = computed(() => hooksLookup.entity.value || [])
watch(hooks, () => {
emit('update:nbHooks', hooks.value.length)
}, {immediate: true})

</script>

<!-- --------------------------------------------------------------------------------------------------------------- -->
<!-- STYLE -->
<!-- --------------------------------------------------------------------------------------------------------------- -->

<style scoped>

</style>
Loading
Loading