mirror of
https://github.com/freqtrade/frequi.git
synced 2024-11-23 11:35:14 +00:00
pinia: add layout store
Navbar to composition API Trading.vue to composition API
This commit is contained in:
parent
2d91945662
commit
e1810eabbf
|
@ -38,6 +38,7 @@
|
||||||
"vue-property-decorator": "^9.1.2",
|
"vue-property-decorator": "^9.1.2",
|
||||||
"vue-router": "^3.5.3",
|
"vue-router": "^3.5.3",
|
||||||
"vue-select": "^3.18.3",
|
"vue-select": "^3.18.3",
|
||||||
|
"vue2-helpers": "^1.1.7",
|
||||||
"vuex": "^3.6.2",
|
"vuex": "^3.6.2",
|
||||||
"vuex-composition-helpers": "^1.1.0"
|
"vuex-composition-helpers": "^1.1.0"
|
||||||
},
|
},
|
||||||
|
|
|
@ -109,11 +109,11 @@ export default class TradeList extends Vue {
|
||||||
|
|
||||||
@Prop({ required: true }) trades!: Array<Trade>;
|
@Prop({ required: true }) trades!: Array<Trade>;
|
||||||
|
|
||||||
@Prop({ default: 'Trades' }) title!: string;
|
@Prop({ default: 'Trades', type: String }) title!: string;
|
||||||
|
|
||||||
@Prop({ required: false, default: '' }) stakeCurrency!: string;
|
@Prop({ required: false, default: '' }) stakeCurrency!: string;
|
||||||
|
|
||||||
@Prop({ default: false }) activeTrades!: boolean;
|
@Prop({ default: false, type: Boolean }) activeTrades!: boolean;
|
||||||
|
|
||||||
@Prop({ default: false }) showFilter!: boolean;
|
@Prop({ default: false }) showFilter!: boolean;
|
||||||
|
|
||||||
|
|
|
@ -62,7 +62,7 @@
|
||||||
</template>
|
</template>
|
||||||
<b-dropdown-item>V: {{ getUiVersion }}</b-dropdown-item>
|
<b-dropdown-item>V: {{ getUiVersion }}</b-dropdown-item>
|
||||||
<router-link class="dropdown-item" to="/settings">Settings</router-link>
|
<router-link class="dropdown-item" to="/settings">Settings</router-link>
|
||||||
<b-checkbox v-model="layoutLockedLocal" class="pl-5">Lock layout</b-checkbox>
|
<b-checkbox v-model="layoutStore.layoutLocked" class="pl-5">Lock layout</b-checkbox>
|
||||||
<b-dropdown-item @click="resetDynamicLayout">Reset Layout</b-dropdown-item>
|
<b-dropdown-item @click="resetDynamicLayout">Reset Layout</b-dropdown-item>
|
||||||
<router-link
|
<router-link
|
||||||
v-if="botCount === 1"
|
v-if="botCount === 1"
|
||||||
|
@ -105,115 +105,70 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Component, Vue, Watch } from 'vue-property-decorator';
|
|
||||||
import LoginModal from '@/views/LoginModal.vue';
|
import LoginModal from '@/views/LoginModal.vue';
|
||||||
import { Action, namespace, Getter } from 'vuex-class';
|
|
||||||
import BootswatchThemeSelect from '@/components/BootswatchThemeSelect.vue';
|
import BootswatchThemeSelect from '@/components/BootswatchThemeSelect.vue';
|
||||||
import { LayoutActions, LayoutGetters } from '@/store/modules/layout';
|
|
||||||
import { BotStoreGetters } from '@/store/modules/ftbot';
|
import { BotStoreGetters } from '@/store/modules/ftbot';
|
||||||
import Favico from 'favico.js';
|
import Favico from 'favico.js';
|
||||||
import { MultiBotStoreGetters } from '@/store/modules/botStoreWrapper';
|
import { MultiBotStoreGetters } from '@/store/modules/botStoreWrapper';
|
||||||
import ReloadControl from '@/components/ftbot/ReloadControl.vue';
|
import ReloadControl from '@/components/ftbot/ReloadControl.vue';
|
||||||
import BotEntry from '@/components/BotEntry.vue';
|
import BotEntry from '@/components/BotEntry.vue';
|
||||||
import BotList from '@/components/BotList.vue';
|
import BotList from '@/components/BotList.vue';
|
||||||
import { BotDescriptor } from '@/types';
|
|
||||||
import StoreModules from '@/store/storeSubModules';
|
import StoreModules from '@/store/storeSubModules';
|
||||||
|
import { defineComponent, ref, onBeforeUnmount, onMounted, watch } from '@vue/composition-api';
|
||||||
const ftbot = namespace(StoreModules.ftbot);
|
import {
|
||||||
const layoutNs = namespace(StoreModules.layout);
|
useActions,
|
||||||
|
useGetters,
|
||||||
|
useNamespacedActions,
|
||||||
|
useNamespacedGetters,
|
||||||
|
} from 'vuex-composition-helpers';
|
||||||
|
import { useRoute } from 'vue2-helpers/vue-router';
|
||||||
import { OpenTradeVizOptions, useSettingsStore } from '@/stores/settings';
|
import { OpenTradeVizOptions, useSettingsStore } from '@/stores/settings';
|
||||||
|
import { useLayoutStore } from '@/stores/layout';
|
||||||
|
|
||||||
@Component({
|
export default defineComponent({
|
||||||
|
name: 'NavBar',
|
||||||
components: { LoginModal, BootswatchThemeSelect, ReloadControl, BotEntry, BotList },
|
components: { LoginModal, BootswatchThemeSelect, ReloadControl, BotEntry, BotList },
|
||||||
})
|
setup() {
|
||||||
export default class NavBar extends Vue {
|
const { getUiVersion } = useGetters(['getUiVersion']);
|
||||||
pingInterval: number | null = null;
|
const { setLoggedIn, loadUIVersion } = useActions(['setLoggedIn', 'loadUIVersion']);
|
||||||
|
const { pingAll, allGetState, logout } = useNamespacedActions(StoreModules.ftbot, [
|
||||||
|
'pingAll',
|
||||||
|
'allGetState',
|
||||||
|
'logout',
|
||||||
|
]);
|
||||||
|
const {
|
||||||
|
isBotOnline,
|
||||||
|
hasBots,
|
||||||
|
botCount,
|
||||||
|
botName,
|
||||||
|
openTradeCount,
|
||||||
|
canRunBacktest,
|
||||||
|
selectedBotObj,
|
||||||
|
} = useNamespacedGetters(StoreModules.ftbot, [
|
||||||
|
BotStoreGetters.isBotOnline,
|
||||||
|
MultiBotStoreGetters.hasBots,
|
||||||
|
MultiBotStoreGetters.botCount,
|
||||||
|
BotStoreGetters.botName,
|
||||||
|
BotStoreGetters.openTradeCount,
|
||||||
|
BotStoreGetters.canRunBacktest,
|
||||||
|
MultiBotStoreGetters.selectedBotObj,
|
||||||
|
]);
|
||||||
|
|
||||||
botSelectOpen = false;
|
|
||||||
|
|
||||||
@Action setLoggedIn;
|
|
||||||
|
|
||||||
@Action loadUIVersion;
|
|
||||||
|
|
||||||
@Getter getUiVersion!: string;
|
|
||||||
|
|
||||||
@ftbot.Action pingAll;
|
|
||||||
|
|
||||||
@ftbot.Action allGetState;
|
|
||||||
|
|
||||||
@ftbot.Action logout;
|
|
||||||
|
|
||||||
@ftbot.Getter [BotStoreGetters.isBotOnline]!: boolean;
|
|
||||||
|
|
||||||
@ftbot.Getter [MultiBotStoreGetters.hasBots]: boolean;
|
|
||||||
|
|
||||||
@ftbot.Getter [MultiBotStoreGetters.botCount]: number;
|
|
||||||
|
|
||||||
@ftbot.Getter [BotStoreGetters.botName]: string;
|
|
||||||
|
|
||||||
@ftbot.Getter [BotStoreGetters.openTradeCount]: number;
|
|
||||||
|
|
||||||
@ftbot.Getter [BotStoreGetters.canRunBacktest]!: boolean;
|
|
||||||
|
|
||||||
@ftbot.Getter [MultiBotStoreGetters.selectedBotObj]!: BotDescriptor;
|
|
||||||
|
|
||||||
@layoutNs.Getter [LayoutGetters.getLayoutLocked]: boolean;
|
|
||||||
|
|
||||||
@layoutNs.Action [LayoutActions.resetDashboardLayout];
|
|
||||||
|
|
||||||
@layoutNs.Action [LayoutActions.resetTradingLayout];
|
|
||||||
|
|
||||||
@layoutNs.Action [LayoutActions.setLayoutLocked];
|
|
||||||
|
|
||||||
openTradesInTitle: string = OpenTradeVizOptions.showPill;
|
|
||||||
|
|
||||||
favicon: Favico | undefined = undefined;
|
|
||||||
|
|
||||||
mounted() {
|
|
||||||
const settingsStore = useSettingsStore();
|
const settingsStore = useSettingsStore();
|
||||||
this.openTradesInTitle = settingsStore.openTradesInTitle;
|
const layoutStore = useLayoutStore();
|
||||||
settingsStore.$subscribe((_, state) => {
|
const route = useRoute();
|
||||||
const needsUpdate = this.openTradesInTitle !== state.openTradesInTitle;
|
const favicon = ref<Favico | undefined>(undefined);
|
||||||
this.openTradesInTitle = state.openTradesInTitle;
|
const pingInterval = ref<number>();
|
||||||
if (needsUpdate) {
|
|
||||||
this.setTitle();
|
|
||||||
this.setOpenTradesAsPill(this.openTradeCount);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.pingAll();
|
const clickLogout = () => {
|
||||||
this.loadUIVersion();
|
logout();
|
||||||
this.pingInterval = window.setInterval(this.pingAll, 60000);
|
|
||||||
|
|
||||||
if (this.hasBots) {
|
|
||||||
// Query botstate - this will enable / disable certain modes
|
|
||||||
this.allGetState();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
beforeDestroy() {
|
|
||||||
if (this.pingInterval) {
|
|
||||||
clearInterval(this.pingInterval);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
clickLogout(): void {
|
|
||||||
this.logout();
|
|
||||||
// TODO: This should be per bot
|
// TODO: This should be per bot
|
||||||
this.setLoggedIn(false);
|
setLoggedIn(false);
|
||||||
}
|
};
|
||||||
|
|
||||||
get layoutLockedLocal() {
|
const setOpenTradesAsPill = (tradeCount: number) => {
|
||||||
return this.getLayoutLocked;
|
if (!favicon) {
|
||||||
}
|
favicon.value = new Favico({
|
||||||
|
|
||||||
set layoutLockedLocal(value: boolean) {
|
|
||||||
this.setLayoutLocked(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
setOpenTradesAsPill(tradeCount: number) {
|
|
||||||
if (!this.favicon) {
|
|
||||||
this.favicon = new Favico({
|
|
||||||
animation: 'none',
|
animation: 'none',
|
||||||
// position: 'up',
|
// position: 'up',
|
||||||
// fontStyle: 'normal',
|
// fontStyle: 'normal',
|
||||||
|
@ -221,54 +176,92 @@ export default class NavBar extends Vue {
|
||||||
// textColor: '#FFFFFF',
|
// textColor: '#FFFFFF',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (tradeCount !== 0 && this.openTradesInTitle === 'showPill') {
|
if (tradeCount !== 0 && settingsStore.openTradesInTitle === 'showPill') {
|
||||||
this.favicon.badge(tradeCount);
|
favicon.badge(tradeCount);
|
||||||
} else {
|
} else {
|
||||||
this.favicon.reset();
|
favicon.reset();
|
||||||
console.log('reset');
|
console.log('reset');
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
const resetDynamicLayout = (): void => {
|
||||||
resetDynamicLayout(): void {
|
console.log(`resetLayout called for ${route?.fullPath}`);
|
||||||
const route = this.$router.currentRoute.path;
|
switch (route?.fullPath) {
|
||||||
console.log(`resetLayout called for ${route}`);
|
|
||||||
switch (route) {
|
|
||||||
case '/trade':
|
case '/trade':
|
||||||
this.resetTradingLayout();
|
layoutStore.resetTradingLayout();
|
||||||
break;
|
break;
|
||||||
case '/dashboard':
|
case '/dashboard':
|
||||||
this.resetDashboardLayout();
|
layoutStore.resetDashboardLayout();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
const setTitle = () => {
|
||||||
setTitle() {
|
|
||||||
let title = 'freqUI';
|
let title = 'freqUI';
|
||||||
if (this.openTradesInTitle === OpenTradeVizOptions.asTitle) {
|
if (settingsStore.openTradesInTitle === OpenTradeVizOptions.asTitle) {
|
||||||
title = `(${this.openTradeCount}) ${title}`;
|
title = `(${openTradeCount}) ${title}`;
|
||||||
}
|
}
|
||||||
if (this.botName) {
|
if (botName) {
|
||||||
title = `${title} - ${this.botName}`;
|
title = `${title} - ${botName}`;
|
||||||
}
|
}
|
||||||
document.title = title;
|
document.title = title;
|
||||||
}
|
};
|
||||||
|
|
||||||
@Watch(BotStoreGetters.botName)
|
onBeforeUnmount(() => {
|
||||||
botnameChanged() {
|
if (pingInterval) {
|
||||||
this.setTitle();
|
clearInterval(pingInterval.value);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
@Watch(BotStoreGetters.openTradeCount)
|
onMounted(() => {
|
||||||
openTradeCountChanged() {
|
pingAll();
|
||||||
|
loadUIVersion();
|
||||||
|
pingInterval.value = window.setInterval(pingAll, 60000);
|
||||||
|
if (hasBots) {
|
||||||
|
// Query botstate - this will enable / disable certain modes
|
||||||
|
allGetState();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
settingsStore.$subscribe((_, state) => {
|
||||||
|
const needsUpdate = settingsStore.openTradesInTitle !== state.openTradesInTitle;
|
||||||
|
if (needsUpdate) {
|
||||||
|
setTitle();
|
||||||
|
setOpenTradesAsPill(openTradeCount.value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(botName, () => setTitle());
|
||||||
|
watch(openTradeCount, () => {
|
||||||
console.log('openTradeCount changed');
|
console.log('openTradeCount changed');
|
||||||
if (this.openTradesInTitle === OpenTradeVizOptions.showPill) {
|
if (settingsStore.openTradesInTitle === OpenTradeVizOptions.showPill) {
|
||||||
this.setOpenTradesAsPill(this.openTradeCount);
|
setOpenTradesAsPill(openTradeCount.value);
|
||||||
} else if (this.openTradesInTitle === OpenTradeVizOptions.asTitle) {
|
} else if (settingsStore.openTradesInTitle === OpenTradeVizOptions.asTitle) {
|
||||||
this.setTitle();
|
setTitle();
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
}
|
|
||||||
|
return {
|
||||||
|
setLoggedIn,
|
||||||
|
loadUIVersion,
|
||||||
|
getUiVersion,
|
||||||
|
pingAll,
|
||||||
|
allGetState,
|
||||||
|
logout,
|
||||||
|
favicon,
|
||||||
|
isBotOnline,
|
||||||
|
hasBots,
|
||||||
|
botCount,
|
||||||
|
botName,
|
||||||
|
openTradeCount,
|
||||||
|
canRunBacktest,
|
||||||
|
selectedBotObj,
|
||||||
|
clickLogout,
|
||||||
|
resetDynamicLayout,
|
||||||
|
setTitle,
|
||||||
|
layoutStore,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|
|
@ -8,7 +8,6 @@ import { UiVersion } from '@/types';
|
||||||
import StoreModules from '@/store/storeSubModules';
|
import StoreModules from '@/store/storeSubModules';
|
||||||
import createBotStore, { MultiBotStoreGetters } from './modules/botStoreWrapper';
|
import createBotStore, { MultiBotStoreGetters } from './modules/botStoreWrapper';
|
||||||
import alertsModule from './modules/alerts';
|
import alertsModule from './modules/alerts';
|
||||||
import layoutModule from './modules/layout';
|
|
||||||
|
|
||||||
Vue.use(Vuex);
|
Vue.use(Vuex);
|
||||||
const initCurrentTheme = getCurrentTheme();
|
const initCurrentTheme = getCurrentTheme();
|
||||||
|
@ -16,7 +15,6 @@ const initCurrentTheme = getCurrentTheme();
|
||||||
const store = new Vuex.Store({
|
const store = new Vuex.Store({
|
||||||
modules: {
|
modules: {
|
||||||
[StoreModules.alerts]: alertsModule,
|
[StoreModules.alerts]: alertsModule,
|
||||||
[StoreModules.layout]: layoutModule,
|
|
||||||
},
|
},
|
||||||
state: {
|
state: {
|
||||||
currentTheme: initCurrentTheme,
|
currentTheme: initCurrentTheme,
|
||||||
|
|
|
@ -1,170 +0,0 @@
|
||||||
import { GridItemData } from 'vue-grid-layout';
|
|
||||||
|
|
||||||
export enum TradeLayout {
|
|
||||||
multiPane = 'g-multiPane',
|
|
||||||
openTrades = 'g-openTrades',
|
|
||||||
tradeHistory = 'g-tradeHistory',
|
|
||||||
tradeDetail = 'g-tradeDetail',
|
|
||||||
chartView = 'g-chartView',
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum DashboardLayout {
|
|
||||||
dailyChart = 'g-dailyChart',
|
|
||||||
botComparison = 'g-botComparison',
|
|
||||||
allOpenTrades = 'g-allOpenTrades',
|
|
||||||
cumChartChart = 'g-cumChartChart',
|
|
||||||
tradesLogChart = 'g-TradesLogChart',
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum LayoutGetters {
|
|
||||||
getDashboardLayoutSm = 'getDashboardLayoutSm',
|
|
||||||
getDashboardLayout = 'getDashboardLayout',
|
|
||||||
getTradingLayoutSm = 'getTradingLayoutSm',
|
|
||||||
getTradingLayout = 'getTradingLayout',
|
|
||||||
getLayoutLocked = 'getLayoutLocked',
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum LayoutActions {
|
|
||||||
setDashboardLayout = 'setDashboardLayout',
|
|
||||||
setTradingLayout = 'setTradingLayout',
|
|
||||||
resetDashboardLayout = 'resetDashboardLayout',
|
|
||||||
resetTradingLayout = 'resetTradingLayout',
|
|
||||||
setLayoutLocked = 'setLayoutLocked',
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum LayoutMutations {
|
|
||||||
setDashboardLayout = 'setDashboardLayout',
|
|
||||||
setTradingLayout = 'setTradingLayout',
|
|
||||||
setLayoutLocked = 'setLayoutLocked',
|
|
||||||
}
|
|
||||||
// Define default layouts
|
|
||||||
const DEFAULT_TRADING_LAYOUT: GridItemData[] = [
|
|
||||||
{ i: TradeLayout.multiPane, x: 0, y: 0, w: 3, h: 35 },
|
|
||||||
{ i: TradeLayout.chartView, x: 3, y: 0, w: 9, h: 14 },
|
|
||||||
{ i: TradeLayout.tradeDetail, x: 3, y: 19, w: 9, h: 6 },
|
|
||||||
{ i: TradeLayout.openTrades, x: 3, y: 14, w: 9, h: 5 },
|
|
||||||
{ i: TradeLayout.tradeHistory, x: 3, y: 25, w: 9, h: 10 },
|
|
||||||
];
|
|
||||||
|
|
||||||
// Currently only multiPane is visible
|
|
||||||
const DEFAULT_TRADING_LAYOUT_SM: GridItemData[] = [
|
|
||||||
{ i: TradeLayout.multiPane, x: 0, y: 0, w: 12, h: 10 },
|
|
||||||
{ i: TradeLayout.chartView, x: 0, y: 10, w: 12, h: 0 },
|
|
||||||
{ i: TradeLayout.tradeDetail, x: 0, y: 19, w: 12, h: 0 },
|
|
||||||
{ i: TradeLayout.openTrades, x: 0, y: 8, w: 12, h: 0 },
|
|
||||||
{ i: TradeLayout.tradeHistory, x: 0, y: 25, w: 12, h: 0 },
|
|
||||||
];
|
|
||||||
|
|
||||||
const DEFAULT_DASHBOARD_LAYOUT: GridItemData[] = [
|
|
||||||
{ i: DashboardLayout.botComparison, x: 0, y: 0, w: 8, h: 6 } /* Bot Comparison */,
|
|
||||||
{ i: DashboardLayout.dailyChart, x: 8, y: 0, w: 4, h: 6 },
|
|
||||||
{ i: DashboardLayout.allOpenTrades, x: 0, y: 6, w: 8, h: 6 },
|
|
||||||
{ i: DashboardLayout.cumChartChart, x: 8, y: 6, w: 4, h: 6 },
|
|
||||||
{ i: DashboardLayout.tradesLogChart, x: 0, y: 12, w: 12, h: 4 },
|
|
||||||
];
|
|
||||||
|
|
||||||
const DEFAULT_DASHBOARD_LAYOUT_SM: GridItemData[] = [
|
|
||||||
{ i: DashboardLayout.botComparison, x: 0, y: 0, w: 12, h: 6 } /* Bot Comparison */,
|
|
||||||
{ i: DashboardLayout.allOpenTrades, x: 0, y: 6, w: 12, h: 8 },
|
|
||||||
{ i: DashboardLayout.dailyChart, x: 0, y: 14, w: 12, h: 6 },
|
|
||||||
{ i: DashboardLayout.cumChartChart, x: 0, y: 20, w: 12, h: 6 },
|
|
||||||
{ i: DashboardLayout.tradesLogChart, x: 0, y: 26, w: 12, h: 4 },
|
|
||||||
];
|
|
||||||
|
|
||||||
const STORE_DASHBOARD_LAYOUT = 'ftDashboardLayout';
|
|
||||||
const STORE_TRADING_LAYOUT = 'ftTradingLayout';
|
|
||||||
const STORE_LAYOUT_LOCK = 'ftLayoutLocked';
|
|
||||||
|
|
||||||
function getLayoutLocked() {
|
|
||||||
const fromStore = localStorage.getItem(STORE_LAYOUT_LOCK);
|
|
||||||
if (fromStore) {
|
|
||||||
return JSON.parse(fromStore);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getLayout(storageString: string, defaultLayout: GridItemData[]) {
|
|
||||||
const fromStore = localStorage.getItem(storageString);
|
|
||||||
if (fromStore) {
|
|
||||||
return JSON.parse(fromStore);
|
|
||||||
}
|
|
||||||
|
|
||||||
return JSON.parse(JSON.stringify(defaultLayout));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper function finding a layout entry
|
|
||||||
* @param gridLayout Array of grid layouts used in this layout. Must be passed to GridLayout, too.
|
|
||||||
* @param name Name within the dashboard layout to find
|
|
||||||
*/
|
|
||||||
export function findGridLayout(gridLayout: GridItemData[], name: string): GridItemData {
|
|
||||||
let layout = gridLayout.find((value) => value.i === name);
|
|
||||||
if (!layout) {
|
|
||||||
layout = { i: name, x: 0, y: 0, w: 4, h: 6 };
|
|
||||||
}
|
|
||||||
return layout;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default {
|
|
||||||
namespaced: true,
|
|
||||||
state: {
|
|
||||||
dashboardLayout: getLayout(STORE_DASHBOARD_LAYOUT, DEFAULT_DASHBOARD_LAYOUT),
|
|
||||||
tradingLayout: getLayout(STORE_TRADING_LAYOUT, DEFAULT_TRADING_LAYOUT),
|
|
||||||
layoutLocked: getLayoutLocked(),
|
|
||||||
},
|
|
||||||
|
|
||||||
getters: {
|
|
||||||
[LayoutGetters.getDashboardLayoutSm]() {
|
|
||||||
return [...DEFAULT_DASHBOARD_LAYOUT_SM];
|
|
||||||
},
|
|
||||||
[LayoutGetters.getDashboardLayout](state) {
|
|
||||||
return state.dashboardLayout;
|
|
||||||
},
|
|
||||||
[LayoutGetters.getTradingLayoutSm]() {
|
|
||||||
return [...DEFAULT_TRADING_LAYOUT_SM];
|
|
||||||
},
|
|
||||||
[LayoutGetters.getTradingLayout](state) {
|
|
||||||
return state.tradingLayout;
|
|
||||||
},
|
|
||||||
[LayoutGetters.getLayoutLocked](state) {
|
|
||||||
return state.layoutLocked;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
mutations: {
|
|
||||||
[LayoutMutations.setDashboardLayout](state, layout) {
|
|
||||||
state.dashboardLayout = layout;
|
|
||||||
localStorage.setItem(STORE_DASHBOARD_LAYOUT, JSON.stringify(layout));
|
|
||||||
},
|
|
||||||
[LayoutMutations.setTradingLayout](state, layout) {
|
|
||||||
state.tradingLayout = layout;
|
|
||||||
localStorage.setItem(STORE_TRADING_LAYOUT, JSON.stringify(layout));
|
|
||||||
},
|
|
||||||
[LayoutMutations.setLayoutLocked](state, locked: boolean) {
|
|
||||||
state.layoutLocked = locked;
|
|
||||||
localStorage.setItem(STORE_LAYOUT_LOCK, JSON.stringify(locked));
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
actions: {
|
|
||||||
[LayoutActions.setDashboardLayout]({ commit }, layout) {
|
|
||||||
commit(LayoutMutations.setDashboardLayout, layout);
|
|
||||||
},
|
|
||||||
[LayoutActions.setTradingLayout]({ commit }, layout) {
|
|
||||||
commit(LayoutMutations.setTradingLayout, layout);
|
|
||||||
},
|
|
||||||
[LayoutActions.setLayoutLocked]({ commit }, locked: boolean) {
|
|
||||||
commit(LayoutMutations.setLayoutLocked, locked);
|
|
||||||
},
|
|
||||||
[LayoutActions.resetDashboardLayout]({ commit }) {
|
|
||||||
commit(
|
|
||||||
LayoutMutations.setDashboardLayout,
|
|
||||||
JSON.parse(JSON.stringify(DEFAULT_DASHBOARD_LAYOUT)),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
[LayoutActions.resetTradingLayout]({ commit }) {
|
|
||||||
commit(LayoutMutations.setTradingLayout, JSON.parse(JSON.stringify(DEFAULT_TRADING_LAYOUT)));
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
|
@ -2,7 +2,6 @@
|
||||||
enum StoreModules {
|
enum StoreModules {
|
||||||
ftbot = 'ftbot',
|
ftbot = 'ftbot',
|
||||||
alerts = 'alerts',
|
alerts = 'alerts',
|
||||||
layout = 'layout',
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default StoreModules;
|
export default StoreModules;
|
||||||
|
|
116
src/stores/layout.ts
Normal file
116
src/stores/layout.ts
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
import { defineStore } from 'pinia';
|
||||||
|
import { GridItemData } from 'vue-grid-layout';
|
||||||
|
|
||||||
|
export enum TradeLayout {
|
||||||
|
multiPane = 'g-multiPane',
|
||||||
|
openTrades = 'g-openTrades',
|
||||||
|
tradeHistory = 'g-tradeHistory',
|
||||||
|
tradeDetail = 'g-tradeDetail',
|
||||||
|
chartView = 'g-chartView',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum DashboardLayout {
|
||||||
|
dailyChart = 'g-dailyChart',
|
||||||
|
botComparison = 'g-botComparison',
|
||||||
|
allOpenTrades = 'g-allOpenTrades',
|
||||||
|
cumChartChart = 'g-cumChartChart',
|
||||||
|
tradesLogChart = 'g-TradesLogChart',
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define default layouts
|
||||||
|
const DEFAULT_TRADING_LAYOUT: GridItemData[] = [
|
||||||
|
{ i: TradeLayout.multiPane, x: 0, y: 0, w: 3, h: 35 },
|
||||||
|
{ i: TradeLayout.chartView, x: 3, y: 0, w: 9, h: 14 },
|
||||||
|
{ i: TradeLayout.tradeDetail, x: 3, y: 19, w: 9, h: 6 },
|
||||||
|
{ i: TradeLayout.openTrades, x: 3, y: 14, w: 9, h: 5 },
|
||||||
|
{ i: TradeLayout.tradeHistory, x: 3, y: 25, w: 9, h: 10 },
|
||||||
|
];
|
||||||
|
|
||||||
|
// Currently only multiPane is visible
|
||||||
|
const DEFAULT_TRADING_LAYOUT_SM: GridItemData[] = [
|
||||||
|
{ i: TradeLayout.multiPane, x: 0, y: 0, w: 12, h: 10 },
|
||||||
|
{ i: TradeLayout.chartView, x: 0, y: 10, w: 12, h: 0 },
|
||||||
|
{ i: TradeLayout.tradeDetail, x: 0, y: 19, w: 12, h: 0 },
|
||||||
|
{ i: TradeLayout.openTrades, x: 0, y: 8, w: 12, h: 0 },
|
||||||
|
{ i: TradeLayout.tradeHistory, x: 0, y: 25, w: 12, h: 0 },
|
||||||
|
];
|
||||||
|
|
||||||
|
const DEFAULT_DASHBOARD_LAYOUT: GridItemData[] = [
|
||||||
|
{ i: DashboardLayout.botComparison, x: 0, y: 0, w: 8, h: 6 } /* Bot Comparison */,
|
||||||
|
{ i: DashboardLayout.dailyChart, x: 8, y: 0, w: 4, h: 6 },
|
||||||
|
{ i: DashboardLayout.allOpenTrades, x: 0, y: 6, w: 8, h: 6 },
|
||||||
|
{ i: DashboardLayout.cumChartChart, x: 8, y: 6, w: 4, h: 6 },
|
||||||
|
{ i: DashboardLayout.tradesLogChart, x: 0, y: 12, w: 12, h: 4 },
|
||||||
|
];
|
||||||
|
|
||||||
|
const DEFAULT_DASHBOARD_LAYOUT_SM: GridItemData[] = [
|
||||||
|
{ i: DashboardLayout.botComparison, x: 0, y: 0, w: 12, h: 6 } /* Bot Comparison */,
|
||||||
|
{ i: DashboardLayout.allOpenTrades, x: 0, y: 6, w: 12, h: 8 },
|
||||||
|
{ i: DashboardLayout.dailyChart, x: 0, y: 14, w: 12, h: 6 },
|
||||||
|
{ i: DashboardLayout.cumChartChart, x: 0, y: 20, w: 12, h: 6 },
|
||||||
|
{ i: DashboardLayout.tradesLogChart, x: 0, y: 26, w: 12, h: 4 },
|
||||||
|
];
|
||||||
|
|
||||||
|
const STORE_LAYOUTS = 'ftLayoutSettings';
|
||||||
|
|
||||||
|
function migrateLayoutSettings() {
|
||||||
|
const STORE_DASHBOARD_LAYOUT = 'ftDashboardLayout';
|
||||||
|
const STORE_TRADING_LAYOUT = 'ftTradingLayout';
|
||||||
|
const STORE_LAYOUT_LOCK = 'ftLayoutLocked';
|
||||||
|
|
||||||
|
// If new does not exist
|
||||||
|
if (localStorage.getItem(STORE_LAYOUTS) === null) {
|
||||||
|
console.log('Migrating dashboard settings');
|
||||||
|
const layoutLocked = localStorage.getItem(STORE_LAYOUT_LOCK);
|
||||||
|
const tradingLayout = localStorage.getItem(STORE_TRADING_LAYOUT);
|
||||||
|
const dashboardLayout = localStorage.getItem(STORE_DASHBOARD_LAYOUT);
|
||||||
|
|
||||||
|
const res = {
|
||||||
|
dashboardLayout: dashboardLayout,
|
||||||
|
tradingLayout,
|
||||||
|
layoutLocked,
|
||||||
|
};
|
||||||
|
localStorage.setItem(STORE_LAYOUTS, JSON.stringify(res));
|
||||||
|
}
|
||||||
|
localStorage.removeItem(STORE_LAYOUT_LOCK);
|
||||||
|
localStorage.removeItem(STORE_TRADING_LAYOUT);
|
||||||
|
localStorage.removeItem(STORE_DASHBOARD_LAYOUT);
|
||||||
|
}
|
||||||
|
migrateLayoutSettings();
|
||||||
|
/**
|
||||||
|
* Helper function finding a layout entry
|
||||||
|
* @param gridLayout Array of grid layouts used in this layout. Must be passed to GridLayout, too.
|
||||||
|
* @param name Name within the dashboard layout to find
|
||||||
|
*/
|
||||||
|
export function findGridLayout(gridLayout: GridItemData[], name: string): GridItemData {
|
||||||
|
let layout = gridLayout.find((value) => value.i === name);
|
||||||
|
if (!layout) {
|
||||||
|
layout = { i: name, x: 0, y: 0, w: 4, h: 6 };
|
||||||
|
}
|
||||||
|
return layout;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useLayoutStore = defineStore('layoutStore', {
|
||||||
|
state: () => {
|
||||||
|
return {
|
||||||
|
dashboardLayout: JSON.parse(JSON.stringify(DEFAULT_DASHBOARD_LAYOUT)),
|
||||||
|
tradingLayout: JSON.parse(JSON.stringify(DEFAULT_TRADING_LAYOUT)),
|
||||||
|
layoutLocked: true,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
getters: {
|
||||||
|
getDashboardLayoutSm: () => [...DEFAULT_DASHBOARD_LAYOUT_SM],
|
||||||
|
getTradingLayoutSm: () => [...DEFAULT_TRADING_LAYOUT_SM],
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
resetTradingLayout() {
|
||||||
|
this.tradingLayout = JSON.parse(JSON.stringify(DEFAULT_TRADING_LAYOUT));
|
||||||
|
},
|
||||||
|
resetDashboardLayout() {
|
||||||
|
this.dashboardLayout = JSON.parse(JSON.stringify(DEFAULT_DASHBOARD_LAYOUT));
|
||||||
|
},
|
||||||
|
},
|
||||||
|
persist: {
|
||||||
|
key: STORE_LAYOUTS,
|
||||||
|
},
|
||||||
|
});
|
|
@ -2,7 +2,7 @@
|
||||||
<GridLayout
|
<GridLayout
|
||||||
class="h-100 w-100"
|
class="h-100 w-100"
|
||||||
:row-height="50"
|
:row-height="50"
|
||||||
:layout.sync="gridLayout"
|
:layout="gridLayout"
|
||||||
:vertical-compact="false"
|
:vertical-compact="false"
|
||||||
:margin="[5, 5]"
|
:margin="[5, 5]"
|
||||||
:responsive-layouts="responsiveGridLayouts"
|
:responsive-layouts="responsiveGridLayouts"
|
||||||
|
@ -11,7 +11,7 @@
|
||||||
:responsive="true"
|
:responsive="true"
|
||||||
:prevent-collision="true"
|
:prevent-collision="true"
|
||||||
:cols="{ lg: 12, md: 12, sm: 12, xs: 4, xxs: 2 }"
|
:cols="{ lg: 12, md: 12, sm: 12, xs: 4, xxs: 2 }"
|
||||||
@layout-updated="layoutUpdated"
|
@layout-updated="layoutUpdatedEvent"
|
||||||
@breakpoint-changed="breakpointChanged"
|
@breakpoint-changed="breakpointChanged"
|
||||||
>
|
>
|
||||||
<GridItem
|
<GridItem
|
||||||
|
@ -94,8 +94,6 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { formatPrice } from '@/shared/formatters';
|
import { formatPrice } from '@/shared/formatters';
|
||||||
|
|
||||||
import { Component, Vue } from 'vue-property-decorator';
|
|
||||||
import { namespace } from 'vuex-class';
|
|
||||||
import { GridLayout, GridItem, GridItemData } from 'vue-grid-layout';
|
import { GridLayout, GridItem, GridItemData } from 'vue-grid-layout';
|
||||||
|
|
||||||
import DailyChart from '@/components/charts/DailyChart.vue';
|
import DailyChart from '@/components/charts/DailyChart.vue';
|
||||||
|
@ -105,21 +103,16 @@ import BotComparisonList from '@/components/ftbot/BotComparisonList.vue';
|
||||||
import TradeList from '@/components/ftbot/TradeList.vue';
|
import TradeList from '@/components/ftbot/TradeList.vue';
|
||||||
import DraggableContainer from '@/components/layout/DraggableContainer.vue';
|
import DraggableContainer from '@/components/layout/DraggableContainer.vue';
|
||||||
|
|
||||||
import {
|
|
||||||
DashboardLayout,
|
|
||||||
findGridLayout,
|
|
||||||
LayoutActions,
|
|
||||||
LayoutGetters,
|
|
||||||
} from '@/store/modules/layout';
|
|
||||||
import { Trade, DailyReturnValue, DailyPayload, ClosedTrade } from '@/types';
|
|
||||||
import { BotStoreGetters } from '@/store/modules/ftbot';
|
import { BotStoreGetters } from '@/store/modules/ftbot';
|
||||||
import { MultiBotStoreGetters } from '@/store/modules/botStoreWrapper';
|
import { MultiBotStoreGetters } from '@/store/modules/botStoreWrapper';
|
||||||
import StoreModules from '@/store/storeSubModules';
|
import StoreModules from '@/store/storeSubModules';
|
||||||
|
|
||||||
const ftbot = namespace(StoreModules.ftbot);
|
import { defineComponent, ref, computed, onMounted } from '@vue/composition-api';
|
||||||
const layoutNs = namespace(StoreModules.layout);
|
import { useNamespacedGetters, useNamespacedActions } from 'vuex-composition-helpers';
|
||||||
|
import { DashboardLayout, findGridLayout, useLayoutStore } from '@/stores/layout';
|
||||||
|
|
||||||
@Component({
|
export default defineComponent({
|
||||||
|
name: 'Dashboard',
|
||||||
components: {
|
components: {
|
||||||
GridLayout,
|
GridLayout,
|
||||||
GridItem,
|
GridItem,
|
||||||
|
@ -130,111 +123,118 @@ const layoutNs = namespace(StoreModules.layout);
|
||||||
TradeList,
|
TradeList,
|
||||||
DraggableContainer,
|
DraggableContainer,
|
||||||
},
|
},
|
||||||
})
|
setup() {
|
||||||
export default class Dashboard extends Vue {
|
const {
|
||||||
@ftbot.Getter [MultiBotStoreGetters.botCount]!: number;
|
botCount,
|
||||||
|
allOpenTradesAllBots,
|
||||||
|
allTradesAllBots,
|
||||||
|
allDailyStatsAllBots,
|
||||||
|
performanceStats,
|
||||||
|
} = useNamespacedGetters(StoreModules.ftbot, [
|
||||||
|
MultiBotStoreGetters.botCount,
|
||||||
|
MultiBotStoreGetters.allOpenTradesAllBots,
|
||||||
|
MultiBotStoreGetters.allTradesAllBots,
|
||||||
|
MultiBotStoreGetters.allDailyStatsAllBots,
|
||||||
|
BotStoreGetters.performanceStats,
|
||||||
|
]);
|
||||||
|
|
||||||
@ftbot.Getter [MultiBotStoreGetters.allOpenTradesAllBots]!: Trade[];
|
const { getPerformance, allGetDaily, getTrades, getOpenTrades, getProfit } =
|
||||||
|
useNamespacedActions(StoreModules.ftbot, [
|
||||||
|
'getPerformance',
|
||||||
|
'allGetDaily',
|
||||||
|
'getTrades',
|
||||||
|
'getOpenTrades',
|
||||||
|
'getProfit',
|
||||||
|
]);
|
||||||
|
|
||||||
@ftbot.Getter [MultiBotStoreGetters.allTradesAllBots]!: ClosedTrade[];
|
const layoutStore = useLayoutStore();
|
||||||
|
const currentBreakpoint = ref('');
|
||||||
|
|
||||||
@ftbot.Getter [MultiBotStoreGetters.allDailyStatsAllBots]!: Record<string, DailyReturnValue>;
|
const breakpointChanged = (newBreakpoint) => {
|
||||||
|
// // console.log('breakpoint:', newBreakpoint);
|
||||||
|
currentBreakpoint.value = newBreakpoint;
|
||||||
|
};
|
||||||
|
const isResizableLayout = computed(() =>
|
||||||
|
['', 'sm', 'md', 'lg', 'xl'].includes(currentBreakpoint.value),
|
||||||
|
);
|
||||||
|
const isLayoutLocked = computed(() => {
|
||||||
|
return layoutStore.layoutLocked || !isResizableLayout;
|
||||||
|
});
|
||||||
|
|
||||||
@ftbot.Getter [BotStoreGetters.performanceStats]!: PerformanceEntry[];
|
const gridLayout = computed((): GridItemData[] => {
|
||||||
|
if (isResizableLayout) {
|
||||||
@ftbot.Action getPerformance;
|
return layoutStore.dashboardLayout;
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
@ftbot.Action allGetDaily!: (payload?: DailyPayload) => void;
|
|
||||||
|
|
||||||
@ftbot.Action getTrades;
|
|
||||||
|
|
||||||
@ftbot.Action getOpenTrades;
|
|
||||||
|
|
||||||
@ftbot.Action getProfit;
|
|
||||||
|
|
||||||
@layoutNs.Getter [LayoutGetters.getDashboardLayoutSm]!: GridItemData[];
|
|
||||||
|
|
||||||
@layoutNs.Getter [LayoutGetters.getDashboardLayout]!: GridItemData[];
|
|
||||||
|
|
||||||
@layoutNs.Action [LayoutActions.setDashboardLayout];
|
|
||||||
|
|
||||||
@layoutNs.Getter [LayoutGetters.getLayoutLocked]: boolean;
|
|
||||||
|
|
||||||
formatPrice = formatPrice;
|
|
||||||
|
|
||||||
localGridLayout: GridItemData[] = [];
|
|
||||||
|
|
||||||
currentBreakpoint = '';
|
|
||||||
|
|
||||||
get isLayoutLocked() {
|
|
||||||
return this.getLayoutLocked || !this.isResizableLayout;
|
|
||||||
}
|
}
|
||||||
|
return [...layoutStore.getDashboardLayoutSm];
|
||||||
|
});
|
||||||
|
|
||||||
get isResizableLayout() {
|
const layoutUpdatedEvent = (newLayout) => {
|
||||||
return ['', 'sm', 'md', 'lg', 'xl'].includes(this.currentBreakpoint);
|
if (isResizableLayout) {
|
||||||
}
|
|
||||||
|
|
||||||
get gridLayout() {
|
|
||||||
if (this.isResizableLayout) {
|
|
||||||
return this.getDashboardLayout;
|
|
||||||
}
|
|
||||||
return this.localGridLayout;
|
|
||||||
}
|
|
||||||
|
|
||||||
set gridLayout(newLayout) {
|
|
||||||
// Dummy setter to make gridLayout happy. Updates happen through layoutUpdated.
|
|
||||||
}
|
|
||||||
|
|
||||||
layoutUpdated(newLayout) {
|
|
||||||
// Frozen layouts for small screen sizes.
|
|
||||||
if (this.isResizableLayout) {
|
|
||||||
console.log('newlayout', newLayout);
|
console.log('newlayout', newLayout);
|
||||||
console.log('saving dashboard');
|
console.log('saving dashboard');
|
||||||
this.setDashboardLayout(newLayout);
|
layoutStore.tradingLayout = newLayout;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
get gridLayoutDaily(): GridItemData {
|
|
||||||
return findGridLayout(this.gridLayout, DashboardLayout.dailyChart);
|
|
||||||
}
|
|
||||||
|
|
||||||
get gridLayoutBotComparison(): GridItemData {
|
|
||||||
return findGridLayout(this.gridLayout, DashboardLayout.botComparison);
|
|
||||||
}
|
|
||||||
|
|
||||||
get gridLayoutAllOpenTrades(): GridItemData {
|
|
||||||
return findGridLayout(this.gridLayout, DashboardLayout.allOpenTrades);
|
|
||||||
}
|
|
||||||
|
|
||||||
get gridLayoutCumChart(): GridItemData {
|
|
||||||
return findGridLayout(this.gridLayout, DashboardLayout.cumChartChart);
|
|
||||||
}
|
|
||||||
|
|
||||||
get gridLayoutTradesLogChart(): GridItemData {
|
|
||||||
return findGridLayout(this.gridLayout, DashboardLayout.tradesLogChart);
|
|
||||||
}
|
|
||||||
|
|
||||||
get responsiveGridLayouts() {
|
|
||||||
return {
|
|
||||||
sm: this.getDashboardLayoutSm,
|
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
mounted() {
|
const gridLayoutDaily = computed((): GridItemData => {
|
||||||
this.allGetDaily({ timescale: 30 });
|
return findGridLayout(gridLayout.value, DashboardLayout.dailyChart);
|
||||||
this.getTrades();
|
});
|
||||||
this.getOpenTrades();
|
|
||||||
this.getPerformance();
|
|
||||||
this.getProfit();
|
|
||||||
this.localGridLayout = [...this.getDashboardLayoutSm];
|
|
||||||
}
|
|
||||||
|
|
||||||
breakpointChanged(newBreakpoint) {
|
const gridLayoutBotComparison = computed((): GridItemData => {
|
||||||
// console.log('breakpoint:', newBreakpoint);
|
return findGridLayout(gridLayout.value, DashboardLayout.botComparison);
|
||||||
this.currentBreakpoint = newBreakpoint;
|
});
|
||||||
}
|
|
||||||
}
|
const gridLayoutAllOpenTrades = computed((): GridItemData => {
|
||||||
|
return findGridLayout(gridLayout.value, DashboardLayout.allOpenTrades);
|
||||||
|
});
|
||||||
|
|
||||||
|
const gridLayoutCumChart = computed((): GridItemData => {
|
||||||
|
return findGridLayout(gridLayout.value, DashboardLayout.cumChartChart);
|
||||||
|
});
|
||||||
|
|
||||||
|
const gridLayoutTradesLogChart = computed((): GridItemData => {
|
||||||
|
return findGridLayout(gridLayout.value, DashboardLayout.tradesLogChart);
|
||||||
|
});
|
||||||
|
|
||||||
|
const responsiveGridLayouts = computed(() => {
|
||||||
|
return {
|
||||||
|
sm: layoutStore.getDashboardLayoutSm,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
allGetDaily({ timescale: 30 });
|
||||||
|
getTrades();
|
||||||
|
getOpenTrades();
|
||||||
|
getPerformance();
|
||||||
|
getProfit();
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
formatPrice,
|
||||||
|
isLayoutLocked,
|
||||||
|
layoutUpdatedEvent,
|
||||||
|
breakpointChanged,
|
||||||
|
gridLayout,
|
||||||
|
gridLayoutDaily,
|
||||||
|
gridLayoutBotComparison,
|
||||||
|
gridLayoutAllOpenTrades,
|
||||||
|
gridLayoutCumChart,
|
||||||
|
gridLayoutTradesLogChart,
|
||||||
|
responsiveGridLayouts,
|
||||||
|
getPerformance,
|
||||||
|
allGetDaily,
|
||||||
|
getTrades,
|
||||||
|
getOpenTrades,
|
||||||
|
getProfit,
|
||||||
|
botCount,
|
||||||
|
allOpenTradesAllBots,
|
||||||
|
allTradesAllBots,
|
||||||
|
allDailyStatsAllBots,
|
||||||
|
performanceStats,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<b-form-group
|
<b-form-group
|
||||||
description="Lock dynamic layouts, so they cannot move anymore. Can also be set from the navbar at the top."
|
description="Lock dynamic layouts, so they cannot move anymore. Can also be set from the navbar at the top."
|
||||||
>
|
>
|
||||||
<b-checkbox v-model="layoutLockedLocal">Lock layout</b-checkbox>
|
<b-checkbox v-model="layoutStore.layoutLocked">Lock layout</b-checkbox>
|
||||||
</b-form-group>
|
</b-form-group>
|
||||||
<b-form-group description="Reset dynamic layouts to how they were.">
|
<b-form-group description="Reset dynamic layouts to how they were.">
|
||||||
<b-button size="sm" @click="resetDynamicLayout">Reset layout</b-button>
|
<b-button size="sm" @click="resetDynamicLayout">Reset layout</b-button>
|
||||||
|
@ -39,28 +39,20 @@
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { AlertActions } from '@/store/modules/alerts';
|
import { AlertActions } from '@/store/modules/alerts';
|
||||||
import { LayoutActions, LayoutGetters } from '@/store/modules/layout';
|
|
||||||
import StoreModules from '@/store/storeSubModules';
|
import StoreModules from '@/store/storeSubModules';
|
||||||
import { defineComponent, WritableComputedRef, computed } from '@vue/composition-api';
|
import { defineComponent } from '@vue/composition-api';
|
||||||
import { useGetters, useNamespacedActions, useNamespacedGetters } from 'vuex-composition-helpers';
|
import { useGetters, useNamespacedActions } from 'vuex-composition-helpers';
|
||||||
import { OpenTradeVizOptions, useSettingsStore } from '@/stores/settings';
|
import { OpenTradeVizOptions, useSettingsStore } from '@/stores/settings';
|
||||||
|
import { useLayoutStore } from '@/stores/layout';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'Settings',
|
name: 'Settings',
|
||||||
setup() {
|
setup() {
|
||||||
const settingsStore = useSettingsStore();
|
const settingsStore = useSettingsStore();
|
||||||
|
const layoutStore = useLayoutStore();
|
||||||
|
|
||||||
const { getUiVersion } = useGetters(['getUiVersion']);
|
const { getUiVersion } = useGetters(['getUiVersion']);
|
||||||
const { setLayoutLocked, resetTradingLayout, resetDashboardLayout } = useNamespacedActions(
|
|
||||||
StoreModules.layout,
|
|
||||||
[
|
|
||||||
LayoutActions.setLayoutLocked,
|
|
||||||
LayoutActions.resetTradingLayout,
|
|
||||||
LayoutActions.resetDashboardLayout,
|
|
||||||
],
|
|
||||||
);
|
|
||||||
const { getLayoutLocked } = useNamespacedGetters(StoreModules.layout, [
|
|
||||||
LayoutGetters.getLayoutLocked,
|
|
||||||
]);
|
|
||||||
const { addAlert } = useNamespacedActions(StoreModules.alerts, [AlertActions.addAlert]);
|
const { addAlert } = useNamespacedActions(StoreModules.alerts, [AlertActions.addAlert]);
|
||||||
|
|
||||||
const timezoneOptions = ['UTC', Intl.DateTimeFormat().resolvedOptions().timeZone];
|
const timezoneOptions = ['UTC', Intl.DateTimeFormat().resolvedOptions().timeZone];
|
||||||
|
@ -69,27 +61,20 @@ export default defineComponent({
|
||||||
{ value: OpenTradeVizOptions.asTitle, text: 'Show in title' },
|
{ value: OpenTradeVizOptions.asTitle, text: 'Show in title' },
|
||||||
{ value: OpenTradeVizOptions.noOpenTrades, text: "Don't show open trades in header" },
|
{ value: OpenTradeVizOptions.noOpenTrades, text: "Don't show open trades in header" },
|
||||||
];
|
];
|
||||||
const layoutLockedLocal: WritableComputedRef<boolean> = computed({
|
|
||||||
get(): boolean {
|
|
||||||
return getLayoutLocked.value;
|
|
||||||
},
|
|
||||||
set(value: boolean): void {
|
|
||||||
setLayoutLocked(value);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
//
|
//
|
||||||
const resetDynamicLayout = () => {
|
const resetDynamicLayout = () => {
|
||||||
resetTradingLayout();
|
layoutStore.resetTradingLayout();
|
||||||
resetDashboardLayout();
|
layoutStore.resetDashboardLayout();
|
||||||
addAlert({ message: 'Layouts have been reset.' });
|
addAlert({ message: 'Layouts have been reset.' });
|
||||||
};
|
};
|
||||||
return {
|
return {
|
||||||
getUiVersion,
|
getUiVersion,
|
||||||
resetDynamicLayout,
|
resetDynamicLayout,
|
||||||
settingsStore,
|
settingsStore,
|
||||||
|
layoutStore,
|
||||||
timezoneOptions,
|
timezoneOptions,
|
||||||
openTradesOptions,
|
openTradesOptions,
|
||||||
layoutLockedLocal,
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -128,8 +128,6 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Component, Vue } from 'vue-property-decorator';
|
|
||||||
import { namespace } from 'vuex-class';
|
|
||||||
import { GridLayout, GridItem, GridItemData } from 'vue-grid-layout';
|
import { GridLayout, GridItem, GridItemData } from 'vue-grid-layout';
|
||||||
|
|
||||||
import Balance from '@/components/ftbot/Balance.vue';
|
import Balance from '@/components/ftbot/Balance.vue';
|
||||||
|
@ -145,15 +143,14 @@ import Performance from '@/components/ftbot/Performance.vue';
|
||||||
import TradeDetail from '@/components/ftbot/TradeDetail.vue';
|
import TradeDetail from '@/components/ftbot/TradeDetail.vue';
|
||||||
import TradeList from '@/components/ftbot/TradeList.vue';
|
import TradeList from '@/components/ftbot/TradeList.vue';
|
||||||
|
|
||||||
import { Lock, Trade } from '@/types';
|
|
||||||
import { BotStoreGetters } from '@/store/modules/ftbot';
|
import { BotStoreGetters } from '@/store/modules/ftbot';
|
||||||
import { TradeLayout, findGridLayout, LayoutGetters, LayoutActions } from '@/store/modules/layout';
|
|
||||||
import StoreModules from '@/store/storeSubModules';
|
import StoreModules from '@/store/storeSubModules';
|
||||||
|
import { defineComponent, ref, computed } from '@vue/composition-api';
|
||||||
|
import { useNamespacedGetters } from 'vuex-composition-helpers';
|
||||||
|
import { useLayoutStore, findGridLayout, TradeLayout } from '@/stores/layout';
|
||||||
|
|
||||||
const ftbot = namespace(StoreModules.ftbot);
|
export default defineComponent({
|
||||||
const layoutNs = namespace(StoreModules.layout);
|
name: 'Trading',
|
||||||
|
|
||||||
@Component({
|
|
||||||
components: {
|
components: {
|
||||||
Balance,
|
Balance,
|
||||||
BotControls,
|
BotControls,
|
||||||
|
@ -170,98 +167,105 @@ const layoutNs = namespace(StoreModules.layout);
|
||||||
TradeDetail,
|
TradeDetail,
|
||||||
TradeList,
|
TradeList,
|
||||||
},
|
},
|
||||||
})
|
setup() {
|
||||||
export default class Trading extends Vue {
|
const {
|
||||||
@ftbot.Getter [BotStoreGetters.detailTradeId]!: number;
|
detailTradeId,
|
||||||
|
openTrades,
|
||||||
|
closedTrades,
|
||||||
|
allTrades,
|
||||||
|
tradeDetail,
|
||||||
|
timeframe,
|
||||||
|
currentLocks,
|
||||||
|
whitelist,
|
||||||
|
stakeCurrency,
|
||||||
|
} = useNamespacedGetters(StoreModules.ftbot, [
|
||||||
|
BotStoreGetters.detailTradeId,
|
||||||
|
BotStoreGetters.openTrades,
|
||||||
|
BotStoreGetters.closedTrades,
|
||||||
|
BotStoreGetters.allTrades,
|
||||||
|
BotStoreGetters.tradeDetail,
|
||||||
|
BotStoreGetters.timeframe,
|
||||||
|
BotStoreGetters.currentLocks,
|
||||||
|
BotStoreGetters.whitelist,
|
||||||
|
BotStoreGetters.stakeCurrency,
|
||||||
|
]);
|
||||||
|
const layoutStore = useLayoutStore();
|
||||||
|
const currentBreakpoint = ref('');
|
||||||
|
|
||||||
@ftbot.Getter [BotStoreGetters.openTrades]!: Trade[];
|
const breakpointChanged = (newBreakpoint) => {
|
||||||
|
// console.log('breakpoint:', newBreakpoint);
|
||||||
@ftbot.Getter [BotStoreGetters.closedTrades]!: Trade[];
|
currentBreakpoint.value = newBreakpoint;
|
||||||
|
|
||||||
@ftbot.Getter [BotStoreGetters.allTrades]!: Trade[];
|
|
||||||
|
|
||||||
@ftbot.Getter [BotStoreGetters.tradeDetail]!: Trade;
|
|
||||||
|
|
||||||
@ftbot.Getter [BotStoreGetters.timeframe]!: string;
|
|
||||||
|
|
||||||
@ftbot.Getter [BotStoreGetters.currentLocks]!: Lock[];
|
|
||||||
|
|
||||||
@ftbot.Getter [BotStoreGetters.whitelist]!: string[];
|
|
||||||
|
|
||||||
@ftbot.Getter [BotStoreGetters.stakeCurrency]!: string;
|
|
||||||
|
|
||||||
@layoutNs.Getter [LayoutGetters.getTradingLayout]!: GridItemData[];
|
|
||||||
|
|
||||||
@layoutNs.Getter [LayoutGetters.getTradingLayoutSm]!: GridItemData[];
|
|
||||||
|
|
||||||
@layoutNs.Action [LayoutActions.setTradingLayout];
|
|
||||||
|
|
||||||
@layoutNs.Getter [LayoutGetters.getLayoutLocked]: boolean;
|
|
||||||
|
|
||||||
currentBreakpoint = '';
|
|
||||||
|
|
||||||
localGridLayout: GridItemData[] = [];
|
|
||||||
|
|
||||||
get isLayoutLocked() {
|
|
||||||
return this.getLayoutLocked || !this.isResizableLayout;
|
|
||||||
}
|
|
||||||
|
|
||||||
get isResizableLayout() {
|
|
||||||
return ['', 'sm', 'md', 'lg', 'xl'].includes(this.currentBreakpoint);
|
|
||||||
}
|
|
||||||
|
|
||||||
get gridLayout(): GridItemData[] {
|
|
||||||
if (this.isResizableLayout) {
|
|
||||||
return this.getTradingLayout;
|
|
||||||
}
|
|
||||||
return this.localGridLayout;
|
|
||||||
}
|
|
||||||
|
|
||||||
set gridLayout(newLayout) {
|
|
||||||
// Dummy setter to make gridLayout happy. Updates happen through layoutUpdated.
|
|
||||||
}
|
|
||||||
|
|
||||||
get gridLayoutMultiPane(): GridItemData {
|
|
||||||
return findGridLayout(this.gridLayout, TradeLayout.multiPane);
|
|
||||||
}
|
|
||||||
|
|
||||||
get gridLayoutOpenTrades(): GridItemData {
|
|
||||||
return findGridLayout(this.gridLayout, TradeLayout.openTrades);
|
|
||||||
}
|
|
||||||
|
|
||||||
get gridLayoutTradeHistory(): GridItemData {
|
|
||||||
return findGridLayout(this.gridLayout, TradeLayout.tradeHistory);
|
|
||||||
}
|
|
||||||
|
|
||||||
get gridLayoutTradeDetail(): GridItemData {
|
|
||||||
return findGridLayout(this.gridLayout, TradeLayout.tradeDetail);
|
|
||||||
}
|
|
||||||
|
|
||||||
get gridLayoutChartView(): GridItemData {
|
|
||||||
return findGridLayout(this.gridLayout, TradeLayout.chartView);
|
|
||||||
}
|
|
||||||
|
|
||||||
mounted() {
|
|
||||||
this.localGridLayout = [...this.getTradingLayoutSm];
|
|
||||||
}
|
|
||||||
|
|
||||||
layoutUpdatedEvent(newLayout) {
|
|
||||||
if (this.isResizableLayout) {
|
|
||||||
this.setTradingLayout(newLayout);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
get responsiveGridLayouts() {
|
|
||||||
return {
|
|
||||||
sm: this[LayoutGetters.getTradingLayoutSm],
|
|
||||||
};
|
};
|
||||||
|
const isResizableLayout = computed(() =>
|
||||||
|
['', 'sm', 'md', 'lg', 'xl'].includes(currentBreakpoint.value),
|
||||||
|
);
|
||||||
|
const isLayoutLocked = computed(() => {
|
||||||
|
return layoutStore.layoutLocked || !isResizableLayout;
|
||||||
|
});
|
||||||
|
const gridLayout = computed((): GridItemData[] => {
|
||||||
|
if (isResizableLayout) {
|
||||||
|
return layoutStore.tradingLayout;
|
||||||
}
|
}
|
||||||
|
return [...layoutStore.getTradingLayoutSm];
|
||||||
|
});
|
||||||
|
|
||||||
breakpointChanged(newBreakpoint) {
|
const gridLayoutMultiPane = computed(() => {
|
||||||
console.log('breakpoint:', newBreakpoint);
|
return findGridLayout(gridLayout.value, TradeLayout.multiPane);
|
||||||
this.currentBreakpoint = newBreakpoint;
|
});
|
||||||
|
|
||||||
|
const gridLayoutOpenTrades = computed(() => {
|
||||||
|
return findGridLayout(gridLayout.value, TradeLayout.openTrades);
|
||||||
|
});
|
||||||
|
|
||||||
|
const gridLayoutTradeHistory = computed(() => {
|
||||||
|
return findGridLayout(gridLayout.value, TradeLayout.tradeHistory);
|
||||||
|
});
|
||||||
|
|
||||||
|
const gridLayoutTradeDetail = computed(() => {
|
||||||
|
return findGridLayout(gridLayout.value, TradeLayout.tradeDetail);
|
||||||
|
});
|
||||||
|
|
||||||
|
const gridLayoutChartView = computed(() => {
|
||||||
|
return findGridLayout(gridLayout.value, TradeLayout.chartView);
|
||||||
|
});
|
||||||
|
|
||||||
|
const responsiveGridLayouts = computed(() => {
|
||||||
|
return {
|
||||||
|
sm: layoutStore.getTradingLayoutSm,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const layoutUpdatedEvent = (newLayout) => {
|
||||||
|
if (isResizableLayout) {
|
||||||
|
layoutStore.tradingLayout = newLayout;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
detailTradeId,
|
||||||
|
openTrades,
|
||||||
|
closedTrades,
|
||||||
|
allTrades,
|
||||||
|
tradeDetail,
|
||||||
|
timeframe,
|
||||||
|
currentLocks,
|
||||||
|
whitelist,
|
||||||
|
stakeCurrency,
|
||||||
|
layoutStore,
|
||||||
|
breakpointChanged,
|
||||||
|
layoutUpdatedEvent,
|
||||||
|
isLayoutLocked,
|
||||||
|
gridLayout,
|
||||||
|
gridLayoutMultiPane,
|
||||||
|
gridLayoutOpenTrades,
|
||||||
|
gridLayoutTradeHistory,
|
||||||
|
gridLayoutTradeDetail,
|
||||||
|
gridLayoutChartView,
|
||||||
|
responsiveGridLayouts,
|
||||||
|
isResizableLayout,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
|
|
@ -5319,6 +5319,11 @@ vue-template-es2015-compiler@^1.9.0, vue-template-es2015-compiler@^1.9.1:
|
||||||
resolved "https://registry.yarnpkg.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz#1ee3bc9a16ecbf5118be334bb15f9c46f82f5825"
|
resolved "https://registry.yarnpkg.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz#1ee3bc9a16ecbf5118be334bb15f9c46f82f5825"
|
||||||
integrity sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==
|
integrity sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==
|
||||||
|
|
||||||
|
vue2-helpers@^1.1.7:
|
||||||
|
version "1.1.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/vue2-helpers/-/vue2-helpers-1.1.7.tgz#f105313979af0260ef446c583fd2fa75b067afd1"
|
||||||
|
integrity sha512-NLF7bYFPyoKMvn/Bkxr7+7Ure/kZpWmd6pQpG613dT0Sn6EwI+2+LwVUQyDkDk4P0UaAwvn/QEYzhBRzDzuGLw==
|
||||||
|
|
||||||
vue@^2.6.14:
|
vue@^2.6.14:
|
||||||
version "2.6.14"
|
version "2.6.14"
|
||||||
resolved "https://registry.yarnpkg.com/vue/-/vue-2.6.14.tgz#e51aa5250250d569a3fbad3a8a5a687d6036e235"
|
resolved "https://registry.yarnpkg.com/vue/-/vue-2.6.14.tgz#e51aa5250250d569a3fbad3a8a5a687d6036e235"
|
||||||
|
|
Loading…
Reference in New Issue
Block a user