forked from MapComplete/MapComplete
		
	UX: add indicicator in settings of pending changes, add button to clear the selected changes
This commit is contained in:
		
							parent
							
								
									13d00608d5
								
							
						
					
					
						commit
						2e7703c8ec
					
				
					 5 changed files with 59 additions and 7 deletions
				
			
		| 
						 | 
					@ -901,6 +901,12 @@
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      ]
 | 
					      ]
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "id": "pending_changes",
 | 
				
			||||||
 | 
					      "render": {
 | 
				
			||||||
 | 
					        "*": "{pending_changes()}"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      "id": "show_debug",
 | 
					      "id": "show_debug",
 | 
				
			||||||
      "question": {
 | 
					      "question": {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -213,6 +213,7 @@
 | 
				
			||||||
        "backgroundMap": "Select a background layer",
 | 
					        "backgroundMap": "Select a background layer",
 | 
				
			||||||
        "backgroundSwitch": "Switch background",
 | 
					        "backgroundSwitch": "Switch background",
 | 
				
			||||||
        "cancel": "Cancel",
 | 
					        "cancel": "Cancel",
 | 
				
			||||||
 | 
					        "clearPendingChanges": "Clear pending changes",
 | 
				
			||||||
        "confirm": "Confirm",
 | 
					        "confirm": "Confirm",
 | 
				
			||||||
        "customThemeIntro": "These are previously visited user-generated themes.",
 | 
					        "customThemeIntro": "These are previously visited user-generated themes.",
 | 
				
			||||||
        "customThemeTitle": "Custom themes",
 | 
					        "customThemeTitle": "Custom themes",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -218,7 +218,7 @@ export class TagUtils {
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * TagUtils.KVtoProperties([new Tag("a","b"), new Tag("c","d")] // => {a: "b", c: "d"}
 | 
					     * TagUtils.KVtoProperties([new Tag("a","b"), new Tag("c","d")] // => {a: "b", c: "d"}
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    static KVtoProperties(tags: Tag[]): Record<string, string> {
 | 
					    static KVtoProperties(tags: {key: string, value: string}[]): Record<string, string> {
 | 
				
			||||||
        const properties: Record<string, string> = {}
 | 
					        const properties: Record<string, string> = {}
 | 
				
			||||||
        for (const tag of tags) {
 | 
					        for (const tag of tags) {
 | 
				
			||||||
            properties[tag.key] = tag.value
 | 
					            properties[tag.key] = tag.value
 | 
				
			||||||
| 
						 | 
					@ -226,6 +226,14 @@ export class TagUtils {
 | 
				
			||||||
        return properties
 | 
					        return properties
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static KVObjtoProperties(tags: {k: string, v: string}[]): Record<string, string> {
 | 
				
			||||||
 | 
					        const properties: Record<string, string> = {}
 | 
				
			||||||
 | 
					        for (const tag of tags) {
 | 
				
			||||||
 | 
					            properties[tag.k] = tag.v
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return properties
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    static changeAsProperties(kvs: { k: string; v: string }[]): Record<string, string> {
 | 
					    static changeAsProperties(kvs: { k: string; v: string }[]): Record<string, string> {
 | 
				
			||||||
        const tags: Record<string, string> = {}
 | 
					        const tags: Record<string, string> = {}
 | 
				
			||||||
        for (const kv of kvs) {
 | 
					        for (const kv of kvs) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,13 +5,15 @@
 | 
				
			||||||
  import Loading from "../Base/Loading.svelte"
 | 
					  import Loading from "../Base/Loading.svelte"
 | 
				
			||||||
  import Translations from "../i18n/Translations"
 | 
					  import Translations from "../i18n/Translations"
 | 
				
			||||||
  import Tr from "../Base/Tr.svelte"
 | 
					  import Tr from "../Base/Tr.svelte"
 | 
				
			||||||
 | 
					  import { TagUtils } from "../../Logic/Tags/TagUtils"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  export let state: SpecialVisualizationState
 | 
					  export let state: SpecialVisualizationState
 | 
				
			||||||
 | 
					  export let compact: boolean = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const changes: Changes = state.changes
 | 
					  const changes: Changes = state.changes
 | 
				
			||||||
  const isUploading: Store<boolean> = changes.isUploading
 | 
					  const isUploading: Store<boolean> = changes.isUploading
 | 
				
			||||||
  const pendingChangesCount: Store<number> = changes.pendingChanges.map((ls) => ls.length)
 | 
					 | 
				
			||||||
  const errors = changes.errors
 | 
					  const errors = changes.errors
 | 
				
			||||||
 | 
					  const pending = changes.pendingChanges
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<div
 | 
					<div
 | 
				
			||||||
| 
						 | 
					@ -22,16 +24,43 @@
 | 
				
			||||||
    <Loading>
 | 
					    <Loading>
 | 
				
			||||||
      <Tr cls="thx" t={Translations.t.general.uploadingChanges} />
 | 
					      <Tr cls="thx" t={Translations.t.general.uploadingChanges} />
 | 
				
			||||||
    </Loading>
 | 
					    </Loading>
 | 
				
			||||||
  {:else if $pendingChangesCount === 1}
 | 
					  {:else if $pending.length === 1}
 | 
				
			||||||
    <Tr cls="alert" t={Translations.t.general.uploadPendingSingle} />
 | 
					    <Tr cls="alert" t={Translations.t.general.uploadPendingSingle} />
 | 
				
			||||||
  {:else if $pendingChangesCount > 1}
 | 
					  {:else if $pending.length > 1}
 | 
				
			||||||
    <Tr
 | 
					    <Tr
 | 
				
			||||||
      cls="alert"
 | 
					      cls="alert"
 | 
				
			||||||
      t={Translations.t.general.uploadPending.Subs({ count: $pendingChangesCount })}
 | 
					      t={Translations.t.general.uploadPending.Subs({ count: $pending.length })}
 | 
				
			||||||
    />
 | 
					    />
 | 
				
			||||||
  {/if}
 | 
					  {/if}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  {#each $errors as error}
 | 
					  {#each $errors as error}
 | 
				
			||||||
    <Tr cls="alert" t={Translations.t.general.uploadError.Subs({ error })} />
 | 
					    <Tr cls="alert" t={Translations.t.general.uploadError.Subs({ error })} />
 | 
				
			||||||
  {/each}
 | 
					  {/each}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  {#if !compact && $pending.length > 0}
 | 
				
			||||||
 | 
					    <button on:click={() => state.changes.pendingChanges.set([])}>
 | 
				
			||||||
 | 
					      <Tr t={Translations.t.general.clearPendingChanges} />
 | 
				
			||||||
 | 
					    </button>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <ul>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      {#each $pending as pending}
 | 
				
			||||||
 | 
					        <li>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          {#if pending.changes !== undefined}
 | 
				
			||||||
 | 
					            Create {pending.type}/{pending.id} {JSON.stringify(TagUtils.KVObjtoProperties(pending.tags))}
 | 
				
			||||||
 | 
					          {:else}
 | 
				
			||||||
 | 
					            Modify {pending.type}/{pending.id} {JSON.stringify(pending.tags)}
 | 
				
			||||||
 | 
					          {/if}
 | 
				
			||||||
 | 
					          {#if pending.type === "way" && pending.changes?.nodes}
 | 
				
			||||||
 | 
					            {pending.changes.nodes.join(" ")}
 | 
				
			||||||
 | 
					          {/if}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        </li>
 | 
				
			||||||
 | 
					      {/each}
 | 
				
			||||||
 | 
					    </ul>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  {/if}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -99,6 +99,7 @@ import NothingKnown from "./Popup/NothingKnown.svelte"
 | 
				
			||||||
import { CombinedFetcher } from "../Logic/Web/NearbyImagesSearch"
 | 
					import { CombinedFetcher } from "../Logic/Web/NearbyImagesSearch"
 | 
				
			||||||
import { And } from "../Logic/Tags/And"
 | 
					import { And } from "../Logic/Tags/And"
 | 
				
			||||||
import CloseNoteButton from "./Popup/Notes/CloseNoteButton.svelte"
 | 
					import CloseNoteButton from "./Popup/Notes/CloseNoteButton.svelte"
 | 
				
			||||||
 | 
					import PendingChangesIndicator from "./BigComponents/PendingChangesIndicator.svelte"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class NearbyImageVis implements SpecialVisualization {
 | 
					class NearbyImageVis implements SpecialVisualization {
 | 
				
			||||||
    // Class must be in SpecialVisualisations due to weird cyclical import that breaks the tests
 | 
					    // Class must be in SpecialVisualisations due to weird cyclical import that breaks the tests
 | 
				
			||||||
| 
						 | 
					@ -118,7 +119,6 @@ class NearbyImageVis implements SpecialVisualization {
 | 
				
			||||||
        "A component showing nearby images loaded from various online services such as Mapillary. In edit mode and when used on a feature, the user can select an image to add to the feature"
 | 
					        "A component showing nearby images loaded from various online services such as Mapillary. In edit mode and when used on a feature, the user can select an image to add to the feature"
 | 
				
			||||||
    funcName = "nearby_images"
 | 
					    funcName = "nearby_images"
 | 
				
			||||||
    needsUrls = CombinedFetcher.apiUrls
 | 
					    needsUrls = CombinedFetcher.apiUrls
 | 
				
			||||||
    svelteBased = true
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    constr(
 | 
					    constr(
 | 
				
			||||||
        state: SpecialVisualizationState,
 | 
					        state: SpecialVisualizationState,
 | 
				
			||||||
| 
						 | 
					@ -2014,8 +2014,16 @@ export default class SpecialVisualizations {
 | 
				
			||||||
                        return mostShadowed?.description ?? matchingPresets[0]?.description
 | 
					                        return mostShadowed?.description ?? matchingPresets[0]?.description
 | 
				
			||||||
                    })
 | 
					                    })
 | 
				
			||||||
                    return new VariableUiElement(translation)
 | 
					                    return new VariableUiElement(translation)
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            },
 | 
					            {
 | 
				
			||||||
 | 
					                funcName:"pending_changes",
 | 
				
			||||||
 | 
					                docs: "A module showing the pending changes, with the option to clear the pending changes",
 | 
				
			||||||
 | 
					                args:[],
 | 
				
			||||||
 | 
					                constr(state: SpecialVisualizationState, tagSource: UIEventSource<Record<string, string>>, argument: string[], feature: Feature, layer: LayerConfig): BaseUIElement {
 | 
				
			||||||
 | 
					                    return new SvelteUIElement(PendingChangesIndicator, {state, compact: false})
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        ]
 | 
					        ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        specialVisualizations.push(new AutoApplyButton(specialVisualizations))
 | 
					        specialVisualizations.push(new AutoApplyButton(specialVisualizations))
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue