welcome dialog; order UI facelift
This commit is contained in:
@@ -1,10 +1,17 @@
|
||||
<template>
|
||||
<router-view/>
|
||||
<support-chat/>
|
||||
<welcome-dialog v-model="prefs.newbie"/>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import SupportChat from "@/components/SupportChat.vue";
|
||||
import {detectChain} from "@/blockchain/wallet.js";
|
||||
import WelcomeDialog from "@/components/WelcomeDialog.vue";
|
||||
import {usePrefStore} from "@/store/store.js";
|
||||
|
||||
detectChain()
|
||||
|
||||
const prefs = usePrefStore()
|
||||
|
||||
</script>
|
||||
|
||||
@@ -619,3 +619,26 @@ export async function addNetwork(chainId) {
|
||||
});
|
||||
}
|
||||
|
||||
export async function addNetworkAndConnectWallet(chainId) {
|
||||
try {
|
||||
await switchChain(chainId)
|
||||
} catch (e) {
|
||||
if (e.code === 4001) {
|
||||
// explicit user rejection
|
||||
return
|
||||
} else if (e.code === 4902) {
|
||||
try {
|
||||
await addNetwork(chainId)
|
||||
} catch (e) {
|
||||
console.log(`Could not add network ${chainId}`)
|
||||
}
|
||||
} else
|
||||
console.log('switchChain() failure', e)
|
||||
}
|
||||
try {
|
||||
await connectWallet(chainId)
|
||||
} catch (e) {
|
||||
if (e.code !== 4001)
|
||||
console.log('connectWallet() failed', e)
|
||||
}
|
||||
}
|
||||
@@ -85,10 +85,14 @@ const subscribeEvents = [
|
||||
|
||||
let poolButtonTextElement = null
|
||||
|
||||
export function initFeeDropdown() {
|
||||
function initFeeDropdown() {
|
||||
const button = widget.createButton()
|
||||
button.setAttribute('title', 'See Pool Info and Choose Fee');
|
||||
button.addEventListener('click', function() { co.showPoolSelection = true });
|
||||
button.addEventListener('click', function () {
|
||||
co.showPoolSelection = true
|
||||
});
|
||||
button.id = 'pool-button'
|
||||
|
||||
button.style.height = '34px';
|
||||
button.style.display = 'flex';
|
||||
button.style.alignItems = 'center';
|
||||
@@ -98,13 +102,25 @@ export function initFeeDropdown() {
|
||||
button.addEventListener('mouseout', () => {
|
||||
button.style.backgroundColor = '';
|
||||
});
|
||||
button.id = 'pool-button'
|
||||
button.style.margin = '2px 0';
|
||||
button.style.borderRadius = '4px';
|
||||
button.classList.add('pool-button')
|
||||
|
||||
let img = document.createElement('img');
|
||||
img.src = '/arbitrum-logo.svg';
|
||||
img.style.width = '1em';
|
||||
img.style.marginRight = '0.2em';
|
||||
button.appendChild(img);
|
||||
|
||||
img = document.createElement('img');
|
||||
img.src = '/uniswap-logo.svg';
|
||||
img.style.height = '1.25em';
|
||||
img.style.marginRight = '0.2em';
|
||||
img.style.backgroundColor = 'white';
|
||||
img.style.borderRadius = '50%';
|
||||
button.appendChild(img);
|
||||
|
||||
const span = document.createElement('span');
|
||||
span.textContent = 'Pool';
|
||||
span.style.marginY = 'auto';
|
||||
button.appendChild(span);
|
||||
poolButtonTextElement = span
|
||||
@@ -115,7 +131,7 @@ export function initFeeDropdown() {
|
||||
export function updateFeeDropdown() {
|
||||
if (poolButtonTextElement===null) return
|
||||
const symbolItem = useChartOrderStore().selectedSymbol
|
||||
let text = 'Pool '
|
||||
let text = ''
|
||||
text += (symbolItem.fee / 10000).toFixed(2) + '%'
|
||||
const index = symbolItem.feeGroup.findIndex((p) => p[1] === symbolItem.fee)
|
||||
if (symbolItem.liquiditySymbol) {
|
||||
@@ -128,6 +144,10 @@ export function updateFeeDropdown() {
|
||||
poolButtonTextElement.textContent = text
|
||||
}
|
||||
|
||||
export function initTVButtons() {
|
||||
initFeeDropdown();
|
||||
}
|
||||
|
||||
export function initWidget(el) {
|
||||
const symbol = prefs.selectedTicker === null ? 'default' : prefs.selectedTicker
|
||||
const interval = prefs.selectedTimeframe === null ? '15' : prefs.selectedTimeframe
|
||||
@@ -166,7 +186,7 @@ export function initWidget(el) {
|
||||
widget.subscribe('onSelectedLineToolChanged', onSelectedLineToolChanged)
|
||||
widget.subscribe('mouse_down', mouseDown)
|
||||
widget.subscribe('mouse_up', mouseUp)
|
||||
widget.headerReady().then(()=>initFeeDropdown())
|
||||
widget.headerReady().then(()=>initTVButtons())
|
||||
widget.onChartReady(initChart)
|
||||
console.log('tv widget initialized')
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
v-model="os.tranches" :rules="[validateRequired,validateTranches]">
|
||||
<template v-slot:append-inner>tranches</template>
|
||||
</v-text-field>
|
||||
<v-text-field label='Skew' type="number" step="10" aria-valuemin="0" min="-100" max="100" variant="outlined"
|
||||
<v-text-field label='Balance' type="number" step="10" aria-valuemin="0" min="-100" max="100" variant="outlined"
|
||||
v-model="skew" clearable @click:clear="skew=0" suffix="%"/>
|
||||
<!-- todo deadline -->
|
||||
<v-table>
|
||||
|
||||
@@ -1,19 +1,31 @@
|
||||
<template>
|
||||
<div class="d-inline-flex">
|
||||
<v-img :src="`dexorder_full_${s.theme}mode.svg`" width="6em" inline/>
|
||||
<beta/>
|
||||
<v-img v-if="variant==='full'" :src="`/logo/dexorder_full_${s.theme}mode.svg`" width="6em" inline/>
|
||||
<v-img v-if="variant==='icon'" :src="`/logo/ico_${s.theme==='dark'?'black':'white'}_clip.png`" :width="width" inline/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
|
||||
import Beta from "@/components/Beta.vue";
|
||||
import {useStore} from "@/store/store.js";
|
||||
import {computed} from "vue";
|
||||
|
||||
const s = useStore()
|
||||
const props = defineProps({
|
||||
showTag: {type: Boolean, default: false}
|
||||
variant: {type: String, default: 'full', /* 'icon', */ },
|
||||
size: {type: String, default: 'medium'},
|
||||
})
|
||||
|
||||
const width = computed(()=>{
|
||||
return {
|
||||
'x-small': '0.75em',
|
||||
'small': '1.0em',
|
||||
'medium': '1.25em',
|
||||
'large': '1.75em',
|
||||
'x-large': '2.5em',
|
||||
}[props.size] || props.size;
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<v-card v-if="status!==Status.OK" rounded="0">
|
||||
<v-card-title>
|
||||
<!-- <v-icon icon="mdi-hand-wave" color="grey"/>-->
|
||||
Welcome to Dexorder Beta!
|
||||
Welcome to Dexorder!
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
Play with the order builder without an account by clicking on the <logo class="logo-small"/> logo or on
|
||||
@@ -40,11 +40,12 @@
|
||||
<script setup>
|
||||
import {useStore} from "@/store/store";
|
||||
import {computed, ref} from "vue";
|
||||
import {addNetwork, connectWallet, switchChain} from "@/blockchain/wallet.js";
|
||||
import {addNetworkAndConnectWallet} from "@/blockchain/wallet.js";
|
||||
import Btn from "@/components/Btn.vue";
|
||||
import Logo from "@/components/Logo.vue";
|
||||
import ApproveRegion from "@/components/ApproveRegion.vue";
|
||||
import TermsOfService from "@/components/TermsOfService.vue";
|
||||
import {track} from "@/track.js";
|
||||
|
||||
const s = useStore()
|
||||
const disabled = ref(false)
|
||||
@@ -67,34 +68,10 @@ function reload() {
|
||||
}
|
||||
|
||||
async function connect() {
|
||||
track('connect_wallet')
|
||||
disabled.value = true
|
||||
try {
|
||||
try {
|
||||
await switchChain(s.chainId)
|
||||
}
|
||||
catch (e) {
|
||||
if (e.code===4001) {
|
||||
// explicit user rejection
|
||||
return
|
||||
}
|
||||
else if (e.code===4902) {
|
||||
try {
|
||||
await addNetwork(s.chainId)
|
||||
}
|
||||
catch (e) {
|
||||
console.log(`Could not add network ${s.chainId}`)
|
||||
}
|
||||
}
|
||||
else
|
||||
console.log('switchChain() failure',e)
|
||||
}
|
||||
try {
|
||||
await connectWallet(s.chainId)
|
||||
}
|
||||
catch (e) {
|
||||
if (e.code!==4001)
|
||||
console.log('connectWallet() failed', e)
|
||||
}
|
||||
await addNetworkAndConnectWallet(s.chainId);
|
||||
}
|
||||
finally {
|
||||
disabled.value = false
|
||||
|
||||
35
src/components/OneTimeHint.vue
Normal file
35
src/components/OneTimeHint.vue
Normal file
@@ -0,0 +1,35 @@
|
||||
<template>
|
||||
<v-tooltip v-model="show" :close-on-content-click="true"/>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {computed, ref} from "vue";
|
||||
import {usePrefStore} from "@/store/store.js";
|
||||
|
||||
const prefs = usePrefStore()
|
||||
|
||||
const modelValue = defineModel()
|
||||
const hasBeenShown = ref(false)
|
||||
|
||||
const props = defineProps({
|
||||
key: {type: String, required: true},
|
||||
after: {type: String, default: null},
|
||||
})
|
||||
|
||||
const show = computed({
|
||||
get() {
|
||||
const result = !prefs.hints[props.key] && modelValue.value && (props.after === null || prefs.hints[props.after])
|
||||
&& !props.finished
|
||||
if (result)
|
||||
hasBeenShown.value = true
|
||||
else if (hasBeenShown.value)
|
||||
prefs.hints[props.key] = true
|
||||
return result
|
||||
},
|
||||
set(v) { if(!v) prefs.hints[props.key] = true}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
</style>
|
||||
@@ -2,6 +2,12 @@
|
||||
<v-dialog v-model="co.showPoolSelection" max-width="300">
|
||||
<v-card title="Pool Selection">
|
||||
<v-card-text>
|
||||
<div class="mb-2">
|
||||
<v-avatar image="/arbitrum-logo.svg" size="x-small" class="mr-1"/> Arbitrum One
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<v-avatar image="/uniswap-logo.svg" size="x-small" class="mr-1" style="background-color: white"/> Uniswap v3
|
||||
</div>
|
||||
<table>
|
||||
<tbody>
|
||||
<tr><td>Pool</td><td><scanner-button :addr="co.selectedSymbol.address"/></td></tr>
|
||||
|
||||
70
src/components/WelcomeDialog.vue
Normal file
70
src/components/WelcomeDialog.vue
Normal file
@@ -0,0 +1,70 @@
|
||||
<template>
|
||||
<v-dialog v-model="modelValue" max-width="600">
|
||||
<v-card title="Welcome to Dexorder!">
|
||||
<template #prepend><logo variant="icon" size="large"/></template>
|
||||
<v-card-text>
|
||||
<div class="d-flex">
|
||||
Dexorder powers up <div class="d-inline-flex align-self-baseline"><v-img src="/uniswap-logo.svg" width="1.25em" inline/></div>Uniswap with advanced ordering capabilities.
|
||||
</div>
|
||||
<p class="mt-2">
|
||||
Dexorder gives you a personal trading vault smart contract that trades its tokens with Uniswap only when
|
||||
your advanced order conditions are met.
|
||||
</p>
|
||||
<p class="mt-2">Level up your DeFi trading with:</p>
|
||||
<v-list density="compact">
|
||||
<v-list-item prepend-icon="mdi-clock-outline"><b>DCA / TWAP</b> <small>up to 1000 parts</small></v-list-item>
|
||||
<v-list-item prepend-icon="mdi-ray-vertex"><b>Limit Ladders</b> <small>capture ranges</small></v-list-item>
|
||||
<v-list-item prepend-icon="mdi-vector-line"><b>Diagonal Limits</b> <small>trade trends and channels</small></v-list-item>
|
||||
<v-list-item prepend-icon="mdi-chart-line"><b>Breakout Orders</b> <small>buy <i>above</i> a price level</small></v-list-item>
|
||||
<v-list-item prepend-icon="mdi-plus-minus"><b>Stop-loss</b> <small>coming soon</small></v-list-item>
|
||||
<!-- <v-list-item prepend-icon="mdi-cancel">One-click Cancel All</v-list-item>-->
|
||||
<v-list-item>
|
||||
<template #prepend>
|
||||
<v-avatar image="/arbitrum-logo.svg" size="1.5em" class="mr-4"/>
|
||||
</template>
|
||||
<template #default>
|
||||
<b>Arbitrum One</b> support <small>fast and cheap</small>
|
||||
</template>
|
||||
</v-list-item>
|
||||
<v-list-item>
|
||||
<template #prepend>
|
||||
<v-avatar image="/uniswap-logo.svg" size="1.5em" class="mr-4" style="background-color: white"/>
|
||||
</template>
|
||||
<template #default>
|
||||
<b>Uniswap</b> v3 support <small>high liquidity</small>
|
||||
</template>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-card-text>
|
||||
<v-card-actions class="justify-center mb-9">
|
||||
<!-- <v-btn variant="text" @click="learnMore" class="justify-end">Learn More</v-btn>-->
|
||||
<v-btn variant="tonal" color="primary" @click="tryIt" class="justify-center">Try It!</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {track} from "@/track.js";
|
||||
import Logo from "@/components/Logo.vue";
|
||||
|
||||
const modelValue = defineModel()
|
||||
|
||||
function tryIt() {
|
||||
track('try-it')
|
||||
modelValue.value = false
|
||||
}
|
||||
|
||||
function learnMore() {
|
||||
track('learn-more')
|
||||
window.open('https://dexorder.trade/introduction.html', 'dexordertrade')
|
||||
modelValue.value = false
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
small {
|
||||
margin-left: 1em;
|
||||
}
|
||||
</style>
|
||||
@@ -1,20 +1,32 @@
|
||||
<template>
|
||||
<row-bar :color="builder.color">
|
||||
<color-band :color="builder.color"/>
|
||||
<slot/>
|
||||
<div class="align-self-center ml-auto mr-3 trashcan">
|
||||
<v-btn icon="mdi-delete" @click="deleteMyBuilder"/>
|
||||
<v-sheet dense style="overflow-y: hidden" class="pa-1 pb-2">
|
||||
<h3 class="ml-1">{{name}}</h3>
|
||||
<div class="d-flex flex-row align-content-stretch">
|
||||
<div class="ml-2"> </div>
|
||||
<slot/>
|
||||
<div class="align-self-center ml-auto mr-3 trashcan">
|
||||
<v-btn icon="mdi-delete" @click="showDeleteDialog=true"/>
|
||||
</div>
|
||||
</div>
|
||||
</row-bar>
|
||||
<v-dialog v-model="showDeleteDialog" :max-width="300">
|
||||
<v-card :title="`Delete ${name}?`" :text="`Do you want to delete this ${name}?`">
|
||||
<v-card-actions>
|
||||
<v-btn @click="showDeleteDialog=false">Keep</v-btn>
|
||||
<v-btn variant="tonal" color="error" @click="deleteMyBuilder">Delete</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</v-sheet>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {builderFuncs, deleteBuilder} from "@/orderbuild.js";
|
||||
import ColorBand from "@/components/chart/ColorBand.vue";
|
||||
import RowBar from "@/components/chart/RowBar.vue";
|
||||
import {onBeforeUnmount, onMounted, onUnmounted, onUpdated, watchEffect} from "vue";
|
||||
import {onBeforeUnmount, onMounted, onUnmounted, onUpdated, ref, watchEffect} from "vue";
|
||||
|
||||
const props = defineProps({
|
||||
name: String,
|
||||
order: Object,
|
||||
builder: Object,
|
||||
buildTranches: {type: Function},
|
||||
@@ -23,6 +35,7 @@ const props = defineProps({
|
||||
})
|
||||
const emit = defineEmits(['update:builder'])
|
||||
|
||||
const showDeleteDialog = ref(false)
|
||||
|
||||
let lastId = props.builder.id
|
||||
builderFuncs[props.builder.id] = props.buildTranches
|
||||
@@ -44,6 +57,7 @@ if (props.deleteShapes)
|
||||
onBeforeUnmount(props.deleteShapes)
|
||||
|
||||
function deleteMyBuilder() {
|
||||
showDeleteDialog.value = false;
|
||||
deleteBuilder(props.order, props.builder);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
|
||||
<template>
|
||||
<row-bar :color="color">
|
||||
<color-band :color="color"/>
|
||||
<row-bar :color="sideColor">
|
||||
<div style="width: 100%" class="justify-start align-content-start">
|
||||
<order-amount :order="props.order" :color="color"/>
|
||||
<order-amount :order="props.order" class="mt-2" :color="sideColor"/>
|
||||
<template v-for="b in builders">
|
||||
<builder-factory :order="order" :builder="b"/>
|
||||
</template>
|
||||
@@ -12,35 +11,35 @@
|
||||
<v-tooltip text="Up to 1000 equal parts spread across time" location="top">
|
||||
<template v-slot:activator="{ props }">
|
||||
<span v-bind="props">
|
||||
<v-btn :color="color" variant="text" prepend-icon="mdi-clock-outline" @click="build(order,'DCABuilder')">DCA</v-btn>
|
||||
<v-btn :class="order.buy?'green':'red'" variant="text" prepend-icon="mdi-clock-outline" @click="build(order,'DCABuilder')">DCA</v-btn>
|
||||
</span>
|
||||
</template>
|
||||
</v-tooltip>
|
||||
<v-tooltip text="Trade a price level" location="top">
|
||||
<template v-slot:activator="{ props }">
|
||||
<span v-bind="props">
|
||||
<v-btn :color="color" variant="text" prepend-icon="mdi-ray-vertex" @click="build(order,'LimitBuilder')">Limit</v-btn>
|
||||
<v-btn :class="order.buy?'green':'red'" variant="text" prepend-icon="mdi-ray-vertex" @click="build(order,'LimitBuilder')">Limit</v-btn>
|
||||
</span>
|
||||
</template>
|
||||
</v-tooltip>
|
||||
<v-tooltip text="Trade trends and channels" location="top">
|
||||
<template v-slot:activator="{ props }">
|
||||
<span v-bind="props">
|
||||
<v-btn :color="color" variant="text" prepend-icon="mdi-vector-line" @click="build(order,'DiagonalBuilder')">Diagonal</v-btn>
|
||||
<v-btn :class="order.buy?'green':'red'" variant="text" prepend-icon="mdi-vector-line" @click="build(order,'DiagonalBuilder')">Diagonal</v-btn>
|
||||
</span>
|
||||
</template>
|
||||
</v-tooltip>
|
||||
<v-tooltip text="Up to 10 weighted parts spaced out in time" location="top">
|
||||
<template v-slot:activator="{ props }">
|
||||
<span v-bind="props">
|
||||
<v-btn :color="color" variant="text" prepend-icon="mdi-reorder-vertical" @click="build(order,'DateBuilder')">Dates</v-btn>
|
||||
<v-btn :class="order.buy?'green':'red'" variant="text" prepend-icon="mdi-reorder-vertical" @click="build(order,'DateBuilder')">Dates</v-btn>
|
||||
</span>
|
||||
</template>
|
||||
</v-tooltip>
|
||||
<v-tooltip text="Coming Soon! Stoplosses and Takeprofits" location="top">
|
||||
<template v-slot:activator="{ props }">
|
||||
<span v-bind="props">
|
||||
<v-btn :color="color" variant="text" prepend-icon="mdi-plus-minus" disabled>Stoploss</v-btn>
|
||||
<v-btn :class="order.buy?'green':'red'" variant="text" prepend-icon="mdi-plus-minus" disabled>Stoploss</v-btn>
|
||||
</span>
|
||||
</template>
|
||||
</v-tooltip>
|
||||
@@ -55,20 +54,19 @@
|
||||
|
||||
import BuilderFactory from "@/components/chart/BuilderFactory.vue";
|
||||
import {builderFuncs, newBuilder, orderFuncs, useChartOrderStore} from "@/orderbuild.js";
|
||||
import {useOrderStore, useStore} from "@/store/store.js";
|
||||
import {useStore} from "@/store/store.js";
|
||||
import {computed, onUnmounted, onUpdated, watchEffect} from "vue";
|
||||
import {toPrecision} from "@/misc.js";
|
||||
import {useTheme} from "vuetify";
|
||||
import RowBar from "@/components/chart/RowBar.vue";
|
||||
import ColorBand from "@/components/chart/ColorBand.vue";
|
||||
import Color from "color";
|
||||
import {newOrder} from "@/blockchain/orderlib.js";
|
||||
import OrderAmount from "@/components/chart/OrderAmount.vue";
|
||||
import {track} from "@/track.js";
|
||||
|
||||
const props = defineProps(['order'])
|
||||
const s = useStore()
|
||||
const co = useChartOrderStore()
|
||||
const os = useOrderStore()
|
||||
|
||||
const marketBuilder = newBuilder('MarketBuilder')
|
||||
|
||||
@@ -79,6 +77,7 @@ const tokenOut = computed(()=>props.order.buy ? co.baseToken : co.quoteToken)
|
||||
|
||||
console.log('order', props.order)
|
||||
function build(order, component, options={}) {
|
||||
track('build', {builder:component})
|
||||
order.builders.push(newBuilder(component, options))
|
||||
}
|
||||
|
||||
@@ -179,7 +178,8 @@ onUnmounted(() => delete orderFuncs[lastId])
|
||||
|
||||
|
||||
const theme = useTheme().current
|
||||
const color = computed(()=>new Color(props.order.buy?theme.value.colors.success:theme.value.colors.error).darken(0.2).string())
|
||||
const sideColor = computed(()=>new Color(props.order.buy?theme.value.colors.success:theme.value.colors.error).darken(0.2).string())
|
||||
const color = computed(()=>theme.value.colors["on-background"])
|
||||
// const lightColor = computed(() => lightenColor(color.value))
|
||||
// const faintColor = computed(() => lightenColor2(color.value))
|
||||
// const colorStyle = computed(() => { return {'color': color.value} })
|
||||
@@ -189,3 +189,8 @@ const color = computed(()=>new Color(props.order.buy?theme.value.colors.success:
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@use "src/styles/vars" as *;
|
||||
.v-btn.green:hover {color: $green !important;}
|
||||
.v-btn.red:hover {color: $red !important;}
|
||||
</style>
|
||||
@@ -65,6 +65,7 @@ import ToolbarPane from "@/components/chart/ToolbarPane.vue";
|
||||
import NeedsChart from "@/components/NeedsChart.vue";
|
||||
import {PlaceOrderTransaction} from "@/blockchain/transaction.js";
|
||||
import {errorSuggestsMissingVault} from "@/misc.js";
|
||||
import {track} from "@/track.js";
|
||||
|
||||
const s = useStore()
|
||||
const co = useChartOrderStore()
|
||||
@@ -148,6 +149,7 @@ watchEffect(()=>{
|
||||
let built = []
|
||||
|
||||
async function placeOrder() {
|
||||
track('place_order')
|
||||
const chartOrders = co.orders;
|
||||
const allWarns = []
|
||||
built = []
|
||||
@@ -187,7 +189,7 @@ async function doPlaceOrder() {
|
||||
else {
|
||||
s.creatingVault = false
|
||||
oldFailed.bind(this)(e)
|
||||
placementError.value = true
|
||||
placementError.value = e.info?.error?.code !== 4001
|
||||
}
|
||||
}
|
||||
tx.submit() // this assigns the tx to walletStore.transaction
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
<template class="d-flex align-content-center flex-column" style="height: 100%; width: 100%;">
|
||||
<builder-panel :order="order" :builder="builder" :build-tranches="buildTranches"
|
||||
:adjust-shapes="adjustShapes" :delete-shapes="deleteShapes">
|
||||
:adjust-shapes="adjustShapes" :delete-shapes="deleteShapes" name="DCA">
|
||||
<div class="d-flex flex-column" style="width: 100%;">
|
||||
|
||||
<h3>DCA</h3>
|
||||
<div class="d-flex flex-row">
|
||||
<div class="align-self-center">Start:</div>
|
||||
<div class="d-flex flex-row mb-5">
|
||||
<div class="align-self-center mr-3">Start:</div>
|
||||
<absolute-time-entry v-model="startTime"/>
|
||||
</div>
|
||||
|
||||
@@ -14,6 +12,7 @@
|
||||
<v-text-field label="Split into" type="number" variant="outlined"
|
||||
aria-valuemin="1" aria-valuemax="100" min="1" max="1000" step="1"
|
||||
:hint="partsGasHint" :persistent-hint="true"
|
||||
:color="color"
|
||||
v-model="parts" v-auto-select class="parts mr-3">
|
||||
<template v-slot:append-inner>
|
||||
parts
|
||||
@@ -22,11 +21,12 @@
|
||||
</div>
|
||||
|
||||
<div class="mr-3">
|
||||
<order-amount :order="props.order" :multiplier="props.builder.tranches" :color="null" style="width: 20em"/>
|
||||
<order-amount :order="props.order" :multiplier="props.builder.tranches" :color="color" style="width: 20em"/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<v-text-field type="number" variant="outlined" :min="1" v-model="displayedInterval" class="interval"
|
||||
:color="color"
|
||||
:label="intervalIsTotal ? 'Total completion time' : 'Time between parts'" v-auto-select>
|
||||
<template v-slot:prepend-inner>
|
||||
<v-btn variant="outlined"
|
||||
|
||||
@@ -74,7 +74,7 @@ builderDefaults(props.builder, {
|
||||
relative: false,
|
||||
slippage: DEFAULT_SLIPPAGE,
|
||||
rungs: 1,
|
||||
skew: 0,
|
||||
balance: 0,
|
||||
color: defaultColor,
|
||||
buy: true,
|
||||
})
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
<v-text-field type="number" v-model="price1A" min="0"
|
||||
density="compact" hide-details variant="outlined"
|
||||
class="mx-1 my-2 price"
|
||||
:color="color" :base-color="color"
|
||||
:color="color"
|
||||
label="Price"
|
||||
/>
|
||||
</td>
|
||||
@@ -49,7 +49,7 @@
|
||||
<v-text-field type="number" v-model="price1B" min="0"
|
||||
density="compact" hide-details variant="outlined"
|
||||
class="mx-1 my-2 price"
|
||||
:color="color" :base-color="color"
|
||||
:color="color"
|
||||
label="Price"
|
||||
/>
|
||||
</td>
|
||||
@@ -68,7 +68,7 @@
|
||||
<v-text-field type="number" v-model="price2A" min="0"
|
||||
density="compact" hide-details variant="outlined"
|
||||
class="mx-1 my-2 price"
|
||||
:color="color" :base-color="color"
|
||||
:color="color"
|
||||
label="Price"
|
||||
/>
|
||||
</td>
|
||||
@@ -82,7 +82,7 @@
|
||||
<v-text-field type="number" v-model="price2B" min="0"
|
||||
density="compact" hide-details variant="outlined"
|
||||
class="mx-1 my-2 price"
|
||||
:color="color" :base-color="color"
|
||||
:color="color"
|
||||
label="Price"
|
||||
/>
|
||||
</td>
|
||||
@@ -123,7 +123,7 @@ builderDefaults(props.builder, {
|
||||
extendLeft: false,
|
||||
extendRight: true,
|
||||
rungs: 1,
|
||||
skew: 0,
|
||||
balance: 0,
|
||||
breakout: false,
|
||||
color: defaultColor,
|
||||
buy: true,
|
||||
@@ -410,7 +410,8 @@ const name = computed(()=>props.builder.breakout?'Breakout':'Limit')
|
||||
const description = computed(()=>{
|
||||
const buy = props.order.buy
|
||||
const above = buy === props.builder.breakout
|
||||
return (buy?'Buy ':'Sell ')+(above?'above':'below')+' the line'
|
||||
const plural = props.builder
|
||||
return (buy?'Buy ':'Sell ')+(above?'above':'below')+' the line'+(plural?'s':'')
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<rung-builder :name="(builder.breakout?'Breakout':'Limit')"
|
||||
<rung-builder :name="(builder.breakout?'Breakout':'Limit')+(builder.rungs>1?' Ladder':'')"
|
||||
:description="description"
|
||||
:order="order" :builder="builder"
|
||||
v-model="priceEndpoints" :mode="0" :flip="flipped"
|
||||
@@ -7,7 +7,7 @@
|
||||
:get-model-value="getModelValue" :set-model-value="setModelValue"
|
||||
:set-values="setPrices" :set-weights="setWeights"
|
||||
:std-width="stdWidth" :build-tranches="buildTranches">
|
||||
<table>
|
||||
<table class="rb">
|
||||
<tbody>
|
||||
<template v-if="prices.length>1">
|
||||
<tr>
|
||||
@@ -15,11 +15,10 @@
|
||||
<v-text-field type="number" v-model="higherPrice" min="0"
|
||||
density="compact" hide-details class="mx-1 my-2" variant="outlined"
|
||||
label="Price"
|
||||
:color="color" :base-color="color"
|
||||
style="flex: 6em"
|
||||
/>
|
||||
</td>
|
||||
<td class="weight">{{ allocationTexts[higherIndex] }}</td>
|
||||
<td class="weight" style="vertical-align: bottom">{{ allocationTexts[higherIndex] }}</td>
|
||||
</tr>
|
||||
<tr v-for="i in innerIndexes" class="ml-5">
|
||||
<td class="pl-5">{{ prices[i] }}</td>
|
||||
@@ -31,7 +30,6 @@
|
||||
<v-text-field type="number" v-model="lowerPrice" min="0"
|
||||
density="compact" hide-details class="mx-1 my-2" variant="outlined"
|
||||
label="Price"
|
||||
:color="color" :base-color="color"
|
||||
style="flex: 6em"
|
||||
/>
|
||||
</td>
|
||||
@@ -71,7 +69,7 @@ builderDefaults(props.builder, {
|
||||
priceA: null,
|
||||
priceB: null,
|
||||
rungs: 1,
|
||||
skew: 0,
|
||||
balance: 0,
|
||||
breakout: false,
|
||||
color: defaultColor,
|
||||
buy: true,
|
||||
@@ -93,7 +91,7 @@ function buildTranches() {
|
||||
fraction: w * MAX_FRACTION,
|
||||
})
|
||||
const symbol = co.selectedSymbol
|
||||
console.log('symbol', symbol, p)
|
||||
// console.log('symbol', symbol, p)
|
||||
applyLinePoint(t, symbol, order.buy, p, builder.breakout)
|
||||
tranches.push(t)
|
||||
}
|
||||
@@ -228,5 +226,16 @@ td.weight {
|
||||
padding-right: 0.5em;
|
||||
text-align: right;
|
||||
}
|
||||
table.rb {
|
||||
padding: 0;
|
||||
border-spacing: 0;
|
||||
tbody {
|
||||
border: none;
|
||||
padding: 0;
|
||||
}
|
||||
td {
|
||||
vertical-align: top;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
@@ -1,39 +1,67 @@
|
||||
<template>
|
||||
<v-text-field type="number" inputmode="numeric" pattern="[0-9]*\.?[0-9]*" v-model="amount" variant="outlined"
|
||||
density="compact"
|
||||
:hint="available" :persistent-hint="true"
|
||||
min="0"
|
||||
class="amount py-2" :color="color"
|
||||
:label="props.order.amountIsTokenA ?
|
||||
<div class="d-flex flex-row align-start">
|
||||
<!--
|
||||
<v-btn v-if="!multiplier"
|
||||
variant="outlined" :color="color"
|
||||
:text="(props.order.buy ? 'Buy ' : 'Sell ') + co.selectedSymbol.base.s"
|
||||
@click="props.order.buy=!props.order.buy"
|
||||
class="ml-3 mt-2 mr-2"
|
||||
size="2.5em"
|
||||
style="width: 9em"
|
||||
/>
|
||||
-->
|
||||
<div class="d-flex flex-column align-center">
|
||||
<v-switch v-if="!multiplier" v-model="switchModel" :color="sideColor" :base-color="sideColor" density="compact"
|
||||
class="my-0 mx-3 clickable">
|
||||
<template #prepend>
|
||||
<span :style="order.buy?{color:theme.colors.success}:{}" @click="order.buy=true" class="bs-button buy">Buy<br/>{{co.selectedSymbol.base.s}}</span>
|
||||
</template>
|
||||
<template #append>
|
||||
<span :style="!order.buy?{color:theme.colors.error}:{}" @click="order.buy=false" class="bs-button sell">Sell<br/>{{co.selectedSymbol.base.s}}</span>
|
||||
</template>
|
||||
</v-switch>
|
||||
</div>
|
||||
<v-text-field type="number" inputmode="numeric" pattern="[0-9]*\.?[0-9]*" v-model="amount" variant="outlined"
|
||||
:hint="available" :persistent-hint="true"
|
||||
min="0"
|
||||
class="amount mx-3"
|
||||
style="max-width: 20em"
|
||||
:color="color"
|
||||
:label="order.amountIsTokenA ?
|
||||
(multiplier ? 'Amount each' : 'Amount') :
|
||||
((multiplier ? 'Value each in ' : 'Value in ')+co.selectedSymbol.quote.s)">
|
||||
<template #prepend>
|
||||
<v-btn v-if="!multiplier"
|
||||
variant="outlined" :color="color" class="ml-3"
|
||||
:text="(props.order.buy ? 'Buy ' : 'Sell ') + co.selectedSymbol.base.s"
|
||||
@click="props.order.buy=!props.order.buy"/>
|
||||
</template>
|
||||
<template #prepend-inner>
|
||||
<v-btn variant="text" text="max" class="px-0" size="small"
|
||||
:disabled="!maxAmount || props.order.amountIsTokenA===props.order.buy && !co.price" @click="setMax"/>
|
||||
</template>
|
||||
<template #append-inner>
|
||||
<v-btn :text="props.order.amountIsTokenA?co.baseToken.s:co.quoteToken.s+' worth'"
|
||||
:color="color" variant="text" @click="toggleAmountToken" style="width: 7em"/>
|
||||
</template>
|
||||
</v-text-field>
|
||||
<template #prepend-inner>
|
||||
<v-btn variant="text" text="max" class="px-0" size="small"
|
||||
:disabled="!maxAmount || order.amountIsTokenA===order.buy && !co.price" @click="setMax"/>
|
||||
</template>
|
||||
<template #append-inner>
|
||||
<v-btn :text="order.amountIsTokenA?co.baseToken.s:co.quoteToken.s+' worth'"
|
||||
variant="text" @click="toggleAmountToken" style="width: 7em"/>
|
||||
</template>
|
||||
</v-text-field>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {useChartOrderStore} from "@/orderbuild.js";
|
||||
import {computed, ref} from "vue";
|
||||
import {useStore} from "@/store/store.js";
|
||||
import Color from "color";
|
||||
import {useTheme} from "vuetify";
|
||||
|
||||
const props = defineProps(['order', 'multiplier', 'color'])
|
||||
|
||||
const s = useStore()
|
||||
const co = useChartOrderStore()
|
||||
|
||||
const theme = useTheme().current
|
||||
console.log('theme', theme.value)
|
||||
const sideColor = computed(()=>new Color(props.order.buy?theme.value.colors.success:theme.value.colors.error).darken(0.2).string())
|
||||
const switchModel = computed({
|
||||
get() {return !props.order.buy},
|
||||
set(v) {props.order.buy=!v}
|
||||
})
|
||||
|
||||
const amount = computed({
|
||||
get() {
|
||||
let result = props.order.amount
|
||||
@@ -94,7 +122,14 @@ function toggleAmountToken() {
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@use "src/styles/vars" as *;
|
||||
.amount {
|
||||
max-width: 30em;
|
||||
}
|
||||
</style>
|
||||
.bs-button {
|
||||
text-align: center;
|
||||
user-select: none;
|
||||
&.buy:hover {color: $green;}
|
||||
&.sell:hover {color: $red;}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,20 +1,28 @@
|
||||
<template>
|
||||
<builder-panel :order="order" :builder="builder" :build-tranches="buildTranches"
|
||||
:adjust-shapes="adjustShapes" :delete-shapes="deleteShapes">
|
||||
:adjust-shapes="adjustShapes" :delete-shapes="deleteShapes" :name="name">
|
||||
<div style="min-width: 4em; font-size: larger" :style="colorStyle"
|
||||
class="d-flex flex-column align-self-start ml-2">
|
||||
class="d-flex flex-column">
|
||||
<!--
|
||||
<div class="flex-row align-items-center">
|
||||
<v-btn variant="outlined" style="width: 8em"
|
||||
@click="()=>{if (props.builder.breakout!==undefined) props.builder.breakout=!props.builder.breakout}">{{ name }}</v-btn>
|
||||
<div class="description w-100 text-center">{{description}}</div>
|
||||
</div>
|
||||
-->
|
||||
<v-text-field type="number" v-model="rungs"
|
||||
density="compact" hide-details class="mx-1 my-2" variant="outlined"
|
||||
label="Rungs"
|
||||
:color="color" :base-color="color" min="1" :max="MAX_RUNGS"
|
||||
:color="color"
|
||||
min="1" :max="MAX_RUNGS"
|
||||
:disabled="rungsDisabled"
|
||||
style="width: 6.6em;"
|
||||
style="width: 6.6em; max-height: 2.5em; height: 2.5em"
|
||||
/>
|
||||
<v-switch v-model="builder.breakout" label="Breakout" persistent-hint :color="color" hide-details/>
|
||||
<div class="mx-auto"><span style="font-size: .7em; vertical-align: top"
|
||||
:style="builder.breakout?{color:color}:null">
|
||||
{{description}}
|
||||
</span></div>
|
||||
</div>
|
||||
|
||||
<slot/>
|
||||
@@ -23,15 +31,15 @@
|
||||
<v-icon icon="mdi-chat-alert-outline" color="grey" class="mr-1"/>
|
||||
Click the chart!
|
||||
</div>
|
||||
<div v-if="rungs>1" class="mx-2 d-flex align-start">
|
||||
<div v-if="rungs>1" class="mx-2 d-flex justify-start">
|
||||
<div class="d-flex align-center mt-2">
|
||||
<v-slider v-if="rungs>1" :direction="orientation?'vertical':'horizontal'" min="-100" max="100" v-model="skew100"
|
||||
<v-slider v-if="rungs>1" :direction="orientation?'vertical':'horizontal'" min="-100" max="100" v-model="balance100"
|
||||
class="no-slider-bg ml-2 mr-4" hide-details/>
|
||||
<v-text-field type="number" v-model="skew100" min="-100" max="100"
|
||||
density="compact" hide-details variant="outlined" label="Skew" step="5"
|
||||
:color="color" :base-color="color" class="skew">
|
||||
<v-text-field type="number" v-model="balance100" min="-100" max="100"
|
||||
density="compact" hide-details variant="outlined" label="Balance" step="5"
|
||||
class="balance">
|
||||
<template v-slot:prepend>
|
||||
<v-btn icon="mdi-scale-balance" variant="plain" @click="builder.skew=0" :color="color"/>
|
||||
<v-btn icon="mdi-scale-balance" variant="plain" @click="builder.balance=0"/>
|
||||
</template>
|
||||
</v-text-field>
|
||||
</div>
|
||||
@@ -70,7 +78,7 @@ const props = defineProps({
|
||||
stdWidth: [Number, Array],
|
||||
shape: Function, // shape() -> Shape
|
||||
mode: { type: Number, default: 0 }, // rung addition mode: 0 = split, 1 = extend
|
||||
flip: { type: Boolean, default: false }, // if true, the skew slider is flipped upside-down
|
||||
flip: { type: Boolean, default: false }, // if true, the balance slider is flipped upside-down
|
||||
orientation: { type: Number, default: 1 }, // 0 = horizontal slider, 1 = vertical
|
||||
// values may be scalars or vector arrays
|
||||
getModelValue: Function, // getModelValue(model) -> value
|
||||
@@ -82,9 +90,9 @@ const props = defineProps({
|
||||
|
||||
const flippedSign = computed(()=>props.flip?-1:1)
|
||||
|
||||
const skew100 = computed( {
|
||||
get() {return flippedSign.value*props.builder.skew*100},
|
||||
set(v) {props.builder.skew = flippedSign.value*v/100; }
|
||||
const balance100 = computed( {
|
||||
get() {return flippedSign.value*props.builder.balance*100},
|
||||
set(v) {props.builder.balance = flippedSign.value*v/100; }
|
||||
} )
|
||||
|
||||
// validity checks
|
||||
@@ -194,14 +202,14 @@ const values = computed(()=>{
|
||||
|
||||
|
||||
const weights = computed(() => {
|
||||
// const skew = props.flip ? -props.builder.skew : props.builder.skew
|
||||
// const balance = props.flip ? -props.builder.balance : props.builder.balance
|
||||
const most = 0.998
|
||||
let skew = -props.builder.skew
|
||||
if (skew <= -1)
|
||||
skew = -most
|
||||
else if (skew >= 1)
|
||||
skew = most
|
||||
const ws = linearWeights(props.builder.rungs, skew)
|
||||
let balance = -props.builder.balance
|
||||
if (balance <= -1)
|
||||
balance = -most
|
||||
else if (balance >= 1)
|
||||
balance = most
|
||||
const ws = linearWeights(props.builder.rungs, balance)
|
||||
if (props.setWeights)
|
||||
props.setWeights(ws)
|
||||
return ws
|
||||
@@ -228,7 +236,8 @@ const color = computed({
|
||||
}
|
||||
})
|
||||
const colorStyle = computed(() => {
|
||||
return {'color': color.value}
|
||||
// return {'color': color.value}
|
||||
return {}
|
||||
})
|
||||
|
||||
|
||||
@@ -428,7 +437,7 @@ if (!endpoints.value[0])
|
||||
:deep(.v-slider.no-slider-bg .v-slider-track__fill) {
|
||||
background-color: inherit !important;
|
||||
}
|
||||
.skew {
|
||||
.balance {
|
||||
min-width: 9em;
|
||||
max-width: 12em;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
<v-icon icon="mdi-arrow-up-bold" size="x-small" class="arrow" color="green"/>
|
||||
<span class="clickable">dexorder</span>
|
||||
</span>
|
||||
<v-chip text="BETA" size="x-small" color="red" class="mx-1"/>
|
||||
</v-app-bar-title>
|
||||
|
||||
<v-btn icon="mdi-safe-square" color="grey-darken-2" text="Vault" @click="route('Assets')"></v-btn>
|
||||
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
darken1,
|
||||
darkMiddleShadeIndex,
|
||||
light,
|
||||
lightMiddleShadeIndex, numShades, pageShade,
|
||||
lightMiddleShadeIndex, numShades, surfaceShade,
|
||||
printContrast
|
||||
} from "../../theme.js";
|
||||
|
||||
@@ -28,13 +28,13 @@ function makeColors(isLight) {
|
||||
const ink = k[printContrast] // text color
|
||||
function darken(cols,shades) {return cols[base+(isLight?-shades:shades)]}
|
||||
// https://vuetifyjs.com/en/introduction/why-vuetify/#feature-guides
|
||||
return {
|
||||
background: k[pageShade],
|
||||
surface: k[pageShade],
|
||||
'surface-bright': k[pageShade],
|
||||
'surface-light': k[pageShade+2],
|
||||
const colors = {
|
||||
background: k[0],
|
||||
surface: k[surfaceShade],
|
||||
'surface-bright': k[surfaceShade],
|
||||
'surface-light': k[surfaceShade+2],
|
||||
'surface-variant': k[14],
|
||||
'on-surface-variant': k[pageShade+2],
|
||||
'on-surface-variant': k[surfaceShade+2],
|
||||
primary: c.greens[base],
|
||||
'primary-darken-1': darken(c.greens, darken1),
|
||||
secondary: c.blues[base],
|
||||
@@ -53,6 +53,8 @@ function makeColors(isLight) {
|
||||
"on-warning": ink,
|
||||
"on-error": ink,
|
||||
}
|
||||
console.log('colors', isLight?'light':'dark', colors)
|
||||
return colors;
|
||||
}
|
||||
|
||||
const lightColors = makeColors(true)
|
||||
|
||||
@@ -215,11 +215,13 @@ export const usePrefStore = defineStore({
|
||||
state: ()=> {
|
||||
// user preferences
|
||||
const inverted = ref({})
|
||||
const hints = ref({})
|
||||
const newbie = ref(true)
|
||||
const acceptedTos = ref('NO TOS ACCEPTED')
|
||||
const selectedTicker = ref(null)
|
||||
const selectedTimeframe = ref(null)
|
||||
const timezone = ref('Etc/UTC')
|
||||
return {inverted, acceptedTos, selectedTicker, selectedTimeframe, timezone}
|
||||
return {inverted, acceptedTos, selectedTicker, selectedTimeframe, timezone, newbie, hints, }
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// these must also be set in vuetify.js for the "theme"
|
||||
// see src/plugins/vuetify.js esp. makeColors()
|
||||
@use 'sass:color';
|
||||
|
||||
// OFFICIAL DEXORDER PALETTE
|
||||
|
||||
12
src/track.js
Normal file
12
src/track.js
Normal file
@@ -0,0 +1,12 @@
|
||||
export let tracking_enabled = true
|
||||
|
||||
export function track(event, info) {
|
||||
if (tracking_enabled) {
|
||||
if (window.gtag !== undefined)
|
||||
window.gtag('event', event, info)
|
||||
else {
|
||||
console.log('gtag not available')
|
||||
tracking_enabled = false
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user