mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 02:11:53 +00:00
Auto poll (#947)
* switch to polling automatically * connected conditional * better logs * refactor * remove old diagnosing connection code * continue refactor * bluj edits * polling working without refresh * pr review * bluj comments addressed * fix: Build for ui Co-authored-by: Drew Ansbacher <drew.ansbacher@gmail.com> * Delete settings.json * lint fix Co-authored-by: Drew Ansbacher <drew.ansbacher@spiredigital.com> Co-authored-by: J M <dragondef@gmail.com> Co-authored-by: Drew Ansbacher <drew.ansbacher@gmail.com> Co-authored-by: J M <2364004+Blu-J@users.noreply.github.com>
This commit is contained in:
committed by
Aiden McClelland
parent
7277b430a1
commit
176342c11f
1
diagnostic-ui/package-lock.json
generated
1
diagnostic-ui/package-lock.json
generated
@@ -5,6 +5,7 @@
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "diagnostic-ui",
|
||||
"version": "0.3.0",
|
||||
"dependencies": {
|
||||
"@angular/animations": "^12.2.5",
|
||||
|
||||
1
setup-wizard/package-lock.json
generated
1
setup-wizard/package-lock.json
generated
@@ -5,6 +5,7 @@
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "setup-wizard",
|
||||
"version": "0.0.1",
|
||||
"dependencies": {
|
||||
"@angular/common": "^12.2.1",
|
||||
|
||||
43
system-images/compat/Cargo.lock
generated
43
system-images/compat/Cargo.lock
generated
@@ -216,6 +216,15 @@ version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bitmaps"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "031043d04099746d8db04daf1fa424b2bc8bd69d92b25962dcde24da39ab64a2"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitvec"
|
||||
version = "0.19.5"
|
||||
@@ -1461,6 +1470,20 @@ dependencies = [
|
||||
"unicode-normalization",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "imbl"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "543682c9082b25e63d03b5acbd65ad111fd49dd93e70843e5175db4ff81d606b"
|
||||
dependencies = [
|
||||
"bitmaps",
|
||||
"rand_core 0.6.3",
|
||||
"rand_xoshiro",
|
||||
"sized-chunks",
|
||||
"typenum",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indenter"
|
||||
version = "0.3.3"
|
||||
@@ -2105,6 +2128,7 @@ dependencies = [
|
||||
"async-trait",
|
||||
"fd-lock-rs",
|
||||
"futures",
|
||||
"imbl",
|
||||
"json-patch",
|
||||
"json-ptr",
|
||||
"lazy_static",
|
||||
@@ -2525,6 +2549,15 @@ dependencies = [
|
||||
"rand_core 0.6.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_xoshiro"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa"
|
||||
dependencies = [
|
||||
"rand_core 0.6.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.1.57"
|
||||
@@ -3078,6 +3111,16 @@ version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "533494a8f9b724d33625ab53c6c4800f7cc445895924a8ef649222dcb76e938b"
|
||||
|
||||
[[package]]
|
||||
name = "sized-chunks"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "16d69225bde7a69b235da73377861095455d298f2b970996eec25ddbb42b3d1e"
|
||||
dependencies = [
|
||||
"bitmaps",
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.4"
|
||||
|
||||
@@ -88,16 +88,15 @@ export class AppComponent {
|
||||
async init () {
|
||||
await this.storage.create()
|
||||
await this.authService.init()
|
||||
await this.patch.init()
|
||||
|
||||
this.router.initialNavigation()
|
||||
|
||||
// watch auth
|
||||
this.authService.watch$()
|
||||
.subscribe(auth => {
|
||||
.subscribe(async auth => {
|
||||
// VERIFIED
|
||||
if (auth === AuthState.VERIFIED) {
|
||||
this.patch.start()
|
||||
await this.patch.start()
|
||||
|
||||
this.showMenu = true
|
||||
// if on the login screen, route to dashboard
|
||||
@@ -196,7 +195,7 @@ export class AppComponent {
|
||||
.subscribe(async connectionFailure => {
|
||||
if (connectionFailure === ConnectionFailure.None) {
|
||||
if (this.offlineToast) {
|
||||
this.offlineToast.dismiss()
|
||||
await this.offlineToast.dismiss()
|
||||
this.offlineToast = undefined
|
||||
}
|
||||
} else {
|
||||
@@ -206,16 +205,13 @@ export class AppComponent {
|
||||
case ConnectionFailure.Network:
|
||||
message = 'Phone or computer has no network connection.'
|
||||
break
|
||||
case ConnectionFailure.Diagnosing:
|
||||
message = new IonicSafeString('Running network diagnostics <ion-spinner style="padding: 0; margin: 0" name="dots"></ion-spinner>')
|
||||
break
|
||||
case ConnectionFailure.Tor:
|
||||
message = 'Browser unable to connect over Tor.'
|
||||
link = 'https://docs.start9.com/support/FAQ/setup-faq.html#tor-failure'
|
||||
link = 'https://docs.start9.com/support/FAQ/troubleshooting.html#tor-failure'
|
||||
break
|
||||
case ConnectionFailure.Lan:
|
||||
message = 'Embassy not found on Local Area Network.'
|
||||
link = 'https://docs.start9.com/support/FAQ/setup-faq.html#lan-failure'
|
||||
link = 'https://docs.start9.com/support/FAQ/troubleshooting.html#lan-failure'
|
||||
break
|
||||
}
|
||||
await this.presentToastOffline(message, link)
|
||||
|
||||
@@ -3,7 +3,7 @@ import { BrowserModule } from '@angular/platform-browser'
|
||||
import { RouteReuseStrategy } from '@angular/router'
|
||||
import { IonicModule, IonicRouteStrategy, IonNav } from '@ionic/angular'
|
||||
import { Drivers } from '@ionic/storage'
|
||||
import { IonicStorageModule } from '@ionic/storage-angular'
|
||||
import { IonicStorageModule, Storage } from '@ionic/storage-angular'
|
||||
import { HttpClientModule } from '@angular/common/http'
|
||||
import { AppComponent } from './app.component'
|
||||
import { AppRoutingModule } from './app-routing.module'
|
||||
@@ -48,8 +48,10 @@ import { GlobalErrorHandler } from './services/global-error-handler.service'
|
||||
providers: [
|
||||
FormBuilder,
|
||||
IonNav,
|
||||
Storage,
|
||||
{ provide: RouteReuseStrategy, useClass: IonicRouteStrategy },
|
||||
{
|
||||
provide: RouteReuseStrategy,
|
||||
useClass: IonicRouteStrategy,
|
||||
},
|
||||
{
|
||||
provide: ApiService,
|
||||
useFactory: ApiServiceFactory,
|
||||
@@ -58,9 +60,12 @@ import { GlobalErrorHandler } from './services/global-error-handler.service'
|
||||
{
|
||||
provide: PatchDbService,
|
||||
useFactory: PatchDbServiceFactory,
|
||||
deps: [ConfigService, ApiService, LocalStorageBootstrap, AuthService],
|
||||
deps: [ConfigService, ApiService, LocalStorageBootstrap, AuthService, Storage],
|
||||
},
|
||||
{
|
||||
provide: ErrorHandler,
|
||||
useClass: GlobalErrorHandler,
|
||||
},
|
||||
{ provide: ErrorHandler, useClass: GlobalErrorHandler},
|
||||
],
|
||||
bootstrap: [AppComponent],
|
||||
schemas: [ CUSTOM_ELEMENTS_SCHEMA ],
|
||||
|
||||
@@ -52,6 +52,7 @@ export class LoginPage {
|
||||
password: this.password,
|
||||
metadata: { platforms: getPlatforms() },
|
||||
})
|
||||
|
||||
this.authService.setVerified()
|
||||
this.password = ''
|
||||
} catch (e) {
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
import { Subject, Observable } from 'rxjs'
|
||||
import { Http, Update, Operation, Revision, Source, Store } from 'patch-db-client'
|
||||
import { Http, Update, Operation, Revision, Source, Store, RPCResponse } from 'patch-db-client'
|
||||
import { RR } from './api.types'
|
||||
import { DataModel } from 'src/app/services/patch-db/data-model'
|
||||
import { RequestError } from '../http.service'
|
||||
import { map } from 'rxjs/operators'
|
||||
|
||||
export abstract class ApiService implements Source<DataModel>, Http<DataModel> {
|
||||
protected readonly sync$ = new Subject<Update<DataModel>>()
|
||||
|
||||
/** PatchDb Source interface. Post/Patch requests provide a source of patches to the db. */
|
||||
// sequenceStream '_' is not used by the live api, but is overridden by the mock
|
||||
watch$ (_?: Store<DataModel>): Observable<Update<DataModel>> {
|
||||
return this.sync$.asObservable()
|
||||
watch$ (_?: Store<DataModel>): Observable<RPCResponse<Update<DataModel>>> {
|
||||
return this.sync$.asObservable().pipe(map( result => ({ result,
|
||||
jsonrpc: '2.0'})))
|
||||
}
|
||||
|
||||
connectionMade$ = new Subject<void>()
|
||||
|
||||
// for getting static files: ex icons, instructions, licenses
|
||||
abstract getStatic (url: string): Promise<string>
|
||||
|
||||
@@ -209,7 +209,6 @@ export abstract class ApiService implements Source<DataModel>, Http<DataModel> {
|
||||
throw e
|
||||
})
|
||||
.then(({ response, revision }) => {
|
||||
this.connectionMade$.next()
|
||||
if (revision) this.sync$.next(revision)
|
||||
return response
|
||||
})
|
||||
|
||||
@@ -59,7 +59,11 @@ export class MockApiService extends ApiService {
|
||||
|
||||
async login (params: RR.LoginReq): Promise<RR.loginRes> {
|
||||
await pauseFor(2000)
|
||||
this.mockPatch$.next({ id: 1, value: mockPatchData, expireId: null })
|
||||
|
||||
setTimeout(() => {
|
||||
this.mockPatch$.next({ id: 1, value: mockPatchData, expireId: null })
|
||||
}, 2000)
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Injectable } from '@angular/core'
|
||||
import { BehaviorSubject, combineLatest, fromEvent, merge, Subscription } from 'rxjs'
|
||||
import { PatchConnection, PatchDbService } from './patch-db/patch-db.service'
|
||||
import { HttpService, Method } from './http.service'
|
||||
import { distinctUntilChanged } from 'rxjs/operators'
|
||||
import { ConfigService } from './config.service'
|
||||
|
||||
@@ -13,7 +12,6 @@ export class ConnectionService {
|
||||
private readonly connectionFailure$ = new BehaviorSubject<ConnectionFailure>(ConnectionFailure.None)
|
||||
|
||||
constructor (
|
||||
private readonly httpService: HttpService,
|
||||
private readonly configService: ConfigService,
|
||||
private readonly patch: PatchDbService,
|
||||
) { }
|
||||
@@ -46,10 +44,10 @@ export class ConnectionService {
|
||||
),
|
||||
])
|
||||
.subscribe(async ([network, patchConnection, progress]) => {
|
||||
if (patchConnection !== PatchConnection.Disconnected) {
|
||||
this.connectionFailure$.next(ConnectionFailure.None)
|
||||
} else if (!network) {
|
||||
if (!network) {
|
||||
this.connectionFailure$.next(ConnectionFailure.Network)
|
||||
} else if (patchConnection !== PatchConnection.Disconnected) {
|
||||
this.connectionFailure$.next(ConnectionFailure.None)
|
||||
} else if (!!progress && progress.downloaded === progress.size) {
|
||||
this.connectionFailure$.next(ConnectionFailure.None)
|
||||
} else if (!this.configService.isTor()) {
|
||||
@@ -64,7 +62,6 @@ export class ConnectionService {
|
||||
|
||||
export enum ConnectionFailure {
|
||||
None = 'none',
|
||||
Diagnosing = 'diagnosing',
|
||||
Network = 'network',
|
||||
Tor = 'tor',
|
||||
Lan = 'lan',
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { MockSource, PollSource, Source, WebsocketSource } from 'patch-db-client'
|
||||
import { MockSource, PollSource, WebsocketSource } from 'patch-db-client'
|
||||
import { ConfigService } from 'src/app/services/config.service'
|
||||
import { DataModel } from './data-model'
|
||||
import { LocalStorageBootstrap } from './local-storage-bootstrap'
|
||||
@@ -8,29 +8,47 @@ import { AuthService } from '../auth.service'
|
||||
import { MockApiService } from '../api/embassy-mock-api.service'
|
||||
import { filter } from 'rxjs/operators'
|
||||
import { exists } from 'src/app/util/misc.util'
|
||||
import { Storage } from '@ionic/storage-angular'
|
||||
|
||||
export function PatchDbServiceFactory (
|
||||
config: ConfigService,
|
||||
embassyApi: ApiService,
|
||||
bootstrapper: LocalStorageBootstrap,
|
||||
auth: AuthService,
|
||||
storage: Storage,
|
||||
): PatchDbService {
|
||||
|
||||
const { mocks, patchDb: { poll }, supportsWebSockets } = config
|
||||
|
||||
let source: Source<DataModel>
|
||||
const {
|
||||
mocks,
|
||||
patchDb: { poll },
|
||||
} = config
|
||||
|
||||
if (mocks.enabled) {
|
||||
source = new MockSource((embassyApi as MockApiService).mockPatch$.pipe(filter(exists)))
|
||||
const source = new MockSource<DataModel>(
|
||||
(embassyApi as MockApiService).mockPatch$.pipe(filter(exists)),
|
||||
)
|
||||
return new PatchDbService(
|
||||
source,
|
||||
source,
|
||||
embassyApi,
|
||||
bootstrapper,
|
||||
auth,
|
||||
storage,
|
||||
)
|
||||
} else {
|
||||
if (!supportsWebSockets) {
|
||||
source = new PollSource({ ...poll }, embassyApi)
|
||||
} else {
|
||||
const protocol = window.location.protocol === 'http:' ? 'ws' : 'wss'
|
||||
const host = window.location.host
|
||||
source = new WebsocketSource(`${protocol}://${host}/ws/db`)
|
||||
}
|
||||
}
|
||||
const protocol = window.location.protocol === 'http:' ? 'ws' : 'wss'
|
||||
const host = window.location.host
|
||||
const wsSource = new WebsocketSource<DataModel>(
|
||||
`${protocol}://${host}/ws/db`,
|
||||
)
|
||||
const pollSource = new PollSource<DataModel>({ ...poll }, embassyApi)
|
||||
|
||||
return new PatchDbService(source, embassyApi, bootstrapper, auth)
|
||||
}
|
||||
return new PatchDbService(
|
||||
wsSource,
|
||||
pollSource,
|
||||
embassyApi,
|
||||
bootstrapper,
|
||||
auth,
|
||||
storage,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,15 @@
|
||||
import { Inject, Injectable, InjectionToken } from '@angular/core'
|
||||
import { Storage } from '@ionic/storage-angular'
|
||||
import { Bootstrapper, PatchDB, Source, Store } from 'patch-db-client'
|
||||
import { BehaviorSubject, Observable, of, Subscription } from 'rxjs'
|
||||
import { catchError, debounceTime, finalize, map, tap } from 'rxjs/operators'
|
||||
import {
|
||||
catchError,
|
||||
debounceTime,
|
||||
finalize,
|
||||
mergeMap,
|
||||
tap,
|
||||
withLatestFrom,
|
||||
} from 'rxjs/operators'
|
||||
import { pauseFor } from 'src/app/util/misc.util'
|
||||
import { ApiService } from '../api/embassy-api.service'
|
||||
import { AuthService } from '../auth.service'
|
||||
@@ -11,6 +19,7 @@ export const PATCH_HTTP = new InjectionToken<Source<DataModel>>('')
|
||||
export const PATCH_SOURCE = new InjectionToken<Source<DataModel>>('')
|
||||
export const BOOTSTRAPPER = new InjectionToken<Bootstrapper<DataModel>>('')
|
||||
export const AUTH = new InjectionToken<AuthService>('')
|
||||
export const STORAGE = new InjectionToken<Storage>('')
|
||||
|
||||
export enum PatchConnection {
|
||||
Initializing = 'initializing',
|
||||
@@ -22,90 +31,153 @@ export enum PatchConnection {
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class PatchDbService {
|
||||
private readonly WS_SUCCESS = 'wsSuccess'
|
||||
private patchConnection$ = new BehaviorSubject(PatchConnection.Initializing)
|
||||
private wsSuccess$ = new BehaviorSubject(false)
|
||||
private polling$ = new BehaviorSubject(false)
|
||||
private patchDb: PatchDB<DataModel>
|
||||
private patchSub: Subscription
|
||||
data: DataModel
|
||||
private subs: Subscription[] = []
|
||||
private sources$: BehaviorSubject<Source<DataModel>[]> = new BehaviorSubject([
|
||||
this.wsSource,
|
||||
])
|
||||
|
||||
getData () { return this.patchDb.store.cache.data }
|
||||
data: DataModel
|
||||
errors = 0
|
||||
|
||||
getData () {
|
||||
return this.patchDb.store.cache.data
|
||||
}
|
||||
|
||||
constructor (
|
||||
@Inject(PATCH_SOURCE) private readonly source: Source<DataModel>,
|
||||
@Inject(PATCH_SOURCE) private readonly wsSource: Source<DataModel>,
|
||||
@Inject(PATCH_SOURCE) private readonly pollSource: Source<DataModel>,
|
||||
@Inject(PATCH_HTTP) private readonly http: ApiService,
|
||||
@Inject(BOOTSTRAPPER) private readonly bootstrapper: Bootstrapper<DataModel>,
|
||||
@Inject(BOOTSTRAPPER)
|
||||
private readonly bootstrapper: Bootstrapper<DataModel>,
|
||||
@Inject(AUTH) private readonly auth: AuthService,
|
||||
@Inject(STORAGE) private readonly storage: Storage,
|
||||
) { }
|
||||
|
||||
async init (): Promise<void> {
|
||||
const cache = await this.bootstrapper.init()
|
||||
this.patchDb = new PatchDB([this.source, this.http], this.http, cache)
|
||||
this.sources$.next([this.wsSource, this.http])
|
||||
|
||||
this.patchDb = new PatchDB(this.sources$, this.http, cache)
|
||||
|
||||
this.patchConnection$.next(PatchConnection.Initializing)
|
||||
this.data = this.patchDb.store.cache.data
|
||||
}
|
||||
|
||||
start (): void {
|
||||
console.log(this.patchSub ? 'restarting patch-db' : 'starting patch-db')
|
||||
async start (): Promise<void> {
|
||||
await this.init()
|
||||
|
||||
// make sure everything is stopped before initializing
|
||||
if (this.patchSub) {
|
||||
this.patchSub.unsubscribe()
|
||||
this.patchSub = undefined
|
||||
}
|
||||
this.subs.push(
|
||||
// Connection Error
|
||||
this.patchDb.connectionError$
|
||||
.pipe(
|
||||
debounceTime(420),
|
||||
withLatestFrom(this.polling$),
|
||||
mergeMap(async ([e, polling]) => {
|
||||
if (polling) {
|
||||
console.log('patchDB: POLLING FAILED', e)
|
||||
this.patchConnection$.next(PatchConnection.Disconnected)
|
||||
await pauseFor(2000)
|
||||
this.sources$.next([this.pollSource, this.http])
|
||||
return
|
||||
}
|
||||
|
||||
this.patchSub = this.patchDb.sync$()
|
||||
.pipe(
|
||||
debounceTime(400),
|
||||
tap(cache => {
|
||||
this.patchConnection$.next(PatchConnection.Connected)
|
||||
this.bootstrapper.update(cache)
|
||||
}),
|
||||
console.log('patchDB: WEBSOCKET FAILED', e)
|
||||
this.polling$.next(true)
|
||||
this.sources$.next([this.pollSource, this.http])
|
||||
}),
|
||||
)
|
||||
.subscribe({
|
||||
complete: () => {
|
||||
console.warn('patchDB: SYNC COMPLETE')
|
||||
},
|
||||
}),
|
||||
|
||||
// RPC ERROR
|
||||
this.patchDb.rpcError$
|
||||
.pipe(
|
||||
tap(({ error }) => {
|
||||
if (error.code === 34) {
|
||||
console.log('patchDB: Unauthorized. Logging out.')
|
||||
this.auth.setUnverified()
|
||||
}
|
||||
}),
|
||||
)
|
||||
.subscribe({
|
||||
complete: () => {
|
||||
console.warn('patchDB: SYNC COMPLETE')
|
||||
},
|
||||
}),
|
||||
|
||||
// GOOD CONNECTION
|
||||
this.patchDb.cache$
|
||||
.pipe(
|
||||
debounceTime(420),
|
||||
withLatestFrom(this.patchConnection$, this.wsSuccess$, this.polling$),
|
||||
tap(async ([cache, connection, wsSuccess, polling]) => {
|
||||
this.bootstrapper.update(cache)
|
||||
|
||||
if (connection === PatchConnection.Initializing) {
|
||||
console.log(
|
||||
polling
|
||||
? 'patchDB: POLL CONNECTED'
|
||||
: 'patchDB: WEBSOCKET CONNECTED',
|
||||
)
|
||||
this.patchConnection$.next(PatchConnection.Connected)
|
||||
if (!wsSuccess && !polling) {
|
||||
console.log('patchDB: WEBSOCKET SUCCESS')
|
||||
this.storage.set(this.WS_SUCCESS, 'true')
|
||||
this.wsSuccess$.next(true)
|
||||
}
|
||||
} else if (
|
||||
connection === PatchConnection.Disconnected &&
|
||||
wsSuccess
|
||||
) {
|
||||
console.log('patchDB: SWITCHING BACK TO WEBSOCKETS')
|
||||
this.patchConnection$.next(PatchConnection.Initializing)
|
||||
this.polling$.next(false)
|
||||
this.sources$.next([this.wsSource, this.http])
|
||||
}
|
||||
}),
|
||||
)
|
||||
.subscribe({
|
||||
complete: () => {
|
||||
console.warn('patchDB: SYNC COMPLETE')
|
||||
},
|
||||
}),
|
||||
)
|
||||
.subscribe({
|
||||
error: async e => {
|
||||
console.error('patch-db SYNC ERROR', e)
|
||||
this.patchConnection$.next(PatchConnection.Disconnected)
|
||||
if (e.code === 34) {
|
||||
this.auth.setUnverified()
|
||||
} else {
|
||||
await pauseFor(4000)
|
||||
this.start()
|
||||
}
|
||||
},
|
||||
complete: () => {
|
||||
console.warn('patch-db SYNC COMPLETE')
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
stop (): void {
|
||||
console.log('stopping patch-db')
|
||||
this.patchConnection$.next(PatchConnection.Initializing)
|
||||
if (this.patchSub) {
|
||||
this.patchSub.unsubscribe()
|
||||
this.patchSub = undefined
|
||||
if (this.patchDb) {
|
||||
console.log('patchDB: STOPPING')
|
||||
this.patchConnection$.next(PatchConnection.Initializing)
|
||||
this.patchDb.store.reset()
|
||||
}
|
||||
}
|
||||
|
||||
connected$ (): Observable<boolean> {
|
||||
return this.patchConnection$
|
||||
.pipe(
|
||||
map(status => status === PatchConnection.Connected),
|
||||
)
|
||||
this.subs.forEach((x) => x.unsubscribe())
|
||||
this.subs = []
|
||||
}
|
||||
|
||||
watchPatchConnection$ (): Observable<PatchConnection> {
|
||||
return this.patchConnection$.asObservable()
|
||||
}
|
||||
|
||||
watch$: Store<DataModel>['watch$'] = (...args: (string | number)[]): Observable<DataModel> => {
|
||||
console.log('WATCHING', ...args)
|
||||
return this.patchDb.store.watch$(...(args as []))
|
||||
.pipe(
|
||||
tap(data => console.log('NEW VALUE', data, ...args)),
|
||||
catchError(e => {
|
||||
console.error('Error watching patch-db', e)
|
||||
watch$: Store<DataModel>['watch$'] = (
|
||||
...args: (string | number)[],
|
||||
): Observable<DataModel> => {
|
||||
const argsString = '/' + args.join('/')
|
||||
console.log('patchDB: WATCHING ', argsString)
|
||||
return this.patchDb.store.watch$(...(args as [])).pipe(
|
||||
tap((data) => console.log('patchDB: NEW VALUE', argsString, data)),
|
||||
catchError((e) => {
|
||||
console.error('patchDB: WATCH ERROR', e)
|
||||
return of(e.message)
|
||||
}),
|
||||
finalize(() => console.log('UNSUBSCRIBING', ...args)),
|
||||
finalize(() => console.log('patchDB: UNSUBSCRIBING', argsString)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user