Implement settings page, Show trades in UI Badge

closes #382
This commit is contained in:
Matthias 2021-06-12 17:19:24 +02:00
parent 2e803a0d57
commit 385a7fca11
9 changed files with 194 additions and 5 deletions

View File

@ -19,6 +19,7 @@
"date-fns": "^2.22.1",
"date-fns-tz": "^1.1.4",
"echarts": "^5.1.0",
"favico.js": "^0.3.10",
"humanize-duration": "^3.27.0",
"vue": "^2.6.14",
"vue-class-component": "^7.2.5",

View File

@ -54,9 +54,12 @@ import userService from '@/shared/userService';
import BootswatchThemeSelect from '@/components/BootswatchThemeSelect.vue';
import { LayoutActions, LayoutGetters } from '@/store/modules/layout';
import { BotStoreGetters } from '@/store/modules/ftbot';
import Favico from 'favico.js';
import { SettingsGetters } from '@/store/modules/settings';
const ftbot = namespace('ftbot');
const layoutNs = namespace('layout');
const uiSettingsNs = namespace('uiSettings');
@Component({
components: { LoginModal, BootswatchThemeSelect },
@ -74,6 +77,8 @@ export default class NavBar extends Vue {
@ftbot.Getter [BotStoreGetters.botName]: string;
@ftbot.Getter [BotStoreGetters.openTradeCount]: number;
@layoutNs.Getter [LayoutGetters.getLayoutLocked]: boolean;
@layoutNs.Action [LayoutActions.resetDashboardLayout];
@ -82,6 +87,10 @@ export default class NavBar extends Vue {
@layoutNs.Action [LayoutActions.setLayoutLocked];
@uiSettingsNs.Getter [SettingsGetters.openTradesInTitle]: string;
favicon: Favico | undefined = undefined;
mounted() {
this.ping();
this.pingInterval = window.setInterval(this.ping, 60000);
@ -103,10 +112,28 @@ export default class NavBar extends Vue {
}
set layoutLockedLocal(value: boolean) {
console.log(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 {
const route = this.$router.currentRoute.path;
console.log(`resetLayout called for ${route}`);
@ -121,13 +148,36 @@ 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)
botnameChanged() {
if (this.botName) {
document.title = `freqUI - ${this.botName}`;
} else {
document.title = 'freqUI';
this.setTitle();
}
@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>

View File

@ -31,6 +31,11 @@ const routes: Array<RouteConfig> = [
name: 'Freqtrade Dashboard',
component: () => import(/* webpackChunkName: "dashboard" */ '@/views/Dashboard.vue'),
},
{
path: '/settings',
name: 'Freqtrade Settings',
component: () => import(/* webpackChunkName: "dashboard" */ '@/views/Settings.vue'),
},
{
path: '/login',
name: 'Login',

View File

@ -7,6 +7,7 @@ import { AxiosInstance } from 'axios';
import ftbotModule, { BotStoreGetters } from './modules/ftbot';
import alertsModule from './modules/alerts';
import layoutModule from './modules/layout';
import settingsModule from './modules/settings';
const AUTO_REFRESH = 'ft_auto_refresh';
@ -38,6 +39,7 @@ export default new Vuex.Store({
ftbot: ftbotModule,
alerts: alertsModule,
layout: layoutModule,
uiSettings: settingsModule,
},
mutations: {
setPing(state, ping) {

View File

@ -35,6 +35,7 @@ import { showAlert } from '../alerts';
export enum BotStoreGetters {
botName = 'botName',
openTrades = 'openTrades',
openTradeCount = 'openTradeCount',
tradeDetail = 'tradeDetail',
selectedPair = 'selectedPair',
closedTrades = 'closedTrades',
@ -64,6 +65,9 @@ export default {
[BotStoreGetters.openTrades](state: FtbotStateType): Trade[] {
return state.openTrades;
},
[BotStoreGetters.openTradeCount](state: FtbotStateType): number {
return state.openTrades.length;
},
[BotStoreGetters.allTrades](state: FtbotStateType): Trade[] {
return [...state.openTrades, ...state.trades];
},
@ -115,6 +119,7 @@ export default {
state.tradeCount = tradesCount;
},
updateOpenTrades(state: FtbotStateType, trades) {
console.log(`Update open trade length ${trades.length}`);
state.openTrades = trades;
},
updateLocks(state: FtbotStateType, locks: LockResponse) {

View 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);
},
},
};

View File

@ -7,6 +7,8 @@
:margin="[5, 5]"
:is-resizable="!getLayoutLocked"
:is-draggable="!getLayoutLocked"
responsive
prevent-collision
@layout-updated="layoutUpdatedEvent"
>
<GridItem

68
src/views/Settings.vue Normal file
View 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>

View File

@ -4980,6 +4980,11 @@ fastq@^1.6.0:
dependencies:
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:
version "0.11.3"
resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.3.tgz#5c0e9a8968e8912c286639fde977a8b209f2508e"