web/storagenode: loading screen added

Change-Id: I7d966d2dfba1c275d98259eb7b368297559f9f2d
This commit is contained in:
NickolaiYurchenko 2020-06-23 20:51:11 +03:00 committed by Nikolay Yurchenko
parent 9dbd511396
commit 6c5d948b82
10 changed files with 197 additions and 0 deletions

View File

@ -10,12 +10,14 @@
<SNOFooter /> <SNOFooter />
</div> </div>
</div> </div>
<LoadingScreen v-if="isLoading" />
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { Component, Vue } from 'vue-property-decorator'; import { Component, Vue } from 'vue-property-decorator';
import LoadingScreen from '@/app/components/LoadingScreen.vue';
import SNOFooter from '@/app/components/SNOFooter.vue'; import SNOFooter from '@/app/components/SNOFooter.vue';
import SNOHeader from '@/app/components/SNOHeader.vue'; import SNOHeader from '@/app/components/SNOHeader.vue';
@ -42,15 +44,30 @@ const elementsClassesToRemoveOnScroll: string[] = [
@Component({ @Component({
components: { components: {
LoadingScreen,
SNOHeader, SNOHeader,
SNOFooter, SNOFooter,
}, },
}) })
export default class App extends Vue { export default class App extends Vue {
/**
* Indicates if loading screen is active.
*/
public get isLoading(): boolean {
return this.$store.state.appStateModule.isLoading;
}
public async beforeCreate(): Promise<void> { public async beforeCreate(): Promise<void> {
document.body.classList.add('js-loading');
window.onload = () => {
document.body.classList.remove('js-loading');
};
// TODO: place key to server config. // TODO: place key to server config.
await this.$telemetry.init('DTEcoJRlUAN2VylCWMiLrqoknW800GNO'); await this.$telemetry.init('DTEcoJRlUAN2VylCWMiLrqoknW800GNO');
} }
public onScroll(): void { public onScroll(): void {
elementsIdsToRemoveOnScroll.forEach(id => { elementsIdsToRemoveOnScroll.forEach(id => {
this.removeElementById(id); this.removeElementById(id);
@ -120,6 +137,12 @@ export default class App extends Vue {
} }
} }
.js-loading *,
.js-loading *:before,
.js-loading *:after {
animation-play-state: paused !important;
}
@font-face { @font-face {
font-display: swap; font-display: swap;
font-family: 'font_regular'; font-family: 'font_regular';

View File

@ -0,0 +1,110 @@
// Copyright (C) 2020 Storj Labs, Inc.
// See LICENSE for copying information.
<template>
<transition name="fade" mode="in-out">
<div class="loading-screen">
<StorjLogo class="logo"/>
<svg height="100" width="100" class="loader">
<circle cx="50" cy="50" r="40" class="background" />
<circle cx="50" cy="50" r="40" class="circle" />
</svg>
</div>
</transition>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import StorjLogo from '@/../static/images/LogoWithoutText.svg';
@Component({
components: {
StorjLogo,
},
})
export default class LoadingScreen extends Vue {}
</script>
<style scoped lang="scss">
.loading-screen {
display: flex;
justify-content: center;
align-items: center;
position: fixed;
top: 0;
left: 0;
right: 0;
height: 100vh;
z-index: 99999;
background-color: var(--loading-screen-background-color);
opacity: 1;
.logo {
position: absolute;
top: 80px;
left: 50%;
transform: translateX(-50%);
}
}
.storj-logo {
path {
fill: var(--loader-logo-color);
}
}
.loader {
transform: rotate(180deg);
}
.circle {
fill: none;
stroke: #c4c4c4;
stroke-width: 20;
stroke-dasharray: 250;
stroke-dashoffset: 250;
stroke-linecap: round;
animation: rotate 2s linear infinite, fill 1s linear infinite;
}
.background {
fill: none;
stroke: rgba(206, 206, 206, 0.2);
stroke-width: 20;
}
@keyframes rotate {
50% {
stroke-dashoffset: 0;
}
100% {
stroke-dashoffset: -250;
}
}
@keyframes fill {
0%,
100% {
stroke: #c4c4c4;
}
50% {
stroke: var(--loader-fill-color);
}
}
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.5s;
}
.fade-enter,
.fade-leave-to {
opacity: 0;
}
</style>

View File

@ -151,6 +151,8 @@ export default class SNOHeader extends Vue {
} }
public async onRefresh(): Promise<void> { public async onRefresh(): Promise<void> {
await this.$store.dispatch(APPSTATE_ACTIONS.SET_LOADING, true);
const selectedSatellite = this.$store.state.node.selectedSatellite.id; const selectedSatellite = this.$store.state.node.selectedSatellite.id;
await this.$store.dispatch(APPSTATE_ACTIONS.SET_NO_PAYOUT_DATA, false); await this.$store.dispatch(APPSTATE_ACTIONS.SET_NO_PAYOUT_DATA, false);
@ -161,6 +163,8 @@ export default class SNOHeader extends Vue {
} catch (error) { } catch (error) {
console.error(`${error.message} satellite data.`); console.error(`${error.message} satellite data.`);
} }
await this.$store.dispatch(APPSTATE_ACTIONS.SET_LOADING, false);
} }
} }
</script> </script>

View File

@ -78,6 +78,8 @@ export default class SatelliteSelectionDropdown extends Vue {
* Fires on satellite click and selects it. * Fires on satellite click and selects it.
*/ */
public async onSatelliteClick(id: string): Promise<void> { public async onSatelliteClick(id: string): Promise<void> {
await this.$store.dispatch(APPSTATE_ACTIONS.SET_LOADING, true);
try { try {
await this.$store.dispatch(APPSTATE_ACTIONS.TOGGLE_SATELLITE_SELECTION); await this.$store.dispatch(APPSTATE_ACTIONS.TOGGLE_SATELLITE_SELECTION);
await this.$store.dispatch(NODE_ACTIONS.SELECT_SATELLITE, id); await this.$store.dispatch(NODE_ACTIONS.SELECT_SATELLITE, id);
@ -85,12 +87,16 @@ export default class SatelliteSelectionDropdown extends Vue {
} catch (error) { } catch (error) {
console.error(error.message); 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. * Fires on all satellites click and sets selected satellite id to null.
*/ */
public async onAllSatellitesClick(): Promise<void> { public async onAllSatellitesClick(): Promise<void> {
await this.$store.dispatch(APPSTATE_ACTIONS.SET_LOADING, true);
try { try {
await this.$store.dispatch(APPSTATE_ACTIONS.TOGGLE_SATELLITE_SELECTION); await this.$store.dispatch(APPSTATE_ACTIONS.TOGGLE_SATELLITE_SELECTION);
await this.$store.dispatch(NODE_ACTIONS.SELECT_SATELLITE, null); await this.$store.dispatch(NODE_ACTIONS.SELECT_SATELLITE, null);
@ -98,6 +104,8 @@ export default class SatelliteSelectionDropdown extends Vue {
} catch (error) { } catch (error) {
console.error(error.message); console.error(error.message);
} }
await this.$store.dispatch(APPSTATE_ACTIONS.SET_LOADING, false);
} }
/** /**

View File

@ -11,6 +11,7 @@ export const APPSTATE_MUTATIONS = {
CLOSE_ALL_POPUPS: 'CLOSE_ALL_POPUPS', CLOSE_ALL_POPUPS: 'CLOSE_ALL_POPUPS',
SET_DARK: 'SET_DARK', SET_DARK: 'SET_DARK',
SET_NO_PAYOUT_INFO: 'SET_NO_PAYOUT_INFO', SET_NO_PAYOUT_INFO: 'SET_NO_PAYOUT_INFO',
SET_LOADING_STATE: 'SET_LOADING_STATE',
}; };
export const APPSTATE_ACTIONS = { export const APPSTATE_ACTIONS = {
@ -23,6 +24,7 @@ export const APPSTATE_ACTIONS = {
CLOSE_ALL_POPUPS: 'CLOSE_ALL_POPUPS', CLOSE_ALL_POPUPS: 'CLOSE_ALL_POPUPS',
SET_DARK_MODE: 'SET_DARK_MODE', SET_DARK_MODE: 'SET_DARK_MODE',
SET_NO_PAYOUT_DATA: 'SET_NO_PAYOUT_DATA', SET_NO_PAYOUT_DATA: 'SET_NO_PAYOUT_DATA',
SET_LOADING: 'SET_LOADING',
}; };
const { const {
@ -44,6 +46,7 @@ export const appStateModule = {
isPayoutCalendarShown: false, isPayoutCalendarShown: false,
isDarkMode: false, isDarkMode: false,
isNoPayoutData: false, isNoPayoutData: false,
isLoading: true,
}, },
mutations: { mutations: {
[TOGGLE_SATELLITE_SELECTION](state: any): void { [TOGGLE_SATELLITE_SELECTION](state: any): void {
@ -72,6 +75,9 @@ export const appStateModule = {
[APPSTATE_MUTATIONS.SET_NO_PAYOUT_INFO](state: any, value): void { [APPSTATE_MUTATIONS.SET_NO_PAYOUT_INFO](state: any, value): void {
state.isNoPayoutData = value; state.isNoPayoutData = value;
}, },
[APPSTATE_MUTATIONS.SET_LOADING_STATE](state: any, value): void {
state.isLoading = value;
},
[CLOSE_ALL_POPUPS](state: any): void { [CLOSE_ALL_POPUPS](state: any): void {
state.isSatelliteSelectionShown = false; state.isSatelliteSelectionShown = false;
}, },
@ -117,6 +123,10 @@ export const appStateModule = {
[APPSTATE_ACTIONS.SET_NO_PAYOUT_DATA]: function ({commit}: any, value: boolean): void { [APPSTATE_ACTIONS.SET_NO_PAYOUT_DATA]: function ({commit}: any, value: boolean): void {
commit(APPSTATE_MUTATIONS.SET_NO_PAYOUT_INFO, value); 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 { [APPSTATE_ACTIONS.CLOSE_ADDITIONAL_CHARTS]: function ({commit}: any): void {
commit(APPSTATE_MUTATIONS.CLOSE_ADDITIONAL_CHARTS); commit(APPSTATE_MUTATIONS.CLOSE_ADDITIONAL_CHARTS);
}, },

View File

@ -16,6 +16,7 @@ import { Component, Vue } from 'vue-property-decorator';
import SNOContentFilling from '@/app/components/SNOContentFilling.vue'; import SNOContentFilling from '@/app/components/SNOContentFilling.vue';
import SNOContentTitle from '@/app/components/SNOContentTitle.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 { NODE_ACTIONS } from '@/app/store/modules/node';
import { NOTIFICATIONS_ACTIONS } from '@/app/store/modules/notifications'; import { NOTIFICATIONS_ACTIONS } from '@/app/store/modules/notifications';
import { PAYOUT_ACTIONS } from '@/app/store/modules/payout'; 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. * Fetches notifications and total payout information for all satellites.
*/ */
public async mounted(): Promise<void> { public async mounted(): Promise<void> {
await this.$store.dispatch(APPSTATE_ACTIONS.SET_LOADING, true);
try { try {
await this.$store.dispatch(NODE_ACTIONS.SELECT_SATELLITE, null); await this.$store.dispatch(NODE_ACTIONS.SELECT_SATELLITE, null);
} catch (error) { } catch (error) {
@ -52,6 +55,8 @@ export default class Dashboard extends Vue {
console.error(error); console.error(error);
} }
await this.$store.dispatch(APPSTATE_ACTIONS.SET_LOADING, false);
this.$telemetry.identify(this.$store.state.node.info.id); this.$telemetry.identify(this.$store.state.node.info.id);
this.$telemetry.view(TelemetryViews.MainPage); this.$telemetry.view(TelemetryViews.MainPage);
} }

View File

@ -47,6 +47,7 @@ import SatelliteSelection from '@/app/components/SatelliteSelection.vue';
import BackArrowIcon from '@/../static/images/notifications/backArrow.svg'; 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 { NODE_ACTIONS } from '@/app/store/modules/node';
import { NOTIFICATIONS_ACTIONS } from '@/app/store/modules/notifications'; import { NOTIFICATIONS_ACTIONS } from '@/app/store/modules/notifications';
import { PAYOUT_ACTIONS } from '@/app/store/modules/payout'; import { PAYOUT_ACTIONS } from '@/app/store/modules/payout';
@ -70,6 +71,8 @@ export default class PayoutArea extends Vue {
* Fetches payout information. * Fetches payout information.
*/ */
public async mounted(): Promise<any> { public async mounted(): Promise<any> {
await this.$store.dispatch(APPSTATE_ACTIONS.SET_LOADING, true);
try { try {
await this.$store.dispatch(NODE_ACTIONS.SELECT_SATELLITE, null); await this.$store.dispatch(NODE_ACTIONS.SELECT_SATELLITE, null);
} catch (error) { } catch (error) {
@ -87,6 +90,8 @@ export default class PayoutArea extends Vue {
} catch (error) { } catch (error) {
console.error(error); console.error(error);
} }
await this.$store.dispatch(APPSTATE_ACTIONS.SET_LOADING, false);
} }
public get totalHeld(): number { public get totalHeld(): number {

View File

@ -43,6 +43,9 @@
--egress-tooltip-info-background-color: rgba(211, 242, 204, 0.3); --egress-tooltip-info-background-color: rgba(211, 242, 204, 0.3);
--disk-stat-chart-text-color: #657284; --disk-stat-chart-text-color: #657284;
--expand-button-background-color: rgba(226, 236, 247, 0.45); --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-background-path: url('../../static/images/tooltipBack.png');
--tooltip-arrow-path: url('../../static/images/tooltipArrow.png'); --tooltip-arrow-path: url('../../static/images/tooltipArrow.png');
--info-image-arrow-middle-path: url('../../static/images/Message.png'); --info-image-arrow-middle-path: url('../../static/images/Message.png');
@ -92,6 +95,9 @@
--egress-tooltip-info-background-color: #212329; --egress-tooltip-info-background-color: #212329;
--disk-stat-chart-text-color: white; --disk-stat-chart-text-color: white;
--expand-button-background-color: #31343d; --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-background-path: url('../../static/images/tooltipBackDark.png');
--tooltip-arrow-path: url('../../static/images/tooltipArrowDark.png'); --tooltip-arrow-path: url('../../static/images/tooltipArrowDark.png');
--info-image-arrow-middle-path: url('../../static/images/MessageDark.png'); --info-image-arrow-middle-path: url('../../static/images/MessageDark.png');

View File

@ -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();
});
});

View File

@ -0,0 +1,12 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`LoadingScreen renders correctly 1`] = `
<transition-stub name="fade" mode="in-out">
<div class="loading-screen">
<storjlogo-stub class="logo"></storjlogo-stub> <svg height="100" width="100" class="loader">
<circle cx="50" cy="50" r="40" class="background"></circle>
<circle cx="50" cy="50" r="40" class="circle"></circle>
</svg>
</div>
</transition-stub>
`;