Merge branch 'auth-views' of github.com:coralproject/talk into auth-views

This commit is contained in:
Belen Curcio
2018-08-13 11:58:22 -03:00
74 changed files with 3232 additions and 497 deletions
@@ -22,7 +22,6 @@ const App: StatelessComponent<AppProps> = props => {
{view}
</Flex>
);
// return <div className={styles.root}>{view}</div>;
};
export default App;
@@ -1,18 +1,28 @@
.root {
width: 100%;
padding-bottom: var(--spacing-unit);
}
.textarea {
composes: bodyCopy from "talk-ui/shared/typography.css";
display: block;
height: 100px;
height: 150px;
width: 100%;
box-sizing: border-box;
margin-bottom: var(--spacing-unit);
padding: var(--spacing-unit);
border-radius: var(--round-corners);
resize: vertical;
}
.postButtonContainer {
display: flex;
justify-content: flex-end;
&:first-child {
flex-grow: 1;
}
&:last-child {
flex-grow: 0;
}
}
@@ -5,8 +5,8 @@ import { Field, Form } from "react-final-form";
import PoweredBy from "./PoweredBy";
import { OnSubmit } from "talk-framework/lib/form";
import { required } from "talk-framework/lib/validation";
import PoweredBy from "talk-stream/components/PoweredBy";
import { Button, Typography } from "talk-ui/components";
import * as styles from "./PostCommentForm.css";
interface FormProps {
@@ -15,6 +15,7 @@ interface FormProps {
export interface PostCommentFormProps {
onSubmit: OnSubmit<FormProps>;
signedIn: boolean;
}
const PostCommentForm: StatelessComponent<PostCommentFormProps> = props => (
@@ -29,6 +30,7 @@ const PostCommentForm: StatelessComponent<PostCommentFormProps> = props => (
name={input.name}
onChange={input.onChange}
value={input.value}
placeholder="Post a comment"
/>
{meta.touched &&
(meta.error || meta.submitError) && (
@@ -39,14 +41,26 @@ const PostCommentForm: StatelessComponent<PostCommentFormProps> = props => (
</div>
)}
</Field>
<div className={styles.postButtonContainer}>
<PoweredBy />
<Localized id="comments-postCommentForm-post">
<Button color="primary" variant="filled" disabled={submitting}>
Post
</Button>
</Localized>
</div>
{props.signedIn ? (
<div className={styles.postButtonContainer}>
<PoweredBy />
<Localized id="comments-postCommentForm-submit">
<Button color="primary" variant="filled" disabled={submitting}>
Submit
</Button>
</Localized>
</div>
) : (
<Button
color="primary"
variant="filled"
disabled
fullWidth
size="large"
>
Sign In and join the conversation
</Button>
)}
</form>
)}
</Form>
@@ -1,16 +1,11 @@
import React, { StatelessComponent } from "react";
import { Flex, Typography } from "talk-ui/components";
import * as styles from "./PoweredBy.css";
const PoweredBy: StatelessComponent = props => {
const PoweredBy: StatelessComponent = () => {
return (
<Flex itemGutter="half">
<Typography className={styles.text} variant="bodyCopy">
Powered by
</Typography>
<Typography className={styles.text} variant="heading4">
The Coral Project
</Typography>
<Typography variant="bodyCopy">Powered by</Typography>
<Typography variant="heading4">The Coral Project</Typography>
</Flex>
);
};
@@ -0,0 +1,3 @@
.child {
width: initial;
}
@@ -0,0 +1,51 @@
import { Localized } from "fluent-react/compat";
import React, { StatelessComponent } from "react";
import { Button, Flex, Typography } from "talk-ui/components";
import MatchMedia from "talk-ui/components/MatchMedia";
import * as styles from "./UserBoxAuthenticated.css";
export interface UserBoxUnauthenticatedProps {
onSignOut: () => void;
}
const UserBoxAuthenticated: StatelessComponent<
UserBoxUnauthenticatedProps
> = props => {
return (
<Flex itemGutter>
<Flex itemGutter="half" className={styles.child}>
<MatchMedia gteWidth="sm">
<Localized id="comments-userBoxAuthenticated-signedInAs">
<Typography variant="bodyCopy" component="span">
Signed in as
</Typography>
</Localized>
<Typography variant="bodyCopyBold" component="span">
okbel
</Typography>
</MatchMedia>
</Flex>
<Flex itemGutter="half" className={styles.child}>
<Localized id="comments-userBoxAuthenticated-notYou">
<Typography variant="bodyCopy" component="span">
Not you?
</Typography>
</Localized>
<Localized id="comments-userBoxAuthenticated-signOut">
<Button
color="primary"
size="small"
variant="underlined"
onClick={props.onSignOut}
>
Sign Out
</Button>
</Localized>
</Flex>
</Flex>
);
};
export default UserBoxAuthenticated;
@@ -0,0 +1,16 @@
import { shallow } from "enzyme";
import { noop } from "lodash";
import React from "react";
import { PropTypesOf } from "talk-framework/types";
import UserBoxUnauthenticated from "./UserBoxUnauthenticated";
it("renders correctly", () => {
const props: PropTypesOf<typeof UserBoxUnauthenticated> = {
onSignIn: noop,
onRegister: noop,
};
const wrapper = shallow(<UserBoxUnauthenticated {...props} />);
expect(wrapper).toMatchSnapshot();
});
@@ -31,7 +31,7 @@ class PostCommentFormContainer extends Component<InnerProps> {
return undefined;
};
public render() {
return <PostCommentForm onSubmit={this.onSubmit} />;
return <PostCommentForm onSubmit={this.onSubmit} signedIn={false} />;
}
}
@@ -11,6 +11,7 @@ import {
} from "talk-stream/mutations";
import { Popup } from "talk-ui/components";
// import UserBoxAuthenticated from "../components/UserBoxAuthenticated";
import UserBoxUnauthenticated from "../components/UserBoxUnauthenticated";
interface InnerProps {
@@ -44,6 +45,7 @@ export class UserBoxContainer extends Component<InnerProps> {
onBlur={this.handleBlur}
onClose={this.handleClose}
/>
{/* <UserBoxAuthenticated onSignOut={this.handleRegister} /> */}
<UserBoxUnauthenticated
onSignIn={this.handleSignIn}
onRegister={this.handleRegister}
+12 -4
View File
@@ -10,16 +10,24 @@ import {
} from "talk-framework/lib/bootstrap";
import AppContainer from "./containers/AppContainer";
import {
onPostMessageAuthError,
onPostMessageSetAuthToken,
onPymSetCommentID,
} from "./listeners";
import { initLocalState } from "./local";
import localesData from "./locales";
import { withSetCommentID } from "./pym";
const pymFeatures = [withSetCommentID];
const listeners = [
onPymSetCommentID,
onPostMessageSetAuthToken,
onPostMessageAuthError,
];
// This is called when the context is first initialized.
async function init(context: TalkContext) {
await initLocalState(context.relayEnvironment);
pymFeatures.forEach(f => f(context));
await initLocalState(context.relayEnvironment, context);
listeners.forEach(f => f(context));
}
async function main() {
@@ -0,0 +1,5 @@
export { default as onPymSetCommentID } from "./onPymSetCommentID";
export {
default as onPostMessageSetAuthToken,
} from "./onPostMessageSetAuthToken";
export { default as onPostMessageAuthError } from "./onPostMessageAuthError";
@@ -0,0 +1,11 @@
import { TalkContext } from "talk-framework/lib/bootstrap";
export default function onPostMessageSetAuthToken({
postMessage,
}: TalkContext) {
// Auth popup will use this to send back errors during login.
postMessage!.on("authError", error => {
// tslint:disable-next-line:no-console
console.error(error);
});
}
@@ -0,0 +1,32 @@
import { Environment, RecordSource } from "relay-runtime";
import { LOCAL_ID } from "talk-framework/lib/relay";
import { createInMemoryStorage } from "talk-framework/lib/storage";
import { createRelayEnvironment } from "talk-framework/testHelpers";
import onPostMessageSetAuthToken from "./onPostMessageSetAuthToken";
let relayEnvironment: Environment;
const source: RecordSource = new RecordSource();
beforeAll(() => {
relayEnvironment = createRelayEnvironment({
source,
});
});
it("Sets auth token", () => {
const token = "auth-token";
const context = {
postMessage: {
on: (name: string, cb: (token: string) => void) => {
expect(name).toBe("setAuthToken");
cb(token);
},
},
relayEnvironment,
localStorage: createInMemoryStorage(),
};
onPostMessageSetAuthToken(context as any);
expect(source.get(LOCAL_ID)!.authToken).toEqual(token);
});
@@ -0,0 +1,11 @@
import { TalkContext } from "talk-framework/lib/bootstrap";
import { commit as setAuthToken } from "talk-framework/mutations/SetAuthTokenMutation";
export default function onPostMessageSetAuthToken(ctx: TalkContext) {
const { relayEnvironment, postMessage } = ctx;
// Auth popup will use this to handle a successful login.
postMessage!.on("setAuthToken", (authToken: string) => {
setAuthToken(relayEnvironment, { authToken }, ctx);
});
}
@@ -3,7 +3,7 @@ import { Environment, RecordSource } from "relay-runtime";
import { LOCAL_ID } from "talk-framework/lib/relay";
import { createRelayEnvironment } from "talk-framework/testHelpers";
import withSetCommentID from "./withSetCommentID";
import onPymSetCommentID from "./onPymSetCommentID";
let relayEnvironment: Environment;
const source: RecordSource = new RecordSource();
@@ -25,7 +25,7 @@ it("Sets comment id", () => {
},
relayEnvironment,
};
withSetCommentID(context as any);
onPymSetCommentID(context as any);
expect(source.get(LOCAL_ID)!.commentID).toEqual(id);
});
@@ -40,6 +40,6 @@ it("Sets comment id to null when empty", () => {
},
relayEnvironment,
};
withSetCommentID(context as any);
onPymSetCommentID(context as any);
expect(source.get(LOCAL_ID)!.commentID).toEqual(null);
});
@@ -3,7 +3,7 @@ import { commitLocalUpdate } from "react-relay";
import { TalkContext } from "talk-framework/lib/bootstrap";
import { LOCAL_ID } from "talk-framework/lib/relay";
export default function withSetCommentID({
export default function onPymSetCommentID({
relayEnvironment,
pym,
}: TalkContext) {
@@ -12,6 +12,7 @@ exports[`init local state 1`] = `
\\"client:root.local\\": {
\\"__id\\": \\"client:root.local\\",
\\"__typename\\": \\"Local\\",
\\"authToken\\": \\"\\",
\\"network\\": {
\\"__ref\\": \\"client:root.local.network\\"
},
@@ -2,6 +2,7 @@ import { Environment, RecordSource } from "relay-runtime";
import { timeout } from "talk-common/utils";
import { LOCAL_ID } from "talk-framework/lib/relay";
import { createInMemoryStorage } from "talk-framework/lib/storage";
import { createRelayEnvironment } from "talk-framework/testHelpers";
import initLocalState from "./initLocalState";
@@ -18,7 +19,7 @@ beforeEach(() => {
});
it("init local state", async () => {
initLocalState(environment);
initLocalState(environment, { localStorage: createInMemoryStorage() } as any);
await timeout();
expect(JSON.stringify(source.toJSON(), null, 2)).toMatchSnapshot();
});
@@ -32,7 +33,7 @@ it("set assetID from query", () => {
document.title,
`http://localhost/?assetID=${assetID}`
);
initLocalState(environment);
initLocalState(environment, { localStorage: createInMemoryStorage() } as any);
expect(source.get(LOCAL_ID)!.assetID).toBe(assetID);
window.history.replaceState(previousState, document.title, previousLocation);
});
@@ -46,7 +47,16 @@ it("set commentID from query", () => {
document.title,
`http://localhost/?commentID=${commentID}`
);
initLocalState(environment);
initLocalState(environment, { localStorage: createInMemoryStorage() } as any);
expect(source.get(LOCAL_ID)!.commentID).toBe(commentID);
window.history.replaceState(previousState, document.title, previousLocation);
});
it("set authToken from localStorage", () => {
const authToken = "auth-token";
const localStorage = createInMemoryStorage();
localStorage.setItem("authToken", authToken);
initLocalState(environment, { localStorage } as any);
expect(source.get(LOCAL_ID)!.authToken).toBe(authToken);
localStorage.removeItem("authToken");
});
@@ -1,6 +1,7 @@
import qs from "query-string";
import { commitLocalUpdate, Environment } from "relay-runtime";
import { TalkContext } from "talk-framework/lib/bootstrap";
import {
createAndRetain,
LOCAL_ID,
@@ -17,14 +18,21 @@ import {
/**
* Initializes the local state, before we start the App.
*/
export default async function initLocalState(environment: Environment) {
export default async function initLocalState(
environment: Environment,
{ localStorage }: TalkContext
) {
commitLocalUpdate(environment, s => {
// TODO: (cvle) move local, auth token and network initialization to framework.
const root = s.getRoot();
// Create the Local Record which is the Root for the client states.
const localRecord = createAndRetain(environment, s, LOCAL_ID, LOCAL_TYPE);
root.setLinkedRecord(localRecord, "local");
// Set auth token
localRecord.setValue(localStorage.getItem("authToken") || "", "authToken");
// Parse query params
const query = qs.parse(location.search);
@@ -21,6 +21,7 @@ type Local {
assetURL: String
commentID: String
authPopup: AuthPopup!
authToken: String
}
extend type Query {
-1
View File
@@ -1 +0,0 @@
export { default as withSetCommentID } from "./withSetCommentID";