Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Show loading spinner when loading data #1645

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions packages/components/src/components/LazyLoader.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<template>
<div v-if="shouldRender" class="lazy-loader-container">
<slot />
</div>
<v-loading-spinner v-else />
</template>

<script lang="ts">
import Vue from 'vue';
import VLoadingSpinner from '@/components/LoadingSpinner.vue';

export default Vue.extend({
name: 'v-lazy-loader',

components: {
VLoadingSpinner,
},

data() {
return {
shouldRender: false,
};
},

methods: {
render() {
Vue.nextTick(() => {
this.shouldRender = true;
});
},
},

activated() {
this.render();
},

mounted() {
this.render();
},

deactivated() {
this.shouldRender = false;
},
});
</script>

<style scoped lang="scss">
.lazy-loader-container {
width: 100%;
height: 100%;
}
</style>
40 changes: 40 additions & 0 deletions packages/components/src/components/LoadingSpinner.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<template>
<div class="loading-background">
<div class="loading-spinner"></div>
</div>
</template>

<script>
export default {
name: 'v-loading-spinner',
};
</script>

<style scoped lang="scss">
.loading-background {
width: 100%;
height: 100%;
background-color: $gray1;

.loading-spinner {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 80px;
height: 80px;

&:after {
content: '';
display: block;
width: 64px;
height: 64px;
margin: 8px;
border-radius: 50%;
border: 6px solid transparent;
border-color: $hotpink transparent $hotpink transparent;
animation: loader-animation 1.2s linear infinite;
}
}
}
</style>
2 changes: 1 addition & 1 deletion packages/components/src/components/SearchBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ export default {
return this.$refs.input === window?.document?.activeElement;
},
onWindowClick(event) {
if (!getEventTarget(event.target, this.$refs.form)) {
if (!getEventTarget(event.target, this.$refs.form) && this.searchValue) {
if (this.$refs.input !== window.document.activeElement) this.showSuggestions = false;

if (
Expand Down
113 changes: 79 additions & 34 deletions packages/components/src/pages/VsCodeExtension.vue
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,13 @@
:tabName="VIEW_COMPONENT"
:ref="VIEW_COMPONENT"
>
<v-diagram-component
:class-map="filteredAppMap.classMap"
:highlighted-event-index="eventFilterMatchIndex"
/>
<v-loading-spinner v-if="isDiagramLoading" />
<v-lazy-loader>
<v-diagram-component
:class-map="filteredAppMap.classMap"
:highlighted-event-index="eventFilterMatchIndex"
/>
</v-lazy-loader>
</v-tab>

<v-tab
Expand All @@ -93,28 +96,34 @@
:tabName="VIEW_SEQUENCE"
:allow-scroll="true"
>
<v-diagram-sequence
ref="viewSequence_diagram"
:app-map="filteredAppMap"
:collapse-depth="seqDiagramCollapseDepth"
:highlighted-event-index="eventFilterMatchIndex"
@setMaxSeqDiagramCollapseDepth="setMaxSeqDiagramCollapseDepth"
@updateCollapseDepth="handleNewCollapseDepth"
/>
<v-loading-spinner v-if="isDiagramLoading" />
<v-lazy-loader>
<v-diagram-sequence
ref="viewSequence_diagram"
:app-map="filteredAppMap"
:collapse-depth="seqDiagramCollapseDepth"
:highlighted-event-index="eventFilterMatchIndex"
@setMaxSeqDiagramCollapseDepth="setMaxSeqDiagramCollapseDepth"
@updateCollapseDepth="handleNewCollapseDepth"
/>
</v-lazy-loader>
</v-tab>

<v-tab name="Trace View" :is-active="isViewingFlow" :tabName="VIEW_FLOW" :ref="VIEW_FLOW">
<div class="trace-view">
<v-diagram-trace
ref="viewFlow_diagram"
:events="filteredAppMap.rootEvents()"
:event-filter-matches="new Set(eventFilterMatches)"
:event-filter-match="eventFilterMatch"
:event-filter-match-index="eventFilterMatchIndex + 1"
:name="VIEW_FLOW"
:zoom-controls="true"
@clickEvent="onClickTraceEvent"
/>
<v-loading-spinner v-if="isDiagramLoading" />
<v-lazy-loader>
<v-diagram-trace
ref="viewFlow_diagram"
:events="filteredAppMap.rootEvents()"
:event-filter-matches="new Set(eventFilterMatches)"
:event-filter-match="eventFilterMatch"
:event-filter-match-index="eventFilterMatchIndex + 1"
:name="VIEW_FLOW"
:zoom-controls="true"
@clickEvent="onClickTraceEvent"
/>
</v-lazy-loader>
</div>
</v-tab>

Expand All @@ -125,13 +134,16 @@
:tabName="VIEW_FLAMEGRAPH"
:allow-scroll="false"
>
<v-diagram-flamegraph
ref="viewFlamegraph_diagram"
:events="filteredAppMap.rootEvents()"
:title="filteredAppMap.name"
:highlighted-event-index="eventFilterMatchIndex"
@select="onFlamegraphSelect"
/>
<v-loading-spinner v-if="isDiagramLoading" />
<v-lazy-loader>
<v-diagram-flamegraph
ref="viewFlamegraph_diagram"
:events="filteredAppMap.rootEvents()"
:title="filteredAppMap.name"
:highlighted-event-index="eventFilterMatchIndex"
@select="onFlamegraphSelect"
/>
</v-lazy-loader>
</v-tab>

<template v-slot:notification>
Expand Down Expand Up @@ -372,6 +384,7 @@ import {
filterStringToFilterState,
base64UrlEncode,
AppMapFilter,
AppMap,
} from '@appland/models';
import { unparseDiagram } from '@appland/sequence-diagram';

Expand Down Expand Up @@ -438,6 +451,8 @@ import isPrecomputedSequenceDiagram from '@/lib/isPrecomputedSequenceDiagram';
import { SAVED_FILTERS_STORAGE_ID } from '../components/FilterMenu.vue';
import { DEFAULT_SEQ_DIAGRAM_COLLAPSE_DEPTH } from '../components/DiagramSequence.vue';
import VCompassIcon from '@/assets/compass-simpler.svg';
import VLazyLoader from '@/components/LazyLoader.vue';
import VLoadingSpinner from '@/components/LoadingSpinner.vue';

const browserPrefixes = ['', 'webkit', 'moz'];

Expand Down Expand Up @@ -478,6 +493,8 @@ export default {
VUnlicensedNotice,
VConfigurationRequired,
VCompassIcon,
VLazyLoader,
VLoadingSpinner,
},
store,
data() {
Expand All @@ -504,6 +521,8 @@ export default {
isFullscreen: false,
showDetailsPanel: false,
rightColumnWidth: 0,
filteredAppMap: this.emptyFilteredAppMap(),
isDiagramLoading: false,
};
},
mixins: [EmitLinkMixin],
Expand Down Expand Up @@ -554,6 +573,17 @@ export default {
},
},
watch: {
'$store.state.filters': {
async handler(filters) {
await this.applyFilters(filters, this.$store.state.appMap, true);
},
deep: true,
},
'$store.state.appMap': {
handler(appMap) {
this.applyFilters(this.filters, appMap);
},
},
'$store.state.currentView': {
handler(view) {
this.$refs.tabs.activateTab(this.$refs[view]);
Expand Down Expand Up @@ -675,10 +705,6 @@ export default {
filters() {
return this.$store.state.filters;
},
filteredAppMap() {
const { appMap } = this.$store.state;
return this.filters.filter(appMap, this.findings);
},
viewState() {
return this.getStateObject();
},
Expand Down Expand Up @@ -884,6 +910,23 @@ export default {
},
},
methods: {
async applyFilters(filter, appMap, isAsync = false) {
if (isAsync) {
this.isDiagramLoading = true;
await new Promise((resolve) => {
setTimeout(() => {
this.filteredAppMap = filter.filter(appMap, this.findings);
this.isDiagramLoading = false;
resolve();
}, 0);
});
} else {
this.filteredAppMap = filter.filter(appMap, this.findings);
}
},
emptyFilteredAppMap() {
return new AppMapFilter().filter(new AppMap());
},
detailsPanelTransitionEnd() {
this.rightColumnWidth = this.$refs.mainColumnRight.offsetWidth;
},
Expand Down Expand Up @@ -1452,9 +1495,11 @@ export default {
if (this.isGiantAppMap) {
this.showStatsPanel = true;
this.setView(null);
} else if (this.showStatsPanel && this.currentView === null) {
this.showStatsPanel = false;
this.setView(this.defaultView);
}
},

beforeDestroy() {
browserPrefixes.forEach((prefix) => {
document.removeEventListener(prefix + 'fullscreenchange', this.checkFullscreen);
Expand Down
5 changes: 5 additions & 0 deletions packages/components/tests/unit/VsCodeExtension.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,11 @@ describe('VsCodeExtension.vue', () => {
await wrapper.find('[data-cy="export-button"]').trigger('click');
const exportJSON = wrapper.find('[data-cy="exportJSON"]');
expect(exportJSON.exists()).toBe(true);

// TODO: This is a hack to wait for the filters to be applied
// We should really fix this at some point
await new Promise((resolve) => setTimeout(resolve, 0));

await exportJSON.trigger('click');

const exportJSONEventParameters = rootWrapper.emitted()['exportJSON'];
Expand Down
2 changes: 1 addition & 1 deletion packages/components/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"useDefineForClassFields": true,
"sourceMap": true,
"baseUrl": ".",
"types": ["webpack-env", "jest"],
"types": ["webpack-env", "jest", "vue/types/vue"],
"paths": {
"@/*": ["src/*"]
},
Expand Down
Loading