From ab0d3143306bc228d354c633974d64b91cfd2000 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Thu, 30 Jul 2020 11:30:04 +0200 Subject: [PATCH] Better upload image flow: more feedback for users --- Logic/Imgur.ts | 2 +- Logic/LayerUpdater.ts | 13 ++++++------ UI/ImageUploadFlow.ts | 45 ++++++++++++++++++++++++++++------------- UI/i18n/Translation.ts | 1 - UI/i18n/Translations.ts | 13 ++++++++++++ createLayouts.ts | 4 ++-- index.css | 11 ++++++++++ test.ts | 32 +++++++++++++++++++++++++++++ 8 files changed, 97 insertions(+), 24 deletions(-) diff --git a/Logic/Imgur.ts b/Logic/Imgur.ts index 2a46723..77837d8 100644 --- a/Logic/Imgur.ts +++ b/Logic/Imgur.ts @@ -113,7 +113,7 @@ export class Imgur { handleSuccessfullUpload(response.data.link); }).fail((reason) => { console.log("Uploading to IMGUR failed", reason); - onFail(reason) + onFail(reason); }); } diff --git a/Logic/LayerUpdater.ts b/Logic/LayerUpdater.ts index 2f7eb02..9f48d2d 100644 --- a/Logic/LayerUpdater.ts +++ b/Logic/LayerUpdater.ts @@ -10,8 +10,8 @@ export class LayerUpdater { private _layers: FilteredLayer[]; public readonly runningQuery: UIEventSource = new UIEventSource(false); - - /** + public readonly retries: UIEventSource = new UIEventSource(0); + /** * The previous bounds for which the query has been run */ private previousBounds: Bounds; @@ -69,15 +69,16 @@ export class LayerUpdater { } - private _failCount = 0; private handleFail(reason: any) { - console.log("QUERY FAILED (retrying in 5 sec)", reason); + console.log(`QUERY FAILED (retrying in ${5 * this.retries.data} sec)`, reason); this.previousBounds = undefined; + this.retries.data ++; + this.retries.ping(); const self = this; - this._failCount++; window?.setTimeout( - function(){self.update()}, this._failCount * 5000 + function(){self.update()}, this.retries.data * 5000 ) + } diff --git a/UI/ImageUploadFlow.ts b/UI/ImageUploadFlow.ts index 25c8e22..582b1a7 100644 --- a/UI/ImageUploadFlow.ts +++ b/UI/ImageUploadFlow.ts @@ -6,12 +6,16 @@ import {UserDetails} from "../Logic/Osm/OsmConnection"; import {DropDown} from "./Input/DropDown"; import {VariableUiElement} from "./Base/VariableUIElement"; import Translations from "./i18n/Translations"; +import {fail} from "assert"; +import Combine from "./Base/Combine"; +import {VerticalCombine} from "./Base/VerticalCombine"; export class ImageUploadFlow extends UIElement { private _licensePicker: UIElement; private _selectedLicence: UIEventSource; private _isUploading: UIEventSource = new UIEventSource(0) private _didFail: UIEventSource = new UIEventSource(false); + private _allDone: UIEventSource = new UIEventSource(false); private _uploadOptions: (license: string) => { title: string; description: string; handleURL: (url: string) => void; allDone: (() => void) }; private _userdetails: UIEventSource; @@ -32,6 +36,7 @@ export class ImageUploadFlow extends UIElement { this._uploadOptions = uploadOptions; this.ListenTo(this._isUploading); this.ListenTo(this._didFail); + this.ListenTo(this._allDone); const licensePicker = new DropDown(Translations.t.image.willBePublished, [ @@ -50,20 +55,28 @@ export class ImageUploadFlow extends UIElement { InnerRender(): string { - if (!this._userdetails.data.loggedIn) { - return `
${Translations.t.image.pleaseLogin.Render()}
`; + const t = Translations.t.image; + if (this._userdetails === undefined) { + return ""; // No user details -> logging in is probably disabled or smthing } - let uploadingMessage = ""; + if (!this._userdetails.data.loggedIn) { + return `
${t.pleaseLogin.Render()}
`; + } + + let currentState: UIElement[] = []; if (this._isUploading.data == 1) { - return `${Translations.t.image.uploadingPicture.Render()}` + currentState.push(t.uploadingPicture); + } else if (this._isUploading.data > 0) { + currentState.push(t.uploadingMultiple.Subs({count: this._isUploading.data})); } - if (this._isUploading.data > 0) { - uploadingMessage = "Uploading multiple pictures, " + this._isUploading.data + " left..." + + if (this._didFail.data) { + currentState.push(t.uploadFailed); } - - if(this._didFail.data){ - uploadingMessage += "Some images failed to upload. Imgur migth be down or you might block third-party API's (e.g. by using Brave or UMatrix)
" + + if (this._allDone.data) { + currentState.push(t.uploadDone) } return "" + @@ -78,8 +91,7 @@ export class ImageUploadFlow extends UIElement { "" + Translations.t.image.respectPrivacy.Render() + "
" + this._licensePicker.Render() + "
" + - uploadingMessage + - + new VerticalCombine(currentState).Render() + "" + "
" + "" + "
" + - "" ; } @@ -111,6 +122,7 @@ export class ImageUploadFlow extends UIElement { function submitHandler() { const files = $(selector).prop('files'); self._isUploading.setData(files.length); + self._allDone.setData(false); const opts = self._uploadOptions(self._selectedLicence.data); @@ -121,11 +133,16 @@ export class ImageUploadFlow extends UIElement { opts.handleURL(url); }, function () { - console.log("All uploads completed") + console.log("All uploads completed"); + self._allDone.setData(true); opts.allDone(); }, function(failReason) { - + console.log("Upload failed due to ", failReason) + // No need to call something from the options -> we handle this here + self._didFail.setData(true); + self._isUploading.data--; + self._isUploading.ping(); },0 ) } diff --git a/UI/i18n/Translation.ts b/UI/i18n/Translation.ts index 58a1d8b..cb32db7 100644 --- a/UI/i18n/Translation.ts +++ b/UI/i18n/Translation.ts @@ -45,7 +45,6 @@ export default class Translation extends UIElement { return txt; } const en = this.translations["en"]; - console.warn("No translation for language ", Locale.language.data, "for", en); if (en !== undefined) { return en; } diff --git a/UI/i18n/Translations.ts b/UI/i18n/Translations.ts index 197e916..ea8bd2c 100644 --- a/UI/i18n/Translations.ts +++ b/UI/i18n/Translations.ts @@ -629,6 +629,11 @@ export default class Translations { nl: 'Bezig met een foto te uploaden...', fr: 'Mettre votre photo en ligne' }), + uploadingMultiple: new T({ + en: 'Uploading {count} of your picture...', + nl: 'Bezig met {count} foto\'s te uploaden...', + fr: 'Mettre votre {count} photos en ligne' + }), pleaseLogin: new T({ en: 'Please login to add a picure or to answer questions', nl: 'Gelieve je aan te melden om een foto toe te voegen of vragen te beantwoorden', @@ -646,6 +651,14 @@ export default class Translations { en: "Please respect privacy. Do not photograph people nor license plates", nl: "Respecteer privacy. Fotografeer geen mensen of nummerplaten", fr: "TODO: fr" + }), + uploadFailed: new T({ + en: "Could not upload your picture. Do you have internet and are third party API's allowed? Brave browser or UMatrix might block them.", + nl: "Afbeelding uploaden mislukt. Heb je internet? Gebruik je Brave of UMatrix? Dan moet je derde partijen toelaten." + }), + uploadDone: new T({ + en: "Your picture has been added. Thanks for helping out!", + nl: "Je afbeelding is toegevoegd. Bedankt om te helpen!" }) }, centerMessage: { diff --git a/createLayouts.ts b/createLayouts.ts index 8d92d73..dbab8db 100644 --- a/createLayouts.ts +++ b/createLayouts.ts @@ -17,7 +17,7 @@ import Translations from "./UI/i18n/Translations"; import {UIElement} from "./UI/UIElement"; import {LayerDefinition} from "./Customizations/LayerDefinition"; -console.log("Building routers") +console.log("Building the layouts") UIElement.runningFromConsole = true; @@ -55,7 +55,7 @@ function validate(layout: Layout) { const txt = translation.translations[ln]; const isMissing = txt === undefined || txt === "" || txt.toLowerCase().indexOf("todo") >= 0; if (isMissing) { - console.log("Missing or suspicious translation for '", translation.txt, "'in", ln, ":", txt) + console.log(`Missing or suspicious ${ln}-translation for '`, translation.txt, ":", txt) missing[ln]++ } else { present[ln]++; diff --git a/index.css b/index.css index e1e3b47..2250009 100644 --- a/index.css +++ b/index.css @@ -74,6 +74,17 @@ form { padding-bottom: 0.15em; } +.thanks { + background-color: #43d904; + font-weight: bold; + border-radius: 1em; + padding: 0.3em; + margin: 0.25em; + text-align: center; + padding-top: 0.15em; + padding-bottom: 0.15em; +} + .activate-osm-authentication { cursor: pointer; diff --git a/test.ts b/test.ts index e69de29..51edd2c 100644 --- a/test.ts +++ b/test.ts @@ -0,0 +1,32 @@ +import {ImageUploadFlow} from "./UI/ImageUploadFlow"; +import {OsmConnection, UserDetails} from "./Logic/Osm/OsmConnection"; +import {OsmImageUploadHandler} from "./Logic/Osm/OsmImageUploadHandler"; +import {UIEventSource} from "./UI/UIEventSource"; +import {Changes} from "./Logic/Osm/Changes"; +import {SlideShow} from "./UI/SlideShow"; +import {ElementStorage} from "./Logic/ElementStorage"; +import {isNullOrUndefined} from "util"; +import Locale from "./UI/i18n/Locale"; + +const osmConnection = new OsmConnection(true, new UIEventSource(undefined)); +const uploadHandler = new OsmImageUploadHandler( + new UIEventSource({}), + osmConnection.userDetails, + new UIEventSource("cc0"), + new Changes("blabla", osmConnection, new ElementStorage()), + undefined); + +new ImageUploadFlow( + osmConnection.userDetails, + new UIEventSource("cc0"), + (license: string) => { + return { + title: "test", + description: "test", + handleURL: console.log, + allDone: () => { + } + } + }).AttachTo("maindiv") + +Locale.language.setData("nl") \ No newline at end of file