// Copyright 2017-2020 @polkadot/react-api authors & contributors // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. // TODO: Lots of duplicated code between this and withObservable, surely there is a better way of doing this? import { CallState } from '../types'; import { HOC, Options, DefaultProps, RenderFn } from './types'; import React from 'react'; import { Observable, of } from 'rxjs'; import { catchError, map } from 'rxjs/operators'; import echoTransform from '../transform/echo'; import { intervalObservable, isEqual, triggerChange } from '../util'; interface State extends CallState { subscriptions: { unsubscribe: () => void }[]; } export default function withObservable (observable: Observable

, { callOnResult, propName = 'value', transform = echoTransform }: Options = {}): HOC { return (Inner: React.ComponentType, defaultProps: DefaultProps = {}, render?: RenderFn): React.ComponentType => { return class WithObservable extends React.Component { private isActive = true; public state: State = { callResult: undefined, callUpdated: false, callUpdatedAt: 0, subscriptions: [] }; public componentDidMount (): void { this.setState({ subscriptions: [ observable .pipe( map(transform), catchError(() => of(undefined)) ) .subscribe((value: any) => this.triggerUpdate(this.props, value)), intervalObservable(this) ] }); } public componentWillUnmount (): void { this.isActive = false; this.state.subscriptions.forEach((subscription): void => subscription.unsubscribe() ); } private triggerUpdate = (props: P, callResult?: T): void => { try { if (!this.isActive || isEqual(callResult, this.state.callResult)) { return; } triggerChange(callResult, callOnResult, (props as Options).callOnResult || defaultProps.callOnResult); this.setState({ callResult, callUpdated: true, callUpdatedAt: Date.now() }); } catch (error) { console.error(this.props, error); } } public render (): React.ReactNode { const { children } = this.props; // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment const { callResult, callUpdated, callUpdatedAt } = this.state; const _props = { ...defaultProps, ...this.props, callUpdated, callUpdatedAt, [propName]: callResult }; return ( {render && render(callResult)}{children} ); } }; }; }