store sync bugfix
This commit is contained in:
@@ -430,7 +430,7 @@ export function useTradingViewShapes(tvWidget: IChartingLibraryWidget) {
|
||||
function setupStoreWatchers() {
|
||||
// Watch for shape store changes and apply to TradingView
|
||||
watch(
|
||||
() => shapeStore.shapes,
|
||||
() => shapeStore.$state,
|
||||
async (newShapes, oldShapes) => {
|
||||
if (isUpdatingStore || !isChartReady) return
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref } from 'vue'
|
||||
import type { CustomIndicatorMetadata } from './indicators'
|
||||
|
||||
export interface CustomIndicatorType {
|
||||
@@ -11,7 +10,12 @@ export interface CustomIndicatorType {
|
||||
modified_at: number
|
||||
}
|
||||
|
||||
export const useIndicatorTypesStore = defineStore('indicator_types', () => {
|
||||
const types = ref<Record<string, CustomIndicatorType>>({})
|
||||
return { types }
|
||||
// Options API: flat $state = Record<string, CustomIndicatorType> matches backend initial state {}.
|
||||
// Composition API would produce $state = { types: {} }, causing deepReplace to delete 'types' on
|
||||
// the initial empty snapshot from the backend, which breaks $subscribe reactivity tracking.
|
||||
export const useIndicatorTypesStore = defineStore('indicator_types', {
|
||||
state: (): Record<string, CustomIndicatorType> => ({}),
|
||||
getters: {
|
||||
types: (state): Record<string, CustomIndicatorType> => state,
|
||||
},
|
||||
})
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref } from 'vue'
|
||||
|
||||
export interface CustomIndicatorParam {
|
||||
type: 'int' | 'float' | 'bool' | 'string'
|
||||
@@ -76,48 +75,48 @@ export interface IndicatorInstance {
|
||||
custom_metadata?: CustomIndicatorMetadata
|
||||
}
|
||||
|
||||
export const useIndicatorStore = defineStore('indicators', () => {
|
||||
const indicators = ref<Record<string, IndicatorInstance>>({})
|
||||
// Options API: flat $state = Record<string, IndicatorInstance> matches backend initial state {}.
|
||||
// Composition API would produce $state = { indicators: {} }, causing deepReplace to delete
|
||||
// 'indicators' on the initial empty snapshot, which breaks $subscribe reactivity tracking.
|
||||
// The 'indicators' getter maintains backwards compatibility with existing callers.
|
||||
export const useIndicatorStore = defineStore('indicators', {
|
||||
state: (): Record<string, IndicatorInstance> => ({}),
|
||||
|
||||
// Helper methods
|
||||
const addIndicator = (indicator: IndicatorInstance) => {
|
||||
indicators.value[indicator.id] = indicator
|
||||
}
|
||||
getters: {
|
||||
indicators: (state): Record<string, IndicatorInstance> => state,
|
||||
},
|
||||
|
||||
const updateIndicator = (id: string, updates: Partial<IndicatorInstance>) => {
|
||||
if (indicators.value[id]) {
|
||||
const updated = {
|
||||
...indicators.value[id],
|
||||
...updates,
|
||||
modified_at: Math.floor(Date.now() / 1000)
|
||||
actions: {
|
||||
addIndicator(indicator: IndicatorInstance) {
|
||||
this.$patch({ [indicator.id]: indicator } as Partial<Record<string, IndicatorInstance>>)
|
||||
},
|
||||
|
||||
updateIndicator(id: string, updates: Partial<IndicatorInstance>) {
|
||||
const existing = (this.$state as Record<string, IndicatorInstance>)[id]
|
||||
if (existing) {
|
||||
this.$patch({
|
||||
[id]: { ...existing, ...updates, modified_at: Math.floor(Date.now() / 1000) }
|
||||
} as Partial<Record<string, IndicatorInstance>>)
|
||||
}
|
||||
indicators.value[id] = updated
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
const removeIndicator = (id: string) => {
|
||||
delete indicators.value[id]
|
||||
}
|
||||
removeIndicator(id: string) {
|
||||
this.$patch((state) => {
|
||||
delete (state as Record<string, IndicatorInstance>)[id]
|
||||
})
|
||||
},
|
||||
|
||||
const getIndicator = (id: string): IndicatorInstance | undefined => {
|
||||
return indicators.value[id]
|
||||
}
|
||||
getIndicator(id: string): IndicatorInstance | undefined {
|
||||
return (this.$state as Record<string, IndicatorInstance>)[id]
|
||||
},
|
||||
|
||||
const getAllIndicators = (): IndicatorInstance[] => {
|
||||
return Object.values(indicators.value)
|
||||
}
|
||||
getAllIndicators(): IndicatorInstance[] {
|
||||
return Object.values(this.$state as Record<string, IndicatorInstance>)
|
||||
},
|
||||
|
||||
const getIndicatorsBySymbol = (symbol: string): IndicatorInstance[] => {
|
||||
return Object.values(indicators.value).filter(ind => ind.symbol === symbol)
|
||||
}
|
||||
|
||||
return {
|
||||
indicators,
|
||||
addIndicator,
|
||||
updateIndicator,
|
||||
removeIndicator,
|
||||
getIndicator,
|
||||
getAllIndicators,
|
||||
getIndicatorsBySymbol
|
||||
}
|
||||
getIndicatorsBySymbol(symbol: string): IndicatorInstance[] {
|
||||
return Object.values(this.$state as Record<string, IndicatorInstance>)
|
||||
.filter(ind => ind.symbol === symbol)
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref } from 'vue'
|
||||
|
||||
export interface ResearchType {
|
||||
display_name: string
|
||||
@@ -8,7 +7,11 @@ export interface ResearchType {
|
||||
modified_at: number
|
||||
}
|
||||
|
||||
export const useResearchTypesStore = defineStore('research_types', () => {
|
||||
const types = ref<Record<string, ResearchType>>({})
|
||||
return { types }
|
||||
// Options API: flat $state matches backend initial state {}.
|
||||
// See indicatorTypes.ts for explanation of why composition API breaks $subscribe.
|
||||
export const useResearchTypesStore = defineStore('research_types', {
|
||||
state: (): Record<string, ResearchType> => ({}),
|
||||
getters: {
|
||||
types: (state): Record<string, ResearchType> => state,
|
||||
},
|
||||
})
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref } from 'vue'
|
||||
|
||||
export interface ControlPoint {
|
||||
time: number
|
||||
@@ -21,44 +20,38 @@ export interface Shape {
|
||||
original_id?: string // Original ID from backend/agent before TradingView assigns its own ID
|
||||
}
|
||||
|
||||
export const useShapeStore = defineStore('shapes', () => {
|
||||
const shapes = ref<Record<string, Shape>>({})
|
||||
// Use Options API so $state is a flat Record<string, Shape> matching the backend's shape store structure.
|
||||
// Composition API would produce $state = { shapes: {} } (extra nesting), which causes deepReplace in
|
||||
// useStateSync to delete the 'shapes' key when the backend sends an empty {} snapshot, breaking $subscribe.
|
||||
export const useShapeStore = defineStore('shapes', {
|
||||
state: () => ({} as Record<string, Shape>),
|
||||
|
||||
// Helper methods
|
||||
const addShape = (shape: Shape) => {
|
||||
shapes.value[shape.id] = shape
|
||||
}
|
||||
actions: {
|
||||
addShape(shape: Shape) {
|
||||
this.$patch({ [shape.id]: shape } as Partial<Record<string, Shape>>)
|
||||
},
|
||||
|
||||
const updateShape = (id: string, updates: Partial<Shape>) => {
|
||||
if (shapes.value[id]) {
|
||||
const updated = {
|
||||
...shapes.value[id],
|
||||
...updates,
|
||||
modified_at: Math.floor(Date.now() / 1000)
|
||||
updateShape(id: string, updates: Partial<Shape>) {
|
||||
const existing = (this.$state as Record<string, Shape>)[id]
|
||||
if (existing) {
|
||||
this.$patch({
|
||||
[id]: { ...existing, ...updates, modified_at: Math.floor(Date.now() / 1000) }
|
||||
} as Partial<Record<string, Shape>>)
|
||||
}
|
||||
// Replace the entire shape object to ensure arrays are replaced atomically
|
||||
shapes.value[id] = updated
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
const removeShape = (id: string) => {
|
||||
delete shapes.value[id]
|
||||
}
|
||||
removeShape(id: string) {
|
||||
this.$patch((state) => {
|
||||
delete (state as Record<string, Shape>)[id]
|
||||
})
|
||||
},
|
||||
|
||||
const getShape = (id: string): Shape | undefined => {
|
||||
return shapes.value[id]
|
||||
}
|
||||
getShape(id: string): Shape | undefined {
|
||||
return (this.$state as Record<string, Shape>)[id]
|
||||
},
|
||||
|
||||
const getAllShapes = (): Shape[] => {
|
||||
return Object.values(shapes.value)
|
||||
}
|
||||
|
||||
return {
|
||||
shapes,
|
||||
addShape,
|
||||
updateShape,
|
||||
removeShape,
|
||||
getShape,
|
||||
getAllShapes
|
||||
}
|
||||
getAllShapes(): Shape[] {
|
||||
return Object.values(this.$state as Record<string, Shape>)
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref } from 'vue'
|
||||
|
||||
export interface StrategyType {
|
||||
display_name: string
|
||||
@@ -8,7 +7,11 @@ export interface StrategyType {
|
||||
modified_at: number
|
||||
}
|
||||
|
||||
export const useStrategyTypesStore = defineStore('strategy_types', () => {
|
||||
const types = ref<Record<string, StrategyType>>({})
|
||||
return { types }
|
||||
// Options API: flat $state matches backend initial state {}.
|
||||
// See indicatorTypes.ts for explanation of why composition API breaks $subscribe.
|
||||
export const useStrategyTypesStore = defineStore('strategy_types', {
|
||||
state: (): Record<string, StrategyType> => ({}),
|
||||
getters: {
|
||||
types: (state): Record<string, StrategyType> => state,
|
||||
},
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user