MOBILE-3817 dashboard: Create observable methods for getDashboardBlocks
parent
89ba05dd3e
commit
3e462979f7
|
@ -59,6 +59,7 @@ import {
|
||||||
} from '@services/database/sites';
|
} from '@services/database/sites';
|
||||||
import { Observable, Subject } from 'rxjs';
|
import { Observable, Subject } from 'rxjs';
|
||||||
import { finalize, map } from 'rxjs/operators';
|
import { finalize, map } from 'rxjs/operators';
|
||||||
|
import { firstValueFrom } from '../utils/observables';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* QR Code type enumeration.
|
* QR Code type enumeration.
|
||||||
|
@ -494,7 +495,7 @@ export class CoreSite {
|
||||||
*/
|
*/
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
read<T = unknown>(method: string, data: any, preSets?: CoreSiteWSPreSets): Promise<T> {
|
read<T = unknown>(method: string, data: any, preSets?: CoreSiteWSPreSets): Promise<T> {
|
||||||
return this.readObservable<T>(method, data, preSets).toPromise();
|
return firstValueFrom(this.readObservable<T>(method, data, preSets));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -525,7 +526,7 @@ export class CoreSite {
|
||||||
*/
|
*/
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
write<T = unknown>(method: string, data: any, preSets?: CoreSiteWSPreSets): Promise<T> {
|
write<T = unknown>(method: string, data: any, preSets?: CoreSiteWSPreSets): Promise<T> {
|
||||||
return this.writeObservable<T>(method, data, preSets).toPromise();
|
return firstValueFrom(this.writeObservable<T>(method, data, preSets));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -556,7 +557,7 @@ export class CoreSite {
|
||||||
*/
|
*/
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
async request<T = unknown>(method: string, data: any, preSets: CoreSiteWSPreSets): Promise<T> {
|
async request<T = unknown>(method: string, data: any, preSets: CoreSiteWSPreSets): Promise<T> {
|
||||||
return this.requestObservable<T>(method, data, preSets).toPromise();
|
return firstValueFrom(this.requestObservable<T>(method, data, preSets));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1640,7 +1641,7 @@ export class CoreSite {
|
||||||
|
|
||||||
// Check for an ongoing identical request if we're not ignoring cache.
|
// Check for an ongoing identical request if we're not ignoring cache.
|
||||||
if (cachePreSets.getFromCache && this.ongoingRequests[cacheId] !== undefined) {
|
if (cachePreSets.getFromCache && this.ongoingRequests[cacheId] !== undefined) {
|
||||||
return await this.ongoingRequests[cacheId].toPromise();
|
return await firstValueFrom(this.ongoingRequests[cacheId]);
|
||||||
}
|
}
|
||||||
|
|
||||||
const subject = new Subject<CoreSitePublicConfigResponse>();
|
const subject = new Subject<CoreSitePublicConfigResponse>();
|
||||||
|
@ -1698,7 +1699,7 @@ export class CoreSite {
|
||||||
subject.error(error);
|
subject.error(error);
|
||||||
});
|
});
|
||||||
|
|
||||||
return observable.toPromise();
|
return firstValueFrom(observable);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -13,12 +13,15 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { CoreSites } from '@services/sites';
|
import { CoreSites, CoreSitesCommonWSOptions } from '@services/sites';
|
||||||
import { CoreSite, CoreSiteWSPreSets } from '@classes/site';
|
import { CoreSite, CoreSiteWSPreSets } from '@classes/site';
|
||||||
import { CoreCourseBlock } from '@features/course/services/course';
|
import { CoreCourseBlock } from '@features/course/services/course';
|
||||||
import { CoreStatusWithWarningsWSResponse } from '@services/ws';
|
import { CoreStatusWithWarningsWSResponse } from '@services/ws';
|
||||||
import { makeSingleton } from '@singletons';
|
import { makeSingleton } from '@singletons';
|
||||||
import { CoreError } from '@classes/errors/error';
|
import { CoreError } from '@classes/errors/error';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { map } from 'rxjs/operators';
|
||||||
|
import { asyncObservable, firstValueFrom } from '@/core/utils/observables';
|
||||||
|
|
||||||
const ROOT_CACHE_KEY = 'CoreCoursesDashboard:';
|
const ROOT_CACHE_KEY = 'CoreCoursesDashboard:';
|
||||||
|
|
||||||
|
@ -51,40 +54,66 @@ export class CoreCoursesDashboardProvider {
|
||||||
* @return Promise resolved with the list of blocks.
|
* @return Promise resolved with the list of blocks.
|
||||||
* @since 3.6
|
* @since 3.6
|
||||||
*/
|
*/
|
||||||
async getDashboardBlocksFromWS(
|
getDashboardBlocksFromWS(
|
||||||
myPage = CoreCoursesDashboardProvider.MY_PAGE_DEFAULT,
|
myPage = CoreCoursesDashboardProvider.MY_PAGE_DEFAULT,
|
||||||
userId?: number,
|
userId?: number,
|
||||||
siteId?: string,
|
siteId?: string,
|
||||||
): Promise<CoreCourseBlock[]> {
|
): Promise<CoreCourseBlock[]> {
|
||||||
const site = await CoreSites.getSite(siteId);
|
return firstValueFrom(this.getDashboardBlocksFromWSObservable({
|
||||||
|
myPage,
|
||||||
|
userId,
|
||||||
|
siteId,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
const params: CoreBlockGetDashboardBlocksWSParams = {
|
/**
|
||||||
returncontents: true,
|
* Get dashboard blocks from WS.
|
||||||
};
|
*
|
||||||
if (CoreSites.getRequiredCurrentSite().isVersionGreaterEqualThan('4.0')) {
|
* @param options Options.
|
||||||
params.mypage = myPage;
|
* @return Observable that returns the list of blocks.
|
||||||
} else if (myPage != CoreCoursesDashboardProvider.MY_PAGE_DEFAULT) {
|
* @since 3.6
|
||||||
throw new CoreError('mypage param is no accessible on core_block_get_dashboard_blocks');
|
*/
|
||||||
}
|
getDashboardBlocksFromWSObservable(options: GetDashboardBlocksOptions = {}): Observable<CoreCourseBlock[]> {
|
||||||
|
return asyncObservable(async () => {
|
||||||
|
const site = await CoreSites.getSite(options.siteId);
|
||||||
|
|
||||||
const preSets: CoreSiteWSPreSets = {
|
const myPage = options.myPage ?? CoreCoursesDashboardProvider.MY_PAGE_DEFAULT;
|
||||||
cacheKey: this.getDashboardBlocksCacheKey(myPage, userId),
|
const params: CoreBlockGetDashboardBlocksWSParams = {
|
||||||
updateFrequency: CoreSite.FREQUENCY_RARELY,
|
returncontents: true,
|
||||||
};
|
};
|
||||||
if (userId) {
|
if (CoreSites.getRequiredCurrentSite().isVersionGreaterEqualThan('4.0')) {
|
||||||
params.userid = userId;
|
params.mypage = myPage;
|
||||||
}
|
} else if (myPage != CoreCoursesDashboardProvider.MY_PAGE_DEFAULT) {
|
||||||
const result = await site.read<CoreBlockGetDashboardBlocksWSResponse>('core_block_get_dashboard_blocks', params, preSets);
|
throw new CoreError('mypage param is no accessible on core_block_get_dashboard_blocks');
|
||||||
|
}
|
||||||
|
|
||||||
if (site.isVersionGreaterEqualThan('4.0')) {
|
const preSets: CoreSiteWSPreSets = {
|
||||||
// Temporary hack to have course overview on 3.9.5 but not on 4.0 onwards.
|
cacheKey: this.getDashboardBlocksCacheKey(myPage, options.userId),
|
||||||
// To be removed in a near future.
|
updateFrequency: CoreSite.FREQUENCY_RARELY,
|
||||||
// Remove myoverview when is forced. See MDL-72092.
|
...CoreSites.getReadingStrategyPreSets(options.readingStrategy),
|
||||||
result.blocks = result.blocks.filter((block) =>
|
};
|
||||||
block.instanceid != 0 || block.name != 'myoverview' || block.region != 'forced');
|
if (options.userId) {
|
||||||
}
|
params.userid = options.userId;
|
||||||
|
}
|
||||||
|
|
||||||
return result.blocks || [];
|
const observable = site.readObservable<CoreBlockGetDashboardBlocksWSResponse>(
|
||||||
|
'core_block_get_dashboard_blocks',
|
||||||
|
params,
|
||||||
|
preSets,
|
||||||
|
);
|
||||||
|
|
||||||
|
return observable.pipe(map(result => {
|
||||||
|
if (site.isVersionGreaterEqualThan('4.0')) {
|
||||||
|
// Temporary hack to have course overview on 3.9.5 but not on 4.0 onwards.
|
||||||
|
// To be removed in a near future.
|
||||||
|
// Remove myoverview when is forced. See MDL-72092.
|
||||||
|
result.blocks = result.blocks.filter((block) =>
|
||||||
|
block.instanceid != 0 || block.name != 'myoverview' || block.region != 'forced');
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.blocks || [];
|
||||||
|
}));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -95,39 +124,52 @@ export class CoreCoursesDashboardProvider {
|
||||||
* @param myPage What my page to return blocks of. Default MY_PAGE_DEFAULT.
|
* @param myPage What my page to return blocks of. Default MY_PAGE_DEFAULT.
|
||||||
* @return Promise resolved with the list of blocks.
|
* @return Promise resolved with the list of blocks.
|
||||||
*/
|
*/
|
||||||
async getDashboardBlocks(
|
getDashboardBlocks(
|
||||||
userId?: number,
|
userId?: number,
|
||||||
siteId?: string,
|
siteId?: string,
|
||||||
myPage = CoreCoursesDashboardProvider.MY_PAGE_DEFAULT,
|
myPage = CoreCoursesDashboardProvider.MY_PAGE_DEFAULT,
|
||||||
): Promise<CoreCoursesDashboardBlocks> {
|
): Promise<CoreCoursesDashboardBlocks> {
|
||||||
const blocks = await this.getDashboardBlocksFromWS(myPage, userId, siteId);
|
return firstValueFrom(this.getDashboardBlocksObservable({
|
||||||
|
myPage,
|
||||||
|
userId,
|
||||||
|
siteId,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
let mainBlocks: CoreCourseBlock[] = [];
|
/**
|
||||||
let sideBlocks: CoreCourseBlock[] = [];
|
* Get dashboard blocks.
|
||||||
|
*
|
||||||
blocks.forEach((block) => {
|
* @param options Options.
|
||||||
if (block.region == 'content' || block.region == 'main') {
|
* @return observable that returns the list of blocks.
|
||||||
mainBlocks.push(block);
|
*/
|
||||||
} else {
|
getDashboardBlocksObservable(options: GetDashboardBlocksOptions = {}): Observable<CoreCoursesDashboardBlocks> {
|
||||||
sideBlocks.push(block);
|
return this.getDashboardBlocksFromWSObservable(options).pipe(map(blocks => {
|
||||||
}
|
let mainBlocks: CoreCourseBlock[] = [];
|
||||||
});
|
let sideBlocks: CoreCourseBlock[] = [];
|
||||||
|
|
||||||
if (mainBlocks.length == 0) {
|
|
||||||
mainBlocks = [];
|
|
||||||
sideBlocks = [];
|
|
||||||
|
|
||||||
blocks.forEach((block) => {
|
blocks.forEach((block) => {
|
||||||
if (block.region.match('side')) {
|
if (block.region == 'content' || block.region == 'main') {
|
||||||
sideBlocks.push(block);
|
|
||||||
} else {
|
|
||||||
mainBlocks.push(block);
|
mainBlocks.push(block);
|
||||||
|
} else {
|
||||||
|
sideBlocks.push(block);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
return { mainBlocks, sideBlocks };
|
if (mainBlocks.length == 0) {
|
||||||
|
mainBlocks = [];
|
||||||
|
sideBlocks = [];
|
||||||
|
|
||||||
|
blocks.forEach((block) => {
|
||||||
|
if (block.region.match('side')) {
|
||||||
|
sideBlocks.push(block);
|
||||||
|
} else {
|
||||||
|
mainBlocks.push(block);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return { mainBlocks, sideBlocks };
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -194,6 +236,14 @@ export type CoreCoursesDashboardBlocks = {
|
||||||
sideBlocks: CoreCourseBlock[];
|
sideBlocks: CoreCourseBlock[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Options for some get dashboard blocks calls.
|
||||||
|
*/
|
||||||
|
export type GetDashboardBlocksOptions = CoreSitesCommonWSOptions & {
|
||||||
|
userId?: number; // User ID. If not defined, current user.
|
||||||
|
myPage?: string; // Page to get. If not defined, CoreCoursesDashboardProvider.MY_PAGE_DEFAULT.
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Params of core_block_get_dashboard_blocks WS.
|
* Params of core_block_get_dashboard_blocks WS.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import { EventEmitter } from '@angular/core';
|
import { EventEmitter } from '@angular/core';
|
||||||
|
import { CoreUtils } from '@services/utils/utils';
|
||||||
import { Observable, Subscription } from 'rxjs';
|
import { Observable, Subscription } from 'rxjs';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -31,37 +32,46 @@ export class CoreSubscriptions {
|
||||||
* @param subscribable Subscribable to listen to.
|
* @param subscribable Subscribable to listen to.
|
||||||
* @param onSuccess Callback to run when the subscription is updated.
|
* @param onSuccess Callback to run when the subscription is updated.
|
||||||
* @param onError Callback to run when the an error happens.
|
* @param onError Callback to run when the an error happens.
|
||||||
|
* @param onComplete Callback to run when the observable completes.
|
||||||
* @return A function to unsubscribe.
|
* @return A function to unsubscribe.
|
||||||
*/
|
*/
|
||||||
static once<T>(
|
static once<T>(
|
||||||
subscribable: Subscribable<T>,
|
subscribable: Subscribable<T>,
|
||||||
onSuccess: (value: T) => unknown,
|
onSuccess: (value: T) => unknown,
|
||||||
onError?: (error: unknown) => unknown,
|
onError?: (error: unknown) => unknown,
|
||||||
|
onComplete?: () => void,
|
||||||
): () => void {
|
): () => void {
|
||||||
let unsubscribe = false;
|
let callbackCalled = false;
|
||||||
let subscription: Subscription | null = null;
|
let subscription: Subscription | null = null;
|
||||||
|
|
||||||
|
const runCallback = (callback) => {
|
||||||
|
if (!callbackCalled) {
|
||||||
|
callbackCalled = true;
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const unsubscribe = async () => {
|
||||||
|
// Subscription variable might not be set because we can receive a value immediately. Wait for next tick.
|
||||||
|
await CoreUtils.nextTick();
|
||||||
|
|
||||||
|
subscription?.unsubscribe();
|
||||||
|
};
|
||||||
|
|
||||||
subscription = subscribable.subscribe(
|
subscription = subscribable.subscribe(
|
||||||
value => {
|
value => {
|
||||||
// Subscription variable might not be set because we can receive a value immediately.
|
unsubscribe();
|
||||||
unsubscribe = true;
|
runCallback(() => onSuccess(value));
|
||||||
subscription?.unsubscribe();
|
|
||||||
|
|
||||||
onSuccess(value);
|
|
||||||
},
|
},
|
||||||
error => {
|
error => {
|
||||||
// Subscription variable might not be set because we can receive a value immediately.
|
unsubscribe();
|
||||||
unsubscribe = true;
|
runCallback(() => onError?.(error));
|
||||||
subscription?.unsubscribe();
|
},
|
||||||
|
() => {
|
||||||
onError && onError(error);
|
unsubscribe();
|
||||||
|
runCallback(() => onComplete?.());
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
if (unsubscribe) {
|
|
||||||
subscription.unsubscribe();
|
|
||||||
}
|
|
||||||
|
|
||||||
return () => subscription?.unsubscribe();
|
return () => subscription?.unsubscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,12 +17,20 @@ import { BehaviorSubject, Subject } from 'rxjs';
|
||||||
|
|
||||||
describe('CoreSubscriptions singleton', () => {
|
describe('CoreSubscriptions singleton', () => {
|
||||||
|
|
||||||
it('calls callbacks only once', async () => {
|
let subject: Subject<unknown>;
|
||||||
// Test call success function.
|
let success: jest.Mock;
|
||||||
let subject = new Subject();
|
let error: jest.Mock;
|
||||||
let success = jest.fn();
|
let complete: jest.Mock;
|
||||||
let error = jest.fn();
|
|
||||||
CoreSubscriptions.once(subject, success, error);
|
beforeEach(() => {
|
||||||
|
subject = new Subject();
|
||||||
|
success = jest.fn();
|
||||||
|
error = jest.fn();
|
||||||
|
complete = jest.fn();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('calls success callback only once', async () => {
|
||||||
|
CoreSubscriptions.once(subject, success, error, complete);
|
||||||
|
|
||||||
subject.next('foo');
|
subject.next('foo');
|
||||||
expect(success).toHaveBeenCalledTimes(1);
|
expect(success).toHaveBeenCalledTimes(1);
|
||||||
|
@ -32,11 +40,11 @@ describe('CoreSubscriptions singleton', () => {
|
||||||
subject.error('foo');
|
subject.error('foo');
|
||||||
expect(success).toHaveBeenCalledTimes(1);
|
expect(success).toHaveBeenCalledTimes(1);
|
||||||
expect(error).not.toHaveBeenCalled();
|
expect(error).not.toHaveBeenCalled();
|
||||||
|
expect(complete).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
// Test call error function.
|
it('calls error callback only once', async () => {
|
||||||
subject = new Subject(); // Create a new Subject because the previous one already has an error.
|
CoreSubscriptions.once(subject, success, error, complete);
|
||||||
success = jest.fn();
|
|
||||||
CoreSubscriptions.once(subject, success, error);
|
|
||||||
|
|
||||||
subject.error('foo');
|
subject.error('foo');
|
||||||
expect(error).toHaveBeenCalledWith('foo');
|
expect(error).toHaveBeenCalledWith('foo');
|
||||||
|
@ -45,11 +53,27 @@ describe('CoreSubscriptions singleton', () => {
|
||||||
subject.error('bar');
|
subject.error('bar');
|
||||||
expect(error).toHaveBeenCalledTimes(1);
|
expect(error).toHaveBeenCalledTimes(1);
|
||||||
expect(success).not.toHaveBeenCalled();
|
expect(success).not.toHaveBeenCalled();
|
||||||
|
expect(complete).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('calls complete callback only once', async () => {
|
||||||
|
CoreSubscriptions.once(subject, success, error, complete);
|
||||||
|
|
||||||
|
subject.complete();
|
||||||
|
expect(complete).toHaveBeenCalled();
|
||||||
|
|
||||||
|
subject.next('foo');
|
||||||
|
subject.error('bar');
|
||||||
|
subject.complete();
|
||||||
|
expect(complete).toHaveBeenCalledTimes(1);
|
||||||
|
expect(success).not.toHaveBeenCalled();
|
||||||
|
expect(error).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('calls success callback only once with behaviour subject', async () => {
|
||||||
// Test with behaviour subject (success callback called immediately).
|
// Test with behaviour subject (success callback called immediately).
|
||||||
const beaviourSubject = new BehaviorSubject('foo');
|
const beaviourSubject = new BehaviorSubject('foo');
|
||||||
error = jest.fn();
|
CoreSubscriptions.once(beaviourSubject, success, error, complete);
|
||||||
CoreSubscriptions.once(beaviourSubject, success, error);
|
|
||||||
|
|
||||||
expect(success).toHaveBeenCalledWith('foo');
|
expect(success).toHaveBeenCalledWith('foo');
|
||||||
|
|
||||||
|
@ -57,6 +81,7 @@ describe('CoreSubscriptions singleton', () => {
|
||||||
beaviourSubject.error('foo');
|
beaviourSubject.error('foo');
|
||||||
expect(success).toHaveBeenCalledTimes(1);
|
expect(success).toHaveBeenCalledTimes(1);
|
||||||
expect(error).not.toHaveBeenCalled();
|
expect(error).not.toHaveBeenCalled();
|
||||||
|
expect(complete).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('allows unsubscribing from outside the once function', async () => {
|
it('allows unsubscribing from outside the once function', async () => {
|
||||||
|
|
|
@ -0,0 +1,74 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { CoreError } from '@classes/errors/error';
|
||||||
|
import { CoreSubscriptions } from '@singletons/subscriptions';
|
||||||
|
import { BehaviorSubject, Observable, of } from 'rxjs';
|
||||||
|
import { catchError } from 'rxjs/operators';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert to an Observable a Promise that resolves to an Observable.
|
||||||
|
*
|
||||||
|
* @param createObservable A function returning a promise that resolves to an Observable.
|
||||||
|
* @returns Observable.
|
||||||
|
*/
|
||||||
|
export function asyncObservable<T>(createObservable: () => Promise<Observable<T>>): Observable<T> {
|
||||||
|
const promise = createObservable();
|
||||||
|
|
||||||
|
return new Observable(subscriber => {
|
||||||
|
promise
|
||||||
|
.then(observable => observable.subscribe(
|
||||||
|
value => subscriber.next(value),
|
||||||
|
error => subscriber.error(error),
|
||||||
|
() => subscriber.complete(),
|
||||||
|
))
|
||||||
|
.catch(error => subscriber.error(error));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a Promise that resolved with the first value returned from an observable.
|
||||||
|
* This function can be removed when the app starts using rxjs v7.
|
||||||
|
*
|
||||||
|
* @param observable Observable.
|
||||||
|
* @returns Promise resolved with the first value returned.
|
||||||
|
*/
|
||||||
|
export function firstValueFrom<T>(observable: Observable<T>): Promise<T> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
CoreSubscriptions.once(observable, resolve, reject, () => {
|
||||||
|
// Subscription is completed, check if we can get its value.
|
||||||
|
if (observable instanceof BehaviorSubject) {
|
||||||
|
resolve(observable.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
reject(new CoreError('Couldn\'t get first value from observable because it\'s already completed'));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ignore errors from an observable, returning a certain value instead.
|
||||||
|
*
|
||||||
|
* @param observable Observable to ignore errors.
|
||||||
|
* @param fallback Value to return if the observer errors.
|
||||||
|
* @return Observable with ignored errors, returning the fallback result if provided.
|
||||||
|
*/
|
||||||
|
export function ignoreErrors<Result>(observable: Observable<Result>): Observable<Result | undefined>;
|
||||||
|
export function ignoreErrors<Result, Fallback>(observable: Observable<Result>, fallback: Fallback): Observable<Result | Fallback>;
|
||||||
|
export function ignoreErrors<Result, Fallback>(
|
||||||
|
observable: Observable<Result>,
|
||||||
|
fallback?: Fallback,
|
||||||
|
): Observable<Result | Fallback | undefined> {
|
||||||
|
return observable.pipe(catchError(() => of(fallback)));
|
||||||
|
}
|
Loading…
Reference in New Issue