mirror of
https://github.com/dergigi/boris.git
synced 2025-12-25 10:34:28 +01:00
- 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.
119 lines
2.7 KiB
TypeScript
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)();
|