welcome dialog; order UI facelift

This commit is contained in:
tim
2025-04-09 20:57:29 -04:00
parent 94c7b6ddb4
commit 556554fbf3
34 changed files with 395 additions and 134 deletions

View File

@@ -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>

View File

@@ -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)
}
}

View File

@@ -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')
}

View File

@@ -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>

View File

@@ -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">

View File

@@ -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

View 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>

View File

@@ -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>

View 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>

View File

@@ -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">&nbsp;</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);
}

View File

@@ -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>

View File

@@ -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

View File

@@ -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"

View File

@@ -74,7 +74,7 @@ builderDefaults(props.builder, {
relative: false,
slippage: DEFAULT_SLIPPAGE,
rungs: 1,
skew: 0,
balance: 0,
color: defaultColor,
buy: true,
})

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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;
}

View File

@@ -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>

View File

@@ -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)

View File

@@ -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, }
},
})

View File

@@ -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
View 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
}
}
}