Skip to content

Commit

Permalink
Merge pull request #40 from InjectiveLabs/feat/layer
Browse files Browse the repository at this point in the history
feat: layer
  • Loading branch information
Thomas authored Dec 12, 2023
2 parents 326b045 + aa174c2 commit 483cc22
Show file tree
Hide file tree
Showing 186 changed files with 7,387 additions and 195 deletions.
7 changes: 5 additions & 2 deletions layer/.eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
module.exports = {
root: true,
extends: ["@nuxt/eslint-config"],
};
extends: ['@nuxt/eslint-config', '@injectivelabs/eslint-config'],
rules: {
'vue/max-attributes-per-line': 'off'
}
}
5 changes: 0 additions & 5 deletions layer/.playground/app.config.ts

This file was deleted.

3 changes: 0 additions & 3 deletions layer/.playground/nuxt.config.ts

This file was deleted.

49 changes: 47 additions & 2 deletions layer/Service.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { TokenPrice, TokenService } from '@injectivelabs/sdk-ui-ts'
import {
TokenPrice,
Web3Client,
Web3Composer,
TokenService,
DenomClientAsync
} from '@injectivelabs/sdk-ui-ts'
import {
DenomClient,
ChainGrpcWasmApi,
Expand All @@ -12,7 +18,16 @@ import {
IndexerGrpcDerivativesApi,
IndexerGrpcAccountPortfolioApi
} from '@injectivelabs/sdk-ts'
import { NETWORK, CHAIN_ID, ENDPOINTS, COINGECKO_KEY } from './utils/constant'
import { MsgBroadcaster, Web3Broadcaster } from '@injectivelabs/wallet-ts'
import { walletStrategy, alchemyRpcEndpoint } from './wallet/wallet-strategy'
import {
NETWORK,
CHAIN_ID,
ENDPOINTS,
COINGECKO_KEY,
ETHEREUM_CHAIN_ID,
FEE_PAYER_PUB_KEY
} from './utils/constant'

// Services
export const bankApi = new ChainGrpcBankApi(ENDPOINTS.grpc)
Expand Down Expand Up @@ -46,3 +61,33 @@ export const tokenPrice = new TokenPrice({
? 'https://pro-api.coingecko.com/api/v3'
: 'https://api.coingecko.com/api/v3'
})

export const denomClientAsync = new DenomClientAsync(NETWORK, {
alchemyRpcUrl: alchemyRpcEndpoint
})

export const web3Client = new Web3Client({
network: NETWORK,
rpc: alchemyRpcEndpoint
})

export const web3Composer = new Web3Composer({
network: NETWORK,
rpc: alchemyRpcEndpoint,
ethereumChainId: ETHEREUM_CHAIN_ID
})

// Transaction broadcaster
export const msgBroadcaster = new MsgBroadcaster({
walletStrategy,
network: NETWORK,
networkEndpoints: ENDPOINTS,
feePayerPubKey: FEE_PAYER_PUB_KEY,
simulateTx: true
})

export const web3Broadcaster = new Web3Broadcaster({
walletStrategy,
network: NETWORK,
ethereumChainId: ETHEREUM_CHAIN_ID
})
10 changes: 0 additions & 10 deletions layer/components/HelloWorld.vue

This file was deleted.

56 changes: 56 additions & 0 deletions layer/components/HoverMenu.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<script lang="ts" setup>
import { ref } from 'vue'
import { Menu } from 'floating-vue'
defineProps({
triggers: {
type: Array,
default: () => ['click', 'hover', 'focus'],
required: false
}
})
const isOpen = ref(false)
function onHide() {
isOpen.value = false
}
function onMouseEnter() {
isOpen.value = true
}
function onMouseLeave() {
isOpen.value = false
}
function onUpdate(value: boolean) {
isOpen.value = value
}
function onToggle() {
isOpen.value = !isOpen.value
}
</script>

<template>
<Menu
v-bind="$attrs"
placement="top"
:triggers="triggers"
:distance="8"
:shown="isOpen"
@update:shown="onUpdate"
@apply-hide="onHide"
>
<div @mouseenter="onMouseEnter" @mouseleave="onMouseLeave">
<slot v-bind="{ isOpen, toggle: onToggle }" />
</div>

<template #popper>
<div @mouseenter="onMouseEnter" @mouseleave="onMouseLeave">
<slot name="content" />
</div>
</template>
</Menu>
</template>
84 changes: 84 additions & 0 deletions layer/components/Icon.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<script setup lang="ts">
import { computed, defineAsyncComponent, useAttrs } from 'vue'
const attrs = useAttrs()
const props = defineProps({
isXs: Boolean,
isSm: Boolean,
isMd: Boolean,
name: {
type: String,
required: true
}
})
const filteredAttrs = computed(() => {
const filteredAttrs = { ...attrs }
const classes = (filteredAttrs.class as string) || ''
const defaultClasses: string[] = []
if (!classes.includes('cursor-')) {
defaultClasses.push('cursor-pointer')
}
if (
!classes.includes('w-') &&
!classes.includes('h-') &&
!classes.includes('min-w-')
) {
if (props.isXs) {
defaultClasses.push('h-2 w-2 min-w-2')
} else if (props.isSm) {
defaultClasses.push('h-3 w-3 min-w-3')
} else if (props.isMd) {
defaultClasses.push('h-4 w-4 min-w-4')
} else {
defaultClasses.push('h-6 w-6 min-w-6')
}
}
return { ...attrs, class: [...defaultClasses, classes].join(' ') }
})
/* temp fix: vite dev not dont support dynamic path
https://github.com/vitejs/vite/issues/4945
https://vitejs.dev/guide/features.html#glob-import
*/
const dynamicComponent = defineAsyncComponent<Record<string, unknown>>(() => {
let name = props.name
if (name.includes('ledger-legacy')) {
name = 'wallet/ledger'
}
if (name.includes('ledger')) {
name = 'wallet/ledger'
}
return new Promise((resolve, _reject) => {
const comps = import.meta.glob('./../icons/**/*.vue')
try {
return comps[`../icons/${name}.vue`]().then((component: any) =>
resolve(component.default)
)
} catch (e) {
// eslint-disable-next-line no-console
console.log({ e, name })
}
})
})
</script>

<script lang="ts">
export default {
inheritAttrs: false
}
</script>

<template>
<component v-bind="filteredAttrs" :is="dynamicComponent" />
</template>
162 changes: 162 additions & 0 deletions layer/components/Notification.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
<script lang="ts" setup>
import { Notification, NotificationType } from './../types'
const notificationStore = useSharedNotificationStore()
const { copy } = useClipboard()
const props = defineProps({
notification: {
type: Object as PropType<Notification>,
required: true
},
wrapperClass: {
type: String,
default: 'bg-gray-800'
},
contentClass: {
type: String,
default: 'text-white'
}
})
const timeout = ref()
const remainingTimeout = ref(6000)
onMounted(
() => (timeout.value = setTimeout(onClose, props.notification.timeout))
)
function onCopy() {
copy(props.notification.context)
}
function onClose() {
notificationStore.clear(props.notification.id)
clearTimeout(timeout.value)
}
function onPause() {
clearTimeout(timeout.value)
remainingTimeout.value -= Date.now() - props.notification.id
}
function onResume() {
timeout.value = setTimeout(onClose, remainingTimeout.value)
console.log('resume', remainingTimeout.value)
}
</script>

<template>
<Transition
enter-active-class="ease-out duration-300"
enter-from-class="opacity-0"
enter-to-class="opacity-100"
leave-active-class="ease-in duration-200"
leave-from-class="opacity-100"
leave-to-class="opacity-0"
>
<div
class="rounded-lg pointer-events-auto"
:class="wrapperClass"
@mouseenter="onPause"
@mouseleave="onResume"
>
<div
class="flex gap-2 justify-start items-start p-4"
:class="{ 'items-center': !notification.description }"
>
<div v-if="notification.type === NotificationType.Error">
<slot name="error">
<SharedIcon name="warn" class="text-red-500" />
</slot>
</div>
<div v-if="notification.type === NotificationType.Warning">
<slot name="warning">
<SharedIcon name="warn" class="text-orange-400" />
</slot>
</div>
<div v-if="notification.type === NotificationType.Success">
<slot name="success">
<SharedIcon name="circle-check-border" class="text-green-400" />
</slot>
</div>
<div v-if="notification.type === NotificationType.Info">
<slot name="info">
<SharedIcon name="circle-info" class="text-primary-500" />
</slot>
</div>
<div class="flex flex-col gap-2" :class="contentClass">
<span class="text-sm font-semibold">{{ notification.title }}</span>

<span
v-if="notification.description"
class="text-xs text-gray-400 flex items-center"
>
{{ notification.description }}
</span>

<span
v-if="notification.context"
class="text-xs text-gray-400 flex items-center"
>
<SharedHoverMenu
v-if="notification.context"
popper-class="notification-context"
@click="onCopy"
>
<template #default>
<slot>
<span class="text-xs text-gray-400 flex items-center">
Show more context
</span>
</slot>
</template>

<template #content>
{{ notification.context }}
</template>
</SharedHoverMenu>
</span>

<div v-if="notification.actions" class="flex justify-start">
<button
v-for="action in notification.actions"
:key="action.key"
@click="() => action.callback()"
>
<span
class="text-primary-500 text-sm font-semibold cursor-pointer"
>
{{ action.label }}
</span>
</button>
</div>
</div>
<slot name="close" :close-notification="onClose">
<SharedIcon name="close" is-sm class="text-white" @click="onClose" />
</slot>
</div>

<!-- <div v-if="showDeactivationTimer" class="w-full h-1 bg-gray-900">
<div
class="progress w-full h-full bg-blue-200 origin-top-right transform-gpu"
:style="progressStyle"
/>
</div> -->
</div>
</Transition>
</template>

<style>
.notification-context .v-popper__wrapper .v-popper__inner {
@apply bg-gray-800 text-gray-300 border-none max-w-xs text-xs px-3 py-1 shadow-sm;
}
.notification-context .v-popper__wrapper .v-popper__arrow-outer,
.notification-context .v-popper__wrapper .v-popper__arrow-inner {
@apply border-gray-800;
}
</style>
Loading

0 comments on commit 483cc22

Please sign in to comment.