forked from MapComplete/MapComplete
Core: improve typing of mapD
This commit is contained in:
parent
09ea799ad4
commit
ab5ec29c02
2 changed files with 30 additions and 12 deletions
|
@ -31,7 +31,7 @@ export class Stores {
|
||||||
* @param promise
|
* @param promise
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
public static FromPromise<T>(promise: Promise<T>): Store<T> {
|
public static FromPromise<T>(promise: Promise<T>): Store<T | undefined> {
|
||||||
const src = new UIEventSource<T>(undefined)
|
const src = new UIEventSource<T>(undefined)
|
||||||
promise?.then((d) => src.setData(d))
|
promise?.then((d) => src.setData(d))
|
||||||
promise?.catch((err) => console.warn("Promise failed:", err))
|
promise?.catch((err) => console.warn("Promise failed:", err))
|
||||||
|
@ -97,7 +97,7 @@ export abstract class Store<T> implements Readable<T> {
|
||||||
abstract map<J>(f: (t: T) => J): Store<J>
|
abstract map<J>(f: (t: T) => J): Store<J>
|
||||||
abstract map<J>(f: (t: T) => J, extraStoresToWatch: Store<any>[]): Store<J>
|
abstract map<J>(f: (t: T) => J, extraStoresToWatch: Store<any>[]): Store<J>
|
||||||
|
|
||||||
public mapD<J>(f: (t: T) => J, extraStoresToWatch?: Store<any>[]): Store<J> {
|
public mapD<J>(f: (t: Exclude<T, undefined | null>) => J, extraStoresToWatch?: Store<any>[]): Store<J> {
|
||||||
return this.map((t) => {
|
return this.map((t) => {
|
||||||
if (t === undefined) {
|
if (t === undefined) {
|
||||||
return undefined
|
return undefined
|
||||||
|
@ -105,7 +105,7 @@ export abstract class Store<T> implements Readable<T> {
|
||||||
if (t === null) {
|
if (t === null) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
return f(t)
|
return f(<Exclude<T, undefined | null>> t)
|
||||||
}, extraStoresToWatch)
|
}, extraStoresToWatch)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -603,7 +603,7 @@ export class UIEventSource<T> extends Store<T> implements Writable<T> {
|
||||||
*/
|
*/
|
||||||
public static FromPromiseWithErr<T>(
|
public static FromPromiseWithErr<T>(
|
||||||
promise: Promise<T>
|
promise: Promise<T>
|
||||||
): UIEventSource<{ success: T } | { error: any }> {
|
): UIEventSource<{ success: T } | { error: any } | undefined> {
|
||||||
const src = new UIEventSource<{ success: T } | { error: any }>(undefined)
|
const src = new UIEventSource<{ success: T } | { error: any }>(undefined)
|
||||||
promise?.then((d) => src.setData({ success: d }))
|
promise?.then((d) => src.setData({ success: d }))
|
||||||
promise?.catch((err) => src.setData({ error: err }))
|
promise?.catch((err) => src.setData({ error: err }))
|
||||||
|
@ -771,18 +771,21 @@ export class UIEventSource<T> extends Store<T> implements Writable<T> {
|
||||||
* Monoidal map which results in a read-only store. 'undefined' is passed 'as is'
|
* Monoidal map which results in a read-only store. 'undefined' is passed 'as is'
|
||||||
* Given a function 'f', will construct a new UIEventSource where the contents will always be "f(this.data)'
|
* Given a function 'f', will construct a new UIEventSource where the contents will always be "f(this.data)'
|
||||||
*/
|
*/
|
||||||
public mapD<J>(f: (t: T) => J, extraSources: Store<any>[] = []): Store<J | undefined> {
|
public mapD<J>(f: (t: Exclude<T, undefined | null>) => J, extraSources: Store<any>[] = []): Store<J | undefined> {
|
||||||
return new MappedStore(
|
return new MappedStore(
|
||||||
this,
|
this,
|
||||||
(t) => {
|
(t) => {
|
||||||
if (t === undefined) {
|
if (t === undefined) {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
return f(t)
|
if (t === null) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return f(<Exclude<T, undefined | null>> t)
|
||||||
},
|
},
|
||||||
extraSources,
|
extraSources,
|
||||||
this._callbacks,
|
this._callbacks,
|
||||||
this.data === undefined ? undefined : f(this.data)
|
(this.data === undefined || this.data === null) ?(<undefined | null> this.data) : f(<any> this.data)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,15 +40,26 @@ export interface P4CPicture {
|
||||||
export default class NearbyImagesSearch {
|
export default class NearbyImagesSearch {
|
||||||
public static readonly services = ["mapillary", "flickr", "kartaview", "wikicommons"] as const
|
public static readonly services = ["mapillary", "flickr", "kartaview", "wikicommons"] as const
|
||||||
public static readonly apiUrls = ["https://api.flickr.com"]
|
public static readonly apiUrls = ["https://api.flickr.com"]
|
||||||
private readonly individualStores: Store<{ images: P4CPicture[]; beforeFilter: number }>[]
|
private readonly individualStores: Store<{ images: P4CPicture[]; beforeFilter: number } | undefined>[]
|
||||||
private readonly _store: UIEventSource<P4CPicture[]> = new UIEventSource<P4CPicture[]>([])
|
private readonly _store: UIEventSource<P4CPicture[]> = new UIEventSource<P4CPicture[]>([])
|
||||||
public readonly store: Store<P4CPicture[]> = this._store
|
public readonly store: Store<P4CPicture[]> = this._store
|
||||||
|
public readonly allDone: Store<boolean>
|
||||||
private readonly _options: NearbyImageOptions
|
private readonly _options: NearbyImageOptions
|
||||||
|
|
||||||
constructor(options: NearbyImageOptions, features: IndexedFeatureSource) {
|
constructor(options: NearbyImageOptions, features: IndexedFeatureSource) {
|
||||||
this.individualStores = NearbyImagesSearch.services.map((s) =>
|
this.individualStores = NearbyImagesSearch.services.map((s) =>
|
||||||
NearbyImagesSearch.buildPictureFetcher(options, s)
|
NearbyImagesSearch.buildPictureFetcher(options, s)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const allDone = new UIEventSource(false)
|
||||||
|
this.allDone = allDone
|
||||||
|
const self = this
|
||||||
|
function updateAllDone(){
|
||||||
|
const stillRunning = self.individualStores.some(store => store.data === undefined)
|
||||||
|
allDone.setData(!stillRunning)
|
||||||
|
}
|
||||||
|
self.individualStores.forEach(s => s.addCallback(_ => updateAllDone()))
|
||||||
|
|
||||||
this._options = options
|
this._options = options
|
||||||
if (features !== undefined) {
|
if (features !== undefined) {
|
||||||
const osmImages = new ImagesInLoadedDataFetcher(features).fetchAround({
|
const osmImages = new ImagesInLoadedDataFetcher(features).fetchAround({
|
||||||
|
@ -93,13 +104,17 @@ export default class NearbyImagesSearch {
|
||||||
private static buildPictureFetcher(
|
private static buildPictureFetcher(
|
||||||
options: NearbyImageOptions,
|
options: NearbyImageOptions,
|
||||||
fetcher: P4CService
|
fetcher: P4CService
|
||||||
): Store<{ images: P4CPicture[]; beforeFilter: number }> {
|
): Store<{ images: P4CPicture[]; beforeFilter: number } | null | undefined> {
|
||||||
const p4cStore = Stores.FromPromise<P4CPicture[]>(
|
const p4cStore = Stores.FromPromiseWithErr<P4CPicture[]>(
|
||||||
NearbyImagesSearch.fetchImages(options, fetcher)
|
NearbyImagesSearch.fetchImages(options, fetcher)
|
||||||
)
|
)
|
||||||
const searchRadius = options.searchRadius ?? 100
|
const searchRadius = options.searchRadius ?? 100
|
||||||
return p4cStore.map(
|
return p4cStore.mapD(
|
||||||
(images) => {
|
(imagesState) => {
|
||||||
|
if(imagesState["error"]){
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
let images = imagesState["success"]
|
||||||
if (images === undefined) {
|
if (images === undefined) {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue