Add loading indicator

This commit is contained in:
simon 2019-06-22 21:36:01 -04:00
parent f2760d8f3e
commit 3ab1e90638
7 changed files with 87 additions and 61 deletions

View File

@ -1,6 +1,5 @@
import * as d3 from 'd3'
import icons from './icons'
import {MusicGraphApi} from './MusicGraphApi'
import {fitCaptionIntoCircle} from './graphGeometry'
// TODO: export somewhere else
@ -22,13 +21,13 @@ export function MusicGraph(data) {
const width = window.innerWidth - 7
const height = window.innerHeight - 7
this._data = data
this.api = this._data.api
this.nodeById = new Map()
this.expandedNodes = new Set()
this.nodes = []
this.links = []
this._originSet = false
this.api = new MusicGraphApi()
this.simulation = d3.forceSimulation()
.force('charge', d3.forceManyBody())
@ -43,7 +42,9 @@ export function MusicGraph(data) {
}
this.dismiss = () => {
this.menu.remove()
if (this.menu) {
this.menu.remove()
}
const menuNode = this.nodes.find(d => d.menu)
if (menuNode !== undefined) {
menuNode.menu = null

View File

@ -52,22 +52,33 @@ const nodeUtils = {
}
}
export function MusicGraphApi() {
export function MusicGraphApi(data) {
this.url = window.location.protocol + '//' + window.location.hostname + '/api'
this._data = data
let loadWrapper = (fn) => {
return (...args) => {
this._data.loading = true
return fn(...args).then(x => {
this._data.loading = false
return x
})
}
}
this.resolveCoverUrl = function (mbid) {
return this.url + '/cover/' + mbid
}
this.getArtistDetails = function (mbid) {
this.getArtistDetails = loadWrapper((mbid) => {
return d3.json(this.url + '/artist/details/' + mbid)
}
})
/**
* Works in both directions
* @returns {Promise<{newNodes: *, relations: *} | never>}
*/
this.getGroupMembers = function (mbid, originId) {
this.getGroupMembers = loadWrapper((mbid, originId) => {
return d3.json(this.url + '/artist/members/' + mbid)
.then((r) => {
return {
@ -81,29 +92,9 @@ export function MusicGraphApi() {
})
}
})
}
})
this.getArtistReleases = function (mbid, originId) {
return d3.json(this.url + '/artist/details/' + mbid)
.then((r) => {
const newNodes = r.releases
.map(nodeUtils.fromRawDict)
.filter(release => release.type === 'Album')
return {
newNodes: newNodes,
relations: newNodes.map(t => {
return {
source: originId,
target: t.id,
weight: 0.8
}
})
}
})
}
this.getArtistLabels = function (mbid, originId) {
this.getArtistLabels = loadWrapper((mbid, originId) => {
return d3.json(this.url + '/artist/details/' + mbid)
.then((r) => {
const newNodes = r.labels
@ -124,25 +115,25 @@ export function MusicGraphApi() {
})
}
})
}
})
this._filterTags = function (tags) {
this._filterTags = loadWrapper((tags) => {
if (ONLY_GENRE_TAGS) {
return tags.filter(tag => genres.has(tag.name))
} else if (IGNORE_DATES_TAG) {
return tags.filter(tag => isNaN(tag.name) && isNaN(tag.name.slice(0, -1)))
}
return tags
}
})
this._addTagLabel = function (objects) {
this._addTagLabel = loadWrapper((objects) => {
return objects.map(tag => {
tag.labels = ['Tag']
return tag
})
}
})
this.getArtistTags = function (mbid, originId) {
this.getArtistTags = loadWrapper((mbid, originId) => {
return d3.json(this.url + '/artist/details/' + mbid)
.then((r) => {
const tags = this._filterTags(r.tags)
@ -158,9 +149,9 @@ export function MusicGraphApi() {
})
}
})
}
})
this.getRelatedTags = function (tagId) {
this.getRelatedTags = loadWrapper((tagId) => {
return d3.json(this.url + '/tag/tag/' + tagId)
.then((r) => {
const tags = this._filterTags(r.tags)
@ -182,9 +173,9 @@ export function MusicGraphApi() {
relations: directedRelations
}
})
}
})
this.getRelatedByMbid = function (mbid) {
this.getRelatedByMbid = loadWrapper((mbid) => {
return d3.json(this.url + '/artist/related/' + mbid)
.then((r) => {
let node = nodeUtils.fromRawDict(r.artists.find(a => a.mbid === mbid))
@ -207,9 +198,9 @@ export function MusicGraphApi() {
relations: directedRelations
}
})
}
})
this.getRelatedByTag = function (tagid) {
this.getRelatedByTag = loadWrapper((tagid) => {
return d3.json(this.url + '/tag/related/' + tagid)
.then((r) => {
return {
@ -229,9 +220,9 @@ export function MusicGraphApi() {
})
}
})
}
})
this.getReleaseDetails = function (mbid, originId) {
this.getReleaseDetails = loadWrapper((mbid, originId) => {
return d3.json(this.url + '/release/details/' + mbid)
.then((r) => {
const tags = this._filterTags(r.tags)
@ -247,12 +238,13 @@ export function MusicGraphApi() {
})
}
})
}
})
this.autoComplete = function (prefix) {
prefix = prefix.replace(/[^\w.\-!?& ]/g, '_').toUpperCase()
prefix = prefix.replace(/ /g, '+')
this.autoComplete = loadWrapper((prefix) => {
prefix = prefix
.replace(/[^\w.\-!?& ]/g, '_').toUpperCase()
.replace(/ /g, '+')
return d3.json(this.url + '/autocomplete/' + prefix)
}
})
}

View File

@ -26,14 +26,12 @@
</template>
<script>
import {MusicGraphApi} from '../MusicGraphApi'
export default {
name: 'AlbumCarousel',
props: ['releases', 'interval', 'alone'],
props: ['releases', 'interval', 'alone', 'api'],
data() {
return {
api: new MusicGraphApi(),
current: '',
index: 0
}

View File

@ -7,6 +7,7 @@
<AlbumCarousel
style="float: right"
:releases="artistInfo.releases"
:api="api"
interval="1250"/>
<div>
<p v-if="artistInfo.comment!==null"
@ -31,13 +32,12 @@
<script>
import AlbumCarousel from './AlbumCarousel'
import {MusicGraphApi} from '../MusicGraphApi'
import {genres} from '../genres'
export default {
name: 'ArtistInfo',
components: {AlbumCarousel},
props: ['artist'],
props: ['artist', 'api'],
watch: {
artist: function (a) {
if (a !== undefined) {
@ -49,8 +49,7 @@ export default {
return {
artistInfo: {
releases: []
},
api: new MusicGraphApi()
}
}
},
methods: {

View File

@ -1,13 +1,19 @@
<template>
<div>
<div id="mm"></div>
<InputBar v-on:addArtist="onAddArtist($event)" v-on:addTag="onAddTag($event)"></InputBar>
<InputBar
v-on:addArtist="onAddArtist($event)"
v-on:addTag="onAddTag($event)"
:api="api"
></InputBar>
<ArtistInfo
v-bind:artist="hoverArtist"
v-on:addTag="onAddTag($event)"
:api="api"
/>
<canvas id="textMeasurementCanvas"></canvas>
<Watermark text="music-graph v1.0"/>
<LoadingIndicator :loading="loading"/>
</div>
</template>
@ -16,14 +22,18 @@ import ArtistInfo from './ArtistInfo'
import Watermark from './Watermark'
import {MusicGraph} from '../MusicGraph'
import InputBar from './InputBar'
import LoadingIndicator from './LoadingIndicator'
import {MusicGraphApi} from '../MusicGraphApi'
let data = {
hoverArtist: undefined,
mm: undefined
mm: undefined,
api: undefined,
loading: undefined
}
export default {
components: {InputBar, ArtistInfo, Watermark},
components: {LoadingIndicator, InputBar, ArtistInfo, Watermark},
data() {
return data
},
@ -36,6 +46,7 @@ export default {
}
},
mounted() {
this.api = new MusicGraphApi(data)
this.mm = new MusicGraph(data)
this.$notify({

View File

@ -20,16 +20,15 @@
</template>
<script>
import {MusicGraphApi} from '../MusicGraphApi'
export default {
name: 'InputBar',
data: () => {
return {
query: '',
api: new MusicGraphApi()
query: ''
}
},
props: ['api'],
methods: {
onSubmit: function (line) {
if (line.type === 'artist') {

View File

@ -0,0 +1,26 @@
<template>
<el-icon class="el-icon-loading" :class="{hidden: !loading}" id="loading"></el-icon>
</template>
<script>
export default {
name: 'LoadingIndicator',
props: ['loading']
}
</script>
<style scoped>
.hidden {
display: none;
}
#loading {
position: fixed;
top: calc(100% - 90px);
left: 75px;
pointer-events: none;
color: rgba(0, 0, 0, 0.67);
font-size: 50px;
}
</style>