mirror of
https://github.com/wassname/talk.git
synced 2026-09-09 11:38:08 +08:00
Merge branch 'auth-views' of github.com:coralproject/talk into auth-views
This commit is contained in:
@@ -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}
|
||||
|
||||
@@ -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
-3
@@ -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);
|
||||
});
|
||||
+1
-1
@@ -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 +0,0 @@
|
||||
export { default as withSetCommentID } from "./withSetCommentID";
|
||||
Reference in New Issue
Block a user