diff --git a/web/storagenode/src/app/App.vue b/web/storagenode/src/app/App.vue
index 64363c258..78199166a 100644
--- a/web/storagenode/src/app/App.vue
+++ b/web/storagenode/src/app/App.vue
@@ -10,12 +10,14 @@
+
+
+
diff --git a/web/storagenode/src/app/components/SNOHeader.vue b/web/storagenode/src/app/components/SNOHeader.vue
index bde262fd0..9af6403ff 100644
--- a/web/storagenode/src/app/components/SNOHeader.vue
+++ b/web/storagenode/src/app/components/SNOHeader.vue
@@ -151,6 +151,8 @@ export default class SNOHeader extends Vue {
}
public async onRefresh(): Promise {
+ await this.$store.dispatch(APPSTATE_ACTIONS.SET_LOADING, true);
+
const selectedSatellite = this.$store.state.node.selectedSatellite.id;
await this.$store.dispatch(APPSTATE_ACTIONS.SET_NO_PAYOUT_DATA, false);
@@ -161,6 +163,8 @@ export default class SNOHeader extends Vue {
} catch (error) {
console.error(`${error.message} satellite data.`);
}
+
+ await this.$store.dispatch(APPSTATE_ACTIONS.SET_LOADING, false);
}
}
diff --git a/web/storagenode/src/app/components/SatelliteSelectionDropdown.vue b/web/storagenode/src/app/components/SatelliteSelectionDropdown.vue
index 490f86945..44427e5da 100644
--- a/web/storagenode/src/app/components/SatelliteSelectionDropdown.vue
+++ b/web/storagenode/src/app/components/SatelliteSelectionDropdown.vue
@@ -78,6 +78,8 @@ export default class SatelliteSelectionDropdown extends Vue {
* Fires on satellite click and selects it.
*/
public async onSatelliteClick(id: string): Promise {
+ await this.$store.dispatch(APPSTATE_ACTIONS.SET_LOADING, true);
+
try {
await this.$store.dispatch(APPSTATE_ACTIONS.TOGGLE_SATELLITE_SELECTION);
await this.$store.dispatch(NODE_ACTIONS.SELECT_SATELLITE, id);
@@ -85,12 +87,16 @@ export default class SatelliteSelectionDropdown extends Vue {
} catch (error) {
console.error(error.message);
}
+
+ await this.$store.dispatch(APPSTATE_ACTIONS.SET_LOADING, false);
}
/**
* Fires on all satellites click and sets selected satellite id to null.
*/
public async onAllSatellitesClick(): Promise {
+ await this.$store.dispatch(APPSTATE_ACTIONS.SET_LOADING, true);
+
try {
await this.$store.dispatch(APPSTATE_ACTIONS.TOGGLE_SATELLITE_SELECTION);
await this.$store.dispatch(NODE_ACTIONS.SELECT_SATELLITE, null);
@@ -98,6 +104,8 @@ export default class SatelliteSelectionDropdown extends Vue {
} catch (error) {
console.error(error.message);
}
+
+ await this.$store.dispatch(APPSTATE_ACTIONS.SET_LOADING, false);
}
/**
diff --git a/web/storagenode/src/app/store/modules/appState.ts b/web/storagenode/src/app/store/modules/appState.ts
index a979076d7..93bd81818 100644
--- a/web/storagenode/src/app/store/modules/appState.ts
+++ b/web/storagenode/src/app/store/modules/appState.ts
@@ -11,6 +11,7 @@ export const APPSTATE_MUTATIONS = {
CLOSE_ALL_POPUPS: 'CLOSE_ALL_POPUPS',
SET_DARK: 'SET_DARK',
SET_NO_PAYOUT_INFO: 'SET_NO_PAYOUT_INFO',
+ SET_LOADING_STATE: 'SET_LOADING_STATE',
};
export const APPSTATE_ACTIONS = {
@@ -23,6 +24,7 @@ export const APPSTATE_ACTIONS = {
CLOSE_ALL_POPUPS: 'CLOSE_ALL_POPUPS',
SET_DARK_MODE: 'SET_DARK_MODE',
SET_NO_PAYOUT_DATA: 'SET_NO_PAYOUT_DATA',
+ SET_LOADING: 'SET_LOADING',
};
const {
@@ -44,6 +46,7 @@ export const appStateModule = {
isPayoutCalendarShown: false,
isDarkMode: false,
isNoPayoutData: false,
+ isLoading: true,
},
mutations: {
[TOGGLE_SATELLITE_SELECTION](state: any): void {
@@ -72,6 +75,9 @@ export const appStateModule = {
[APPSTATE_MUTATIONS.SET_NO_PAYOUT_INFO](state: any, value): void {
state.isNoPayoutData = value;
},
+ [APPSTATE_MUTATIONS.SET_LOADING_STATE](state: any, value): void {
+ state.isLoading = value;
+ },
[CLOSE_ALL_POPUPS](state: any): void {
state.isSatelliteSelectionShown = false;
},
@@ -117,6 +123,10 @@ export const appStateModule = {
[APPSTATE_ACTIONS.SET_NO_PAYOUT_DATA]: function ({commit}: any, value: boolean): void {
commit(APPSTATE_MUTATIONS.SET_NO_PAYOUT_INFO, value);
},
+ [APPSTATE_ACTIONS.SET_LOADING]: function ({commit}: any, value: boolean): void {
+ value ? commit(APPSTATE_MUTATIONS.SET_LOADING_STATE, value) :
+ setTimeout(() => { commit(APPSTATE_MUTATIONS.SET_LOADING_STATE, value); }, 1000);
+ },
[APPSTATE_ACTIONS.CLOSE_ADDITIONAL_CHARTS]: function ({commit}: any): void {
commit(APPSTATE_MUTATIONS.CLOSE_ADDITIONAL_CHARTS);
},
diff --git a/web/storagenode/src/app/views/DashboardArea.vue b/web/storagenode/src/app/views/DashboardArea.vue
index d77a10c65..5f0893b7a 100644
--- a/web/storagenode/src/app/views/DashboardArea.vue
+++ b/web/storagenode/src/app/views/DashboardArea.vue
@@ -16,6 +16,7 @@ import { Component, Vue } from 'vue-property-decorator';
import SNOContentFilling from '@/app/components/SNOContentFilling.vue';
import SNOContentTitle from '@/app/components/SNOContentTitle.vue';
+import { APPSTATE_ACTIONS } from '@/app/store/modules/appState';
import { NODE_ACTIONS } from '@/app/store/modules/node';
import { NOTIFICATIONS_ACTIONS } from '@/app/store/modules/notifications';
import { PAYOUT_ACTIONS } from '@/app/store/modules/payout';
@@ -34,6 +35,8 @@ export default class Dashboard extends Vue {
* Fetches notifications and total payout information for all satellites.
*/
public async mounted(): Promise {
+ await this.$store.dispatch(APPSTATE_ACTIONS.SET_LOADING, true);
+
try {
await this.$store.dispatch(NODE_ACTIONS.SELECT_SATELLITE, null);
} catch (error) {
@@ -52,6 +55,8 @@ export default class Dashboard extends Vue {
console.error(error);
}
+ await this.$store.dispatch(APPSTATE_ACTIONS.SET_LOADING, false);
+
this.$telemetry.identify(this.$store.state.node.info.id);
this.$telemetry.view(TelemetryViews.MainPage);
}
diff --git a/web/storagenode/src/app/views/PayoutArea.vue b/web/storagenode/src/app/views/PayoutArea.vue
index 0b37485ac..74bba8393 100644
--- a/web/storagenode/src/app/views/PayoutArea.vue
+++ b/web/storagenode/src/app/views/PayoutArea.vue
@@ -47,6 +47,7 @@ import SatelliteSelection from '@/app/components/SatelliteSelection.vue';
import BackArrowIcon from '@/../static/images/notifications/backArrow.svg';
+import { APPSTATE_ACTIONS } from '@/app/store/modules/appState';
import { NODE_ACTIONS } from '@/app/store/modules/node';
import { NOTIFICATIONS_ACTIONS } from '@/app/store/modules/notifications';
import { PAYOUT_ACTIONS } from '@/app/store/modules/payout';
@@ -70,6 +71,8 @@ export default class PayoutArea extends Vue {
* Fetches payout information.
*/
public async mounted(): Promise {
+ await this.$store.dispatch(APPSTATE_ACTIONS.SET_LOADING, true);
+
try {
await this.$store.dispatch(NODE_ACTIONS.SELECT_SATELLITE, null);
} catch (error) {
@@ -87,6 +90,8 @@ export default class PayoutArea extends Vue {
} catch (error) {
console.error(error);
}
+
+ await this.$store.dispatch(APPSTATE_ACTIONS.SET_LOADING, false);
}
public get totalHeld(): number {
diff --git a/web/storagenode/static/styles/_variables.scss b/web/storagenode/static/styles/_variables.scss
index 542864c29..94a02d32d 100644
--- a/web/storagenode/static/styles/_variables.scss
+++ b/web/storagenode/static/styles/_variables.scss
@@ -43,6 +43,9 @@
--egress-tooltip-info-background-color: rgba(211, 242, 204, 0.3);
--disk-stat-chart-text-color: #657284;
--expand-button-background-color: rgba(226, 236, 247, 0.45);
+ --loading-screen-background-color: #e9ecf2;
+ --loader-fill-color: #133e9c;
+ --loader-logo-color: #929baf;
--tooltip-background-path: url('../../static/images/tooltipBack.png');
--tooltip-arrow-path: url('../../static/images/tooltipArrow.png');
--info-image-arrow-middle-path: url('../../static/images/Message.png');
@@ -92,6 +95,9 @@
--egress-tooltip-info-background-color: #212329;
--disk-stat-chart-text-color: white;
--expand-button-background-color: #31343d;
+ --loading-screen-background-color: #1e1e26;
+ --loader-fill-color: #4f97f7;
+ --loader-logo-color: #414148;
--tooltip-background-path: url('../../static/images/tooltipBackDark.png');
--tooltip-arrow-path: url('../../static/images/tooltipArrowDark.png');
--info-image-arrow-middle-path: url('../../static/images/MessageDark.png');
diff --git a/web/storagenode/tests/unit/components/LoadingScreen.spec.ts b/web/storagenode/tests/unit/components/LoadingScreen.spec.ts
new file mode 100644
index 000000000..c99df3a53
--- /dev/null
+++ b/web/storagenode/tests/unit/components/LoadingScreen.spec.ts
@@ -0,0 +1,14 @@
+// Copyright (C) 2020 Storj Labs, Inc.
+// See LICENSE for copying information.
+
+import LoadingScreen from '@/app/components/LoadingScreen.vue';
+
+import { shallowMount } from '@vue/test-utils';
+
+describe('LoadingScreen', (): void => {
+ it('renders correctly', (): void => {
+ const wrapper = shallowMount(LoadingScreen);
+
+ expect(wrapper).toMatchSnapshot();
+ });
+});
diff --git a/web/storagenode/tests/unit/components/__snapshots__/LoadingScreen.spec.ts.snap b/web/storagenode/tests/unit/components/__snapshots__/LoadingScreen.spec.ts.snap
new file mode 100644
index 000000000..935603f03
--- /dev/null
+++ b/web/storagenode/tests/unit/components/__snapshots__/LoadingScreen.spec.ts.snap
@@ -0,0 +1,12 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`LoadingScreen renders correctly 1`] = `
+
+
+
+
+
+`;