Files
boris/node_modules/observable-hooks/src/helpers.ts
Gigi 5d53a827e0 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.
2025-10-02 07:17:07 +02:00

119 lines
2.7 KiB
TypeScript

/**
* 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)();