Integrate with embed

This commit is contained in:
Chi Vinh Le
2018-08-03 18:19:37 +02:00
parent 09cc76a973
commit b3fa5e1e73
14 changed files with 74 additions and 108 deletions
+5 -2
View File
@@ -5,9 +5,9 @@ import {
Decorator,
withAutoHeight,
withClickEvent,
withCommentID,
withEventEmitter,
withIOSSafariWidthWorkaround,
withSetCommentID,
} from "./decorators";
import PymControl from "./PymControl";
import { ensureEndSlash } from "./utils";
@@ -15,6 +15,7 @@ import { ensureEndSlash } from "./utils";
interface CreatePymControlConfig {
assetID?: string;
assetURL?: string;
commentID?: string;
title?: string;
eventEmitter: EventEmitter2;
id: string;
@@ -26,13 +27,14 @@ export function createPymControl(config: CreatePymControlConfig) {
withIOSSafariWidthWorkaround,
withAutoHeight,
withClickEvent,
withCommentID,
withSetCommentID,
withEventEmitter(config.eventEmitter),
];
const query = qs.stringify({
assetID: config.assetID,
assetURL: config.assetURL,
commentID: config.commentID,
});
const url = `${ensureEndSlash(config.rootURL)}stream.html?${query}`;
return new PymControl({
@@ -73,6 +75,7 @@ export type StreamInterface = ReturnType<typeof createStreamInterface>;
export interface CreateConfig {
assetID?: string;
assetURL?: string;
commentID?: string;
title?: string;
eventEmitter: EventEmitter2;
id: string;
+1 -1
View File
@@ -4,7 +4,7 @@ export type CleanupCallback = () => void;
export type Decorator = (pym: pym.Parent) => CleanupCallback | void;
export { default as withAutoHeight } from "./withAutoHeight";
export { default as withClickEvent } from "./withClickEvent";
export { default as withCommentID } from "./withCommentID";
export { default as withSetCommentID } from "./withSetCommentID";
export { default as withEventEmitter } from "./withEventEmitter";
export {
default as withIOSSafariWidthWorkaround,
@@ -1,36 +0,0 @@
import qs from "query-string";
import { buildURL } from "../utils";
import { Decorator } from "./";
const withCommentID: Decorator = pym => {
// Remove the comment id from the query.
pym.onMessage("view-all-comments", () => {
const search = qs.stringify({
...qs.parse(location.search),
commentId: undefined,
});
// Remove the commentId url param.
const url = buildURL({ search });
// Change the url.
window.history.replaceState({}, document.title, url);
});
// Add the permalink comment id to the query.
pym.onMessage("view-comment", (id: string) => {
const search = qs.stringify({
...qs.parse(location.search),
commentId: id,
});
// Remove the commentId url param.
const url = buildURL({ search });
// Change the url.
window.history.replaceState({}, document.title, url);
});
};
export default withCommentID;
@@ -1,16 +1,16 @@
import withCommentID from "./withCommentID";
import withSetCommentID from "./withSetCommentID";
it("should add commentID", () => {
const previousLocation = location.toString();
const previousState = window.history.state;
const fakePym = {
onMessage: (type: string, callback: (id: string) => void) => {
if (type === "view-comment") {
if (type === "setCommentID") {
callback("comment-id");
}
},
};
withCommentID(fakePym as any);
withSetCommentID(fakePym as any);
expect(location.toString()).toBe("http://localhost/?commentId=comment-id");
window.history.replaceState(previousState, document.title, previousLocation);
});
@@ -25,12 +25,12 @@ it("should remove commentID", () => {
);
const fakePym = {
onMessage: (type: string, callback: () => void) => {
if (type === "view-all-comments") {
if (type === "setCommentID") {
callback();
}
},
};
withCommentID(fakePym as any);
withSetCommentID(fakePym as any);
expect(location.toString()).toBe("http://localhost/");
window.history.replaceState(previousState, document.title, previousLocation);
});
@@ -0,0 +1,22 @@
import qs from "query-string";
import { buildURL } from "../utils";
import { Decorator } from "./";
const withSetCommentID: Decorator = pym => {
// Add the permalink comment id to the query.
pym.onMessage("setCommentID", (id: string) => {
const search = qs.stringify({
...qs.parse(location.search),
commentID: id || undefined,
});
// Remove the commentId url param.
const url = buildURL({ search });
// Change the url.
window.history.replaceState({}, document.title, url);
});
};
export default withSetCommentID;
+2
View File
@@ -6,6 +6,7 @@ import createStreamInterface from "./Stream";
export interface Config {
assetID?: string;
assetURL?: string;
commentID?: string;
rootURL?: string;
id?: string;
events?: (eventEmitter: EventEmitter2) => void;
@@ -23,6 +24,7 @@ export function render(config: Config = {}) {
return createStreamInterface({
assetID: config.assetID || query.assetID,
assetURL: config.assetURL || query.assetURL,
commentID: config.commentID || query.commentID,
id: config.id || "talk-embed-stream",
rootURL: config.rootURL || location.origin,
eventEmitter,
@@ -7,7 +7,7 @@ import {
} from "recompose";
import { Environment } from "relay-runtime";
import { withContext } from "../bootstrap";
import { TalkContext, withContext } from "../bootstrap";
/**
* createMutationContainer creates a HOC that
@@ -18,10 +18,14 @@ import { withContext } from "../bootstrap";
*/
function createMutationContainer<T extends string, I, R>(
propName: T,
commit: (environment: Environment, input: I) => Promise<R>
commit: (
environment: Environment,
input: I,
context: TalkContext
) => Promise<R>
): InferableComponentEnhancer<{ [P in T]: (input: I) => Promise<R> }> {
return compose(
withContext(({ relayEnvironment }) => ({ relayEnvironment })),
withContext(context => ({ context })),
hoistStatics((BaseComponent: React.ComponentType<any>) => {
class CreateMutationContainer extends React.Component<any> {
public static displayName = wrapDisplayName(
@@ -30,7 +34,11 @@ function createMutationContainer<T extends string, I, R>(
);
private commit = (input: I) => {
return commit(this.props.relayEnvironment, input);
return commit(
this.props.context.relayEnvironment,
input,
this.props.context
);
};
public render() {
@@ -1,22 +0,0 @@
import { Localized } from "fluent-react/compat";
import * as React from "react";
import { StatelessComponent } from "react";
import { Typography } from "talk-ui/components";
export interface LogoProps {
className?: string;
gutterBottom?: boolean;
}
const Logo: StatelessComponent<LogoProps> = props => {
return (
<Localized id="stream-logo">
<Typography variant="heading1" gutterBottom={props.gutterBottom}>
Talk NEO
</Typography>
</Localized>
);
};
export default Logo;
@@ -4,7 +4,6 @@
margin: calc(0.5 * var(--spacing-unit)) auto;
}
.comment,
.commentNotFound {
margin: calc(5 * var(--spacing-unit)) auto;
.button {
margin-bottom: calc(2 * var(--spacing-unit));
}
@@ -1,6 +1,5 @@
import React, { StatelessComponent } from "react";
import Logo from "talk-stream/components/Logo";
import { Button, Flex, Typography } from "talk-ui/components";
import CommentContainer from "../containers/CommentContainer";
@@ -17,44 +16,25 @@ const PermalinkView: StatelessComponent<PermalinkViewProps> = ({
comment,
onShowAllComments,
}) => {
if (comment) {
return (
<div className={styles.root}>
<Logo />
{assetURL && (
<Button
variant="outlined"
color="primary"
onClick={onShowAllComments}
fullWidth
>
Show all Comments
</Button>
)}
<Flex direction="column" className={styles.comment}>
<CommentContainer data={comment} />
</Flex>
</div>
);
}
return (
<div className={styles.root}>
<Logo />
<Typography className={styles.commentNotFound}>
Comment not found
</Typography>
{assetURL && (
<Button
variant="filled"
variant="outlined"
color="primary"
onClick={() => {
window.location.href = assetURL;
}}
onClick={onShowAllComments}
className={styles.button}
fullWidth
>
Show all Comments
</Button>
)}
{!comment && <Typography>Comment not found</Typography>}
{comment && (
<Flex direction="column">
<CommentContainer data={comment} />
</Flex>
)}
</div>
);
};
@@ -7,7 +7,6 @@ import { Button, Flex } from "talk-ui/components";
import CommentContainer from "../containers/CommentContainer";
import PostCommentFormContainer from "../containers/PostCommentFormContainer";
import ReplyListContainer from "../containers/ReplyListContainer";
import Logo from "./Logo";
import * as styles from "./Stream.css";
export interface StreamProps {
@@ -22,7 +21,6 @@ export interface StreamProps {
const Stream: StatelessComponent<StreamProps> = props => {
return (
<div className={styles.root}>
<Logo gutterBottom />
<PostCommentFormContainer assetID={props.assetID} />
<Flex
direction="column"
@@ -1,4 +1,6 @@
import { commitLocalUpdate, Environment } from "relay-runtime";
import { TalkContext } from "talk-framework/lib/bootstrap";
import { createMutationContainer } from "talk-framework/lib/relay";
import { LOCAL_ID } from "talk-framework/lib/relay/withLocalStateContainer";
@@ -8,10 +10,18 @@ export interface SetCommentIDInput {
export type SetCommentIDMutation = (input: SetCommentIDInput) => Promise<void>;
async function commit(environment: Environment, input: SetCommentIDInput) {
async function commit(
environment: Environment,
input: SetCommentIDInput,
{ pym }: TalkContext
) {
return commitLocalUpdate(environment, store => {
const record = store.get(LOCAL_ID)!;
record.setValue(input.id, "commentID");
if (pym) {
// This sets the comment id on the parent url.
pym.sendMessage("setCommentID", input.id || "");
}
});
}
@@ -65,7 +65,9 @@ export class ClickOutside extends React.Component<ClickOutsideProps> {
}
}
const ClickOutsideWithContext: StatelessComponent<Props> = props => (
const ClickOutsideWithContext: StatelessComponent<
ClickOutsideProps
> = props => (
<UIContext.Consumer>
{({ registerClickFarAway }) => (
<ClickOutside {...props} registerClickFarAway={registerClickFarAway} />
@@ -1 +1 @@
export { default as ClickOutside, ClickFarAwayRegister } from "./ClickOutside";
export { default, ClickFarAwayRegister } from "./ClickOutside";