forked from MapComplete/MapComplete
112 lines
3.8 KiB
TypeScript
112 lines
3.8 KiB
TypeScript
import { IdbLocalStorage } from "../Web/IdbLocalStorage"
|
|
import { Store, UIEventSource } from "../UIEventSource"
|
|
import ThemeViewState from "../../Models/ThemeViewState"
|
|
import LinkImageAction from "../Osm/Actions/LinkImageAction"
|
|
import { WithImageState } from "../../Models/ThemeViewState/WithImageState"
|
|
|
|
export interface FailedImageArgs {
|
|
readonly featureId: string,
|
|
readonly author: string,
|
|
readonly blob: File,
|
|
readonly targetKey: string | undefined,
|
|
readonly noblur: boolean,
|
|
readonly ignoreGps: boolean,
|
|
readonly lastGpsLocation: GeolocationCoordinates,
|
|
readonly layoutId: string
|
|
readonly date: number
|
|
}
|
|
|
|
export default class EmergencyImageBackup {
|
|
|
|
public static readonly singleton = new EmergencyImageBackup()
|
|
private readonly _failedImages: UIEventSource<FailedImageArgs[]>
|
|
|
|
public readonly failedImages: Store<FailedImageArgs[]>
|
|
|
|
private readonly _isUploading: UIEventSource<boolean> = new UIEventSource(false)
|
|
public readonly isUploading: Store<boolean> = this._isUploading
|
|
|
|
private constructor() {
|
|
this._failedImages = IdbLocalStorage.Get<FailedImageArgs[]>("failed-images-backup", { defaultValue: [] })
|
|
this.failedImages = this._failedImages
|
|
}
|
|
|
|
public addFailedImage(args: FailedImageArgs) {
|
|
this._failedImages.data.push(args)
|
|
this._failedImages.ping()
|
|
}
|
|
|
|
public delete(img: FailedImageArgs) {
|
|
const index = this._failedImages.data.indexOf(img)
|
|
if (index < 0) {
|
|
return
|
|
}
|
|
this._failedImages.data.splice(index, 1)
|
|
this._failedImages.ping()
|
|
}
|
|
|
|
/**
|
|
* Retries uploading the given image
|
|
* Returns 'true' if the image got correctly uploaded and linked (or upload is no longer necessary, e.g. deleted iem)
|
|
* @param state
|
|
* @param i
|
|
*/
|
|
public async retryUploading(state: ThemeViewState, i: FailedImageArgs): Promise<boolean> {
|
|
this._isUploading.set(true)
|
|
try {
|
|
|
|
const feature = await state.osmObjectDownloader.DownloadObjectAsync(i.featureId)
|
|
if (feature === "deleted") {
|
|
return true
|
|
}
|
|
|
|
const asGeojson = feature.asGeoJson()
|
|
const uploadResult = await state.imageUploadManager.uploadImageWithLicense(
|
|
i.featureId,
|
|
i.author,
|
|
i.blob,
|
|
i.targetKey,
|
|
i.noblur,
|
|
asGeojson,
|
|
{
|
|
ignoreGps: i.ignoreGps,
|
|
noBackup: true,// Don't save this _again_
|
|
overwriteGps: i.lastGpsLocation
|
|
}
|
|
)
|
|
if (!uploadResult) {
|
|
// Upload failed again
|
|
return false
|
|
}
|
|
state.featureProperties.trackFeature(asGeojson)
|
|
const properties = state.featureProperties.getStore(i.featureId)
|
|
// Upload successful, time to link this to the image
|
|
const action = new LinkImageAction(
|
|
i.featureId,
|
|
uploadResult.key,
|
|
uploadResult.value,
|
|
properties,
|
|
{
|
|
theme: i.layoutId,
|
|
changeType: "add-image"
|
|
}
|
|
)
|
|
|
|
await state.changes.applyAction(action)
|
|
await state.changes.flushChanges("delayed image upload link")
|
|
this.delete(i)
|
|
return true
|
|
} finally {
|
|
this._isUploading.set(false)
|
|
}
|
|
}
|
|
|
|
public async retryAll(state: WithImageState) {
|
|
for (const img of [...this._failedImages.data]) {
|
|
await this.retryUploading(state, img)
|
|
/*this._isUploading.setData(true)
|
|
await Utils.waitFor(2000)
|
|
this._isUploading.set(false)*/
|
|
}
|
|
}
|
|
}
|