mirror of
https://github.com/freqtrade/frequi.git
synced 2024-11-22 11:05:17 +00:00
parent
2e803a0d57
commit
385a7fca11
|
@ -19,6 +19,7 @@
|
||||||
"date-fns": "^2.22.1",
|
"date-fns": "^2.22.1",
|
||||||
"date-fns-tz": "^1.1.4",
|
"date-fns-tz": "^1.1.4",
|
||||||
"echarts": "^5.1.0",
|
"echarts": "^5.1.0",
|
||||||
|
"favico.js": "^0.3.10",
|
||||||
"humanize-duration": "^3.27.0",
|
"humanize-duration": "^3.27.0",
|
||||||
"vue": "^2.6.14",
|
"vue": "^2.6.14",
|
||||||
"vue-class-component": "^7.2.5",
|
"vue-class-component": "^7.2.5",
|
||||||
|
|
|
@ -54,9 +54,12 @@ import userService from '@/shared/userService';
|
||||||
import BootswatchThemeSelect from '@/components/BootswatchThemeSelect.vue';
|
import BootswatchThemeSelect from '@/components/BootswatchThemeSelect.vue';
|
||||||
import { LayoutActions, LayoutGetters } from '@/store/modules/layout';
|
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 { SettingsGetters } from '@/store/modules/settings';
|
||||||
|
|
||||||
const ftbot = namespace('ftbot');
|
const ftbot = namespace('ftbot');
|
||||||
const layoutNs = namespace('layout');
|
const layoutNs = namespace('layout');
|
||||||
|
const uiSettingsNs = namespace('uiSettings');
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
components: { LoginModal, BootswatchThemeSelect },
|
components: { LoginModal, BootswatchThemeSelect },
|
||||||
|
@ -74,6 +77,8 @@ export default class NavBar extends Vue {
|
||||||
|
|
||||||
@ftbot.Getter [BotStoreGetters.botName]: string;
|
@ftbot.Getter [BotStoreGetters.botName]: string;
|
||||||
|
|
||||||
|
@ftbot.Getter [BotStoreGetters.openTradeCount]: number;
|
||||||
|
|
||||||
@layoutNs.Getter [LayoutGetters.getLayoutLocked]: boolean;
|
@layoutNs.Getter [LayoutGetters.getLayoutLocked]: boolean;
|
||||||
|
|
||||||
@layoutNs.Action [LayoutActions.resetDashboardLayout];
|
@layoutNs.Action [LayoutActions.resetDashboardLayout];
|
||||||
|
@ -82,6 +87,10 @@ export default class NavBar extends Vue {
|
||||||
|
|
||||||
@layoutNs.Action [LayoutActions.setLayoutLocked];
|
@layoutNs.Action [LayoutActions.setLayoutLocked];
|
||||||
|
|
||||||
|
@uiSettingsNs.Getter [SettingsGetters.openTradesInTitle]: string;
|
||||||
|
|
||||||
|
favicon: Favico | undefined = undefined;
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
this.ping();
|
this.ping();
|
||||||
this.pingInterval = window.setInterval(this.ping, 60000);
|
this.pingInterval = window.setInterval(this.ping, 60000);
|
||||||
|
@ -103,10 +112,28 @@ export default class NavBar extends Vue {
|
||||||
}
|
}
|
||||||
|
|
||||||
set layoutLockedLocal(value: boolean) {
|
set layoutLockedLocal(value: boolean) {
|
||||||
console.log(value);
|
|
||||||
this.setLayoutLocked(value);
|
this.setLayoutLocked(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setOpenTradesAsPill(tradeCount: number) {
|
||||||
|
console.log('setPill', tradeCount);
|
||||||
|
if (!this.favicon) {
|
||||||
|
this.favicon = new Favico({
|
||||||
|
animation: 'none',
|
||||||
|
// position: 'up',
|
||||||
|
// fontStyle: 'normal',
|
||||||
|
// bgColor: '#',
|
||||||
|
// textColor: '#FFFFFF',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (tradeCount !== 0 && this.openTradesInTitle === 'showPill') {
|
||||||
|
this.favicon.badge(tradeCount);
|
||||||
|
} else {
|
||||||
|
this.favicon.reset();
|
||||||
|
console.log('reset');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
resetDynamicLayout(): void {
|
resetDynamicLayout(): void {
|
||||||
const route = this.$router.currentRoute.path;
|
const route = this.$router.currentRoute.path;
|
||||||
console.log(`resetLayout called for ${route}`);
|
console.log(`resetLayout called for ${route}`);
|
||||||
|
@ -121,14 +148,37 @@ export default class NavBar extends Vue {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setTitle() {
|
||||||
|
let title = 'freqUI';
|
||||||
|
if (this.openTradesInTitle === 'asTitle') {
|
||||||
|
title = `(${this.openTradeCount}) ${title}`;
|
||||||
|
}
|
||||||
|
if (this.botName) {
|
||||||
|
title = `${title} - ${this.botName}`;
|
||||||
|
}
|
||||||
|
document.title = title;
|
||||||
|
}
|
||||||
|
|
||||||
@Watch(BotStoreGetters.botName)
|
@Watch(BotStoreGetters.botName)
|
||||||
botnameChanged() {
|
botnameChanged() {
|
||||||
if (this.botName) {
|
this.setTitle();
|
||||||
document.title = `freqUI - ${this.botName}`;
|
}
|
||||||
} else {
|
|
||||||
document.title = 'freqUI';
|
@Watch(BotStoreGetters.openTradeCount)
|
||||||
|
openTradeCountChanged() {
|
||||||
|
console.log('openTradeCount changed');
|
||||||
|
if (this.openTradesInTitle === 'showPill') {
|
||||||
|
this.setOpenTradesAsPill(this.openTradeCount);
|
||||||
|
} else if (this.openTradesInTitle === 'asTitle') {
|
||||||
|
this.setTitle();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Watch(SettingsGetters.openTradesInTitle)
|
||||||
|
openTradesSettingChanged() {
|
||||||
|
this.setTitle();
|
||||||
|
this.setOpenTradesAsPill(this.openTradeCount);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,11 @@ const routes: Array<RouteConfig> = [
|
||||||
name: 'Freqtrade Dashboard',
|
name: 'Freqtrade Dashboard',
|
||||||
component: () => import(/* webpackChunkName: "dashboard" */ '@/views/Dashboard.vue'),
|
component: () => import(/* webpackChunkName: "dashboard" */ '@/views/Dashboard.vue'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/settings',
|
||||||
|
name: 'Freqtrade Settings',
|
||||||
|
component: () => import(/* webpackChunkName: "dashboard" */ '@/views/Settings.vue'),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/login',
|
path: '/login',
|
||||||
name: 'Login',
|
name: 'Login',
|
||||||
|
|
|
@ -7,6 +7,7 @@ import { AxiosInstance } from 'axios';
|
||||||
import ftbotModule, { BotStoreGetters } from './modules/ftbot';
|
import ftbotModule, { BotStoreGetters } from './modules/ftbot';
|
||||||
import alertsModule from './modules/alerts';
|
import alertsModule from './modules/alerts';
|
||||||
import layoutModule from './modules/layout';
|
import layoutModule from './modules/layout';
|
||||||
|
import settingsModule from './modules/settings';
|
||||||
|
|
||||||
const AUTO_REFRESH = 'ft_auto_refresh';
|
const AUTO_REFRESH = 'ft_auto_refresh';
|
||||||
|
|
||||||
|
@ -38,6 +39,7 @@ export default new Vuex.Store({
|
||||||
ftbot: ftbotModule,
|
ftbot: ftbotModule,
|
||||||
alerts: alertsModule,
|
alerts: alertsModule,
|
||||||
layout: layoutModule,
|
layout: layoutModule,
|
||||||
|
uiSettings: settingsModule,
|
||||||
},
|
},
|
||||||
mutations: {
|
mutations: {
|
||||||
setPing(state, ping) {
|
setPing(state, ping) {
|
||||||
|
|
|
@ -35,6 +35,7 @@ import { showAlert } from '../alerts';
|
||||||
export enum BotStoreGetters {
|
export enum BotStoreGetters {
|
||||||
botName = 'botName',
|
botName = 'botName',
|
||||||
openTrades = 'openTrades',
|
openTrades = 'openTrades',
|
||||||
|
openTradeCount = 'openTradeCount',
|
||||||
tradeDetail = 'tradeDetail',
|
tradeDetail = 'tradeDetail',
|
||||||
selectedPair = 'selectedPair',
|
selectedPair = 'selectedPair',
|
||||||
closedTrades = 'closedTrades',
|
closedTrades = 'closedTrades',
|
||||||
|
@ -64,6 +65,9 @@ export default {
|
||||||
[BotStoreGetters.openTrades](state: FtbotStateType): Trade[] {
|
[BotStoreGetters.openTrades](state: FtbotStateType): Trade[] {
|
||||||
return state.openTrades;
|
return state.openTrades;
|
||||||
},
|
},
|
||||||
|
[BotStoreGetters.openTradeCount](state: FtbotStateType): number {
|
||||||
|
return state.openTrades.length;
|
||||||
|
},
|
||||||
[BotStoreGetters.allTrades](state: FtbotStateType): Trade[] {
|
[BotStoreGetters.allTrades](state: FtbotStateType): Trade[] {
|
||||||
return [...state.openTrades, ...state.trades];
|
return [...state.openTrades, ...state.trades];
|
||||||
},
|
},
|
||||||
|
@ -115,6 +119,7 @@ export default {
|
||||||
state.tradeCount = tradesCount;
|
state.tradeCount = tradesCount;
|
||||||
},
|
},
|
||||||
updateOpenTrades(state: FtbotStateType, trades) {
|
updateOpenTrades(state: FtbotStateType, trades) {
|
||||||
|
console.log(`Update open trade length ${trades.length}`);
|
||||||
state.openTrades = trades;
|
state.openTrades = trades;
|
||||||
},
|
},
|
||||||
updateLocks(state: FtbotStateType, locks: LockResponse) {
|
updateLocks(state: FtbotStateType, locks: LockResponse) {
|
||||||
|
|
51
src/store/modules/settings.ts
Normal file
51
src/store/modules/settings.ts
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
const STORE_UI_SETTINGS = 'ftUISettings';
|
||||||
|
|
||||||
|
export enum SettingsGetters {
|
||||||
|
openTradesInTitle = 'openTradesInTitle',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum SettingsActions {
|
||||||
|
setOpenTradesInTitle = 'setOpenTradesInTitle',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum SettingsMutations {
|
||||||
|
setOpenTrades = 'setOpenTrades',
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSettings() {
|
||||||
|
const fromStore = localStorage.getItem(STORE_UI_SETTINGS);
|
||||||
|
if (fromStore) {
|
||||||
|
return JSON.parse(fromStore);
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
const storedSettings = getSettings();
|
||||||
|
|
||||||
|
function updateSetting(key: string, value: string) {
|
||||||
|
const settings = getSettings() || {};
|
||||||
|
settings[key] = value;
|
||||||
|
localStorage.setItem(STORE_UI_SETTINGS, JSON.stringify(settings));
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
namespaced: true,
|
||||||
|
state: {
|
||||||
|
openTradesInTitle: storedSettings?.openTradesInTitle || 'showPill',
|
||||||
|
},
|
||||||
|
getters: {
|
||||||
|
[SettingsGetters.openTradesInTitle](state) {
|
||||||
|
return state.openTradesInTitle;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mutations: {
|
||||||
|
[SettingsMutations.setOpenTrades](state, value: string) {
|
||||||
|
state.openTradesInTitle = value;
|
||||||
|
updateSetting('openTradesInTitle', value);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
[SettingsActions.setOpenTradesInTitle]({ commit }, locked: boolean) {
|
||||||
|
commit(SettingsMutations.setOpenTrades, locked);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
|
@ -7,6 +7,8 @@
|
||||||
:margin="[5, 5]"
|
:margin="[5, 5]"
|
||||||
:is-resizable="!getLayoutLocked"
|
:is-resizable="!getLayoutLocked"
|
||||||
:is-draggable="!getLayoutLocked"
|
:is-draggable="!getLayoutLocked"
|
||||||
|
responsive
|
||||||
|
prevent-collision
|
||||||
@layout-updated="layoutUpdatedEvent"
|
@layout-updated="layoutUpdatedEvent"
|
||||||
>
|
>
|
||||||
<GridItem
|
<GridItem
|
||||||
|
|
68
src/views/Settings.vue
Normal file
68
src/views/Settings.vue
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
<template>
|
||||||
|
<div class="container mt-3">
|
||||||
|
<b-card header="FreqUI Settings">
|
||||||
|
<div class="text-left">
|
||||||
|
<b-form-group
|
||||||
|
description="Lock dynamic layouts, so they cannot move anymore.\nCan also be set from the navbar at the top."
|
||||||
|
>
|
||||||
|
<b-checkbox v-model="layoutLockedLocal">Lock layout</b-checkbox>
|
||||||
|
</b-form-group>
|
||||||
|
<b-form-group
|
||||||
|
label="Show open trades in header"
|
||||||
|
description="Decide if open trades should be visualized"
|
||||||
|
>
|
||||||
|
<b-form-select
|
||||||
|
v-model="openTradesVisualization"
|
||||||
|
:options="openTradesOptions"
|
||||||
|
></b-form-select>
|
||||||
|
</b-form-group>
|
||||||
|
</div>
|
||||||
|
</b-card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { LayoutActions, LayoutGetters } from '@/store/modules/layout';
|
||||||
|
import { SettingsActions, SettingsGetters } from '@/store/modules/settings';
|
||||||
|
import { Component, Vue } from 'vue-property-decorator';
|
||||||
|
import { namespace } from 'vuex-class';
|
||||||
|
|
||||||
|
const layoutNs = namespace('layout');
|
||||||
|
const uiSettingsNs = namespace('uiSettings');
|
||||||
|
|
||||||
|
@Component({})
|
||||||
|
export default class Template extends Vue {
|
||||||
|
@layoutNs.Getter [LayoutGetters.getLayoutLocked]: boolean;
|
||||||
|
|
||||||
|
@layoutNs.Action [LayoutActions.setLayoutLocked];
|
||||||
|
|
||||||
|
@uiSettingsNs.Getter [SettingsGetters.openTradesInTitle]: string;
|
||||||
|
|
||||||
|
@uiSettingsNs.Action [SettingsActions.setOpenTradesInTitle];
|
||||||
|
|
||||||
|
openTradesOptions = [
|
||||||
|
{ value: 'showPill', text: 'Show pill in icon' },
|
||||||
|
{ value: 'asTitle', text: 'Show in title' },
|
||||||
|
{ value: 'noOpenTrades', text: "Don't show open trades in header" },
|
||||||
|
];
|
||||||
|
|
||||||
|
get openTradesVisualization() {
|
||||||
|
return this.openTradesInTitle;
|
||||||
|
}
|
||||||
|
|
||||||
|
set openTradesVisualization(value: string) {
|
||||||
|
console.log('show_open_trades', value);
|
||||||
|
this.setOpenTradesInTitle(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
get layoutLockedLocal() {
|
||||||
|
return this.getLayoutLocked;
|
||||||
|
}
|
||||||
|
|
||||||
|
set layoutLockedLocal(value: boolean) {
|
||||||
|
this.setLayoutLocked(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped></style>
|
|
@ -4980,6 +4980,11 @@ fastq@^1.6.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
reusify "^1.0.4"
|
reusify "^1.0.4"
|
||||||
|
|
||||||
|
favico.js@^0.3.10:
|
||||||
|
version "0.3.10"
|
||||||
|
resolved "https://registry.yarnpkg.com/favico.js/-/favico.js-0.3.10.tgz#80586e27a117f24a8d51c18a99bdc714d4339301"
|
||||||
|
integrity sha1-gFhuJ6EX8kqNUcGKmb3HFNQzkwE=
|
||||||
|
|
||||||
faye-websocket@^0.11.3:
|
faye-websocket@^0.11.3:
|
||||||
version "0.11.3"
|
version "0.11.3"
|
||||||
resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.3.tgz#5c0e9a8968e8912c286639fde977a8b209f2508e"
|
resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.3.tgz#5c0e9a8968e8912c286639fde977a8b209f2508e"
|
||||||
|
|
Loading…
Reference in New Issue
Block a user