mirror of
https://github.com/wassname/talk.git
synced 2026-08-12 12:30:39 +08:00
[next] Auth Popup v2 (#2101)
* feat: Implement new Sign In view * feat: Move forgot + resetPassword to new design * feat: Implement sign up with new design * fix: narrow gutter * test: add unit tests * test: integration tests * feat: support show / hide password * feat: support oauth2 flow * feat: add views for user completion * feat: implement oauth2 sign up * test: fix snapshots * fix: lint * fix: get more complete mutation response * fix: removed array of OIDC integrations * fix: renamed resolver function * fix: adapt oidc client implementation * fix: targetFilter should be stream on signup * fix: removed unneeded message * fix: moved password into local profile * fix: made username optional, removed valid null value * fix: linting * fix: respect targetFilter * feat: support user registration mutations - Added `setUsername` - Added `setEmail` - Added `setPassword` - Added `permit` to `@auth` - Added `email` to `User` * fix: fixed issue with query * feat: added user password update * feat: complete sign in mutation * fix: adapt some rebasing gitches * test: improve tests * test: unittest for setting auth token * fix: failing tests * test: move most tests from enzyme to react-test-renderer * fix: remove schema warnings in tests * test: improve window mock * test: test different social login configurations * test: test social logins for sign up * fix: use htmlFor instead of for * test: more feature tests * feat: always go through account completion * test: feature test account completion * feat: addtional account completion test * Update start.ts * chore: refactor auth token retrieval logic
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
import * as React from "react";
|
||||
import { Component } from "react";
|
||||
|
||||
import { AccountCompletionContainer_auth as AuthData } from "talk-auth/__generated__/AccountCompletionContainer_auth.graphql";
|
||||
import { AccountCompletionContainer_me as UserData } from "talk-auth/__generated__/AccountCompletionContainer_me.graphql";
|
||||
import { AccountCompletionContainerLocal as Local } from "talk-auth/__generated__/AccountCompletionContainerLocal.graphql";
|
||||
import {
|
||||
CompleteAccountMutation,
|
||||
SetViewMutation,
|
||||
withCompleteAccountMutation,
|
||||
withSetViewMutation,
|
||||
} from "talk-auth/mutations";
|
||||
import {
|
||||
graphql,
|
||||
withFragmentContainer,
|
||||
withLocalStateContainer,
|
||||
} from "talk-framework/lib/relay";
|
||||
|
||||
interface Props {
|
||||
completeAccount: CompleteAccountMutation;
|
||||
setView: SetViewMutation;
|
||||
local: Local;
|
||||
auth: AuthData;
|
||||
me: UserData | null;
|
||||
}
|
||||
|
||||
function handleAccountCompletion(props: Props) {
|
||||
const {
|
||||
local: { view, authToken },
|
||||
me,
|
||||
auth,
|
||||
setView,
|
||||
completeAccount,
|
||||
} = props;
|
||||
if (me) {
|
||||
if (!me.email) {
|
||||
if (view !== "ADD_EMAIL_ADDRESS") {
|
||||
setView({ view: "ADD_EMAIL_ADDRESS" });
|
||||
}
|
||||
} else if (!me.username) {
|
||||
if (view !== "CREATE_USERNAME") {
|
||||
setView({ view: "CREATE_USERNAME" });
|
||||
}
|
||||
} else if (
|
||||
!me.profiles.some(p => p.__typename === "LocalProfile") &&
|
||||
auth.integrations.local.enabled &&
|
||||
auth.integrations.local.targetFilter.stream
|
||||
) {
|
||||
if (view !== "CREATE_PASSWORD") {
|
||||
setView({ view: "CREATE_PASSWORD" });
|
||||
}
|
||||
} else {
|
||||
completeAccount({ authToken: authToken! });
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
interface State {
|
||||
checkedCompletionStatus: boolean;
|
||||
}
|
||||
|
||||
class AccountCompletionContainer extends Component<Props, State> {
|
||||
public state = {
|
||||
checkedCompletionStatus: false,
|
||||
};
|
||||
|
||||
public componentDidMount() {
|
||||
handleAccountCompletion(this.props);
|
||||
this.setState({ checkedCompletionStatus: true });
|
||||
}
|
||||
public componentDidUpdate() {
|
||||
handleAccountCompletion(this.props);
|
||||
}
|
||||
|
||||
public render() {
|
||||
const { children } = this.props;
|
||||
// We skip first frame to check for completion status.
|
||||
if (!this.state.checkedCompletionStatus) {
|
||||
return null;
|
||||
}
|
||||
return <>{children}</>;
|
||||
}
|
||||
}
|
||||
|
||||
const enhanced = withLocalStateContainer(
|
||||
graphql`
|
||||
fragment AccountCompletionContainerLocal on Local {
|
||||
authToken
|
||||
view
|
||||
}
|
||||
`
|
||||
)(
|
||||
withFragmentContainer<Props>({
|
||||
auth: graphql`
|
||||
fragment AccountCompletionContainer_auth on Auth {
|
||||
integrations {
|
||||
local {
|
||||
enabled
|
||||
targetFilter {
|
||||
stream
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
me: graphql`
|
||||
fragment AccountCompletionContainer_me on User {
|
||||
username
|
||||
email
|
||||
profiles {
|
||||
__typename
|
||||
}
|
||||
}
|
||||
`,
|
||||
})(
|
||||
withSetViewMutation(withCompleteAccountMutation(AccountCompletionContainer))
|
||||
)
|
||||
);
|
||||
|
||||
export default enhanced;
|
||||
@@ -1,18 +1,38 @@
|
||||
import * as React from "react";
|
||||
import { StatelessComponent } from "react";
|
||||
import { Component } from "react";
|
||||
|
||||
import { AppContainer_auth as AuthData } from "talk-auth/__generated__/AppContainer_auth.graphql";
|
||||
import { AppContainer_me as UserData } from "talk-auth/__generated__/AppContainer_me.graphql";
|
||||
import { AppContainerLocal as Local } from "talk-auth/__generated__/AppContainerLocal.graphql";
|
||||
import { graphql, withLocalStateContainer } from "talk-framework/lib/relay";
|
||||
import {
|
||||
graphql,
|
||||
withFragmentContainer,
|
||||
withLocalStateContainer,
|
||||
} from "talk-framework/lib/relay";
|
||||
|
||||
import App from "../components/App";
|
||||
import AccountCompletionContainer from "./AccountCompletionContainer";
|
||||
|
||||
interface InnerProps {
|
||||
interface Props {
|
||||
local: Local;
|
||||
auth: AuthData;
|
||||
me: UserData | null;
|
||||
}
|
||||
|
||||
const AppContainer: StatelessComponent<InnerProps> = ({ local: { view } }) => {
|
||||
return <App view={view} />;
|
||||
};
|
||||
class AppContainer extends Component<Props> {
|
||||
public render() {
|
||||
const {
|
||||
local: { view },
|
||||
auth,
|
||||
me,
|
||||
} = this.props;
|
||||
return (
|
||||
<AccountCompletionContainer auth={auth} me={me}>
|
||||
<App view={view} auth={auth} />
|
||||
</AccountCompletionContainer>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const enhanced = withLocalStateContainer(
|
||||
graphql`
|
||||
@@ -20,6 +40,21 @@ const enhanced = withLocalStateContainer(
|
||||
view
|
||||
}
|
||||
`
|
||||
)(AppContainer);
|
||||
)(
|
||||
withFragmentContainer<Props>({
|
||||
auth: graphql`
|
||||
fragment AppContainer_auth on Auth {
|
||||
...SignInContainer_auth
|
||||
...SignUpContainer_auth
|
||||
...AccountCompletionContainer_auth
|
||||
}
|
||||
`,
|
||||
me: graphql`
|
||||
fragment AppContainer_me on User {
|
||||
...AccountCompletionContainer_me
|
||||
}
|
||||
`,
|
||||
})(AppContainer)
|
||||
);
|
||||
|
||||
export default enhanced;
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
import React, { Component } from "react";
|
||||
import ForgotPassword from "../components/ForgotPassword";
|
||||
|
||||
class ForgotPasswordContainer extends Component {
|
||||
public render() {
|
||||
// tslint:disable-next-line:no-empty
|
||||
return <ForgotPassword onSubmit={() => {}} />;
|
||||
}
|
||||
}
|
||||
|
||||
export default ForgotPasswordContainer;
|
||||
@@ -1,11 +0,0 @@
|
||||
import React, { Component } from "react";
|
||||
import ResetPassword from "../components/ResetPassword";
|
||||
|
||||
class ResetPasswordContainer extends Component {
|
||||
public render() {
|
||||
// tslint:disable-next-line:no-empty
|
||||
return <ResetPassword onSubmit={() => {}} />;
|
||||
}
|
||||
}
|
||||
|
||||
export default ResetPasswordContainer;
|
||||
@@ -1,40 +0,0 @@
|
||||
import { FORM_ERROR } from "final-form";
|
||||
import React, { Component } from "react";
|
||||
import SignIn, { SignInForm } from "../components/SignIn";
|
||||
import {
|
||||
SetViewMutation,
|
||||
SignInMutation,
|
||||
withSetViewMutation,
|
||||
withSignInMutation,
|
||||
} from "../mutations";
|
||||
|
||||
interface SignInContainerProps {
|
||||
signIn: SignInMutation;
|
||||
setView: SetViewMutation;
|
||||
}
|
||||
|
||||
class SignInContainer extends Component<SignInContainerProps> {
|
||||
private onSubmit: SignInForm["onSubmit"] = async (input, form) => {
|
||||
try {
|
||||
await this.props.signIn(input);
|
||||
return form.reset();
|
||||
} catch (error) {
|
||||
return { [FORM_ERROR]: error.message };
|
||||
}
|
||||
};
|
||||
private goToForgotPassword = () =>
|
||||
this.props.setView({ view: "FORGOT_PASSWORD" });
|
||||
private goToSignUp = () => this.props.setView({ view: "SIGN_UP" });
|
||||
public render() {
|
||||
return (
|
||||
<SignIn
|
||||
onSubmit={this.onSubmit}
|
||||
onGotoForgotPassword={this.goToForgotPassword}
|
||||
onGotoSignUp={this.goToSignUp}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const enhanced = withSetViewMutation(withSignInMutation(SignInContainer));
|
||||
export default enhanced;
|
||||
@@ -1,33 +0,0 @@
|
||||
import { FORM_ERROR } from "final-form";
|
||||
import React, { Component } from "react";
|
||||
import SignUp, { SignUpForm } from "../components/SignUp";
|
||||
|
||||
import {
|
||||
SetViewMutation,
|
||||
SignUpMutation,
|
||||
withSetViewMutation,
|
||||
withSignUpMutation,
|
||||
} from "../mutations";
|
||||
|
||||
interface SignUpContainerProps {
|
||||
signUp: SignUpMutation;
|
||||
setView: SetViewMutation;
|
||||
}
|
||||
|
||||
class SignUpContainer extends Component<SignUpContainerProps> {
|
||||
private onSubmit: SignUpForm["onSubmit"] = async (input, form) => {
|
||||
try {
|
||||
await this.props.signUp(input);
|
||||
return form.reset();
|
||||
} catch (error) {
|
||||
return { [FORM_ERROR]: error.message };
|
||||
}
|
||||
};
|
||||
private goToSignIn = () => this.props.setView({ view: "SIGN_IN" });
|
||||
public render() {
|
||||
return <SignUp onSubmit={this.onSubmit} onGotoSignIn={this.goToSignIn} />;
|
||||
}
|
||||
}
|
||||
|
||||
const enhanced = withSetViewMutation(withSignUpMutation(SignUpContainer));
|
||||
export default enhanced;
|
||||
Reference in New Issue
Block a user