mirror of
https://github.com/wassname/talk.git
synced 2026-07-14 11:18:50 +08:00
[CORL-666] Viewer Events (#2681)
* feat: viewer event system * feat: more events * feat: MORE events * fix: tests * fix: rte focus events * chore: add comments * fix: remove listening to events * chore: update RTE * fix: tests * feature: generate event docs * fix: remove obsolete line in docs * chore: improve docs * chore: improve formatting * feature: protect events.md from getting out of sync * chore: small improvements * fix: removing redundant lambda
This commit is contained in:
@@ -0,0 +1,140 @@
|
||||
import { EventEmitter2 } from "eventemitter2";
|
||||
import { useCoralContext } from "./bootstrap";
|
||||
|
||||
/**
|
||||
* _Viewer Events_ are emitted when the viewer performs certain actions.
|
||||
* They can be subscribed to using the `events` parameter in
|
||||
* `Coral.createStreamEmbed`.
|
||||
*/
|
||||
export interface ViewerEvent<T> {
|
||||
emit: keyof T extends never
|
||||
? (eventEmitter: EventEmitter2) => void
|
||||
: (eventEmitter: EventEmitter2, data: T) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* A ViewerNetworkEventStarted represents ViewerNetworkEvent that has
|
||||
* started and is waiting for the response.
|
||||
*/
|
||||
export interface ViewerNetworkEventStarted<
|
||||
T extends { success: object; error: object }
|
||||
> {
|
||||
/**
|
||||
* Emits a success event and include the rtt time.
|
||||
*/
|
||||
success: keyof T["success"] extends never
|
||||
? () => void
|
||||
: (success: T["success"]) => void;
|
||||
|
||||
/**
|
||||
* Emits an error event and include the rtt time.
|
||||
*/
|
||||
error: keyof T["error"] extends never
|
||||
? () => void
|
||||
: (error: T["error"]) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* _Viewer Network Events_ are _Viewer Events_ that involves a network request and
|
||||
* thus can succeed or fail. Succeeding events have the suffix `.success`
|
||||
* while failing events an `.error` suffix.
|
||||
*
|
||||
* Moreover _Viewer Network Events_ contain the `rtt` field which indicates
|
||||
* the time it needed from initiating the request until the _UI_ has been
|
||||
* updated with the response data.
|
||||
*/
|
||||
export interface ViewerNetworkEvent<
|
||||
T extends { success: object; error: object }
|
||||
> {
|
||||
/**
|
||||
* Mark the network request as started. This will also start tracking the rtt time.
|
||||
*/
|
||||
begin: keyof T extends "success" | "error"
|
||||
? (eventEmitter: EventEmitter2) => ViewerNetworkEventStarted<T>
|
||||
: (
|
||||
eventEmitter: EventEmitter2,
|
||||
data: Pick<T, Exclude<keyof T, "success" | "error">>
|
||||
) => ViewerNetworkEventStarted<T>;
|
||||
}
|
||||
|
||||
/**
|
||||
* createViewerEvent creates a ViewerNetworkEvent object.
|
||||
*
|
||||
* @param name name of the event
|
||||
*/
|
||||
export function createViewerNetworkEvent<
|
||||
T extends { success: object; error: object }
|
||||
>(name: string): ViewerNetworkEvent<T> {
|
||||
return {
|
||||
begin: ((eventEmitter, data) => {
|
||||
const ms = Date.now();
|
||||
return {
|
||||
success: (success => {
|
||||
const final: any = {
|
||||
...data,
|
||||
rtt: Date.now() - ms,
|
||||
};
|
||||
if (success) {
|
||||
final.success = success;
|
||||
}
|
||||
eventEmitter.emit(`${name}.success`, final);
|
||||
}) as ViewerNetworkEventStarted<T>["success"],
|
||||
error: (error => {
|
||||
const final: any = {
|
||||
...data,
|
||||
rtt: Date.now() - ms,
|
||||
};
|
||||
if (error) {
|
||||
final.error = error;
|
||||
}
|
||||
eventEmitter.emit(`${name}.error`, final);
|
||||
}) as ViewerNetworkEventStarted<T>["error"],
|
||||
};
|
||||
}) as ViewerNetworkEvent<T>["begin"],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* createViewerEvent creates a ViewerEvent object.
|
||||
*
|
||||
* @param name name of the event
|
||||
*/
|
||||
export function createViewerEvent<T>(name: string): ViewerEvent<T> {
|
||||
return {
|
||||
emit: ((eventEmitter, data) => {
|
||||
eventEmitter.emit(name, data);
|
||||
}) as ViewerEvent<T>["emit"],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* useViewerEvent inject the eventEmitter and returns a simple
|
||||
* callback to emit the event.
|
||||
*/
|
||||
export function useViewerEvent<T>(
|
||||
viewerEvent: ViewerEvent<T>
|
||||
): keyof T extends never ? () => void : (data: T) => void {
|
||||
const { eventEmitter } = useCoralContext();
|
||||
return ((data?: T) => {
|
||||
viewerEvent.emit(eventEmitter, data as any);
|
||||
}) as any;
|
||||
}
|
||||
|
||||
/**
|
||||
* useViewerNetworkEvent injects the eventEmitter into a ViewNetworkEvent
|
||||
* and returns a simple callback to begin the event.
|
||||
*/
|
||||
export function useViewerNetworkEvent<
|
||||
T extends { success: object; error: object }
|
||||
>(
|
||||
viewerNetworkEvent: ViewerNetworkEvent<T>
|
||||
): keyof T extends "success" | "error"
|
||||
? () => ViewerNetworkEventStarted<T>
|
||||
: (
|
||||
data: Pick<T, Exclude<keyof T, "success" | "error">>
|
||||
) => ViewerNetworkEventStarted<T> {
|
||||
const { eventEmitter } = useCoralContext();
|
||||
return ((data?: T) => {
|
||||
return viewerNetworkEvent.begin(eventEmitter, data as any);
|
||||
}) as any;
|
||||
}
|
||||
@@ -38,7 +38,6 @@ function createMutationContainer<T extends string, I, R>(
|
||||
);
|
||||
|
||||
private commit = (input: I) => {
|
||||
this.props.context.eventEmitter.emit(`mutation.${propName}`, input);
|
||||
return commit(
|
||||
this.props.context.relayEnvironment,
|
||||
input,
|
||||
|
||||
@@ -69,7 +69,6 @@ export function useFetch<V, R>(
|
||||
const context = useCoralContext();
|
||||
return useCallback<FetchProp<typeof fetch>>(
|
||||
((variables: V) => {
|
||||
context.eventEmitter.emit(`fetch.${fetch.name}`, variables);
|
||||
return fetch.fetch(context.relayEnvironment, variables, context);
|
||||
}) as any,
|
||||
[context]
|
||||
@@ -94,10 +93,6 @@ export function withFetch<N extends string, V, R>(
|
||||
public static displayName = wrapDisplayName(BaseComponent, "withFetch");
|
||||
|
||||
private fetch = (variables: V) => {
|
||||
this.props.context.eventEmitter.emit(
|
||||
`fetch.${fetch.name}`,
|
||||
variables
|
||||
);
|
||||
return fetch.fetch(
|
||||
this.props.context.relayEnvironment,
|
||||
variables,
|
||||
|
||||
@@ -71,7 +71,6 @@ export function useMutation<I, R>(
|
||||
const context = useCoralContext();
|
||||
return useCallback<MutationProp<typeof mutation>>(
|
||||
((input: I) => {
|
||||
context.eventEmitter.emit(`mutation.${mutation.name}`, input);
|
||||
return mutation.commit(context.relayEnvironment, input, context);
|
||||
}) as any,
|
||||
[context]
|
||||
@@ -99,10 +98,6 @@ export function withMutation<N extends string, I, R>(
|
||||
);
|
||||
|
||||
private commit = (input: I) => {
|
||||
this.props.context.eventEmitter.emit(
|
||||
`mutation.${mutation.name}`,
|
||||
input
|
||||
);
|
||||
return mutation.commit(
|
||||
this.props.context.relayEnvironment,
|
||||
input,
|
||||
|
||||
@@ -50,7 +50,6 @@ export function useSubscription<V>(
|
||||
const context = useCoralContext();
|
||||
return useCallback<SubscriptionProp<typeof subscription>>(
|
||||
((variables: V) => {
|
||||
context.eventEmitter.emit(`subscription.${subscription.name}`, variables);
|
||||
return subscription.subscribe(
|
||||
context.relayEnvironment,
|
||||
variables,
|
||||
|
||||
@@ -4,23 +4,29 @@ import { RelayPaginationProp } from "react-relay";
|
||||
/**
|
||||
* useLoadMore is a react hook that returns a `loadMore` callback
|
||||
* and a `isLoadingMore` boolean.
|
||||
*
|
||||
* @param relay {RelayPaginationProp}
|
||||
* @param count {number}
|
||||
*/
|
||||
export default function useLoadMore(
|
||||
relay: RelayPaginationProp,
|
||||
count: number
|
||||
): [() => void, boolean] {
|
||||
): [() => Promise<void>, boolean] {
|
||||
const [isLoadingMore, setIsLoadingMore] = useState(false);
|
||||
const loadMore = useCallback(() => {
|
||||
if (!relay.hasMore() || relay.isLoading()) {
|
||||
return;
|
||||
return Promise.resolve();
|
||||
}
|
||||
setIsLoadingMore(true);
|
||||
relay.loadMore(count, error => {
|
||||
setIsLoadingMore(false);
|
||||
if (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(error);
|
||||
}
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
relay.loadMore(count, error => {
|
||||
setIsLoadingMore(false);
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
}, [relay]);
|
||||
return [loadMore, isLoadingMore];
|
||||
|
||||
Reference in New Issue
Block a user