feat: get rid of cyclic dep between patch-db and api service

Signed-off-by: waterplea <alexander@inkin.ru>
This commit is contained in:
waterplea
2024-06-21 15:51:04 +05:00
parent 07104b18f5
commit e6abf4e33b
6 changed files with 70 additions and 80 deletions

View File

@@ -0,0 +1,56 @@
import { inject, Injectable, InjectionToken } from '@angular/core'
import { Dump, Revision, Update } from 'patch-db-client'
import { BehaviorSubject, EMPTY, Observable } from 'rxjs'
import {
bufferTime,
catchError,
filter,
startWith,
switchMap,
take,
} from 'rxjs/operators'
import { StateService } from 'src/app/services/state.service'
import { ApiService } from '../api/embassy-api.service'
import { AuthService } from '../auth.service'
import { DataModel } from './data-model'
import { LocalStorageBootstrap } from './local-storage-bootstrap'
export const PATCH_CACHE = new InjectionToken('', {
factory: () =>
new BehaviorSubject<Dump<DataModel>>({
id: 0,
value: {} as DataModel,
}),
})
@Injectable({
providedIn: 'root',
})
export class PatchDbSource extends Observable<Update<DataModel>[]> {
private readonly api = inject(ApiService)
private readonly state = inject(StateService)
private readonly stream$ = inject(AuthService).isVerified$.pipe(
switchMap(verified => (verified ? this.api.subscribeToPatchDB({}) : EMPTY)),
switchMap(({ dump, guid }) =>
this.api.openWebsocket$<Revision>(guid, {}).pipe(
bufferTime(250),
filter(revisions => !!revisions.length),
startWith([dump]),
),
),
catchError((_, original$) => {
this.state.retrigger()
return this.state.pipe(
filter(current => current === 'running'),
take(1),
switchMap(() => original$),
)
}),
startWith([inject(LocalStorageBootstrap).init()]),
)
constructor() {
super(subscriber => this.stream$.subscribe(subscriber))
}
}

View File

@@ -1,55 +0,0 @@
import { InjectionToken, Injector } from '@angular/core'
import { Revision, Update } from 'patch-db-client'
import { defer, EMPTY, from, Observable } from 'rxjs'
import {
bufferTime,
catchError,
filter,
startWith,
switchMap,
take,
} from 'rxjs/operators'
import { StateService } from 'src/app/services/state.service'
import { ApiService } from '../api/embassy-api.service'
import { AuthService } from '../auth.service'
import { DataModel } from './data-model'
import { LocalStorageBootstrap } from './local-storage-bootstrap'
export const PATCH_SOURCE = new InjectionToken<Observable<Update<DataModel>[]>>(
'',
)
export function sourceFactory(
injector: Injector,
): Observable<Update<DataModel>[]> {
// defer() needed to avoid circular dependency with ApiService, since PatchDB is needed there
return defer(() => {
const api = injector.get(ApiService)
const auth = injector.get(AuthService)
const state = injector.get(StateService)
const bootstrapper = injector.get(LocalStorageBootstrap)
return auth.isVerified$.pipe(
switchMap(verified =>
verified ? from(api.subscribeToPatchDB({})) : EMPTY,
),
switchMap(({ dump, guid }) =>
api.openWebsocket$<Revision>(guid, {}).pipe(
bufferTime(250),
filter(revisions => !!revisions.length),
startWith([dump]),
),
),
catchError((_, original$) => {
state.retrigger()
return state.pipe(
filter(current => current === 'running'),
take(1),
switchMap(() => original$),
)
}),
startWith([bootstrapper.init()]),
)
})
}

View File

@@ -1,20 +0,0 @@
import { PatchDB } from 'patch-db-client'
import { Injector, NgModule } from '@angular/core'
import { PATCH_SOURCE, sourceFactory } from './patch-db.factory'
// This module is purely for providers organization purposes
@NgModule({
providers: [
{
provide: PATCH_SOURCE,
deps: [Injector],
useFactory: sourceFactory,
},
{
provide: PatchDB,
deps: [PATCH_SOURCE],
useClass: PatchDB,
},
],
})
export class PatchDbModule {}