forked from MapComplete/MapComplete
UX: show loading icon if images are being loaded
This commit is contained in:
parent
d7509c8d6f
commit
32993df92a
7 changed files with 97 additions and 57 deletions
|
@ -22,7 +22,7 @@ export default class AllImageProviders {
|
|||
...WikimediaImageProvider.commonsPrefixes,
|
||||
...Mapillary.valuePrefixes,
|
||||
...AllImageProviders.dontLoadFromPrefixes,
|
||||
"Category:",
|
||||
"Category:"
|
||||
])
|
||||
|
||||
private static ImageAttributionSource: ImageProvider[] = [
|
||||
|
@ -31,7 +31,7 @@ export default class AllImageProviders {
|
|||
WikidataImageProvider.singleton,
|
||||
WikimediaImageProvider.singleton,
|
||||
Panoramax.singleton,
|
||||
AllImageProviders.genericImageProvider,
|
||||
AllImageProviders.genericImageProvider
|
||||
]
|
||||
public static apiUrls: string[] = [].concat(
|
||||
...AllImageProviders.ImageAttributionSource.map((src) => src.apiUrls())
|
||||
|
@ -44,7 +44,7 @@ export default class AllImageProviders {
|
|||
mapillary: Mapillary.singleton,
|
||||
wikidata: WikidataImageProvider.singleton,
|
||||
wikimedia: WikimediaImageProvider.singleton,
|
||||
panoramax: Panoramax.singleton,
|
||||
panoramax: Panoramax.singleton
|
||||
}
|
||||
|
||||
public static byName(name: string) {
|
||||
|
@ -67,6 +67,28 @@ export default class AllImageProviders {
|
|||
}
|
||||
|
||||
private static readonly _cachedImageStores: Record<string, Store<ProvidedImage[]>> = {}
|
||||
|
||||
/**
|
||||
* Does a guess on the number of images that are probably there.
|
||||
* Will simply count all image tags
|
||||
*
|
||||
* AllImageProviders.estimateNumberOfImages({image:"abc", "mapillary": "123", "panoramax:0"}) // => 3
|
||||
*
|
||||
*/
|
||||
public static estimateNumberOfImages(tags: Record<string, string>, prefixes: string[] = undefined): number {
|
||||
let count = 0
|
||||
|
||||
const allPrefixes = prefixes ?? [].concat(...AllImageProviders.ImageAttributionSource.map(s => s.defaultKeyPrefixes))
|
||||
for (const k in tags) {
|
||||
for (const prefix of allPrefixes) {
|
||||
if (k === prefix || k.startsWith(prefix + ":")) {
|
||||
count++
|
||||
}
|
||||
}
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to extract all image data for this image. Cached on tags?.data?.id
|
||||
*/
|
||||
|
@ -108,7 +130,7 @@ export default class AllImageProviders {
|
|||
*/
|
||||
public static loadImagesFrom(urls: string[]): Store<ProvidedImage[]> {
|
||||
const tags = {
|
||||
id: urls.join(";"),
|
||||
id: urls.join(";")
|
||||
}
|
||||
for (let i = 0; i < urls.length; i++) {
|
||||
tags["image:" + i] = urls[i]
|
||||
|
|
|
@ -149,7 +149,7 @@ export default class PanoramaxImageProvider extends ImageProvider {
|
|||
)
|
||||
}
|
||||
|
||||
Stores.Chronic(1500, () => hasLoading(source.data)).addCallback(() => {
|
||||
Stores.Chronic(5000, () => hasLoading(source.data)).addCallback(() => {
|
||||
super.getRelevantUrlsFor(tags, prefixes).then((data) => {
|
||||
source.set(data)
|
||||
return !hasLoading(data)
|
||||
|
|
11
src/UI/Base/LoadingPlaceholder.svelte
Normal file
11
src/UI/Base/LoadingPlaceholder.svelte
Normal file
|
@ -0,0 +1,11 @@
|
|||
<script lang="ts">
|
||||
import Loading from "./Loading.svelte"
|
||||
|
||||
</script>
|
||||
<div class="relative w-60 h-80">
|
||||
<div class="animate-pulse w-full h-full bg-gray-400">
|
||||
</div>
|
||||
<div class="w-full h-full absolute top-0 flex items-center justify-center">
|
||||
<Loading />
|
||||
</div>
|
||||
</div>
|
|
@ -17,6 +17,7 @@
|
|||
import Translations from "../i18n/Translations"
|
||||
import Tr from "../Base/Tr.svelte"
|
||||
import DotMenu from "../Base/DotMenu.svelte"
|
||||
import LoadingPlaceholder from "../Base/LoadingPlaceholder.svelte"
|
||||
|
||||
export let image: Partial<ProvidedImage>
|
||||
let fallbackImage: string = undefined
|
||||
|
@ -111,7 +112,11 @@
|
|||
<slot name="dot-menu-actions" />
|
||||
</DotMenu>
|
||||
{/if}
|
||||
{#if !loaded}
|
||||
<LoadingPlaceholder />
|
||||
{/if}
|
||||
<img
|
||||
class:hidden={!loaded}
|
||||
bind:this={imgEl}
|
||||
on:load={() => (loaded = true)}
|
||||
class={imgClass ?? ""}
|
||||
|
|
|
@ -3,14 +3,28 @@
|
|||
import type { ProvidedImage } from "../../Logic/ImageProviders/ImageProvider"
|
||||
import type { SpecialVisualizationState } from "../SpecialVisualization"
|
||||
import DeletableImage from "./DeletableImage.svelte"
|
||||
import Loading from "../Base/Loading.svelte"
|
||||
import LoadingPlaceholder from "../Base/LoadingPlaceholder.svelte"
|
||||
|
||||
export let images: Store<ProvidedImage[]>
|
||||
export let state: SpecialVisualizationState
|
||||
export let tags: UIEventSource<Record<string, string>>
|
||||
</script>
|
||||
|
||||
<div class="flex w-full space-x-2 overflow-x-auto" style="scroll-snap-type: x proximity">
|
||||
{#each $images as image (image.url)}
|
||||
<DeletableImage {image} {state} {tags} />
|
||||
{/each}
|
||||
</div>
|
||||
export let estimated: Store<number>
|
||||
|
||||
images.addCallbackAndRun(imgs => {
|
||||
console.log(">>><<< imgs are", imgs)
|
||||
})
|
||||
|
||||
</script>
|
||||
{#if $estimated > 0 && $images.length < 1}
|
||||
<LoadingPlaceholder />
|
||||
{:else}
|
||||
<div class="w-full overflow-x-auto" style="scroll-snap-type: x proximity">
|
||||
<div class="flex space-x-2">
|
||||
{#each $images as image (image.url)}
|
||||
<DeletableImage {image} {state} {tags} />
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
|
|
@ -717,7 +717,8 @@ export default class SpecialVisualizations {
|
|||
imagePrefixes = [].concat(...args.map((a) => a.split(",")))
|
||||
}
|
||||
const images = AllImageProviders.loadImagesFor(tags, imagePrefixes)
|
||||
return new SvelteUIElement(ImageCarousel, { state, tags, images })
|
||||
const estimated = tags.mapD(tags => AllImageProviders.estimateNumberOfImages(tags, imagePrefixes))
|
||||
return new SvelteUIElement(ImageCarousel, { state, tags, images, estimated })
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue