forked from MapComplete/MapComplete
Feature(offline): better support for making changes while offline
This commit is contained in:
parent
f671cd342f
commit
7155cd7f61
8 changed files with 89 additions and 10 deletions
|
@ -430,10 +430,10 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "favourite_icon",
|
"condition": "_favourite=yes",
|
||||||
"description": "Only for rendering",
|
"description": "Only for rendering",
|
||||||
"icon": "circle:white;heart:red",
|
"icon": "circle:white;heart:red",
|
||||||
"condition": "_favourite=yes",
|
"id": "favourite_icon",
|
||||||
"metacondition": "__showTimeSensitiveIcons!=no"
|
"metacondition": "__showTimeSensitiveIcons!=no"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -639,6 +639,7 @@
|
||||||
"uploading": "{count} images are being uploaded…"
|
"uploading": "{count} images are being uploaded…"
|
||||||
},
|
},
|
||||||
"noBlur": "Images will not be blurred. Do not photograph people",
|
"noBlur": "Images will not be blurred. Do not photograph people",
|
||||||
|
"offline": "You are currently offline. Uploading images be attempted when your internet is back",
|
||||||
"one": {
|
"one": {
|
||||||
"done": "Your image was successfully uploaded. Thank you!",
|
"done": "Your image was successfully uploaded. Thank you!",
|
||||||
"failed": "Sorry, we could not upload your image",
|
"failed": "Sorry, we could not upload your image",
|
||||||
|
@ -653,7 +654,7 @@
|
||||||
"confirmDeleteTitle": "Delete this image?",
|
"confirmDeleteTitle": "Delete this image?",
|
||||||
"delete": "Delete this image",
|
"delete": "Delete this image",
|
||||||
"intro": "The following images are queued for upload",
|
"intro": "The following images are queued for upload",
|
||||||
"menu": "Image upload queue ({count})",
|
"menu": "Pending changes and image uploads ({count})",
|
||||||
"noFailedImages": "There are currently no images in the upload queue",
|
"noFailedImages": "There are currently no images in the upload queue",
|
||||||
"retryAll": "Retry uploading all images"
|
"retryAll": "Retry uploading all images"
|
||||||
},
|
},
|
||||||
|
|
|
@ -19,6 +19,7 @@ import MarkdownUtils from "../../Utils/MarkdownUtils"
|
||||||
import FeaturePropertiesStore from "../FeatureSource/Actors/FeaturePropertiesStore"
|
import FeaturePropertiesStore from "../FeatureSource/Actors/FeaturePropertiesStore"
|
||||||
import { Feature, Point } from "geojson"
|
import { Feature, Point } from "geojson"
|
||||||
import { Lists } from "../../Utils/Lists"
|
import { Lists } from "../../Utils/Lists"
|
||||||
|
import { IsOnline } from "../Web/IsOnline"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles all changes made to OSM.
|
* Handles all changes made to OSM.
|
||||||
|
@ -287,6 +288,10 @@ export class Changes {
|
||||||
if (this.pendingChanges.data.length === 0) {
|
if (this.pendingChanges.data.length === 0) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if(!IsOnline.isOnline.data){
|
||||||
|
// No use to upload, we aren't connected anyway
|
||||||
|
return
|
||||||
|
}
|
||||||
if (this.isUploading.data) {
|
if (this.isUploading.data) {
|
||||||
console.log("Is already uploading... Abort")
|
console.log("Is already uploading... Abort")
|
||||||
return
|
return
|
||||||
|
|
|
@ -64,6 +64,10 @@
|
||||||
import { GlobeEuropeAfrica } from "@babeard/svelte-heroicons/solid/GlobeEuropeAfrica"
|
import { GlobeEuropeAfrica } from "@babeard/svelte-heroicons/solid/GlobeEuropeAfrica"
|
||||||
import { onDestroy } from "svelte"
|
import { onDestroy } from "svelte"
|
||||||
import Avatar from "../Base/Avatar.svelte"
|
import Avatar from "../Base/Avatar.svelte"
|
||||||
|
import { SpecialVisualizationSvelte } from "../SpecialVisualization"
|
||||||
|
import ThemeViewState from "../../Models/ThemeViewState"
|
||||||
|
import { Changes } from "../../Logic/Osm/Changes"
|
||||||
|
import PendingChangesView from "./PendingChangesView.svelte"
|
||||||
|
|
||||||
export let state: {
|
export let state: {
|
||||||
favourites: FavouritesFeatureSource
|
favourites: FavouritesFeatureSource
|
||||||
|
@ -73,6 +77,7 @@
|
||||||
featureSwitches: Partial<FeatureSwitchState>
|
featureSwitches: Partial<FeatureSwitchState>
|
||||||
mapProperties?: MapProperties
|
mapProperties?: MapProperties
|
||||||
userRelatedState?: UserRelatedState
|
userRelatedState?: UserRelatedState
|
||||||
|
changes?: Changes
|
||||||
}
|
}
|
||||||
let userdetails = state.osmConnection.userDetails
|
let userdetails = state.osmConnection.userDetails
|
||||||
|
|
||||||
|
@ -81,6 +86,7 @@
|
||||||
let featureSwitches = state.featureSwitches
|
let featureSwitches = state.featureSwitches
|
||||||
let showHome = featureSwitches?.featureSwitchBackToThemeOverview
|
let showHome = featureSwitches?.featureSwitchBackToThemeOverview
|
||||||
let pg = state.guistate.pageStates
|
let pg = state.guistate.pageStates
|
||||||
|
let pendingChanges = state?.changes?.pendingChanges
|
||||||
export let onlyLink: boolean
|
export let onlyLink: boolean
|
||||||
const t = Translations.t.general.menu
|
const t = Translations.t.general.menu
|
||||||
let shown = new UIEventSource(state.guistate.pageStates.menu.data || !onlyLink)
|
let shown = new UIEventSource(state.guistate.pageStates.menu.data || !onlyLink)
|
||||||
|
@ -164,12 +170,14 @@
|
||||||
/>
|
/>
|
||||||
</Page>
|
</Page>
|
||||||
|
|
||||||
{#if $nrOfFailedImages.length > 0 || $failedImagesOpen}
|
{#if $nrOfFailedImages.length > 0 || $failedImagesOpen || $pendingChanges?.length > 0 }
|
||||||
<Page {onlyLink} shown={pg.failedImages} bodyPadding="p-0 pb-4">
|
<Page {onlyLink} shown={pg.failedImages} bodyPadding="p-0 pb-4">
|
||||||
<svelte:fragment slot="header">
|
<svelte:fragment slot="header">
|
||||||
<PhotoIcon />
|
<PhotoIcon />
|
||||||
<Tr t={Translations.t.imageQueue.menu.Subs({ count: $nrOfFailedImages.length })} />
|
<Tr
|
||||||
|
t={Translations.t.imageQueue.menu.Subs({ count: ($nrOfFailedImages?.length ?? 0) + ($pendingChanges?.length ?? 0) })} />
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
|
<PendingChangesView {state} />
|
||||||
<QueuedImagesView {state} />
|
<QueuedImagesView {state} />
|
||||||
</Page>
|
</Page>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
58
src/UI/BigComponents/PendingChangesView.svelte
Normal file
58
src/UI/BigComponents/PendingChangesView.svelte
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { Changes } from "../../Logic/Osm/Changes"
|
||||||
|
import type { SpecialVisualizationState } from "../SpecialVisualization"
|
||||||
|
|
||||||
|
export let state: { changes: Changes } & SpecialVisualizationState
|
||||||
|
let pending = state.changes.pendingChanges
|
||||||
|
let backend = state.osmConnection.Backend()
|
||||||
|
let debug = state.featureSwitches.featureSwitchIsDebugging
|
||||||
|
</script>
|
||||||
|
{#if $pending?.length > 0}
|
||||||
|
<div class="p-4">
|
||||||
|
|
||||||
|
<h3>Pending changes</h3>
|
||||||
|
|
||||||
|
There are currently {$pending.length} pending changes:
|
||||||
|
|
||||||
|
<table class="gap-x-2">
|
||||||
|
<tr>
|
||||||
|
<th>
|
||||||
|
Theme
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
Type
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
Object
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
{#each $pending as change}
|
||||||
|
<tr>
|
||||||
|
<td>{change.meta.theme}</td>
|
||||||
|
<td>{change.meta.changeType}</td>
|
||||||
|
<td>
|
||||||
|
<a href={`${backend}/${change.type}/${change.id}`} target="_blank">
|
||||||
|
{change.type}/{change.id}
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
</tr>
|
||||||
|
{/each}
|
||||||
|
</table>
|
||||||
|
|
||||||
|
{#if $debug}
|
||||||
|
{#each $pending as change}
|
||||||
|
{JSON.stringify(change)}
|
||||||
|
{/each}
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
|
||||||
|
<style>
|
||||||
|
td {
|
||||||
|
padding-left: 2rem;
|
||||||
|
padding-right: 2rem;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -8,9 +8,11 @@
|
||||||
import type { ImageUploadArguments } from "../../Logic/ImageProviders/ImageUploadQueue"
|
import type { ImageUploadArguments } from "../../Logic/ImageProviders/ImageUploadQueue"
|
||||||
import { Store } from "../../Logic/UIEventSource"
|
import { Store } from "../../Logic/UIEventSource"
|
||||||
import UploadingImageCounter from "./UploadingImageCounter.svelte"
|
import UploadingImageCounter from "./UploadingImageCounter.svelte"
|
||||||
|
import { IsOnline } from "../../Logic/Web/IsOnline"
|
||||||
export let state: WithImageState
|
export let state: WithImageState
|
||||||
let queued: Store<ImageUploadArguments[]> = state.imageUploadManager.queuedArgs
|
let queued: Store<ImageUploadArguments[]> = state.imageUploadManager.queuedArgs
|
||||||
let isUploading = state.imageUploadManager.isUploading
|
let isUploading = state.imageUploadManager.isUploading
|
||||||
|
let online = IsOnline.isOnline
|
||||||
const t = Translations.t
|
const t = Translations.t
|
||||||
const q = t.imageQueue
|
const q = t.imageQueue
|
||||||
</script>
|
</script>
|
||||||
|
@ -27,7 +29,7 @@
|
||||||
|
|
||||||
{#if $isUploading}
|
{#if $isUploading}
|
||||||
<Loading />
|
<Loading />
|
||||||
{:else}
|
{:else if $online}
|
||||||
<button class="primary" on:click={() => state.imageUploadManager.uploadQueue()}>
|
<button class="primary" on:click={() => state.imageUploadManager.uploadQueue()}>
|
||||||
<ArrowPathIcon class="m-1 h-8 w-8" />
|
<ArrowPathIcon class="m-1 h-8 w-8" />
|
||||||
<Tr t={q.retryAll} />
|
<Tr t={q.retryAll} />
|
||||||
|
|
|
@ -66,7 +66,7 @@
|
||||||
let maintenanceBusy = false
|
let maintenanceBusy = false
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<LoginToggle {state}>
|
<LoginToggle {state} offline>
|
||||||
<LoginButton clss="small w-full" osmConnection={state.osmConnection} slot="not-logged-in">
|
<LoginButton clss="small w-full" osmConnection={state.osmConnection} slot="not-logged-in">
|
||||||
<Tr t={Translations.t.image.pleaseLogin} />
|
<Tr t={Translations.t.image.pleaseLogin} />
|
||||||
</LoginButton>
|
</LoginButton>
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
import Tr from "../Base/Tr.svelte"
|
import Tr from "../Base/Tr.svelte"
|
||||||
import Loading from "../Base/Loading.svelte"
|
import Loading from "../Base/Loading.svelte"
|
||||||
import UploadFailedMessage from "./UploadFailedMessage.svelte"
|
import UploadFailedMessage from "./UploadFailedMessage.svelte"
|
||||||
|
import { IsOnline } from "../../Logic/Web/IsOnline"
|
||||||
|
|
||||||
export let state: SpecialVisualizationState
|
export let state: SpecialVisualizationState
|
||||||
export let tags: Store<OsmTags> = undefined
|
export let tags: Store<OsmTags> = undefined
|
||||||
|
@ -59,6 +60,7 @@
|
||||||
failed.addCallbackAndRun((failed) => {
|
failed.addCallbackAndRun((failed) => {
|
||||||
dismissed = Math.min(failed, dismissed)
|
dismissed = Math.min(failed, dismissed)
|
||||||
})
|
})
|
||||||
|
let online = IsOnline.isOnline
|
||||||
let progress = state.imageUploadManager.progressCurrentImage
|
let progress = state.imageUploadManager.progressCurrentImage
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -91,8 +93,11 @@
|
||||||
</Loading>
|
</Loading>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
{#if !$online}
|
||||||
{#if $failed > dismissed}
|
<div class="alert">
|
||||||
|
<Tr t={t.upload.offline} />
|
||||||
|
</div>
|
||||||
|
{:else if $failed > dismissed}
|
||||||
<UploadFailedMessage failed={$failed} on:click={() => (dismissed = $failed)} {state} />
|
<UploadFailedMessage failed={$failed} on:click={() => (dismissed = $failed)} {state} />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue