short share urls

This commit is contained in:
tim
2025-04-23 12:55:49 -04:00
parent 6f7388bb10
commit 441a514acc
8 changed files with 53 additions and 52 deletions

View File

@@ -1,2 +1,2 @@
VITE_WS_URL=wss://ws.dexorder.trade
VITE_SERVER_URL=https://share.dexorder.trade
VITE_SHARE_URL=https://dexorder.trade

View File

@@ -1,3 +1,3 @@
VITE_WS_URL=ws://localhost:3001
VITE_SNAPSHOT_URL=http://localhost:3001/snapshot
REQUIRE_AUTH=NOAUTH
VITE_SHARE_URL=http://localhost:3001
VITE_REQUIRE_APPROVAL=NO

View File

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

View File

@@ -158,3 +158,23 @@ export function dateString(datetime) {
export function logicalXOR(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);
}

View File

@@ -11,7 +11,7 @@
<v-btn id="share-btn" variant="text" prepend-icon="mdi-share" v-if="co.orders.length>0"
:disabled="sharing"
@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"
/>
</template>
@@ -205,6 +205,7 @@ async function doPlaceOrder() {
const sharing = ref(false)
const showSharedTooltip = ref(false)
const shareError = ref(false)
const showShareDialog = ref(false)
const shareUrl = ref(null)
@@ -212,6 +213,12 @@ function shareOrder() {
sharing.value = true
track('share')
getShareUrl().then(url => {
shareError.value = url === null
if (url === null) {
sharing.value = false
return
}
console.log('share url', url)
shareUrl.value = url
sharing.value = false
navigator.permissions.query({name: "clipboard-write"}).then((permission) => {

View File

@@ -1,12 +1,17 @@
<template>
<h2>Shared Order</h2>
<p>Loading the shared order into the app...</p>
</template>
<script setup>
import {loadShareUrl} from "@/share.js";
import router from "@/router/index.js";
import {socket} from "@/socket.js";
import {useRoute} from "vue-router";
loadShareUrl()
router.replace('/order')
const route = useRoute()
const code = route.params.code
loadShareUrl(code).then(()=> router.replace('/order')).catch((e)=> {console.error(e); router.replace('/order')})
</script>
<style scoped lang="scss">

View File

@@ -16,7 +16,7 @@ const routes = [
},
{
name: 'Shared',
path: '/shared',
path: '/shared/:code',
component: ()=> import('@/components/chart/Shared.vue')
},
{

View File

@@ -4,6 +4,7 @@ import {changeIntervalSecs, onChartReady, setSymbol, widget} from "@/charts/char
import {usePrefStore, useStore} from "@/store/store.js";
import {lookupSymbol} from "@/charts/datafeed.js";
import {track} from "@/track.js";
import {socket} from "@/socket.js";
export async function getShareUrl() {
const co = useChartOrderStore();
@@ -26,18 +27,18 @@ export async function getShareUrl() {
}
const json = JSON.stringify(data)
console.log('sharing data', json, data)
const compressed = compressToEncodedURIComponent(json);
const imageFile = await takeSnapshot()
return import.meta.env.VITE_SERVER_URL+ `/share?i=${imageFile}&d=${compressed}`;
const snapshot = await takeSnapshot()
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() {
const urlParams = new URLSearchParams(window.location.search);
const dataStr = urlParams.get('d');
if (!dataStr) return
const json = decompressFromEncodedURIComponent(dataStr);
const data = JSON.parse(json);
export async function loadShareUrl(code) {
const data = await new Promise((resolve)=>socket.emit('shared', code, resolve))
console.log('loaded shared orders data', data)
if (data===null) return
const co = useChartOrderStore();
const s = useStore()
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()
prefs.selectedSymbol = ticker
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.amountIsTokenA = true
order.valid = false
}
co.orders = data.orders
@@ -65,11 +68,5 @@ export function loadShareUrl() {
export async function takeSnapshot() {
const screenshotCanvas = await widget.takeClientScreenshot();
const image = 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()
return await new Promise((resolve) => screenshotCanvas.toBlob(resolve));
}