mirror of
https://github.com/dergigi/boris.git
synced 2025-12-18 23:24:22 +01:00
feat: initialize markr nostr bookmark client
- Add project structure with TypeScript, React, and Vite - Implement nostr authentication using browser extension (NIP-07) - Add NIP-51 compliant bookmark fetching and display - Create minimal UI with login and bookmark components - Integrate applesauce-core and applesauce-react libraries - Add responsive styling with dark/light mode support - Include comprehensive README with setup instructions This is a minimal MVP for a nostr bookmark client that allows users to view their bookmarks according to NIP-51 specification.
This commit is contained in:
118
node_modules/observable-hooks/src/helpers.ts
generated
vendored
Normal file
118
node_modules/observable-hooks/src/helpers.ts
generated
vendored
Normal file
@@ -0,0 +1,118 @@
|
||||
/**
|
||||
* Useful utilities
|
||||
*/
|
||||
import {
|
||||
MutableRefObject,
|
||||
useEffect,
|
||||
useLayoutEffect,
|
||||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import { Observable, Subject } from "rxjs";
|
||||
import { map } from "rxjs/operators";
|
||||
|
||||
/**
|
||||
* Returns the first argument it receives.
|
||||
*/
|
||||
export const identity = <T>(value: T): T => value;
|
||||
|
||||
/**
|
||||
* Maps an Observable of Arraylike to an Observable
|
||||
* of the first item.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```typescript
|
||||
* const text$ = useObservable(pluckFirst, [props.text])
|
||||
* ```
|
||||
*
|
||||
* @param inputs$ An Observable of array-like.
|
||||
*
|
||||
*/
|
||||
export const pluckFirst = <TArr extends ArrayLike<any>>(
|
||||
inputs$: Observable<TArr>
|
||||
): Observable<TArr[0]> => map<TArr, 0>(input => input[0])(inputs$);
|
||||
|
||||
/**
|
||||
* Maps an Observable of DOM events to an Observable
|
||||
* of the currentTarget value.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```typescript
|
||||
* const [onChange, textChange$] = useObservableCallback<
|
||||
* string,
|
||||
* React.FormEvent<HTMLInputElement>
|
||||
* >(pluckCurrentTargetValue)
|
||||
* ```
|
||||
*
|
||||
*/
|
||||
export const pluckCurrentTargetValue = <
|
||||
TEvent extends { currentTarget: { value: any } }
|
||||
>(
|
||||
event$: Observable<TEvent>
|
||||
): Observable<TEvent["currentTarget"]["value"]> =>
|
||||
map<TEvent, TEvent["currentTarget"]["value"]>(
|
||||
event => event.currentTarget.value
|
||||
)(event$);
|
||||
|
||||
/**
|
||||
* Maps an Observable of DOM events to an Observable
|
||||
* of the currentTarget checked.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```typescript
|
||||
* const [onChange, checked$] = useObservableCallback<
|
||||
* boolean,
|
||||
* React.FormEvent<HTMLInputElement>
|
||||
* >(pluckCurrentTargetChecked)
|
||||
* ```
|
||||
*
|
||||
*/
|
||||
export const pluckCurrentTargetChecked = <
|
||||
TEvent extends { currentTarget: { checked: any } }
|
||||
>(
|
||||
event$: Observable<TEvent>
|
||||
): Observable<TEvent["currentTarget"]["checked"]> =>
|
||||
map<TEvent, TEvent["currentTarget"]["checked"]>(
|
||||
event => event.currentTarget.checked
|
||||
)(event$);
|
||||
|
||||
/**
|
||||
* Return an empty Subject
|
||||
* @ignore
|
||||
*/
|
||||
export const getEmptySubject = <T>() => new Subject<T>();
|
||||
|
||||
/**
|
||||
* One-time ref init.
|
||||
* @param init A function that returns a value. Will be called only once.
|
||||
* @returns A ref object with the returned value.
|
||||
*/
|
||||
export const useRefFn = <T>(init: () => T) => {
|
||||
const firstRef = useRef(true);
|
||||
const ref = useRef<T | null>(null);
|
||||
if (firstRef.current) {
|
||||
firstRef.current = false;
|
||||
ref.current = init();
|
||||
}
|
||||
return ref as MutableRefObject<T>;
|
||||
};
|
||||
|
||||
const increment = (n: number): number => (n + 1) | 0;
|
||||
|
||||
/**
|
||||
* Force re-renders Component.
|
||||
*/
|
||||
export const useForceUpdate = (): (() => void) => {
|
||||
const updateState = useState(0)[1];
|
||||
return useRef(() => updateState(increment)).current;
|
||||
};
|
||||
|
||||
/**
|
||||
* Prevent React warning when using useLayoutEffect on server.
|
||||
*/
|
||||
export const useIsomorphicLayoutEffect = /* @__PURE__ */ (() =>
|
||||
/* istanbul ignore next */
|
||||
typeof window === "undefined" ? useEffect : useLayoutEffect)();
|
||||
22
node_modules/observable-hooks/src/index.ts
generated
vendored
Normal file
22
node_modules/observable-hooks/src/index.ts
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
export { useObservable } from "./use-observable";
|
||||
export { useLayoutObservable } from "./use-layout-observable";
|
||||
export { useObservableCallback } from "./use-observable-callback";
|
||||
export { useSubscription } from "./use-subscription";
|
||||
export { useLayoutSubscription } from "./use-layout-subscription";
|
||||
export { useRenderThrow } from "./use-render-throw";
|
||||
export { useObservableState } from "./use-observable-state";
|
||||
export { useLayoutObservableState } from "./use-layout-observable-state";
|
||||
export { useObservableEagerState } from "./use-observable-eager-state";
|
||||
export { useObservableGetState } from "./use-observable-get-state";
|
||||
export { useObservablePickState } from "./use-observable-pick-state";
|
||||
export { useObservableSuspense } from "./use-observable-suspense";
|
||||
export { useObservableRef } from "./use-observable-ref";
|
||||
export { ObservableResource } from "./observable-resource";
|
||||
export {
|
||||
useRefFn,
|
||||
useForceUpdate,
|
||||
identity,
|
||||
pluckFirst,
|
||||
pluckCurrentTargetValue,
|
||||
pluckCurrentTargetChecked,
|
||||
} from "./helpers";
|
||||
42
node_modules/observable-hooks/src/internal/use-observable-internal.ts
generated
vendored
Normal file
42
node_modules/observable-hooks/src/internal/use-observable-internal.ts
generated
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
import { Observable, BehaviorSubject } from "rxjs";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
|
||||
/**
|
||||
* @template TOutput Output value within Observable.
|
||||
* @template TInputs A readonly tuple of all dependencies.
|
||||
*
|
||||
* @param useCustomEffect useEffect or useLayoutEffect
|
||||
* @param init A pure function that, when applied to an Observable,
|
||||
* returns an Observable.
|
||||
* @param inputs An dependency array with fixed length. When one of the dependencies
|
||||
* changes the Observable in `init` will emit an array of all the dependencies.
|
||||
*/
|
||||
export function useObservableInternal<TOutput, TInputs extends Readonly<any[]>>(
|
||||
useCustomEffect: typeof useEffect,
|
||||
init:
|
||||
| (() => Observable<TOutput>)
|
||||
| ((inputs$: Observable<[...TInputs]>) => Observable<TOutput>),
|
||||
inputs?: [...TInputs]
|
||||
): Observable<TOutput> {
|
||||
// Even though hooks are under conditional block
|
||||
// it is for a completely different use case
|
||||
// which unlikely coexists with the other one.
|
||||
// A warning is also added to the docs.
|
||||
if (!inputs) {
|
||||
return useState(init as () => Observable<TOutput>)[0];
|
||||
}
|
||||
|
||||
const [inputs$] = useState(() => new BehaviorSubject(inputs));
|
||||
const [source$] = useState(() => init(inputs$));
|
||||
|
||||
const firstEffectRef = useRef(true);
|
||||
useCustomEffect(() => {
|
||||
if (firstEffectRef.current) {
|
||||
firstEffectRef.current = false;
|
||||
return;
|
||||
}
|
||||
inputs$.next(inputs);
|
||||
}, inputs);
|
||||
|
||||
return source$;
|
||||
}
|
||||
55
node_modules/observable-hooks/src/internal/use-observable-state-internal.ts
generated
vendored
Normal file
55
node_modules/observable-hooks/src/internal/use-observable-state-internal.ts
generated
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
import { Observable, isObservable, Subject, BehaviorSubject } from "rxjs";
|
||||
import { useState, useRef, useDebugValue } from "react";
|
||||
import type { useSubscription as useSubscriptionType } from "../use-subscription";
|
||||
import { getEmptySubject } from "../helpers";
|
||||
|
||||
export function useObservableStateInternal<TState, TInput = TState>(
|
||||
useSubscription: typeof useSubscriptionType,
|
||||
state$OrInit:
|
||||
| Observable<TState>
|
||||
| ((
|
||||
input$: Observable<TInput>,
|
||||
initialState?: TState
|
||||
) => Observable<TState>),
|
||||
initialState?: TState | (() => TState)
|
||||
): TState | undefined | [TState | undefined, (input: TInput) => void] {
|
||||
// Even though hooks are under conditional block
|
||||
// it is for a completely different use case
|
||||
// which unlikely coexists with the other one.
|
||||
// A warning is also added to the docs.
|
||||
if (isObservable(state$OrInit)) {
|
||||
const state$ = state$OrInit;
|
||||
const [state, setState] = useState<TState | undefined>(() => {
|
||||
if (
|
||||
state$ instanceof BehaviorSubject ||
|
||||
(state$ as BehaviorSubject<TState>).value !== undefined
|
||||
) {
|
||||
return (state$ as BehaviorSubject<TState>).value;
|
||||
}
|
||||
if (typeof initialState === "function") {
|
||||
return (initialState as () => TState)();
|
||||
}
|
||||
return initialState;
|
||||
});
|
||||
|
||||
useSubscription(state$, setState);
|
||||
|
||||
useDebugValue(state);
|
||||
|
||||
return state;
|
||||
} else {
|
||||
const init = state$OrInit;
|
||||
const [state, setState] = useState<TState | undefined>(initialState);
|
||||
|
||||
const [input$] = useState<Subject<TInput>>(getEmptySubject);
|
||||
|
||||
const [state$] = useState(() => init(input$, state));
|
||||
const callback = useRef((state: TInput) => input$.next(state)).current;
|
||||
|
||||
useSubscription(state$, setState);
|
||||
|
||||
useDebugValue(state);
|
||||
|
||||
return [state, callback];
|
||||
}
|
||||
}
|
||||
89
node_modules/observable-hooks/src/internal/use-subscription-internal.ts
generated
vendored
Normal file
89
node_modules/observable-hooks/src/internal/use-subscription-internal.ts
generated
vendored
Normal file
@@ -0,0 +1,89 @@
|
||||
import { MutableRefObject, useEffect, useRef } from "react";
|
||||
import { Observable, PartialObserver, Subscription } from "rxjs";
|
||||
import { useIsomorphicLayoutEffect } from "../helpers";
|
||||
|
||||
interface Observer<T> {
|
||||
next?: (value: T) => void;
|
||||
error?: (err: any) => void;
|
||||
complete?: () => void;
|
||||
}
|
||||
|
||||
type Args<TInput> = [
|
||||
Observable<TInput>, // inputs$
|
||||
PartialObserver<TInput> | ((value: TInput) => void) | null | undefined,
|
||||
((error: any) => void) | null | undefined,
|
||||
(() => void) | null | undefined
|
||||
];
|
||||
|
||||
const toObserver = <T>(args: Args<T>): Observer<T> =>
|
||||
(args[1] as PartialObserver<T>)?.next
|
||||
? (args[1] as Observer<T>)
|
||||
: {
|
||||
next: args[1] as Observer<T>["next"],
|
||||
error: args[2] as Observer<T>["error"],
|
||||
complete: args[3] as Observer<T>["complete"],
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @template TInput Input value within Observable.
|
||||
*
|
||||
* @param useCustomEffect useEffect or useLayoutEffect
|
||||
* @param args collected arguments
|
||||
*/
|
||||
export function useSubscriptionInternal<TInput>(
|
||||
useCustomEffect: typeof useEffect,
|
||||
args: Args<TInput>
|
||||
): MutableRefObject<Subscription | undefined> {
|
||||
const argsRef = useRef(args);
|
||||
const observerRef = useRef<Observer<TInput>>();
|
||||
const subscriptionRef = useRef<Subscription>();
|
||||
|
||||
// Update the latest observable and callbacks
|
||||
// synchronously after render being committed
|
||||
useIsomorphicLayoutEffect(() => {
|
||||
argsRef.current = args;
|
||||
observerRef.current = toObserver(args);
|
||||
});
|
||||
|
||||
useCustomEffect(() => {
|
||||
// keep in closure for checking staleness
|
||||
const input$ = argsRef.current[0];
|
||||
|
||||
/* istanbul ignore if: Just in case the layoutEffect order is agnostic */
|
||||
if (!observerRef.current) {
|
||||
observerRef.current = toObserver(argsRef.current);
|
||||
}
|
||||
|
||||
const subscription = input$.subscribe({
|
||||
next: value => {
|
||||
if (input$ === argsRef.current[0]) {
|
||||
observerRef.current!.next?.(value);
|
||||
}
|
||||
// else: stale observable
|
||||
},
|
||||
error: error => {
|
||||
if (input$ === argsRef.current[0]) {
|
||||
observerRef.current!.error
|
||||
? observerRef.current!.error(error)
|
||||
: console.error(error);
|
||||
}
|
||||
// else: stale observable
|
||||
},
|
||||
complete: () => {
|
||||
if (input$ === argsRef.current[0]) {
|
||||
observerRef.current!.complete?.();
|
||||
}
|
||||
// else: stale observable
|
||||
},
|
||||
});
|
||||
|
||||
subscriptionRef.current = subscription;
|
||||
|
||||
return () => {
|
||||
subscription.unsubscribe();
|
||||
};
|
||||
}, [args[0]]);
|
||||
|
||||
return subscriptionRef;
|
||||
}
|
||||
156
node_modules/observable-hooks/src/observable-resource.ts
generated
vendored
Normal file
156
node_modules/observable-hooks/src/observable-resource.ts
generated
vendored
Normal file
@@ -0,0 +1,156 @@
|
||||
import {
|
||||
BehaviorSubject,
|
||||
Observable,
|
||||
Observer,
|
||||
Subject,
|
||||
Subscription,
|
||||
} from "rxjs";
|
||||
|
||||
interface Handler<T = any> {
|
||||
suspender_: Promise<T>;
|
||||
resolve_: (value?: T) => void;
|
||||
}
|
||||
|
||||
const createHandler = (): Handler => {
|
||||
const handler: Partial<Handler> = {};
|
||||
handler.suspender_ = new Promise(resolve => {
|
||||
handler.resolve_ = resolve;
|
||||
});
|
||||
return handler as Handler;
|
||||
};
|
||||
|
||||
/**
|
||||
* Rewires Observable to Relay-like Suspense resource.
|
||||
*/
|
||||
export class ObservableResource<TInput, TOutput extends TInput = TInput> {
|
||||
/**
|
||||
* Unlike Promise, Observable is a multiple push mechanism.
|
||||
* Only force update when Suspense needs to restart.
|
||||
*/
|
||||
public readonly shouldUpdate$$ = new Subject<true>();
|
||||
|
||||
public get isDestroyed(): boolean {
|
||||
return this._isDestroyed_;
|
||||
}
|
||||
|
||||
public readonly valueRef$$ = new BehaviorSubject<
|
||||
{ current: TOutput } | undefined
|
||||
>(undefined);
|
||||
|
||||
public input$: Observable<TInput>;
|
||||
|
||||
private _handler_: Handler | null = createHandler();
|
||||
|
||||
private _error_: unknown = null;
|
||||
|
||||
private _subscription_: Subscription;
|
||||
|
||||
private _isDestroyed_ = false;
|
||||
|
||||
private readonly _observer_: Observer<TInput>;
|
||||
|
||||
/**
|
||||
* @param input$ An Observable.
|
||||
* @param isSuccess A function that determines if the value emitted from
|
||||
* `input$` is of success state. If false a Suspense is triggered.
|
||||
* Default all true.
|
||||
*/
|
||||
public constructor(
|
||||
input$: Observable<TInput>,
|
||||
isSuccess?: TInput extends TOutput
|
||||
? (value: TInput) => boolean
|
||||
: (value: TInput) => value is TOutput
|
||||
) {
|
||||
this.input$ = input$;
|
||||
|
||||
this._observer_ = {
|
||||
next: (value: TInput): void => {
|
||||
this._error_ = null;
|
||||
if (!isSuccess || isSuccess(value)) {
|
||||
if (this.valueRef$$.value?.current !== value) {
|
||||
this.valueRef$$.next({ current: value as TOutput });
|
||||
}
|
||||
if (this._handler_) {
|
||||
// This will also remove the initial
|
||||
// suspender if sync values are emitted.
|
||||
const { resolve_: resolve } = this._handler_;
|
||||
this._handler_ = null;
|
||||
resolve();
|
||||
}
|
||||
} else if (!this._handler_) {
|
||||
// start a new Suspense
|
||||
this._handler_ = createHandler();
|
||||
this.shouldUpdate$$.next(true);
|
||||
}
|
||||
},
|
||||
error: (error: unknown): void => {
|
||||
this._error_ = error;
|
||||
if (this._handler_) {
|
||||
const { resolve_: resolve } = this._handler_;
|
||||
this._handler_ = null;
|
||||
// Errors thrown from the request is not catch-able by error boundaries.
|
||||
// Here we resolve the suspender and let this.read throw the error.
|
||||
resolve();
|
||||
} else {
|
||||
this.shouldUpdate$$.next(true);
|
||||
}
|
||||
},
|
||||
complete: (): void => {
|
||||
if (this._handler_) {
|
||||
this._error_ = new Error("Suspender ended unexpectedly.");
|
||||
const { resolve_: resolve } = this._handler_;
|
||||
this._handler_ = null;
|
||||
// Errors thrown from the request is not catch-able by error boundaries.
|
||||
// Here we resolve the suspender and let this.read throw the error.
|
||||
resolve();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
this._subscription_ = input$.subscribe(this._observer_);
|
||||
}
|
||||
|
||||
public read(): TOutput {
|
||||
if (this._error_) {
|
||||
throw this._error_;
|
||||
}
|
||||
if (this._handler_) {
|
||||
throw this._handler_.suspender_;
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain
|
||||
return this.valueRef$$.value?.current!;
|
||||
}
|
||||
|
||||
public reload(newInput$?: Observable<TInput>): void {
|
||||
if (this._isDestroyed_) {
|
||||
throw new Error("Cannot reload a destroyed Observable Resource");
|
||||
}
|
||||
|
||||
if (newInput$) {
|
||||
this.input$ = newInput$;
|
||||
}
|
||||
|
||||
this._subscription_.unsubscribe();
|
||||
|
||||
this._error_ = null;
|
||||
|
||||
if (this._handler_) {
|
||||
this._handler_.resolve_();
|
||||
this._handler_ = createHandler();
|
||||
}
|
||||
|
||||
this._subscription_ = this.input$.subscribe(this._observer_);
|
||||
}
|
||||
|
||||
public destroy(): void {
|
||||
this._isDestroyed_ = true;
|
||||
this._subscription_.unsubscribe();
|
||||
this.shouldUpdate$$.complete();
|
||||
if (this._handler_) {
|
||||
this._error_ = new Error("Resource has been destroyed.");
|
||||
const { resolve_: resolve } = this._handler_;
|
||||
this._handler_ = null;
|
||||
resolve();
|
||||
}
|
||||
}
|
||||
}
|
||||
112
node_modules/observable-hooks/src/use-layout-observable-state.ts
generated
vendored
Normal file
112
node_modules/observable-hooks/src/use-layout-observable-state.ts
generated
vendored
Normal file
@@ -0,0 +1,112 @@
|
||||
import { BehaviorSubject, Observable } from "rxjs";
|
||||
import { useLayoutSubscription } from "./use-layout-subscription";
|
||||
import { useObservableStateInternal } from "./internal/use-observable-state-internal";
|
||||
|
||||
/**
|
||||
* Same as [[useObservableState]] except the subscription is established
|
||||
* under `useLayoutEffect`.
|
||||
*
|
||||
* A sugar hook for getting values from an Observable.
|
||||
*
|
||||
* It can be used in two ways:
|
||||
*
|
||||
* 1. Offer an Observable and an optional initial state.
|
||||
* ```js
|
||||
* const output = useLayoutObservableState(input$, initialState)
|
||||
* ```
|
||||
* 2. Offer an epic-like function and an optional initial state.
|
||||
* ```js
|
||||
* const [output, onInput] = useLayoutObservableState(
|
||||
* (input$, initialState) => input$.pipe(...),
|
||||
* initialState
|
||||
* )
|
||||
* ```
|
||||
*
|
||||
* The optional `initialState` is internally passed to `useState(initialState)`.
|
||||
* This means it can be either a state value or a function that returns the state
|
||||
* which is for expensive initialization.
|
||||
*
|
||||
* The `initialState`(or its returned result) is also passed to the `init` function.
|
||||
* This is useful if you want to implement reduer pattern which requires an initial state.
|
||||
*
|
||||
* ⚠ **Note:** These two ways use different hooks, choose either one each time
|
||||
* and do not change to the other one during Component's life cycle.
|
||||
*
|
||||
* ⚠ **Note:** `useLayoutObservableState` will call the epic-like `init` function only once
|
||||
* and always return the same Observable.
|
||||
* It is not safe to access closure directly inside `init`.
|
||||
* Use [[useObservable]] with `withLatestFrom` instead.
|
||||
*
|
||||
* ⚠ **Note:** To make it concurrent mode compatible, the subscription happens
|
||||
* after the render is committed to the screen which means even the Observable emits synchronous values
|
||||
* they will arrive after the first rendering.
|
||||
*
|
||||
* @template TState Output state.
|
||||
*
|
||||
* @param input$ A BehaviorSubject.
|
||||
*/
|
||||
export function useLayoutObservableState<TState>(
|
||||
input$: BehaviorSubject<TState>
|
||||
): TState;
|
||||
/**
|
||||
* @template TState Output state.
|
||||
*
|
||||
* @param input$ An Observable.
|
||||
*/
|
||||
export function useLayoutObservableState<TState>(
|
||||
input$: Observable<TState>
|
||||
): TState | undefined;
|
||||
/**
|
||||
* @template TState Output state.
|
||||
*
|
||||
* @param input$ An Observable.
|
||||
* @param initialState Optional initial state.
|
||||
* Can be the state value or a function that returns the state.
|
||||
*/
|
||||
export function useLayoutObservableState<TState>(
|
||||
input$: Observable<TState>,
|
||||
initialState: TState | (() => TState)
|
||||
): TState;
|
||||
/**
|
||||
* @template TState Output state.
|
||||
* @template TInput Input values.
|
||||
*
|
||||
* @param init A epic-like function that, when applied to an Observable
|
||||
* and the initial state value, returns an Observable.
|
||||
*/
|
||||
export function useLayoutObservableState<TState, TInput = TState>(
|
||||
init: (input$: Observable<TInput>) => Observable<TState>
|
||||
): [TState | undefined, (input: TInput) => void];
|
||||
/**
|
||||
* Different input output types with initial state.
|
||||
*
|
||||
* @template TState Output state.
|
||||
* @template TInput Input values.
|
||||
*
|
||||
* @param init A epic-like function that, when applied to an Observable
|
||||
* and the initial state value, returns an Observable.
|
||||
* @param initialState Optional initial state.
|
||||
* Can be the state value or a function that returns the state.
|
||||
*/
|
||||
export function useLayoutObservableState<TState, TInput = TState>(
|
||||
init: (
|
||||
input$: Observable<TInput>,
|
||||
initialState: TState
|
||||
) => Observable<TState>,
|
||||
initialState: TState | (() => TState)
|
||||
): [TState, (input: TInput) => void];
|
||||
export function useLayoutObservableState<TState, TInput = TState>(
|
||||
state$OrInit:
|
||||
| Observable<TState>
|
||||
| ((
|
||||
input$: Observable<TInput>,
|
||||
initialState?: TState
|
||||
) => Observable<TState>),
|
||||
initialState?: TState | (() => TState)
|
||||
): TState | undefined | [TState | undefined, (input: TInput) => void] {
|
||||
return useObservableStateInternal(
|
||||
useLayoutSubscription,
|
||||
state$OrInit,
|
||||
initialState
|
||||
);
|
||||
}
|
||||
54
node_modules/observable-hooks/src/use-layout-observable.ts
generated
vendored
Normal file
54
node_modules/observable-hooks/src/use-layout-observable.ts
generated
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
import { Observable } from "rxjs";
|
||||
import { useIsomorphicLayoutEffect } from "./helpers";
|
||||
import { useObservableInternal } from "./internal/use-observable-internal";
|
||||
|
||||
/**
|
||||
* Same as [[useObservable]] excepts using `useLayoutEffect`.
|
||||
*
|
||||
* Accepts a function that returns an Observable.
|
||||
* Optionally accepts an array of dependencies which
|
||||
* will be turned into Observable and be passed to the
|
||||
* `init` function.
|
||||
*
|
||||
* React functional components are called many times during their lifecycle.
|
||||
* Create or transform Observables in `init` function so that the operations
|
||||
* won't be repeatedly performed.
|
||||
*
|
||||
* ⚠ **Note:** `useLayoutObservable` will call `init` once and always return
|
||||
* the same Observable. It is not safe to access closure (except other Observables)
|
||||
* directly inside `init`.
|
||||
* You should use ref or pass them as dependencies through the second argument.
|
||||
*
|
||||
* ⚠ **Note:** Due to rules of hooks you can either offer or omit the
|
||||
* dependencies array but do not change to one another during Component's life cycle.
|
||||
* The length of the dependencies array must also be fixed.
|
||||
*
|
||||
* @template TOutput Output value in Observable
|
||||
*
|
||||
* @param init A pure function that, when applied to an Observable,
|
||||
* returns an Observable.
|
||||
*/
|
||||
export function useLayoutObservable<TOutput>(
|
||||
init: () => Observable<TOutput>
|
||||
): Observable<TOutput>;
|
||||
/**
|
||||
* @template TOutput Output value within Observable.
|
||||
* @template TInputs A readonly tuple of all dependencies.
|
||||
*
|
||||
* @param init A pure function that, when applied to an Observable,
|
||||
* returns an Observable.
|
||||
* @param inputs An dependency array with fixed length. When one of the dependencies
|
||||
* changes the Observable in `init` will emit an array of all the dependencies.
|
||||
*/
|
||||
export function useLayoutObservable<TOutput, TInputs extends Readonly<any[]>>(
|
||||
init: (inputs$: Observable<[...TInputs]>) => Observable<TOutput>,
|
||||
inputs: [...TInputs]
|
||||
): Observable<TOutput>;
|
||||
export function useLayoutObservable<TOutput, TInputs extends Readonly<any[]>>(
|
||||
init:
|
||||
| (() => Observable<TOutput>)
|
||||
| ((inputs$: Observable<[...TInputs]>) => Observable<TOutput>),
|
||||
inputs?: [...TInputs]
|
||||
): Observable<TOutput> {
|
||||
return useObservableInternal(useIsomorphicLayoutEffect, init, inputs);
|
||||
}
|
||||
55
node_modules/observable-hooks/src/use-layout-subscription.ts
generated
vendored
Normal file
55
node_modules/observable-hooks/src/use-layout-subscription.ts
generated
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
import { MutableRefObject } from "react";
|
||||
import { Observable, PartialObserver, Subscription } from "rxjs";
|
||||
import { useIsomorphicLayoutEffect } from "./helpers";
|
||||
import { useSubscriptionInternal } from "./internal/use-subscription-internal";
|
||||
|
||||
/**
|
||||
* Same as [[useSubscription]] except the subscription is established
|
||||
* under `useLayoutEffect`.
|
||||
*
|
||||
* Useful when values are needed before DOM paint.
|
||||
*
|
||||
* Use it scarcely as it runs synchronously before browser paint.
|
||||
* Too many synchronous emissions from the observable could
|
||||
* stretch the commit phase.
|
||||
*
|
||||
* @template TInput Input value within Observable.
|
||||
*
|
||||
* @param input$ Input Observable.
|
||||
* @param observer Observer
|
||||
*/
|
||||
export function useLayoutSubscription<TInput>(
|
||||
input$: Observable<TInput>,
|
||||
observer?: PartialObserver<TInput>
|
||||
): MutableRefObject<Subscription | undefined>;
|
||||
/**
|
||||
* @template TInput Input value within Observable.
|
||||
*
|
||||
* @param input$ Input Observable.
|
||||
* @param next Notify when a new value is emitted.
|
||||
* @param error Notify when a new error is thrown.
|
||||
* @param complete Notify when the Observable is complete.
|
||||
*/
|
||||
export function useLayoutSubscription<TInput>(
|
||||
input$: Observable<TInput>,
|
||||
next?: ((value: TInput) => void) | null | undefined,
|
||||
error?: ((error: any) => void) | null | undefined,
|
||||
complete?: (() => void) | null | undefined
|
||||
): MutableRefObject<Subscription | undefined>;
|
||||
export function useLayoutSubscription<TInput>(
|
||||
input$: Observable<TInput>,
|
||||
observerOrNext$?:
|
||||
| PartialObserver<TInput>
|
||||
| ((value: TInput) => void)
|
||||
| null
|
||||
| undefined,
|
||||
error?: ((error: any) => void) | null | undefined,
|
||||
complete?: (() => void) | null | undefined
|
||||
): MutableRefObject<Subscription | undefined> {
|
||||
return useSubscriptionInternal(useIsomorphicLayoutEffect, [
|
||||
input$,
|
||||
observerOrNext$,
|
||||
error,
|
||||
complete,
|
||||
]);
|
||||
}
|
||||
80
node_modules/observable-hooks/src/use-observable-callback.ts
generated
vendored
Normal file
80
node_modules/observable-hooks/src/use-observable-callback.ts
generated
vendored
Normal file
@@ -0,0 +1,80 @@
|
||||
import { Observable, Subject } from "rxjs";
|
||||
import { useRef, useState } from "react";
|
||||
import { getEmptySubject, identity } from "./helpers";
|
||||
|
||||
/**
|
||||
* Returns a callback function and an events Observable.
|
||||
*
|
||||
* When the callback is called, the Observable will
|
||||
* emit the first argument of the callback.
|
||||
*
|
||||
* @template TEvent Output value of Observable.
|
||||
*/
|
||||
export function useObservableCallback<TEvent = void>(): [
|
||||
(event: TEvent) => void,
|
||||
Observable<TEvent>
|
||||
];
|
||||
/**
|
||||
* Returns a callback function and an events Observable.
|
||||
*
|
||||
* When the callback is called, the Observable will
|
||||
* emit the first argument of the callback.
|
||||
*
|
||||
* ⚠ **Note:** `useObservableCallback` will call `init` once and always return
|
||||
* the same Observable. It is not safe to access closure (except other Observables)
|
||||
* directly inside `init`. Use ref or [[useObservable]] with `withLatestFrom` instead.
|
||||
*
|
||||
* @template TOutput Output value within Observable.
|
||||
* @template TInput Selected values.
|
||||
*
|
||||
* @param init A pure function that, when applied to an Observable,
|
||||
* returns an Observable.
|
||||
*/
|
||||
export function useObservableCallback<TOutput, TInput = TOutput>(
|
||||
init: (events$: Observable<TInput>) => Observable<TOutput>
|
||||
): [(event: TInput) => void, Observable<TOutput>];
|
||||
/**
|
||||
* Returns a callback function and an events Observable.
|
||||
*
|
||||
* When the callback is called, the Observable will
|
||||
* emit the first argument of the callback.
|
||||
*
|
||||
* (From v2.1.0) Optionally accepts a selector function that transforms
|
||||
* a list of event arguments into a single value.
|
||||
*
|
||||
* ⚠ **Note:** `useObservableCallback` will call `init` once and always return
|
||||
* the same Observable. It is not safe to access closure (except other Observables)
|
||||
* directly inside `init`. Use ref or [[useObservable]] with `withLatestFrom` instead.
|
||||
*
|
||||
* @template TOutput Output value within Observable.
|
||||
* @template TInput Selected values.
|
||||
* @template TParams A tuple of event callback parameters.
|
||||
*
|
||||
* @param init A pure function that, when applied to an Observable,
|
||||
* returns an Observable.
|
||||
* @param selector A function that transforms an array of event arguments
|
||||
* into a single value.
|
||||
*/
|
||||
export function useObservableCallback<
|
||||
TOutput = undefined,
|
||||
TInput = TOutput,
|
||||
TParams extends Readonly<any[]> = [TInput]
|
||||
>(
|
||||
init: (events$: Observable<TInput>) => Observable<TOutput>,
|
||||
selector: (args: TParams) => TInput
|
||||
): [(...args: TParams) => void, Observable<TOutput>];
|
||||
export function useObservableCallback<
|
||||
TOutput,
|
||||
TInput = TOutput,
|
||||
TParams extends Readonly<any[]> = [TInput]
|
||||
>(
|
||||
init = identity as (events$: Observable<TInput>) => Observable<TOutput>,
|
||||
selector?: (args: TParams) => TInput
|
||||
): [(...args: TParams) => void, Observable<TOutput>] {
|
||||
const [events$] = useState<Subject<TInput>>(getEmptySubject);
|
||||
const [outputs$] = useState(() => init(events$));
|
||||
const callback = useRef((...args: TParams) => {
|
||||
events$.next(selector ? selector(args) : args[0]);
|
||||
}).current;
|
||||
return [callback, outputs$];
|
||||
}
|
||||
111
node_modules/observable-hooks/src/use-observable-eager-state.ts
generated
vendored
Normal file
111
node_modules/observable-hooks/src/use-observable-eager-state.ts
generated
vendored
Normal file
@@ -0,0 +1,111 @@
|
||||
import { useDebugValue, useEffect, useRef, useState } from "react";
|
||||
import { Observable } from "rxjs";
|
||||
import { useForceUpdate, useIsomorphicLayoutEffect } from "./helpers";
|
||||
|
||||
/**
|
||||
* Optimized for safely getting synchronous values from hot or pure observables
|
||||
* without triggering extra initial re-rendering.
|
||||
*
|
||||
* ⚠ If the observable is cold and with side effects
|
||||
* they will be performed at least twice!
|
||||
*
|
||||
* By default this hook will subscribe to the observable at least twice.
|
||||
* The first time is for getting synchronous value to prevent extra initial re-rendering.
|
||||
* In concurrent mode this may happen more than one time.
|
||||
*
|
||||
* @template TState State.
|
||||
*
|
||||
* @param state$ An observable of state value.
|
||||
*/
|
||||
export function useObservableEagerState<TState>(
|
||||
state$: Observable<TState>
|
||||
): TState {
|
||||
const forceUpdate = useForceUpdate();
|
||||
|
||||
const state$Ref = useRef(state$);
|
||||
const errorRef = useRef<Error | null>();
|
||||
const isAsyncEmissionRef = useRef(false);
|
||||
|
||||
const didSyncEmit = useRef(false);
|
||||
|
||||
const [state, setState] = useState<TState>(() => {
|
||||
let state: TState;
|
||||
state$
|
||||
.subscribe({
|
||||
next: value => {
|
||||
didSyncEmit.current = true;
|
||||
state = value;
|
||||
},
|
||||
error: error => {
|
||||
errorRef.current = error;
|
||||
},
|
||||
})
|
||||
.unsubscribe();
|
||||
return state!;
|
||||
});
|
||||
|
||||
// update the latest observable
|
||||
// synchronously after render being committed
|
||||
useIsomorphicLayoutEffect(() => {
|
||||
state$Ref.current = state$;
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
errorRef.current = null;
|
||||
|
||||
// keep in closure for checking staleness
|
||||
const input$ = state$Ref.current;
|
||||
|
||||
let secondInitialValue = state;
|
||||
|
||||
const subscription = input$.subscribe({
|
||||
next: value => {
|
||||
if (input$ !== state$Ref.current) {
|
||||
// stale observable
|
||||
return;
|
||||
}
|
||||
if (isAsyncEmissionRef.current) {
|
||||
// ignore synchronous value
|
||||
// prevent initial re-rendering
|
||||
setState(() => value);
|
||||
} else {
|
||||
secondInitialValue = value;
|
||||
}
|
||||
},
|
||||
error: error => {
|
||||
if (input$ !== state$Ref.current) {
|
||||
// stale observable
|
||||
return;
|
||||
}
|
||||
errorRef.current = error;
|
||||
forceUpdate();
|
||||
},
|
||||
});
|
||||
|
||||
if (!isAsyncEmissionRef.current) {
|
||||
// fix #86 where sync emission may happen before useEffect
|
||||
if (secondInitialValue !== state) {
|
||||
setState(() => secondInitialValue);
|
||||
}
|
||||
}
|
||||
|
||||
isAsyncEmissionRef.current = true;
|
||||
|
||||
return () => {
|
||||
subscription.unsubscribe();
|
||||
};
|
||||
}, [state$]);
|
||||
|
||||
if (errorRef.current) {
|
||||
// Let error boundary catch the error
|
||||
throw errorRef.current;
|
||||
}
|
||||
|
||||
if (didSyncEmit.current) {
|
||||
useDebugValue(state);
|
||||
|
||||
return state;
|
||||
} else {
|
||||
throw new Error("Observable did not synchronously emit a value.");
|
||||
}
|
||||
}
|
||||
366
node_modules/observable-hooks/src/use-observable-get-state.ts
generated
vendored
Normal file
366
node_modules/observable-hooks/src/use-observable-get-state.ts
generated
vendored
Normal file
@@ -0,0 +1,366 @@
|
||||
import { useDebugValue } from "react";
|
||||
import { Observable } from "rxjs";
|
||||
import { map } from "rxjs/operators";
|
||||
import { useObservableState } from "./use-observable-state";
|
||||
import { useObservable } from "./use-observable";
|
||||
|
||||
/**
|
||||
* Gets the value at path of state. Similar to lodash `get`.
|
||||
* Only changes of the resulted value will trigger a rerendering.
|
||||
* Errors are thrown on unreachable path.
|
||||
*
|
||||
* @param state$ Output state.
|
||||
*/
|
||||
export function useObservableGetState<TState>(
|
||||
state$: Observable<TState>,
|
||||
initialState: TState | (() => TState)
|
||||
): TState;
|
||||
export function useObservableGetState<
|
||||
TState,
|
||||
TInitial extends null | undefined | void
|
||||
>(state$: Observable<TState>, initialState: TInitial): TState | TInitial;
|
||||
export function useObservableGetState<TState, A extends keyof TState>(
|
||||
state$: Observable<TState>,
|
||||
initialState: TState[A] | (() => TState[A]),
|
||||
pA: A
|
||||
): TState[A];
|
||||
export function useObservableGetState<
|
||||
TState,
|
||||
TInitial extends null | undefined | void,
|
||||
A extends keyof TState
|
||||
>(
|
||||
state$: Observable<TState>,
|
||||
initialState: TInitial,
|
||||
pA: A
|
||||
): TState[A] | TInitial;
|
||||
export function useObservableGetState<
|
||||
TState,
|
||||
A extends keyof TState,
|
||||
B extends keyof TState[A]
|
||||
>(
|
||||
state$: Observable<TState>,
|
||||
initialState: TState[A][B] | (() => TState[A][B]),
|
||||
pA: A,
|
||||
pB: B
|
||||
): TState[A][B];
|
||||
export function useObservableGetState<
|
||||
TState,
|
||||
TInitial extends null | undefined | void,
|
||||
A extends keyof TState,
|
||||
B extends keyof TState[A]
|
||||
>(
|
||||
state$: Observable<TState>,
|
||||
initialState: TInitial,
|
||||
pA: A,
|
||||
pB: B
|
||||
): TState[A][B] | TInitial;
|
||||
export function useObservableGetState<
|
||||
TState,
|
||||
A extends keyof TState,
|
||||
B extends keyof TState[A],
|
||||
C extends keyof TState[A][B]
|
||||
>(
|
||||
state$: Observable<TState>,
|
||||
initialState: TState[A][B][C] | (() => TState[A][B][C]),
|
||||
pA: A,
|
||||
pB: B,
|
||||
pC: C
|
||||
): TState[A][B][C];
|
||||
export function useObservableGetState<
|
||||
TState,
|
||||
TInitial extends null | undefined | void,
|
||||
A extends keyof TState,
|
||||
B extends keyof TState[A],
|
||||
C extends keyof TState[A][B]
|
||||
>(
|
||||
state$: Observable<TState>,
|
||||
initialState: TInitial,
|
||||
pA: A,
|
||||
pB: B,
|
||||
pC: C
|
||||
): TState[A][B][C] | TInitial;
|
||||
export function useObservableGetState<
|
||||
TState,
|
||||
A extends keyof TState,
|
||||
B extends keyof TState[A],
|
||||
C extends keyof TState[A][B],
|
||||
D extends keyof TState[A][B][C]
|
||||
>(
|
||||
state$: Observable<TState>,
|
||||
initialState: TState[A][B][C][D] | (() => TState[A][B][C][D]),
|
||||
pA: A,
|
||||
pB: B,
|
||||
pC: C,
|
||||
pD: D
|
||||
): TState[A][B][C][D];
|
||||
export function useObservableGetState<
|
||||
TState,
|
||||
TInitial extends null | undefined | void,
|
||||
A extends keyof TState,
|
||||
B extends keyof TState[A],
|
||||
C extends keyof TState[A][B],
|
||||
D extends keyof TState[A][B][C]
|
||||
>(
|
||||
state$: Observable<TState>,
|
||||
initialState: TInitial,
|
||||
pA: A,
|
||||
pB: B,
|
||||
pC: C,
|
||||
pD: D
|
||||
): TState[A][B][C][D] | TInitial;
|
||||
export function useObservableGetState<
|
||||
TState,
|
||||
A extends keyof TState,
|
||||
B extends keyof TState[A],
|
||||
C extends keyof TState[A][B],
|
||||
D extends keyof TState[A][B][C],
|
||||
E extends keyof TState[A][B][C][D]
|
||||
>(
|
||||
state$: Observable<TState>,
|
||||
initialState: TState[A][B][C][D][E] | (() => TState[A][B][C][D][E]),
|
||||
pA: A,
|
||||
pB: B,
|
||||
pC: C,
|
||||
pD: D,
|
||||
pE: E
|
||||
): TState[A][B][C][D][E];
|
||||
export function useObservableGetState<
|
||||
TState,
|
||||
TInitial extends null | undefined | void,
|
||||
A extends keyof TState,
|
||||
B extends keyof TState[A],
|
||||
C extends keyof TState[A][B],
|
||||
D extends keyof TState[A][B][C],
|
||||
E extends keyof TState[A][B][C][D]
|
||||
>(
|
||||
state$: Observable<TState>,
|
||||
initialState: TInitial,
|
||||
pA: A,
|
||||
pB: B,
|
||||
pC: C,
|
||||
pD: D,
|
||||
pE: E
|
||||
): TState[A][B][C][D][E] | TInitial;
|
||||
export function useObservableGetState<
|
||||
TState,
|
||||
A extends keyof TState,
|
||||
B extends keyof TState[A],
|
||||
C extends keyof TState[A][B],
|
||||
D extends keyof TState[A][B][C],
|
||||
E extends keyof TState[A][B][C][D],
|
||||
F extends keyof TState[A][B][C][D][E]
|
||||
>(
|
||||
state$: Observable<TState>,
|
||||
initialState: TState[A][B][C][D][E][F] | (() => TState[A][B][C][D][E][F]),
|
||||
pA: A,
|
||||
pB: B,
|
||||
pC: C,
|
||||
pD: D,
|
||||
pE: E,
|
||||
pF: F
|
||||
): TState[A][B][C][D][E][F];
|
||||
export function useObservableGetState<
|
||||
TState,
|
||||
TInitial extends null | undefined | void,
|
||||
A extends keyof TState,
|
||||
B extends keyof TState[A],
|
||||
C extends keyof TState[A][B],
|
||||
D extends keyof TState[A][B][C],
|
||||
E extends keyof TState[A][B][C][D],
|
||||
F extends keyof TState[A][B][C][D][E]
|
||||
>(
|
||||
state$: Observable<TState>,
|
||||
initialState: TInitial,
|
||||
pA: A,
|
||||
pB: B,
|
||||
pC: C,
|
||||
pD: D,
|
||||
pE: E,
|
||||
pF: F
|
||||
): TState[A][B][C][D][E][F] | TInitial;
|
||||
export function useObservableGetState<
|
||||
TState,
|
||||
A extends keyof TState,
|
||||
B extends keyof TState[A],
|
||||
C extends keyof TState[A][B],
|
||||
D extends keyof TState[A][B][C],
|
||||
E extends keyof TState[A][B][C][D],
|
||||
F extends keyof TState[A][B][C][D][E],
|
||||
G extends keyof TState[A][B][C][D][E][F]
|
||||
>(
|
||||
state$: Observable<TState>,
|
||||
initialState:
|
||||
| TState[A][B][C][D][E][F][G]
|
||||
| (() => TState[A][B][C][D][E][F][G]),
|
||||
pA: A,
|
||||
pB: B,
|
||||
pC: C,
|
||||
pD: D,
|
||||
pE: E,
|
||||
pF: F,
|
||||
pG: G
|
||||
): TState[A][B][C][D][E][F][G];
|
||||
export function useObservableGetState<
|
||||
TState,
|
||||
TInitial extends null | undefined | void,
|
||||
A extends keyof TState,
|
||||
B extends keyof TState[A],
|
||||
C extends keyof TState[A][B],
|
||||
D extends keyof TState[A][B][C],
|
||||
E extends keyof TState[A][B][C][D],
|
||||
F extends keyof TState[A][B][C][D][E],
|
||||
G extends keyof TState[A][B][C][D][E][F]
|
||||
>(
|
||||
state$: Observable<TState>,
|
||||
initialState: TInitial,
|
||||
pA: A,
|
||||
pB: B,
|
||||
pC: C,
|
||||
pD: D,
|
||||
pE: E,
|
||||
pF: F,
|
||||
pG: G
|
||||
): TState[A][B][C][D][E][F][G] | TInitial;
|
||||
export function useObservableGetState<
|
||||
TState,
|
||||
A extends keyof TState,
|
||||
B extends keyof TState[A],
|
||||
C extends keyof TState[A][B],
|
||||
D extends keyof TState[A][B][C],
|
||||
E extends keyof TState[A][B][C][D],
|
||||
F extends keyof TState[A][B][C][D][E],
|
||||
G extends keyof TState[A][B][C][D][E][F],
|
||||
H extends keyof TState[A][B][C][D][E][F][G]
|
||||
>(
|
||||
state$: Observable<TState>,
|
||||
initialState:
|
||||
| TState[A][B][C][D][E][F][G][H]
|
||||
| (() => TState[A][B][C][D][E][F][G][H]),
|
||||
pA: A,
|
||||
pB: B,
|
||||
pC: C,
|
||||
pD: D,
|
||||
pE: E,
|
||||
pF: F,
|
||||
pG: G,
|
||||
pH: H
|
||||
): TState[A][B][C][D][E][F][G][H];
|
||||
export function useObservableGetState<
|
||||
TState,
|
||||
TInitial extends null | undefined | void,
|
||||
A extends keyof TState,
|
||||
B extends keyof TState[A],
|
||||
C extends keyof TState[A][B],
|
||||
D extends keyof TState[A][B][C],
|
||||
E extends keyof TState[A][B][C][D],
|
||||
F extends keyof TState[A][B][C][D][E],
|
||||
G extends keyof TState[A][B][C][D][E][F],
|
||||
H extends keyof TState[A][B][C][D][E][F][G]
|
||||
>(
|
||||
state$: Observable<TState>,
|
||||
initialState: TInitial,
|
||||
pA: A,
|
||||
pB: B,
|
||||
pC: C,
|
||||
pD: D,
|
||||
pE: E,
|
||||
pF: F,
|
||||
pG: G,
|
||||
pH: H
|
||||
): TState[A][B][C][D][E][F][G][H] | TInitial;
|
||||
export function useObservableGetState<
|
||||
TState,
|
||||
A extends keyof TState,
|
||||
B extends keyof TState[A],
|
||||
C extends keyof TState[A][B],
|
||||
D extends keyof TState[A][B][C],
|
||||
E extends keyof TState[A][B][C][D],
|
||||
F extends keyof TState[A][B][C][D][E],
|
||||
G extends keyof TState[A][B][C][D][E][F],
|
||||
H extends keyof TState[A][B][C][D][E][F][G],
|
||||
I extends keyof TState[A][B][C][D][E][F][G][H]
|
||||
>(
|
||||
state$: Observable<TState>,
|
||||
initialState:
|
||||
| TState[A][B][C][D][E][F][G][H][I]
|
||||
| (() => TState[A][B][C][D][E][F][G][H][I]),
|
||||
pA: A,
|
||||
pB: B,
|
||||
pC: C,
|
||||
pD: D,
|
||||
pE: E,
|
||||
pF: F,
|
||||
pG: G,
|
||||
pH: H,
|
||||
pI: I
|
||||
): TState[A][B][C][D][E][F][G][H][I];
|
||||
export function useObservableGetState<
|
||||
TState,
|
||||
TInitial extends null | undefined | void,
|
||||
A extends keyof TState,
|
||||
B extends keyof TState[A],
|
||||
C extends keyof TState[A][B],
|
||||
D extends keyof TState[A][B][C],
|
||||
E extends keyof TState[A][B][C][D],
|
||||
F extends keyof TState[A][B][C][D][E],
|
||||
G extends keyof TState[A][B][C][D][E][F],
|
||||
H extends keyof TState[A][B][C][D][E][F][G],
|
||||
I extends keyof TState[A][B][C][D][E][F][G][H]
|
||||
>(
|
||||
state$: Observable<TState>,
|
||||
initialState: TInitial,
|
||||
pA: A,
|
||||
pB: B,
|
||||
pC: C,
|
||||
pD: D,
|
||||
pE: E,
|
||||
pF: F,
|
||||
pG: G,
|
||||
pH: H,
|
||||
pI: I
|
||||
): TState[A][B][C][D][E][F][G][H][I] | TInitial;
|
||||
export function useObservableGetState<
|
||||
TState,
|
||||
TInitial extends null | undefined | void,
|
||||
A extends keyof TState,
|
||||
B extends keyof TState[A],
|
||||
C extends keyof TState[A][B],
|
||||
D extends keyof TState[A][B][C],
|
||||
E extends keyof TState[A][B][C][D],
|
||||
F extends keyof TState[A][B][C][D][E],
|
||||
G extends keyof TState[A][B][C][D][E][F],
|
||||
H extends keyof TState[A][B][C][D][E][F][G],
|
||||
I extends keyof TState[A][B][C][D][E][F][G][H],
|
||||
J extends keyof TState[A][B][C][D][E][F][G][H][I]
|
||||
>(
|
||||
state$: Observable<TState>,
|
||||
initialState: TInitial,
|
||||
pA: A,
|
||||
pB: B,
|
||||
pC: C,
|
||||
pD: D,
|
||||
pE: E,
|
||||
pF: F,
|
||||
pG: G,
|
||||
pH: H,
|
||||
pI: I,
|
||||
pJ: J
|
||||
): TState[A][B][C][D][E][F][G][H][I][J] | TInitial;
|
||||
export function useObservableGetState<TState, TInit>(
|
||||
state$: Observable<TState>,
|
||||
initialState: TInit | (() => TInit),
|
||||
...path: any[]
|
||||
) {
|
||||
const value = useObservableState(
|
||||
useObservable(() =>
|
||||
state$.pipe(map(state => path.reduce(getValue, state)))
|
||||
),
|
||||
initialState
|
||||
);
|
||||
useDebugValue(value);
|
||||
return value;
|
||||
}
|
||||
|
||||
function getValue<T, K extends keyof T>(obj: T, key: K): T[K] {
|
||||
return obj[key];
|
||||
}
|
||||
55
node_modules/observable-hooks/src/use-observable-pick-state.ts
generated
vendored
Normal file
55
node_modules/observable-hooks/src/use-observable-pick-state.ts
generated
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
import { useDebugValue } from "react";
|
||||
import { Observable } from "rxjs";
|
||||
import { map, distinctUntilChanged } from "rxjs/operators";
|
||||
import { useObservableState } from "./use-observable-state";
|
||||
import { useObservable } from "./use-observable";
|
||||
|
||||
/**
|
||||
* Creates an object composed of the picked state properties. Similar to lodash `pick`.
|
||||
* Changes of any of these properties will trigger a rerendering.
|
||||
* Errors are thrown on unreachable path.
|
||||
*
|
||||
* @param state$ Output state.
|
||||
* @param keys keys of state
|
||||
*/
|
||||
export function useObservablePickState<
|
||||
TState,
|
||||
TKeys extends keyof TState,
|
||||
TInitial extends null | undefined | void
|
||||
>(
|
||||
state$: Observable<TState>,
|
||||
initialState: TInitial,
|
||||
...keys: TKeys[]
|
||||
): { [K in TKeys]: TState[K] } | TInitial;
|
||||
export function useObservablePickState<TState, TKeys extends keyof TState>(
|
||||
state$: Observable<TState>,
|
||||
initialState:
|
||||
| { [K in TKeys]: TState[K] }
|
||||
| (() => { [K in TKeys]: TState[K] }),
|
||||
...keys: TKeys[]
|
||||
): { [K in TKeys]: TState[K] };
|
||||
export function useObservablePickState<TState, TKeys extends keyof TState>(
|
||||
state$: Observable<TState>,
|
||||
initialState:
|
||||
| { [K in TKeys]: TState[K] }
|
||||
| (() => { [K in TKeys]: TState[K] }),
|
||||
...keys: TKeys[]
|
||||
): { [K in TKeys]: TState[K] } {
|
||||
const value = useObservableState(
|
||||
useObservable(() =>
|
||||
state$.pipe(
|
||||
distinctUntilChanged((s1, s2) => keys.every(k => s1[k] === s2[k])),
|
||||
map(state =>
|
||||
keys.reduce(
|
||||
// eslint-disable-next-line no-sequences
|
||||
(o, k) => ((o[k] = state[k]), o),
|
||||
{} as { [K in TKeys]: TState[K] }
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
initialState
|
||||
);
|
||||
useDebugValue(value);
|
||||
return value;
|
||||
}
|
||||
48
node_modules/observable-hooks/src/use-observable-ref.ts
generated
vendored
Normal file
48
node_modules/observable-hooks/src/use-observable-ref.ts
generated
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
import type { MutableRefObject, RefObject } from "react";
|
||||
import { BehaviorSubject } from "rxjs";
|
||||
import { useState } from "react";
|
||||
|
||||
/**
|
||||
* Returns a mutable ref object and a BehaviorSubject.
|
||||
*
|
||||
* Whenever ref.current is changed, the BehaviorSubject will emit the new value.
|
||||
*
|
||||
* @param initialValue The initial value of the BehaviorSubject.
|
||||
*/
|
||||
export function useObservableRef<TValue>(
|
||||
initialValue: TValue
|
||||
): [MutableRefObject<TValue>, BehaviorSubject<TValue>];
|
||||
/**
|
||||
* Returns a ref object and a BehaviorSubject.
|
||||
*
|
||||
* Whenever ref.current is changed, the BehaviorSubject will emit the new value.
|
||||
*
|
||||
* @param initialValue The initial value of the BehaviorSubject.
|
||||
*/
|
||||
export function useObservableRef<TValue>(
|
||||
initialValue: TValue | null
|
||||
): [RefObject<TValue>, BehaviorSubject<TValue>];
|
||||
/**
|
||||
* Returns a mutable ref object and a BehaviorSubject.
|
||||
*
|
||||
* Whenever ref.current is changed, the BehaviorSubject will emit the new value.
|
||||
*
|
||||
* @param initialValue A optional initial value of the BehaviorSubject.
|
||||
*/
|
||||
export function useObservableRef<TValue = undefined>(
|
||||
initialValue?: TValue
|
||||
): [MutableRefObject<TValue | undefined>, BehaviorSubject<TValue | undefined>];
|
||||
export function useObservableRef<TValue>(
|
||||
initialValue?: TValue
|
||||
): [MutableRefObject<TValue | undefined>, BehaviorSubject<TValue | undefined>] {
|
||||
const [value$] = useState(() => new BehaviorSubject(initialValue));
|
||||
const [ref] = useState<MutableRefObject<TValue | undefined>>(() => ({
|
||||
get current(): TValue | undefined {
|
||||
return value$.value;
|
||||
},
|
||||
set current(value: TValue | undefined) {
|
||||
value$.next(value);
|
||||
},
|
||||
}));
|
||||
return [ref, value$];
|
||||
}
|
||||
109
node_modules/observable-hooks/src/use-observable-state.ts
generated
vendored
Normal file
109
node_modules/observable-hooks/src/use-observable-state.ts
generated
vendored
Normal file
@@ -0,0 +1,109 @@
|
||||
import { BehaviorSubject, Observable } from "rxjs";
|
||||
import { useSubscription } from "./use-subscription";
|
||||
import { useObservableStateInternal } from "./internal/use-observable-state-internal";
|
||||
|
||||
/**
|
||||
* A sugar hook for getting values from an Observable.
|
||||
*
|
||||
* It can be used in two ways:
|
||||
*
|
||||
* 1. Offer an Observable and an optional initial state.
|
||||
* ```js
|
||||
* const output = useObservableState(input$, initialState)
|
||||
* ```
|
||||
* 2. Offer an epic-like function and an optional initial state.
|
||||
* ```js
|
||||
* const [output, onInput] = useObservableState(
|
||||
* (input$, initialState) => input$.pipe(...),
|
||||
* initialState
|
||||
* )
|
||||
* ```
|
||||
*
|
||||
* The optional `initialState` is internally passed to `useState(initialState)`.
|
||||
* This means it can be either a state value or a function that returns the state
|
||||
* which is for expensive initialization.
|
||||
*
|
||||
* The `initialState`(or its returned result) is also passed to the `init` function.
|
||||
* This is useful if you want to implement reducer pattern which requires an initial state.
|
||||
*
|
||||
* ⚠ **Note:** These two ways use different hooks, choose either one each time
|
||||
* and do not change to the other one during Component's life cycle.
|
||||
*
|
||||
* ⚠ **Note:** `useObservableState` will call the epic-like `init` function only once
|
||||
* and always return the same Observable.
|
||||
* It is not safe to access closure directly inside `init`.
|
||||
* Use [[useObservable]] with `withLatestFrom` instead.
|
||||
*
|
||||
* ⚠ **Note:** To make it concurrent mode compatible, the subscription happens
|
||||
* after the render is committed to the screen which means even the Observable emits synchronous values
|
||||
* they will arrive after the first rendering.
|
||||
*
|
||||
* @template TState Output state.
|
||||
*
|
||||
* @param input$ A BehaviorSubject.
|
||||
*/
|
||||
export function useObservableState<TState>(
|
||||
input$: BehaviorSubject<TState>
|
||||
): TState;
|
||||
/**
|
||||
* @template TState Output state.
|
||||
*
|
||||
* @param input$ An Observable.
|
||||
*/
|
||||
export function useObservableState<TState>(
|
||||
input$: Observable<TState>
|
||||
): TState | undefined;
|
||||
/**
|
||||
* @template TState Output state.
|
||||
*
|
||||
* @param input$ An Observable.
|
||||
* @param initialState Optional initial state.
|
||||
* Can be the state value or a function that returns the state.
|
||||
*/
|
||||
export function useObservableState<TState>(
|
||||
input$: Observable<TState>,
|
||||
initialState: TState | (() => TState)
|
||||
): TState;
|
||||
/**
|
||||
* @template TState Output state.
|
||||
* @template TInput Input values.
|
||||
*
|
||||
* @param init A epic-like function that, when applied to an Observable
|
||||
* and the initial state value, returns an Observable.
|
||||
*/
|
||||
export function useObservableState<TState, TInput = TState>(
|
||||
init: (input$: Observable<TInput>) => Observable<TState>
|
||||
): [TState | undefined, (input: TInput) => void];
|
||||
/**
|
||||
* Different input output types with initial state.
|
||||
*
|
||||
* @template TState Output state.
|
||||
* @template TInput Input values.
|
||||
*
|
||||
* @param init A epic-like function that, when applied to an Observable
|
||||
* and the initial state value, returns an Observable.
|
||||
* @param initialState Optional initial state.
|
||||
* Can be the state value or a function that returns the state.
|
||||
*/
|
||||
export function useObservableState<TState, TInput = TState>(
|
||||
init: (
|
||||
input$: Observable<TInput>,
|
||||
initialState: TState
|
||||
) => Observable<TState>,
|
||||
initialState: TState | (() => TState)
|
||||
): [TState, (input: TInput) => void];
|
||||
export function useObservableState<TState, TInput = TState>(
|
||||
state$OrInit:
|
||||
| Observable<TState>
|
||||
| ((
|
||||
input$: Observable<TInput>,
|
||||
initialState?: TState
|
||||
) => Observable<TState>),
|
||||
initialState?: TState | (() => TState)
|
||||
): TState | undefined | [TState | undefined, (input: TInput) => void] {
|
||||
return useObservableStateInternal(
|
||||
useSubscription,
|
||||
state$OrInit,
|
||||
initialState
|
||||
);
|
||||
}
|
||||
33
node_modules/observable-hooks/src/use-observable-suspense.ts
generated
vendored
Normal file
33
node_modules/observable-hooks/src/use-observable-suspense.ts
generated
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
import { useDebugValue, useState } from "react";
|
||||
import { useSubscription } from "./use-subscription";
|
||||
import { useForceUpdate } from "./helpers";
|
||||
import { ObservableResource } from "./observable-resource";
|
||||
|
||||
/**
|
||||
* Consume the Observable resource.
|
||||
*
|
||||
* Unlike Promise, Observable is a multiple push mechanism.
|
||||
* This hook triggers extra re-rendering when Suspense should restart.
|
||||
*
|
||||
* @param resource Observable resource
|
||||
*/
|
||||
export function useObservableSuspense<TInput, TOutput extends TInput = TInput>(
|
||||
resource: ObservableResource<TInput, TOutput>
|
||||
): TOutput {
|
||||
const resourceValue = resource.read();
|
||||
const forceUpdate = useForceUpdate();
|
||||
const [state, setState] = useState<TOutput>(resourceValue);
|
||||
|
||||
useSubscription(resource.valueRef$$, valueRef => {
|
||||
/* Guard code. Value should always be ready when reaching this far. */
|
||||
/* istanbul ignore else */
|
||||
if (valueRef) {
|
||||
setState(valueRef.current);
|
||||
}
|
||||
});
|
||||
|
||||
useSubscription(resource.shouldUpdate$$, forceUpdate);
|
||||
|
||||
useDebugValue(state);
|
||||
return state;
|
||||
}
|
||||
57
node_modules/observable-hooks/src/use-observable.ts
generated
vendored
Normal file
57
node_modules/observable-hooks/src/use-observable.ts
generated
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
import { Observable } from "rxjs";
|
||||
import { useObservableInternal } from "./internal/use-observable-internal";
|
||||
import { useEffect } from "react";
|
||||
|
||||
/**
|
||||
* Accepts a function that returns an Observable.
|
||||
* Optionally accepts an array of dependencies which
|
||||
* will be turned into Observable and be passed to the
|
||||
* `init` function.
|
||||
*
|
||||
* React functional components are called many times during their lifecycle.
|
||||
* Create or transform Observables in `init` function so that the operations
|
||||
* won't be repeatedly performed.
|
||||
*
|
||||
* ⚠ **Note:** `useObservable` will call `init` once and always return
|
||||
* the same Observable. It is not safe to access closure (except other Observables)
|
||||
* directly inside `init`.
|
||||
* You should use ref or pass them as dependencies through the second argument.
|
||||
*
|
||||
* ⚠ **Note:** Due to rules of hooks you can either offer or omit the
|
||||
* dependencies array but do not change to one another during Component's life cycle.
|
||||
* The length of the dependencies array must also be fixed.
|
||||
*
|
||||
* @template TOutput Output value in Observable
|
||||
*
|
||||
* @param init A pure function that, when applied to an Observable,
|
||||
* returns an Observable.
|
||||
*/
|
||||
export function useObservable<
|
||||
TOutput,
|
||||
TObservable extends Observable<TOutput> = Observable<TOutput>
|
||||
>(init: () => TObservable): TObservable;
|
||||
/**
|
||||
* @template TOutput Output value within Observable.
|
||||
* @template TInputs A readonly tuple of all dependencies.
|
||||
*
|
||||
* @param init A pure function that, when applied to an Observable,
|
||||
* returns an Observable.
|
||||
* @param inputs An dependency array with fixed length. When one of the dependencies
|
||||
* changes the Observable in `init` will emit an array of all the dependencies.
|
||||
*/
|
||||
export function useObservable<
|
||||
TOutput,
|
||||
TInputs extends Readonly<any[]>,
|
||||
TObservable extends Observable<TOutput> = Observable<TOutput>
|
||||
>(
|
||||
init: (inputs$: Observable<[...TInputs]>) => TObservable,
|
||||
inputs: [...TInputs]
|
||||
): TObservable;
|
||||
export function useObservable<TOutput, TInputs extends Readonly<any[]>>(
|
||||
init:
|
||||
| (() => Observable<TOutput>)
|
||||
| ((inputs$: Observable<[...TInputs]>) => Observable<TOutput>),
|
||||
inputs?: [...TInputs]
|
||||
): Observable<TOutput> {
|
||||
return useObservableInternal(useEffect, init, inputs);
|
||||
}
|
||||
46
node_modules/observable-hooks/src/use-render-throw.ts
generated
vendored
Normal file
46
node_modules/observable-hooks/src/use-render-throw.ts
generated
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
import { useRef } from "react";
|
||||
import { Observable, NEVER } from "rxjs";
|
||||
import { catchError, switchMap } from "rxjs/operators";
|
||||
import { useForceUpdate } from "./helpers";
|
||||
import { useObservable } from "./use-observable";
|
||||
|
||||
/**
|
||||
* Enhance an Observable by making errors catch-able to ErrorBoundary.
|
||||
*
|
||||
* It catches Observable error and re-throw it as React render error.
|
||||
*
|
||||
* @template TInput Input value within Observable.
|
||||
*
|
||||
* @param input$ Input Observable.
|
||||
* @returns Observable with the same input type
|
||||
*/
|
||||
export function useRenderThrow<TInput>(
|
||||
input$: Observable<TInput>
|
||||
): Observable<TInput> {
|
||||
const forceUpdate = useForceUpdate();
|
||||
const errorRef = useRef<Error | null>();
|
||||
|
||||
const output$ = useObservable(
|
||||
inputs$ =>
|
||||
inputs$.pipe(
|
||||
switchMap(([input$]) => {
|
||||
errorRef.current = null;
|
||||
return input$.pipe(
|
||||
catchError(error => {
|
||||
errorRef.current = error;
|
||||
forceUpdate();
|
||||
return NEVER;
|
||||
})
|
||||
);
|
||||
})
|
||||
),
|
||||
[input$]
|
||||
);
|
||||
|
||||
if (errorRef.current) {
|
||||
// Let error boundary catch the error
|
||||
throw errorRef.current;
|
||||
}
|
||||
|
||||
return output$;
|
||||
}
|
||||
73
node_modules/observable-hooks/src/use-subscription.ts
generated
vendored
Normal file
73
node_modules/observable-hooks/src/use-subscription.ts
generated
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
import { Observable, PartialObserver, Subscription } from "rxjs";
|
||||
import { MutableRefObject, useEffect } from "react";
|
||||
import { useSubscriptionInternal } from "./internal/use-subscription-internal";
|
||||
|
||||
/**
|
||||
* Accepts an Observable and optional `next`, `error`, `complete` functions.
|
||||
* These functions must be in correct order.
|
||||
* Use `undefined` or `null` for placeholder.
|
||||
*
|
||||
* Subscription will unsubscribe when unmount, you can also
|
||||
* unsubscribe manually.
|
||||
*
|
||||
* ⚠ **Note:** To make it concurrent mode compatible, the subscription happens
|
||||
* after the render is committed to the screen
|
||||
* which means even the Observable emits synchronous values
|
||||
* they will arrive after the first rendering.
|
||||
*
|
||||
* Note that changes of callbacks will not trigger
|
||||
* an emission. If you need that just create another
|
||||
* Observable of the callback with [[useObservable]].
|
||||
*
|
||||
* (From v2.0) You can access closure directly inside callback like in `useEffect`.
|
||||
* `useSubscription` will ensure the latest callback is called.
|
||||
*
|
||||
* (From v2.3.4) when the Observable changes `useSubscription` will automatically
|
||||
* unsubscribe the old one and resubscribe to the new one.
|
||||
*
|
||||
* ⚠ **Note:** Due to the design of RxJS, once an error occurs in an observable, the observable
|
||||
* is killed.
|
||||
* You should prevent errors from reaching observables or `catchError` in sub-observables.
|
||||
* You can also make the observable as state and replace it on error.
|
||||
* `useSubscription` will automatically switch to the new one.
|
||||
*
|
||||
* @template TInput Input value within Observable.
|
||||
*
|
||||
* @param input$ Input Observable.
|
||||
* @param observer Observer
|
||||
*/
|
||||
export function useSubscription<TInput>(
|
||||
input$: Observable<TInput>,
|
||||
observer?: PartialObserver<TInput>
|
||||
): MutableRefObject<Subscription | undefined>;
|
||||
/**
|
||||
* @template TInput Input value within Observable.
|
||||
*
|
||||
* @param input$ Input Observable.
|
||||
* @param next Notify when a new value is emitted.
|
||||
* @param error Notify when a new error is thrown.
|
||||
* @param complete Notify when the Observable is complete.
|
||||
*/
|
||||
export function useSubscription<TInput>(
|
||||
input$: Observable<TInput>,
|
||||
next?: ((value: TInput) => void) | null | undefined,
|
||||
error?: ((error: any) => void) | null | undefined,
|
||||
complete?: (() => void) | null | undefined
|
||||
): MutableRefObject<Subscription | undefined>;
|
||||
export function useSubscription<TInput>(
|
||||
input$: Observable<TInput>,
|
||||
observerOrNext$?:
|
||||
| PartialObserver<TInput>
|
||||
| ((value: TInput) => void)
|
||||
| null
|
||||
| undefined,
|
||||
error?: ((error: any) => void) | null | undefined,
|
||||
complete?: (() => void) | null | undefined
|
||||
): MutableRefObject<Subscription | undefined> {
|
||||
return useSubscriptionInternal(useEffect, [
|
||||
input$,
|
||||
observerOrNext$,
|
||||
error,
|
||||
complete,
|
||||
]);
|
||||
}
|
||||
Reference in New Issue
Block a user