short share urls
This commit is contained in:
@@ -1,2 +1,2 @@
|
|||||||
VITE_WS_URL=wss://ws.dexorder.trade
|
VITE_WS_URL=wss://ws.dexorder.trade
|
||||||
VITE_SERVER_URL=https://share.dexorder.trade
|
VITE_SHARE_URL=https://dexorder.trade
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
VITE_WS_URL=ws://localhost:3001
|
VITE_WS_URL=ws://localhost:3001
|
||||||
VITE_SNAPSHOT_URL=http://localhost:3001/snapshot
|
VITE_SHARE_URL=http://localhost:3001
|
||||||
REQUIRE_AUTH=NOAUTH
|
VITE_REQUIRE_APPROVAL=NO
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
console.log('snapshot.js')
|
|
||||||
|
|
||||||
async function getSnapCode() {
|
|
||||||
const formData = new FormData;
|
|
||||||
formData.append('language', 'en');
|
|
||||||
formData.append('timezone', 'Etc/UTC');
|
|
||||||
formData.append('symbol', 'BTC/USD');
|
|
||||||
formData.append('preparedImage', window.snapshotImage, 'blob');
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await fetch('https://tradingview.com/snapshot/', {
|
|
||||||
method: 'POST',
|
|
||||||
body: formData,
|
|
||||||
credentials: "same-origin",
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
console.error('Failed to upload snapshot:', response.status, response);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return await response.text();
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error uploading snapshot:', error);
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getSnapCode().then((code)=>console.log('snapshot code', code)).catch((e)=>console.error('snapshot error', e))
|
|
||||||
@@ -158,3 +158,23 @@ export function dateString(datetime) {
|
|||||||
export function logicalXOR(a, b) {
|
export function logicalXOR(a, b) {
|
||||||
return (a || b) && !(a && b)
|
return (a || b) && !(a && b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Base62
|
||||||
|
|
||||||
|
// base62.js
|
||||||
|
const base62charset = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
||||||
|
|
||||||
|
export function encodeBase62(num) {
|
||||||
|
if (num === 0) return base62charset[0];
|
||||||
|
let encoded = "";
|
||||||
|
while (num > 0) {
|
||||||
|
encoded = base62charset[num % 62] + encoded;
|
||||||
|
num = Math.floor(num / 62);
|
||||||
|
}
|
||||||
|
return encoded;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function decodeBase62(str) {
|
||||||
|
return str.split('').reverse().reduce((acc, char, i) =>
|
||||||
|
acc + base62charset.indexOf(char) * Math.pow(62, i), 0);
|
||||||
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
<v-btn id="share-btn" variant="text" prepend-icon="mdi-share" v-if="co.orders.length>0"
|
<v-btn id="share-btn" variant="text" prepend-icon="mdi-share" v-if="co.orders.length>0"
|
||||||
:disabled="sharing"
|
:disabled="sharing"
|
||||||
@click="shareOrder">{{sharing?'Preparing...':'Share'}}</v-btn>
|
@click="shareOrder">{{sharing?'Preparing...':'Share'}}</v-btn>
|
||||||
<v-tooltip activator="#share-btn" text="Copied share link!" v-model="showSharedTooltip"
|
<v-tooltip activator="#share-btn" :text="shareError?'Error copying share link :(':'Copied share link!'" v-model="showSharedTooltip"
|
||||||
:open-on-hover="false" :open-on-click="false"
|
:open-on-hover="false" :open-on-click="false"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
@@ -205,6 +205,7 @@ async function doPlaceOrder() {
|
|||||||
|
|
||||||
const sharing = ref(false)
|
const sharing = ref(false)
|
||||||
const showSharedTooltip = ref(false)
|
const showSharedTooltip = ref(false)
|
||||||
|
const shareError = ref(false)
|
||||||
const showShareDialog = ref(false)
|
const showShareDialog = ref(false)
|
||||||
const shareUrl = ref(null)
|
const shareUrl = ref(null)
|
||||||
|
|
||||||
@@ -212,6 +213,12 @@ function shareOrder() {
|
|||||||
sharing.value = true
|
sharing.value = true
|
||||||
track('share')
|
track('share')
|
||||||
getShareUrl().then(url => {
|
getShareUrl().then(url => {
|
||||||
|
shareError.value = url === null
|
||||||
|
if (url === null) {
|
||||||
|
sharing.value = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
console.log('share url', url)
|
||||||
shareUrl.value = url
|
shareUrl.value = url
|
||||||
sharing.value = false
|
sharing.value = false
|
||||||
navigator.permissions.query({name: "clipboard-write"}).then((permission) => {
|
navigator.permissions.query({name: "clipboard-write"}).then((permission) => {
|
||||||
|
|||||||
@@ -1,14 +1,19 @@
|
|||||||
<template>
|
<template>
|
||||||
|
<h2>Shared Order</h2>
|
||||||
|
<p>Loading the shared order into the app...</p>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import {loadShareUrl} from "@/share.js";
|
import {loadShareUrl} from "@/share.js";
|
||||||
import router from "@/router/index.js";
|
import router from "@/router/index.js";
|
||||||
|
import {socket} from "@/socket.js";
|
||||||
|
import {useRoute} from "vue-router";
|
||||||
|
|
||||||
loadShareUrl()
|
const route = useRoute()
|
||||||
router.replace('/order')
|
const code = route.params.code
|
||||||
|
loadShareUrl(code).then(()=> router.replace('/order')).catch((e)=> {console.error(e); router.replace('/order')})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ const routes = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Shared',
|
name: 'Shared',
|
||||||
path: '/shared',
|
path: '/shared/:code',
|
||||||
component: ()=> import('@/components/chart/Shared.vue')
|
component: ()=> import('@/components/chart/Shared.vue')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
29
src/share.js
29
src/share.js
@@ -4,6 +4,7 @@ import {changeIntervalSecs, onChartReady, setSymbol, widget} from "@/charts/char
|
|||||||
import {usePrefStore, useStore} from "@/store/store.js";
|
import {usePrefStore, useStore} from "@/store/store.js";
|
||||||
import {lookupSymbol} from "@/charts/datafeed.js";
|
import {lookupSymbol} from "@/charts/datafeed.js";
|
||||||
import {track} from "@/track.js";
|
import {track} from "@/track.js";
|
||||||
|
import {socket} from "@/socket.js";
|
||||||
|
|
||||||
export async function getShareUrl() {
|
export async function getShareUrl() {
|
||||||
const co = useChartOrderStore();
|
const co = useChartOrderStore();
|
||||||
@@ -26,18 +27,18 @@ export async function getShareUrl() {
|
|||||||
}
|
}
|
||||||
const json = JSON.stringify(data)
|
const json = JSON.stringify(data)
|
||||||
console.log('sharing data', json, data)
|
console.log('sharing data', json, data)
|
||||||
const compressed = compressToEncodedURIComponent(json);
|
const snapshot = await takeSnapshot()
|
||||||
const imageFile = await takeSnapshot()
|
|
||||||
return import.meta.env.VITE_SERVER_URL+ `/share?i=${imageFile}&d=${compressed}`;
|
const code = await new Promise((resolve)=>socket.emit('share', data, snapshot, resolve))
|
||||||
|
if (code===null) return null
|
||||||
|
|
||||||
|
return import.meta.env.VITE_SHARE_URL+ '/share/'+code;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function loadShareUrl() {
|
export async function loadShareUrl(code) {
|
||||||
const urlParams = new URLSearchParams(window.location.search);
|
const data = await new Promise((resolve)=>socket.emit('shared', code, resolve))
|
||||||
const dataStr = urlParams.get('d');
|
|
||||||
if (!dataStr) return
|
|
||||||
const json = decompressFromEncodedURIComponent(dataStr);
|
|
||||||
const data = JSON.parse(json);
|
|
||||||
console.log('loaded shared orders data', data)
|
console.log('loaded shared orders data', data)
|
||||||
|
if (data===null) return
|
||||||
const co = useChartOrderStore();
|
const co = useChartOrderStore();
|
||||||
const s = useStore()
|
const s = useStore()
|
||||||
const ticker = `${data.chainId}|${data.symbol.route.exchange}|${data.symbol.base.a}|${data.symbol.quote.a}|${data.symbol.route.fee}`;
|
const ticker = `${data.chainId}|${data.symbol.route.exchange}|${data.symbol.base.a}|${data.symbol.quote.a}|${data.symbol.route.fee}`;
|
||||||
@@ -50,7 +51,9 @@ export function loadShareUrl() {
|
|||||||
const prefs = usePrefStore()
|
const prefs = usePrefStore()
|
||||||
prefs.selectedSymbol = ticker
|
prefs.selectedSymbol = ticker
|
||||||
for (const order of data.orders) {
|
for (const order of data.orders) {
|
||||||
|
// force amount to be zero so that the user MUST enter a size before confirming the placement
|
||||||
order.amount = 0
|
order.amount = 0
|
||||||
|
order.amountIsTokenA = true
|
||||||
order.valid = false
|
order.valid = false
|
||||||
}
|
}
|
||||||
co.orders = data.orders
|
co.orders = data.orders
|
||||||
@@ -65,11 +68,5 @@ export function loadShareUrl() {
|
|||||||
|
|
||||||
export async function takeSnapshot() {
|
export async function takeSnapshot() {
|
||||||
const screenshotCanvas = await widget.takeClientScreenshot();
|
const screenshotCanvas = await widget.takeClientScreenshot();
|
||||||
const image = await new Promise((resolve) => screenshotCanvas.toBlob(resolve));
|
return await new Promise((resolve) => screenshotCanvas.toBlob(resolve));
|
||||||
const response = await fetch(import.meta.env.VITE_SERVER_URL + '/snapshot', {
|
|
||||||
method: 'PUT',
|
|
||||||
body: image,
|
|
||||||
credentials: 'same-origin',
|
|
||||||
});
|
|
||||||
return response.text()
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user