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

@@ -12,7 +12,16 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<link href="https://fonts.googleapis.com/css2?family=Orbitron&family=Saira+Semi+Condensed&display=swap" rel="stylesheet">
</head>
<body>
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-L6F3Z6SBC7"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-L6F3Z6SBC7');
</script>
<div id="app"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.5.4/socket.io.js"></script>
<script src="/charting_library/charting_library.js"></script>

View File

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

11
public/uniswap-logo.svg Normal file
View File

@@ -0,0 +1,11 @@
<svg width="641" height="640" viewBox="0 0 641 640" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M224.534 123.226C218.692 122.32 218.445 122.213 221.195 121.791C226.464 120.98 238.905 122.085 247.479 124.123C267.494 128.881 285.707 141.069 305.148 162.714L310.313 168.465L317.701 167.277C348.828 162.275 380.493 166.25 406.978 178.485C414.264 181.851 425.752 188.552 427.187 190.274C427.645 190.822 428.485 194.355 429.053 198.124C431.02 211.164 430.036 221.16 426.047 228.625C423.877 232.688 423.756 233.975 425.215 237.452C426.38 240.227 429.627 242.28 432.843 242.276C439.425 242.267 446.509 231.627 449.791 216.823L451.095 210.943L453.678 213.868C467.846 229.92 478.974 251.811 480.885 267.393L481.383 271.455L479.002 267.762C474.903 261.407 470.785 257.08 465.512 253.591C456.006 247.301 445.955 245.161 419.337 243.758C395.296 242.491 381.69 240.438 368.198 236.038C345.244 228.554 333.672 218.587 306.405 182.812C294.294 166.923 286.808 158.131 279.362 151.051C262.442 134.964 245.816 126.527 224.534 123.226Z" fill="#FF007A"/>
<path d="M432.61 158.704C433.215 148.057 434.659 141.033 437.562 134.62C438.711 132.081 439.788 130.003 439.954 130.003C440.12 130.003 439.621 131.877 438.844 134.167C436.733 140.392 436.387 148.905 437.84 158.811C439.686 171.379 440.735 173.192 454.019 186.769C460.25 193.137 467.497 201.168 470.124 204.616L474.901 210.886L470.124 206.405C464.282 200.926 450.847 190.24 447.879 188.712C445.89 187.688 445.594 187.705 444.366 188.927C443.235 190.053 442.997 191.744 442.84 199.741C442.596 212.204 440.897 220.204 436.797 228.203C434.58 232.529 434.23 231.606 436.237 226.723C437.735 223.077 437.887 221.474 437.876 209.408C437.853 185.167 434.975 179.339 418.097 169.355C413.821 166.826 406.776 163.178 402.442 161.249C398.107 159.32 394.664 157.639 394.789 157.514C395.267 157.038 411.727 161.842 418.352 164.39C428.206 168.181 429.833 168.672 431.03 168.215C431.832 167.909 432.22 165.572 432.61 158.704Z" fill="#FF007A"/>
<path d="M235.883 200.175C224.022 183.846 216.684 158.809 218.272 140.093L218.764 134.301L221.463 134.794C226.534 135.719 235.275 138.973 239.369 141.459C250.602 148.281 255.465 157.263 260.413 180.328C261.862 187.083 263.763 194.728 264.638 197.317C266.047 201.483 271.369 211.214 275.696 217.534C278.813 222.085 276.743 224.242 269.853 223.62C259.331 222.67 245.078 212.834 235.883 200.175Z" fill="#FF007A"/>
<path d="M418.223 321.707C362.793 299.389 343.271 280.017 343.271 247.331C343.271 242.521 343.437 238.585 343.638 238.585C343.84 238.585 345.985 240.173 348.404 242.113C359.644 251.128 372.231 254.979 407.076 260.062C427.58 263.054 439.119 265.47 449.763 269C483.595 280.22 504.527 302.99 509.518 334.004C510.969 343.016 510.118 359.915 507.766 368.822C505.91 375.857 500.245 388.537 498.742 389.023C498.325 389.158 497.917 387.562 497.81 385.389C497.24 373.744 491.355 362.406 481.472 353.913C470.235 344.257 455.137 336.569 418.223 321.707Z" fill="#FF007A"/>
<path d="M379.31 330.978C378.615 326.846 377.411 321.568 376.633 319.25L375.219 315.036L377.846 317.985C381.481 322.065 384.354 327.287 386.789 334.241C388.647 339.549 388.856 341.127 388.842 349.753C388.828 358.221 388.596 359.996 386.88 364.773C384.174 372.307 380.816 377.649 375.181 383.383C365.056 393.688 352.038 399.393 333.253 401.76C329.987 402.171 320.47 402.864 312.103 403.299C291.016 404.395 277.138 406.661 264.668 411.04C262.875 411.67 261.274 412.052 261.112 411.89C260.607 411.388 269.098 406.326 276.111 402.948C285.999 398.185 295.842 395.586 317.897 391.913C328.792 390.098 340.043 387.897 342.9 387.021C369.88 378.749 383.748 357.402 379.31 330.978Z" fill="#FF007A"/>
<path d="M404.719 376.105C397.355 360.273 395.664 344.988 399.698 330.732C400.13 329.209 400.824 327.962 401.242 327.962C401.659 327.962 403.397 328.902 405.103 330.05C408.497 332.335 415.303 336.182 433.437 346.069C456.065 358.406 468.966 367.959 477.74 378.873C485.423 388.432 490.178 399.318 492.467 412.593C493.762 420.113 493.003 438.206 491.074 445.778C484.99 469.653 470.85 488.406 450.682 499.349C447.727 500.952 445.075 502.269 444.788 502.275C444.501 502.28 445.577 499.543 447.18 496.191C453.965 482.009 454.737 468.214 449.608 452.859C446.467 443.457 440.064 431.985 427.135 412.596C412.103 390.054 408.417 384.054 404.719 376.105Z" fill="#FF007A"/>
<path d="M196.519 461.525C217.089 444.157 242.682 431.819 265.996 428.032C276.043 426.399 292.78 427.047 302.084 429.428C316.998 433.245 330.338 441.793 337.276 451.978C344.057 461.932 346.966 470.606 349.995 489.906C351.189 497.519 352.489 505.164 352.882 506.895C355.156 516.897 359.583 524.892 365.067 528.907C373.779 535.283 388.78 535.68 403.536 529.924C406.041 528.947 408.215 528.271 408.368 528.424C408.903 528.955 401.473 533.93 396.23 536.548C389.177 540.071 383.568 541.434 376.115 541.434C362.6 541.434 351.379 534.558 342.016 520.539C340.174 517.78 336.032 509.516 332.813 502.176C322.928 479.628 318.046 472.759 306.568 465.242C296.579 458.701 283.697 457.53 274.006 462.282C261.276 468.523 257.724 484.791 266.842 495.101C270.465 499.198 277.223 502.732 282.749 503.419C293.086 504.705 301.97 496.841 301.97 486.404C301.97 479.627 299.365 475.76 292.808 472.801C283.852 468.76 274.226 473.483 274.272 481.897C274.292 485.484 275.854 487.737 279.45 489.364C281.757 490.408 281.811 490.491 279.929 490.1C271.712 488.396 269.787 478.49 276.394 471.913C284.326 464.018 300.729 467.502 306.362 478.279C308.728 482.805 309.003 491.82 306.94 497.264C302.322 509.448 288.859 515.855 275.201 512.368C265.903 509.994 262.117 507.424 250.906 495.876C231.425 475.809 223.862 471.92 195.777 467.536L190.395 466.696L196.519 461.525Z" fill="#FF007A"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M49.6202 12.0031C114.678 90.9638 214.977 213.901 219.957 220.784C224.068 226.467 222.521 231.576 215.478 235.58C211.561 237.807 203.508 240.063 199.476 240.063C194.916 240.063 189.779 237.867 186.038 234.318C183.393 231.81 172.721 215.874 148.084 177.646C129.233 148.396 113.457 124.131 113.027 123.725C112.032 122.785 112.049 122.817 146.162 183.854C167.582 222.181 174.813 235.731 174.813 237.543C174.813 241.229 173.808 243.166 169.261 248.238C161.681 256.694 158.293 266.195 155.847 285.859C153.104 307.902 145.394 323.473 124.026 350.122C111.518 365.722 109.471 368.581 106.315 374.869C102.339 382.786 101.246 387.221 100.803 397.219C100.335 407.79 101.247 414.619 104.477 424.726C107.304 433.575 110.255 439.417 117.8 451.104C124.311 461.188 128.061 468.683 128.061 471.614C128.061 473.947 128.506 473.95 138.596 471.672C162.741 466.219 182.348 456.629 193.375 444.877C200.199 437.603 201.801 433.586 201.853 423.618C201.887 417.098 201.658 415.733 199.896 411.982C197.027 405.877 191.804 400.801 180.292 392.932C165.209 382.621 158.767 374.32 156.987 362.904C155.527 353.537 157.221 346.928 165.565 329.44C174.202 311.338 176.342 303.624 177.79 285.378C178.725 273.589 180.02 268.94 183.407 265.209C186.939 261.317 190.119 260 198.861 258.805C213.113 256.858 222.188 253.171 229.648 246.297C236.119 240.334 238.827 234.588 239.243 225.938L239.558 219.382L235.942 215.166C222.846 199.896 40.85 0 40.044 0C39.8719 0 44.1813 5.40178 49.6202 12.0031ZM135.412 409.18C138.373 403.937 136.8 397.195 131.847 393.902C127.167 390.79 119.897 392.256 119.897 396.311C119.897 397.548 120.582 398.449 122.124 399.243C124.72 400.579 124.909 402.081 122.866 405.152C120.797 408.262 120.964 410.996 123.337 412.854C127.162 415.849 132.576 414.202 135.412 409.18Z" fill="#FF007A"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M248.552 262.244C241.862 264.299 235.358 271.39 233.344 278.826C232.116 283.362 232.813 291.319 234.653 293.776C237.625 297.745 240.499 298.791 248.282 298.736C263.518 298.63 276.764 292.095 278.304 283.925C279.567 277.229 273.749 267.948 265.736 263.874C261.601 261.772 252.807 260.938 248.552 262.244ZM266.364 276.172C268.714 272.834 267.686 269.225 263.69 266.785C256.08 262.138 244.571 265.983 244.571 273.173C244.571 276.752 250.572 280.656 256.074 280.656C259.735 280.656 264.746 278.473 266.364 276.172Z" fill="#FF007A"/>
</svg>

After

Width:  |  Height:  |  Size: 7.9 KiB

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

View File

@@ -24,7 +24,7 @@ export const darkMiddleShadeIndex = 9
export const numShades = 20 // if you change this, see vuetify.js colors that hardcode indexes
// these parameters are expressed in terms of numShades:
export const pageShade = 2
export const surfaceShade = 3
export const printContrast = 15;
// vuetify darken. values are added/substracted from the middleShadeIndex. use positive numbers here.
export const colorContrast = 4;