auth
This commit is contained in:
217
web/src/components/ChartView.vue
Normal file
217
web/src/components/ChartView.vue
Normal file
@@ -0,0 +1,217 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onBeforeUnmount, watch } from 'vue'
|
||||
import Card from 'primevue/card'
|
||||
import { createTradingViewDatafeed } from '../composables/useTradingViewDatafeed'
|
||||
import { useChartStore } from '../stores/chart'
|
||||
import type { IChartingLibraryWidget } from '../types/tradingview'
|
||||
|
||||
const chartContainer = ref<HTMLDivElement | null>(null)
|
||||
const chartStore = useChartStore()
|
||||
let tvWidget: IChartingLibraryWidget | null = null
|
||||
let datafeed: any = null
|
||||
let isUpdatingFromChart = false // Flag to prevent circular updates
|
||||
|
||||
onMounted(() => {
|
||||
if (!chartContainer.value) return
|
||||
|
||||
// Wait for TradingView library to load
|
||||
const initChart = () => {
|
||||
if (!window.TradingView) {
|
||||
setTimeout(initChart, 100)
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
datafeed = createTradingViewDatafeed()
|
||||
|
||||
tvWidget = new window.TradingView.widget({
|
||||
symbol: chartStore.chart_state.symbol, // Use symbol from store
|
||||
datafeed: datafeed,
|
||||
interval: chartStore.chart_state.interval as any,
|
||||
container: chartContainer.value!,
|
||||
library_path: '/charting_library/',
|
||||
locale: 'en',
|
||||
disabled_features: [
|
||||
'use_localstorage_for_settings',
|
||||
'header_symbol_search',
|
||||
'symbol_search_hot_key'
|
||||
],
|
||||
enabled_features: ['study_templates'],
|
||||
fullscreen: false,
|
||||
autosize: true,
|
||||
theme: 'Dark',
|
||||
timezone: 'Etc/UTC'
|
||||
})
|
||||
|
||||
tvWidget.onChartReady(() => {
|
||||
console.log('TradingView chart ready')
|
||||
setupChartListeners()
|
||||
setupStoreWatchers()
|
||||
// Initialize visible range on chart load
|
||||
initializeVisibleRange()
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Failed to initialize TradingView widget:', error)
|
||||
}
|
||||
}
|
||||
|
||||
initChart()
|
||||
})
|
||||
|
||||
function initializeVisibleRange() {
|
||||
if (!tvWidget) return
|
||||
|
||||
const chart = tvWidget.activeChart()
|
||||
const timeScale = chart.getVisibleRange()
|
||||
|
||||
if (timeScale) {
|
||||
const startTime = Math.floor(timeScale.from)
|
||||
const endTime = Math.floor(timeScale.to)
|
||||
|
||||
console.log('[ChartView] Initial visible range:', {
|
||||
from: new Date(startTime * 1000).toISOString(),
|
||||
to: new Date(endTime * 1000).toISOString()
|
||||
})
|
||||
|
||||
chartStore.chart_state.start_time = startTime
|
||||
chartStore.chart_state.end_time = endTime
|
||||
}
|
||||
}
|
||||
|
||||
function setupChartListeners() {
|
||||
if (!tvWidget) return
|
||||
|
||||
const chart = tvWidget.activeChart()
|
||||
|
||||
// Listen for symbol changes
|
||||
chart.onSymbolChanged().subscribe(null, () => {
|
||||
const symbolInfo = chart.symbolExt()
|
||||
if (symbolInfo && symbolInfo.ticker) {
|
||||
console.log('[ChartView] Symbol changed to:', symbolInfo.ticker)
|
||||
isUpdatingFromChart = true
|
||||
chartStore.chart_state.symbol = symbolInfo.ticker
|
||||
isUpdatingFromChart = false
|
||||
}
|
||||
})
|
||||
|
||||
// Listen for interval changes
|
||||
chart.onIntervalChanged().subscribe(null, (interval: string) => {
|
||||
console.log('[ChartView] Interval changed to:', interval)
|
||||
isUpdatingFromChart = true
|
||||
chartStore.chart_state.interval = interval
|
||||
isUpdatingFromChart = false
|
||||
})
|
||||
|
||||
// Listen for visible range changes (when user pans/zooms)
|
||||
chart.onVisibleRangeChanged().subscribe(null, () => {
|
||||
const timeScale = chart.getVisibleRange()
|
||||
if (timeScale) {
|
||||
// Convert from seconds to seconds (TradingView uses seconds for visible range)
|
||||
const startTime = Math.floor(timeScale.from)
|
||||
const endTime = Math.floor(timeScale.to)
|
||||
|
||||
console.log('[ChartView] Visible range changed:', {
|
||||
from: new Date(startTime * 1000).toISOString(),
|
||||
to: new Date(endTime * 1000).toISOString()
|
||||
})
|
||||
|
||||
isUpdatingFromChart = true
|
||||
chartStore.chart_state.start_time = startTime
|
||||
chartStore.chart_state.end_time = endTime
|
||||
isUpdatingFromChart = false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function setupStoreWatchers() {
|
||||
if (!tvWidget) return
|
||||
|
||||
const chart = tvWidget.activeChart()
|
||||
|
||||
// Watch for external changes to symbol (e.g., from backend/agent)
|
||||
watch(
|
||||
() => chartStore.chart_state.symbol,
|
||||
(newSymbol) => {
|
||||
if (isUpdatingFromChart) return // Ignore updates that came from the chart itself
|
||||
|
||||
console.log('[ChartView] Store symbol changed externally to:', newSymbol)
|
||||
const currentSymbol = chart.symbolExt()
|
||||
if (currentSymbol && currentSymbol.ticker !== newSymbol) {
|
||||
chart.setSymbol(newSymbol, () => {
|
||||
console.log('[ChartView] Chart symbol updated to:', newSymbol)
|
||||
})
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
// Watch for external changes to interval
|
||||
watch(
|
||||
() => chartStore.chart_state.interval,
|
||||
(newInterval) => {
|
||||
if (isUpdatingFromChart) return
|
||||
|
||||
console.log('[ChartView] Store interval changed externally to:', newInterval)
|
||||
if (chart.resolution() !== newInterval) {
|
||||
chart.setResolution(newInterval, () => {
|
||||
console.log('[ChartView] Chart interval updated to:', newInterval)
|
||||
})
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
// Note: Visible range is typically only set by user interaction,
|
||||
// but we could add a watcher here if we want the backend to be able
|
||||
// to change the visible range programmatically
|
||||
}
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (tvWidget) {
|
||||
tvWidget.remove()
|
||||
tvWidget = null
|
||||
}
|
||||
if (datafeed && typeof datafeed.destroy === 'function') {
|
||||
datafeed.destroy()
|
||||
datafeed = null
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Card class="chart-card">
|
||||
<template #content>
|
||||
<div ref="chartContainer" class="chart-container"></div>
|
||||
</template>
|
||||
</Card>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.chart-card {
|
||||
height: 100% !important;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
border: none;
|
||||
background: var(--p-surface-0);
|
||||
}
|
||||
|
||||
.chart-card :deep(.p-card-body) {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.chart-card :deep(.p-card-content) {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user