wip - balances ui

This commit is contained in:
Georges KABBOUCHI 2021-08-01 21:29:41 +03:00
parent 14cb6b7bc2
commit e0964abe0f
18 changed files with 536 additions and 23 deletions

View File

@ -0,0 +1,42 @@
<template>
<div class="flex flex-col flex-grow">
<div class="flex flex-col flex-grow">
<div v-if="loading" class="flex items-center justify-center">
<Spinner class="w-6 h-6" />
</div>
<div v-else class="flex flex-col flex-grow">
<div v-if="items.length" :class="{ ' divide-y divide-grey-light divide-opacity-50': divided }">
<div v-for="(item, i) in items" :key="i">
<slot name="default" :item="item" :index="i"></slot>
</div>
</div>
<div v-else class="flex flex-col items-center justify-center flex-grow">
<slot name="no-items"></slot>
</div>
</div>
</div>
</div>
</template>
<script>
import { defineComponent } from '@nuxtjs/composition-api'
export default defineComponent({
props: {
items: {
type: Array,
default: () => [],
},
divided: {
type: Boolean,
default: false,
},
loading: {
type: Boolean,
default: false,
},
},
})
</script>
<style></style>

View File

@ -0,0 +1,51 @@
<template>
<client-only>
<v-popover
:popover-base-class="baseClass"
popover-inner-class="menu-inner"
popover-wrapper-class="menu-wrapper"
placement="auto-start"
offset="8"
:delay="delay"
>
<slot name="activator"> </slot>
<template slot="popover">
<div
v-close-popover="closeOnClick"
:border-radius="borderRadius"
class="flex overflow-hidden border border-opacity-50 shadow-lg border-grey-light dark:bg-dark-400"
:class="{ 'py-2': !noPadding }"
>
<slot />
</div>
</template>
</v-popover>
</client-only>
</template>
<script>
import { defineComponent } from '@nuxtjs/composition-api'
export default defineComponent({
props: {
closeOnClick: { type: Boolean, default: true },
borderRadius: { type: String, default: 'rounded' },
noPadding: { type: Boolean, default: false },
baseClass: { type: String, default: 'menu' },
delay: { type: Object, default: () => ({ show: 100, hide: 200 }) },
},
})
</script>
<style>
.menu {
outline: none;
min-width: 200px;
z-index: 11;
}
.v-popover > .trigger {
display: initial !important;
}
</style>

View File

@ -0,0 +1,20 @@
<template>
<Button
color="white"
class="border rounded-sm border-grey-light dark:border-opacity-25 dark:bg-opacity-100 dark:bg-dark-400"
:style="`width: ${size}px; height: ${size}px`"
v-on="$listeners"
>
<Icon name="dots-horizontal" :style="`height: ${+size / 2}`" />
</Button>
</template>
<script>
import { defineComponent } from '@nuxtjs/composition-api'
export default defineComponent({
props: { size: { type: String, default: '30' } },
})
</script>
<style></style>

View File

@ -0,0 +1,29 @@
<template>
<div
class="flex items-center px-4 py-3 font-semibold transition-colors duration-150 text-12 dark:bg-opacity-17"
:class="{
'pointer-events-none text-grey-pure': disabled,
'cursor-pointer text-ocean-blue-pure hover:bg-ocean-blue-light dark:text-light': !disabled,
}"
v-on="$listeners"
>
<div :is="icon" v-if="icon" class="mr-2" :class="{ 'w-5 h-5': largeIcon, 'w-3 h-3': !largeIcon }"></div>
<div class="leading-none">
{{ item }}
</div>
</div>
</template>
<script>
import { defineComponent } from '@nuxtjs/composition-api'
export default defineComponent({
props: {
item: { type: String, required: true },
icon: { type: Object },
disabled: { type: Boolean, default: false },
largeIcon: { type: Boolean, default: false },
},
})
</script>
<style></style>

View File

@ -1,7 +1,9 @@
<template>
<div class="flex-shrink-0 w-full mx-auto p-4">
<div class="flex justify-between items-center w-full">
<button
<div>
<button
v-if="showBackButton"
class="flex items-center justify-center text-opacity-50 group hover:text-opacity-100 focus:hover:text-opacity-100 text-grey-pure focus:outline-none"
@click="back"
>
@ -9,6 +11,7 @@
class="transition-transform duration-75 ease-out transform group-hover:-translate-x-1"
/>
</button>
</div>
<button
class="flex items-center justify-center text-opacity-50 group hover:text-opacity-100 focus:hover:text-opacity-100 text-grey-pure focus:outline-none"
@ -31,6 +34,12 @@ import SVGArrowLeft from '@/assets/icons/arrow-left.svg?inline'
import { useSidebar } from '~/composables/useSidebar'
export default defineComponent({
props: {
showBackButton: {
type: Boolean,
default: true
}
},
components: {
SVGClose,
SVGArrowLeft,

View File

@ -1,6 +1,6 @@
<template>
<div v-if="filteredAssets.length" class="pb-6">
<CardCurrency
<card-currency
v-for="token in filteredAssets"
:key="token.address"
class="mt-2 sm:mt-4 first:mt-0"
@ -20,19 +20,21 @@
<script>
import { computed, defineComponent, watchEffect } from '@nuxtjs/composition-api'
import { useBalances } from '~/composables/useBalances'
import { useSearchFilter } from '~/composables/useSearchFilter'
import { useToken } from '~/composables/useToken'
import CardCurrency from '../overview/CardCurrency.vue'
export default defineComponent({
components: { CardCurrency },
props: {
search: { type: String, default: null },
type: { type: String },
actionLabel: { type: String, required: true },
},
setup(props) {
const { assets } = useToken()
const { getAssets } = useBalances()
const typedAssets = computed(() => assets.value(props.type))
const typedAssets = computed(() => getAssets(props.type))
const { filtered: filteredAssets, search } = useSearchFilter(typedAssets, 'name', 'symbol')
watchEffect(() => (search.value = props.search))

View File

@ -0,0 +1,31 @@
<template>
<div class="flex items-center px-4 py-4 select-none dark:bg-dark-400">
<IconCurrency :currency="tokenKey" />
<div class="flex flex-col px-4">
<div class="mb-1 font-semibold whitespace-no-wrap text-12 text-navi-pure dark:text-light">
{{ formatDecimal(balance) }} {{ symbol }}
</div>
<div class="font-medium whitespace-no-wrap text-12 text-grey-pure">{{ formatUsd(netWorth, 2) }}</div>
</div>
<Button class="ml-auto w-18" color="ocean-blue" @click="$emit('action')">{{ actionLabel }}</Button>
</div>
</template>
<script>
import { defineComponent } from '@nuxtjs/composition-api'
import { useFormatting } from '~/composables/useFormatting'
export default defineComponent({
props: {
tokenKey: { type: String, required: true },
symbol: { type: String, required: true },
balance: { type: String, required: true },
netWorth: { type: String, required: true },
actionLabel: { type: String, required: true },
},
setup() {
const { formatUsd, formatDecimal } = useFormatting()
return { formatUsd, formatDecimal }
},
})
</script>

View File

@ -0,0 +1,90 @@
<template>
<SidebarContextContainer class="flex-1 h-full overflow-hidden">
<SidebarContextHeader :showBackButton="false"></SidebarContextHeader>
<div class="flex-grow overflow-y-scroll scrollbar-hover">
<div class="h-full mx-auto" style="max-width: 296px">
<div class="flex flex-col h-full py-2 sm:py-4">
<SidebarOverviewBalance />
<div class="flex flex-col flex-grow mt-2 sm:mt-4">
<div class="flex flex-shrink-0">
<search-input
v-model.trim="search"
placeholder="Search Currency"
class="mr-2"
/>
<Menu>
<template v-slot:activator="{ on }">
<menu-button size="38" v-on="on" />
</template>
<template>
<list :items="menuActions">
<template v-slot:default="{ item }">
<menu-list-item
:item="item.text"
:icon="item.icon"
:disabled="item.disabled"
@click="item.onClick"
></menu-list-item>
</template>
</list>
</template>
</Menu>
</div>
<div class="flex flex-col flex-grow mt-2 sm:mt-4">
<currency-list :search="search" type="dsa" action-label="Trade">
<template v-slot:no-items>
<div class="flex flex-col">
<div class="font-medium text-center text-grey-pure">
Can't find existing token?
</div>
<Button
color="ocean-blue"
large
style="background: none"
@click="startAddingCustomToken"
>Add custom token</Button
>
</div>
</template>
</currency-list>
</div>
</div>
</div>
</div>
</div>
</SidebarContextContainer>
</template>
<script>
import { defineComponent, ref, computed } from '@nuxtjs/composition-api'
import SVGAdd from '@/assets/img/icons/add.svg?inline'
import CurrencyList from '../components/CurrencyList.vue'
import List from '~/components/common/list/List.vue'
import Menu from '~/components/common/menu/Menu.vue'
import MenuButton from '~/components/common/menu/MenuButton.vue'
import MenuListItem from '~/components/common/menu/MenuListItem.vue'
export default defineComponent({
components: { SVGAdd, CurrencyList, List, Menu, MenuButton, MenuListItem },
setup() {
const menuActions = computed(() => [
{
text: 'Add custom token',
onClick: startAddingCustomToken,
icon: SVGAdd,
disabled: false,
},
])
function startAddingCustomToken() {
}
const search = ref(null)
return { search, menuActions }
},
})
</script>

View File

@ -0,0 +1,29 @@
<template>
<div class="flex flex-col items-center flex-shrink-0 w-full px-8 mt-6 mb-2 text-center sm:mb-10">
<h3 class="flex items-center leading-none">Balance <Info text="This is your DSA balance and doesn't reflect your wallet balance (like Metamask)" class="ml-1" /></h3>
<div class="mt-4 font-semibold text-32">{{ formatUsd(balanceTotal) }}</div>
</div>
</template>
<script>
import { defineComponent, onMounted } from '@nuxtjs/composition-api'
import SVGChevronUp from '@/assets/img/icons/chevron-up.svg'
import { useFormatting } from '~/composables/useFormatting'
import { useBalances } from '~/composables/useBalances'
export default defineComponent({
components: {
SVGChevronUp,
},
setup() {
const { formatUsd } = useFormatting()
const { balanceTotal, fetchBalances, balances, prices } = useBalances()
onMounted(() => {
fetchBalances(true)
})
return { formatUsd, balanceTotal, balances, prices }
},
})
</script>

View File

@ -1,4 +1,9 @@
import { nextTick, onMounted, reactive, watch } from "@nuxtjs/composition-api";
import {
computed,
reactive,
onMounted,
useContext
} from "@nuxtjs/composition-api";
import BigNumber from "bignumber.js";
import abis from "~/constant/abis";
import addresses from "~/constant/addresses";
@ -11,17 +16,33 @@ import Web3 from "web3";
import { AbiItem } from "web3-utils";
import { mainnetWeb3, polygonWeb3 } from "~/utils/web3";
import { useToken } from "./useToken";
import { useBigNumber } from "./useBigNumber";
import { useSorting } from "./useSorting";
const balances = reactive({
user: null,
dsa: null
});
const prices = reactive({
mainnet: {},
polygon: {}
});
export function useBalances() {
const { account, web3, networkName } = useWeb3();
const { $axios } = useContext();
const { times, plus, ensureValue } = useBigNumber();
const { account, networkName } = useWeb3();
const { activeAccount } = useDSA();
const { getTokenByKey } = useToken();
const { by } = useSorting();
onMounted(async () => {
prices.mainnet = await $axios.$get("https://api.instadapp.io/defi/prices");
prices.polygon = await $axios.$get(
"https://api.instadapp.io/defi/polygon/prices"
);
});
const fetchBalances = async (refresh = false) => {
if (!balances.user || refresh) {
balances.user = {
@ -46,27 +67,57 @@ export function useBalances() {
}
};
const getBalanceByKey = (tokenKey, network = null) => {
const getBalanceByKey = (tokenKey, network = null, type = "dsa") => {
return getBalanceByAddress(getTokenByKey(tokenKey)?.address, network, type);
};
const getBalanceByAddress = (address, network = null, type = "dsa") => {
return (
balances.dsa?.[network || networkName.value][
getTokenByKey(tokenKey)?.address
]?.balance || "0"
balances[type]?.[network || networkName.value][address]?.balance || "0"
);
};
const getBalanceRawByKey = (tokenKey, network = null) => {
const getBalanceRawByKey = (tokenKey, network = null, type = "dsa") => {
return (
balances.dsa?.[network || networkName.value][
balances[type]?.[network || networkName.value][
getTokenByKey(tokenKey)?.address
]?.raw || "0"
);
};
const netWorth = (address, type = "dsa") => {
const balance = getBalanceByAddress(address, networkName.value, type);
const price = ensureValue(prices[networkName.value][address]).toFixed();
return times(balance, price).toFixed();
};
const balanceTotal = computed(() =>
tokens[networkName.value].allTokens.reduce(
(totalNetWorth, token) =>
plus(totalNetWorth, netWorth(token.address)).toFixed(),
"0"
)
);
const getAssets = (type = "dsa") => {
return tokens[networkName.value].allTokens
.map(token => ({
...token,
balance: getBalanceByAddress(token.address, networkName.value, type),
netWorth: netWorth(token.address, type)
}))
.sort(by("-netWorth"));
};
return {
balances,
fetchBalances,
getBalanceByKey,
getBalanceRawByKey
getBalanceRawByKey,
balanceTotal,
prices,
getAssets
};
}

View File

@ -18,9 +18,12 @@ import SidebarAaveV2Borrow from '~/components/sidebar/context/aaveV2/SidebarAave
//@ts-ignore
import SidebarAaveV2Payback from '~/components/sidebar/context/aaveV2/SidebarAaveV2Payback.vue'
//@ts-ignore
import SidebarOverview from '~/components/sidebar/context/overview/SidebarOverview.vue'
const sidebars = {
"#overview" : {component: SidebarOverview, back : false, close : true },
"/polygon/aave-v2": { component: null },
"/polygon/aave-v2#overview": { component: null },
"/polygon/aave-v2#supply": { component: SidebarAaveV2Supply },
"/polygon/aave-v2#borrow": { component: SidebarAaveV2Borrow },
"/polygon/aave-v2#payback": { component: SidebarAaveV2Payback },
@ -32,7 +35,6 @@ const sidebars = {
"/mainnet/aave-v2": { component: null },
"/mainnet/aave-v2#overview": { component: null },
"/mainnet/aave-v2#supply": { component: SidebarAaveV2Supply },
"/mainnet/aave-v2#borrow": { component: SidebarAaveV2Borrow },
"/mainnet/aave-v2#payback": { component: SidebarAaveV2Payback },
@ -68,6 +70,8 @@ export function init() {
return
}
console.log(hash, sidebars[route.path + hash], sidebars[hash]);
sidebar.value = sidebars[route.path + hash] || sidebars[hash];
if (!sidebar.value) {
@ -113,11 +117,16 @@ export function useSidebar() {
return !!component.value;
});
const showSidebarBalances = () => {
router.push({ hash: 'overview' });
}
return {
close,
back,
component,
props,
isOpen
isOpen,
showSidebarBalances
};
}

27
composables/useSorting.ts Normal file
View File

@ -0,0 +1,27 @@
import { useBigNumber } from './useBigNumber'
export function useSorting() {
const { times, minus, max } = useBigNumber()
/**
* Return a sorting function for the specified parameter.
*
* @param {string} sorting Property key for sorting. Prefix with `-` to sort descending.
* Property value should be Number or Number in String representation
*/
function by(sorting) {
if (sorting.startsWith('-')) {
return (a, b) => minus(b[sorting.substr(1)], a[sorting.substr(1)]).toNumber()
} else {
return (a, b) => minus(a[sorting], b[sorting]).toNumber()
}
}
return {
by,
byMaxSupplyOrBorrowDesc: (a, b) => minus(max(b.supplyUsd, b.borrowUsd), max(a.supplyUsd, a.borrowUsd)).toNumber(),
byNetWorthVaultDesc: (a, b) =>
minus(minus(times(b.col, b.price), b.debt), minus(times(a.col, a.price), a.debt)).toNumber(),
byTotalSupply: (a, b) => minus(b.poolTokenUsd, a.poolTokenUsd).toNumber(),
}
}

View File

@ -16,6 +16,36 @@
<Modal />
<NotificationBar />
<div class="fixed bottom-0 right-0 mr-10 mb-16">
<button
@click="showSidebarBalances"
class="px-9 h-[56px] bg-primary-blue-dark hover:bg-primary-blue-hover text-white rounded-[28px] text-lg font-semibold shadow flex items-center"
>
<svg
class="mr-3"
width="14"
height="16"
viewBox="0 0 14 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<g clip-path="url(#clip0)">
<path
d="M12.6109 4L2.1875 4C1.94578 4 1.75 3.77625 1.75 3.5C1.75 3.22375 1.94578 3 2.1875 3L12.6875 3C12.9292 3 13.125 2.77625 13.125 2.5C13.125 1.67156 12.5374 1 11.8125 1L1.75 1C0.783399 1 0 1.89531 0 3L0 13C0 14.1047 0.783399 15 1.75 15L12.6109 15C13.3771 15 14 14.3272 14 13.5L14 5.5C14 4.67281 13.3771 4 12.6109 4ZM11.375 10.5C10.8918 10.5 10.5 10.0522 10.5 9.5C10.5 8.94781 10.8918 8.5 11.375 8.5C11.8582 8.5 12.25 8.94781 12.25 9.5C12.25 10.0522 11.8582 10.5 11.375 10.5Z"
fill="white"
/>
</g>
<defs>
<clipPath id="clip0">
<rect width="14" height="16" fill="white" />
</clipPath>
</defs>
</svg>
Balance
</button>
</div>
</div>
</template>
@ -25,7 +55,7 @@ import MakerDAOIcon from '~/assets/icons/makerdao.svg?inline'
import CompoundIcon from '~/assets/icons/compound.svg?inline'
import AaveIcon from '~/assets/icons/aave.svg?inline'
import { useWeb3 } from '~/composables/useWeb3'
import { init as initSidebars } from '~/composables/useSidebar'
import { init as initSidebars, useSidebar } from '~/composables/useSidebar'
import { useBackdrop } from '@/composables/useBackdrop'
import { useNetwork } from "~/composables/useNetwork";
@ -40,6 +70,7 @@ export default defineComponent({
const { activeNetwork, checkForNetworkMismatch } = useNetwork();
const { isShown: isBackdropShown, close: closeBackdrop } = useBackdrop()
const { redirect } = useContext()
const { showSidebarBalances } = useSidebar()
const route = useRoute()
watch(isBackdropShown, () => {
@ -62,7 +93,7 @@ export default defineComponent({
return;
}
if (!route.value.path.includes(activeNetwork.value.id)) {
if (route.value.path.includes(['mainnet', 'polygon']) && route.value.path.includes(activeNetwork.value.id)) {
redirect('/')
}
}, { immediate: true })
@ -79,6 +110,7 @@ export default defineComponent({
deactivate,
isBackdropShown,
closeBackdrop,
showSidebarBalances,
}
}

View File

@ -57,6 +57,7 @@ export default {
// Modules: https://go.nuxtjs.dev/config-modules
modules: [
'@nuxtjs/axios',
],
// Build Configuration: https://go.nuxtjs.dev/config-build

View File

@ -9,6 +9,7 @@
"generate": "nuxt generate"
},
"dependencies": {
"@nuxtjs/axios": "^5.13.6",
"@nuxtjs/composition-api": "^0.24.7",
"@portis/web3": "^4.0.5",
"@tailwindcss/forms": "^0.3.3",

14
pages/overview.vue Normal file
View File

@ -0,0 +1,14 @@
<template>
<div>
<sidebar-overview />
</div>
</template>
<script lang="ts">
import { defineComponent } from "@nuxtjs/composition-api";
import SidebarOverview from "~/components/sidebar/context/overview/SidebarOverview.vue";
export default defineComponent({
components: {SidebarOverview },
});
</script>

View File

@ -26,6 +26,7 @@
},
"types": [
"@nuxt/types",
"@nuxtjs/axios",
"@types/node"
]
},

View File

@ -1561,6 +1561,17 @@
webpack-node-externals "^3.0.0"
webpackbar "^4.0.0"
"@nuxtjs/axios@^5.13.6":
version "5.13.6"
resolved "https://registry.yarnpkg.com/@nuxtjs/axios/-/axios-5.13.6.tgz#6f4bbd98a3a7799a5d2c0726c6ad2a98aa111881"
integrity sha512-XS+pOE0xsDODs1zAIbo95A0LKlilvJi8YW0NoXYuq3/jjxGgWDxizZ6Yx0AIIjZOoGsXJOPc0/BcnSEUQ2mFBA==
dependencies:
"@nuxtjs/proxy" "^2.1.0"
axios "^0.21.1"
axios-retry "^3.1.9"
consola "^2.15.3"
defu "^5.0.0"
"@nuxtjs/composition-api@^0.24.7":
version "0.24.7"
resolved "https://registry.yarnpkg.com/@nuxtjs/composition-api/-/composition-api-0.24.7.tgz#76ec3660a03cd7bdb8b85fd31e8dc6a2b7194e8f"
@ -1582,6 +1593,13 @@
consola "^2.15.3"
google-fonts-helper "^1.2.0"
"@nuxtjs/proxy@^2.1.0":
version "2.1.0"
resolved "https://registry.yarnpkg.com/@nuxtjs/proxy/-/proxy-2.1.0.tgz#fa7715a11d237fa1273503c4e9e137dd1bf5575b"
integrity sha512-/qtoeqXgZ4Mg6LRg/gDUZQrFpOlOdHrol/vQYMnKu3aN3bP90UfOUB3QSDghUUK7OISAJ0xp8Ld78aHyCTcKCQ==
dependencies:
http-proxy-middleware "^1.0.6"
"@nuxtjs/svg@^0.1.12":
version "0.1.12"
resolved "https://registry.yarnpkg.com/@nuxtjs/svg/-/svg-0.1.12.tgz#a5b2a66070a36e3c5c5183db9e3d89485ef3eb1c"
@ -1857,6 +1875,13 @@
resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz#0ea7b61496902b95890dc4c3a116b60cb8dae812"
integrity sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==
"@types/http-proxy@^1.17.5":
version "1.17.7"
resolved "https://registry.yarnpkg.com/@types/http-proxy/-/http-proxy-1.17.7.tgz#30ea85cc2c868368352a37f0d0d3581e24834c6f"
integrity sha512-9hdj6iXH64tHSLTY+Vt2eYOGzSogC+JQ2H7bdPWkuh7KXP5qLllWx++t+K9Wk556c3dkDdPws/SpMRi0sdCT1w==
dependencies:
"@types/node" "*"
"@types/json-schema@^7.0.4", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.7":
version "7.0.8"
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.8.tgz#edf1bf1dbf4e04413ca8e5b17b3b7d7d54b59818"
@ -2800,6 +2825,13 @@ aws4@^1.8.0:
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59"
integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==
axios-retry@^3.1.9:
version "3.1.9"
resolved "https://registry.yarnpkg.com/axios-retry/-/axios-retry-3.1.9.tgz#6c30fc9aeb4519aebaec758b90ef56fa03fe72e8"
integrity sha512-NFCoNIHq8lYkJa6ku4m+V1837TP6lCa7n79Iuf8/AqATAHYB0ISaAS1eyIenDOfHOLtym34W65Sjke2xjg2fsA==
dependencies:
is-retry-allowed "^1.1.0"
axios@^0.18.0:
version "0.18.1"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.18.1.tgz#ff3f0de2e7b5d180e757ad98000f1081b87bcea3"
@ -2808,6 +2840,13 @@ axios@^0.18.0:
follow-redirects "1.5.10"
is-buffer "^2.0.2"
axios@^0.21.1:
version "0.21.1"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.1.tgz#22563481962f4d6bde9a76d516ef0e5d3c09b2b8"
integrity sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==
dependencies:
follow-redirects "^1.10.0"
babel-loader@^8.2.2:
version "8.2.2"
resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.2.2.tgz#9363ce84c10c9a40e6c753748e1441b60c8a0b81"
@ -5168,7 +5207,7 @@ eventemitter3@4.0.4:
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.4.tgz#b5463ace635a083d018bdc7c917b4c5f10a85384"
integrity sha512-rlaVLnVxtxvoyLsQQFBx53YmXHDxRIzzTLbdfxqi4yocpSjAxXwkU0cScM5JgSKMqEhrZpnvQ2D9gjylR0AimQ==
eventemitter3@4.0.7:
eventemitter3@4.0.7, eventemitter3@^4.0.0:
version "4.0.7"
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f"
integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==
@ -5493,6 +5532,11 @@ follow-redirects@1.5.10:
dependencies:
debug "=3.1.0"
follow-redirects@^1.0.0, follow-redirects@^1.10.0:
version "1.14.1"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.1.tgz#d9114ded0a1cfdd334e164e6662ad02bfd91ff43"
integrity sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg==
for-in@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80"
@ -6185,6 +6229,26 @@ http-https@^1.0.0:
resolved "https://registry.yarnpkg.com/http-https/-/http-https-1.0.0.tgz#2f908dd5f1db4068c058cd6e6d4ce392c913389b"
integrity sha1-L5CN1fHbQGjAWM1ubUzjkskTOJs=
http-proxy-middleware@^1.0.6:
version "1.3.1"
resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-1.3.1.tgz#43700d6d9eecb7419bf086a128d0f7205d9eb665"
integrity sha512-13eVVDYS4z79w7f1+NPllJtOQFx/FdUW4btIvVRMaRlUY9VGstAbo5MOhLEuUgZFRHn3x50ufn25zkj/boZnEg==
dependencies:
"@types/http-proxy" "^1.17.5"
http-proxy "^1.18.1"
is-glob "^4.0.1"
is-plain-obj "^3.0.0"
micromatch "^4.0.2"
http-proxy@^1.18.1:
version "1.18.1"
resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549"
integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==
dependencies:
eventemitter3 "^4.0.0"
follow-redirects "^1.0.0"
requires-port "^1.0.0"
http-signature@~1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1"
@ -6620,6 +6684,11 @@ is-plain-obj@^1.0.0, is-plain-obj@^1.1.0:
resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e"
integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4=
is-plain-obj@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-3.0.0.tgz#af6f2ea14ac5a646183a5bbdb5baabbc156ad9d7"
integrity sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==
is-plain-object@^2.0.3, is-plain-object@^2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677"
@ -6640,7 +6709,7 @@ is-resolvable@^1.0.0:
resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88"
integrity sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==
is-retry-allowed@^1.0.0:
is-retry-allowed@^1.0.0, is-retry-allowed@^1.1.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz#d778488bd0a4666a3be8a1482b9f2baafedea8b4"
integrity sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==
@ -7468,7 +7537,7 @@ micromatch@^3.1.10, micromatch@^3.1.4:
snapdragon "^0.8.1"
to-regex "^3.0.2"
micromatch@^4.0.0, micromatch@^4.0.4:
micromatch@^4.0.0, micromatch@^4.0.2, micromatch@^4.0.4:
version "4.0.4"
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9"
integrity sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==
@ -9806,6 +9875,11 @@ require-main-filename@^2.0.0:
resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b"
integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==
requires-port@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=
resolve-alpn@^1.0.0:
version "1.1.2"
resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.1.2.tgz#30b60cfbb0c0b8dc897940fe13fe255afcdd4d28"