mirror of
https://github.com/wassname/talk.git
synced 2026-09-12 13:01:11 +08:00
[next] Moderate (#2118)
* fix: load .env before building / watching * feat: Implement AppBar, Brand, and SubBar * feat: add card ui component * feat: add modqueue components * feat: implement modqueue * feat: add translations * test: add unit tests * feat: single comment view * test: feature / integration tests for modqueue * test: fix remaining tests * feature: support TextMatchOptions * fix: remove body count marker * fix: remove accidently added package * feat: testHelper toJSON * chore: cleanup + comments * chore: better types * test: fix test * chore: refactor decision history test * chore: tiny fix * fix: adjust to recent server changes * fix: marking suspect and banned words * feat: added moderation queue edge to accept/reject comment payloads - Simplified moderationQueue returns - Simplified resolvers * feat: update counts * feat: added id's to moderation queue and settings * fix+test: test count changes, apply fix * chore: adapt to server change, and remove custom mutation handlers * fix: use common utils * fix: purify fix, babel fix * fix: workaround css treeshake issue and upgrade css plugins * fix: fixed snapshot * fix: support empty word lists * feat: separate client config
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
import * as React from "react";
|
||||
|
||||
import { createContextHOC } from "talk-framework/helpers";
|
||||
import ensurePolyfill from "./ensurePolyfill";
|
||||
|
||||
export type IntersectionCallback = (entry: IntersectionObserverEntry) => void;
|
||||
export type Observe = (
|
||||
target: Element,
|
||||
callback: IntersectionCallback
|
||||
) => () => void;
|
||||
export interface IntersectionContext {
|
||||
observe: Observe;
|
||||
}
|
||||
|
||||
const { Provider, Consumer } = React.createContext<IntersectionContext>(
|
||||
{} as any
|
||||
);
|
||||
export const IntersectionConsumer = Consumer;
|
||||
|
||||
export class IntersectionProvider extends React.Component<any, any> {
|
||||
private observer: IntersectionObserver;
|
||||
private elements = new Map();
|
||||
private elementBuffer: Element[] = [];
|
||||
private unmounted = false;
|
||||
|
||||
public componentDidMount() {
|
||||
ensurePolyfill().then(() => {
|
||||
if (this.unmounted) {
|
||||
return;
|
||||
}
|
||||
this.observer = new IntersectionObserver(this.onIntersect, {
|
||||
root: this.props.node ? this.props.node : undefined,
|
||||
rootMargin: "0px",
|
||||
threshold: 0.25,
|
||||
});
|
||||
this.elementBuffer.forEach(element => this.observer.observe(element));
|
||||
this.elementBuffer = [];
|
||||
});
|
||||
}
|
||||
|
||||
public componentWillUnmount() {
|
||||
this.unmounted = true;
|
||||
}
|
||||
|
||||
private unobserve = (element: Element) => {
|
||||
this.elements.delete(element);
|
||||
if (!this.observer) {
|
||||
this.elementBuffer = this.elementBuffer.filter(e => e !== element);
|
||||
} else {
|
||||
this.observer.unobserve(element);
|
||||
}
|
||||
};
|
||||
|
||||
private observe: Observe = (element, callback) => {
|
||||
this.elements.set(element, callback);
|
||||
// this funny bit to handle react's lifecycle order and also wait
|
||||
// for polyfill.
|
||||
if (!this.observer) {
|
||||
this.elementBuffer.push(element);
|
||||
} else {
|
||||
this.observer.observe(element);
|
||||
}
|
||||
return () => this.unobserve(element);
|
||||
};
|
||||
|
||||
private onIntersect = (
|
||||
entries: IntersectionObserverEntry[],
|
||||
observer: IntersectionObserver
|
||||
) => {
|
||||
entries.forEach(entry => this.elements.get(entry.target)(entry));
|
||||
};
|
||||
|
||||
public render() {
|
||||
return (
|
||||
<Provider value={{ observe: this.observe }}>
|
||||
{this.props.children}
|
||||
</Provider>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const withIntersectionContext = createContextHOC<IntersectionContext>(
|
||||
"withContext",
|
||||
IntersectionConsumer
|
||||
);
|
||||
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* Loads intersection-observer polyfill if it doesn't exist.
|
||||
*/
|
||||
export default async function ensurePolyfill() {
|
||||
if (!(window as any).IntersectionObserver) {
|
||||
await import("intersection-observer");
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
export {
|
||||
IntersectionProvider,
|
||||
Observe,
|
||||
withIntersectionContext,
|
||||
} from "./IntersectionContext";
|
||||
export { default as withInView } from "./withInView";
|
||||
export { default as ensurePolyfill } from "./ensurePolyfill";
|
||||
@@ -0,0 +1,81 @@
|
||||
import * as React from "react";
|
||||
import { DefaultingInferableComponentEnhancer, hoistStatics } from "recompose";
|
||||
|
||||
import { Observe, withIntersectionContext } from "./IntersectionContext";
|
||||
|
||||
interface InjectedProps {
|
||||
inView: boolean | undefined;
|
||||
intersectionRef: React.Ref<any>;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
observe: Observe;
|
||||
}
|
||||
|
||||
interface State {
|
||||
inView: boolean | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* withInView provides a property `inView: boolean`
|
||||
* to indicate whether or not the referenced element is
|
||||
* in the current browser view.
|
||||
*/
|
||||
const withInView: DefaultingInferableComponentEnhancer<
|
||||
InjectedProps
|
||||
> = hoistStatics<InjectedProps>(
|
||||
<T extends InjectedProps>(BaseComponent: React.ComponentType<T>) => {
|
||||
class WithInView extends React.Component<Props, State> {
|
||||
private unobserve: (() => void) | null = null;
|
||||
|
||||
public state = {
|
||||
inView: undefined,
|
||||
};
|
||||
|
||||
private changeRef = (ref: any) => {
|
||||
if (this.unobserve) {
|
||||
this.unobserve();
|
||||
this.unobserve = null;
|
||||
}
|
||||
if (ref) {
|
||||
this.unobserve = this.props.observe(
|
||||
ref,
|
||||
({ intersectionRatio }: any) => {
|
||||
// Callback is called whenever we run observe.
|
||||
if (this.state.inView === undefined) {
|
||||
this.setState({ inView: intersectionRatio > 0 });
|
||||
} else {
|
||||
this.setState(s => ({
|
||||
inView: !s.inView,
|
||||
}));
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
public componentWillUnmount() {
|
||||
if (this.unobserve) {
|
||||
this.unobserve();
|
||||
}
|
||||
}
|
||||
|
||||
public render() {
|
||||
return (
|
||||
<BaseComponent
|
||||
{...this.props}
|
||||
inView={this.state.inView}
|
||||
intersectionRef={this.changeRef}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const enhanced = withIntersectionContext(({ observe }) => ({ observe }))(
|
||||
WithInView
|
||||
);
|
||||
return enhanced as React.ComponentType<any>;
|
||||
}
|
||||
);
|
||||
|
||||
export default withInView;
|
||||
@@ -25,8 +25,7 @@ export async function commitMutationPromiseNormalized<T extends OperationBase>(
|
||||
config: MutationPromiseConfig<T>
|
||||
): Promise<T["response"][keyof T["response"]]> {
|
||||
try {
|
||||
const response = await commitMutationPromise(environment, config);
|
||||
return extractPayload(response);
|
||||
return await commitMutationPromise(environment, config);
|
||||
} catch (e) {
|
||||
throw e;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user