浏览代码

Add deomstrative login/registration routes to frontend

Andrew Swistak 5 年之前
父节点
当前提交
d8a4d7671c

+ 23 - 10
app/javascript/src/components/layout/ApplicationLayout.tsx

@@ -1,4 +1,4 @@
-import React from 'react';
+import React, {useContext} from 'react';
 import {Navbar, Nav} from 'react-bootstrap';
 import {Link} from 'found';
 
@@ -9,6 +9,8 @@ interface Props {
 }
 
 function ApplicationLayout({children}: Props): React.ReactElement {
+  const {user} = useContext(UserContext);
+
   return (
     <div>
       <nav>
@@ -23,20 +25,31 @@ function ApplicationLayout({children}: Props): React.ReactElement {
                 View Pokemon
               </Nav.Link>
             </Nav>
-            <UserContext.Consumer>
-              {user => user && user.email}
-            </UserContext.Consumer>
+            {user && user.email}
           </Navbar.Collapse>
         </Navbar>
       </nav>
 
       {children}
-      <p>
-        <Link to='/login'>Signin</Link>
-      </p>
-      <p>
-        <Link to='/signup'>Signup</Link>
-      </p>
+
+      {
+        user ? (
+          <p>
+            <Link to='/logout'>
+              Logout
+            </Link>
+          </p>
+        ) : (
+          <>
+            <p>
+              <Link to='/login'>Signin</Link>
+            </p>
+            <p>
+              <Link to='/signup'>Signup</Link>
+            </p>
+          </>
+        )
+      }
       <p>
         <Link to='/pokemon'>Show me the pokemon!</Link>
       </p>

+ 24 - 7
app/javascript/src/components/pages/Login.tsx

@@ -1,36 +1,50 @@
+import React, {useState, useContext} from 'react';
+import {useRouter} from 'found';
 import {commitMutation, graphql} from 'react-relay';
-import React, {useState} from 'react';
 import {Form, Row, Col, Button} from 'react-bootstrap';
 
-import {authEnvironment} from '../../graphqlEnvironment';
+import {UserContext} from '../../context/User';
+import graphqlEnvironment from '../../graphqlEnvironment';
+
+import {LoginMutationResponse as LoginResponse} from './__generated__/LoginMutation.graphql';
+import {User} from '../../context/User';
 
 const mutation = graphql`
   mutation LoginMutation($login: String!, $password: String!) {
     userLogin(login: $login, password: $password) {
       user {
+        iid
         username
+        email
       }
     }
   }
 `;
 
-const onSubmit = (login, password): void => {
+const onSubmit = (login, password, onCompleted): void => {
   const variables = {
     login,
     password,
   };
 
-  commitMutation(authEnvironment, {
+  commitMutation(graphqlEnvironment, {
     mutation,
     variables,
-    onCompleted: e => console.log(e),
-    onError: (err): void => console.error(err),
+    onCompleted: (e: LoginResponse) => onCompleted(e.userLogin.user),
+    onError: (err): void => console.error(err), //FIXME
   });
 };
 
 const Login: React.FC = () => {
   const [login, setLogin] = useState('');
   const [password, setPassword] = useState('');
+  const {setUser} = useContext(UserContext);
+  const {router} = useRouter();
+
+  const onLogin = (user: User): void => {
+    setUser(user);
+    router.push('/');
+  };
 
   return (
     <Form className="my-4" onSubmit={e => e.preventDefault()}>
@@ -53,7 +67,10 @@ const Login: React.FC = () => {
       </Form.Group>
       <Form.Group as={Row}>
         <Col sm={{span: 10, offset: 2}}>
-          <Button onClick={() => onSubmit(login, password)} type="submit">Login</Button>
+          <Button className="mr-2" onClick={() => onSubmit(login, password, onLogin)} type="submit">Login</Button>
+          <Button className="mx-2" href="/auth/discord"><span className="fab fa-discord" /></Button>
+          <Button className="mx-2" href="/auth/google"><span className="fab fa-google" /></Button>
+          <Button className="ml-2" href="/auth/reddit"><span className="fab fa-reddit" /></Button>
         </Col>
       </Form.Group>
     </Form>

+ 30 - 0
app/javascript/src/components/pages/Logout.tsx

@@ -0,0 +1,30 @@
+import React, {useContext} from 'react';
+import {commitMutation, graphql} from 'react-relay';
+
+import {UserContext} from '../../context/User';
+import graphqlEnvironment from '../../graphqlEnvironment';
+
+const mutation = graphql`
+  mutation LogoutMutation {
+    userLogout {
+      success
+    }
+  }
+`;
+
+const logout = (onLogout): void => {
+  commitMutation(graphqlEnvironment, {
+    mutation,
+    variables: {},
+    onCompleted: onLogout, // FIXME
+    onError: (err): void => console.error(err), //FIXME
+  });
+};
+
+const Logout: React.FC = () => {
+  const {setUser} = useContext(UserContext);
+  logout(() => setUser(null));
+  return <>You&apos;ve been logged out!</>;
+};
+
+export default Logout;

+ 2 - 2
app/javascript/src/components/pages/Signup.tsx

@@ -2,7 +2,7 @@ import {commitMutation, graphql} from 'react-relay';
 import React, {useState} from 'react';
 import {Form, Row, Col, Button} from 'react-bootstrap';
 
-import {authEnvironment} from '../../graphqlEnvironment';
+import graphqlEnvironment from '../../graphqlEnvironment';
 
 const mutation = graphql`
   mutation SignupMutation($username: String!, $email: String!, $password: String!, $passwordConfirmation: String!) {
@@ -22,7 +22,7 @@ const onSubmit = (username, email, password, passwordConfirmation): void => {
     passwordConfirmation,
   };
 
-  commitMutation(authEnvironment, {
+  commitMutation(graphqlEnvironment, {
     mutation,
     variables,
     onCompleted: () => {

+ 25 - 3
app/javascript/src/components/pages/__generated__/LoginMutation.graphql.ts

@@ -8,7 +8,9 @@ export type LoginMutationVariables = {
 export type LoginMutationResponse = {
     readonly userLogin: {
         readonly user: {
+            readonly iid: string;
             readonly username: string | null;
+            readonly email: string | null;
         };
     } | null;
 };
@@ -26,7 +28,9 @@ mutation LoginMutation(
 ) {
   userLogin(login: $login, password: $password) {
     user {
+      iid
       username
+      email
       id
     }
   }
@@ -61,11 +65,25 @@ v1 = [
   }
 ],
 v2 = {
+  "kind": "ScalarField",
+  "alias": null,
+  "name": "iid",
+  "args": null,
+  "storageKey": null
+},
+v3 = {
   "kind": "ScalarField",
   "alias": null,
   "name": "username",
   "args": null,
   "storageKey": null
+},
+v4 = {
+  "kind": "ScalarField",
+  "alias": null,
+  "name": "email",
+  "args": null,
+  "storageKey": null
 };
 return {
   "kind": "Request",
@@ -94,7 +112,9 @@ return {
             "concreteType": "User",
             "plural": false,
             "selections": [
-              (v2/*: any*/)
+              (v2/*: any*/),
+              (v3/*: any*/),
+              (v4/*: any*/)
             ]
           }
         ]
@@ -125,6 +145,8 @@ return {
             "plural": false,
             "selections": [
               (v2/*: any*/),
+              (v3/*: any*/),
+              (v4/*: any*/),
               {
                 "kind": "ScalarField",
                 "alias": null,
@@ -142,10 +164,10 @@ return {
     "operationKind": "mutation",
     "name": "LoginMutation",
     "id": null,
-    "text": "mutation LoginMutation(\n  $login: String!\n  $password: String!\n) {\n  userLogin(login: $login, password: $password) {\n    user {\n      username\n      id\n    }\n  }\n}\n",
+    "text": "mutation LoginMutation(\n  $login: String!\n  $password: String!\n) {\n  userLogin(login: $login, password: $password) {\n    user {\n      iid\n      username\n      email\n      id\n    }\n  }\n}\n",
     "metadata": {}
   }
 };
 })();
-(node as any).hash = '69f202bbc304aec0370d7c3d6a394bf3';
+(node as any).hash = 'b09cfc93f0bc74a76ff09884f6b2dfe1';
 export default node;

+ 72 - 0
app/javascript/src/components/pages/__generated__/LogoutMutation.graphql.ts

@@ -0,0 +1,72 @@
+/* tslint:disable */
+
+import { ConcreteRequest } from "relay-runtime";
+export type LogoutMutationVariables = {};
+export type LogoutMutationResponse = {
+    readonly userLogout: {
+        readonly success: boolean;
+    } | null;
+};
+export type LogoutMutation = {
+    readonly response: LogoutMutationResponse;
+    readonly variables: LogoutMutationVariables;
+};
+
+
+
+/*
+mutation LogoutMutation {
+  userLogout {
+    success
+  }
+}
+*/
+
+const node: ConcreteRequest = (function(){
+var v0 = [
+  {
+    "kind": "LinkedField",
+    "alias": null,
+    "name": "userLogout",
+    "storageKey": null,
+    "args": null,
+    "concreteType": "LogoutPayload",
+    "plural": false,
+    "selections": [
+      {
+        "kind": "ScalarField",
+        "alias": null,
+        "name": "success",
+        "args": null,
+        "storageKey": null
+      }
+    ]
+  }
+];
+return {
+  "kind": "Request",
+  "fragment": {
+    "kind": "Fragment",
+    "name": "LogoutMutation",
+    "type": "Mutation",
+    "metadata": null,
+    "argumentDefinitions": [],
+    "selections": (v0/*: any*/)
+  },
+  "operation": {
+    "kind": "Operation",
+    "name": "LogoutMutation",
+    "argumentDefinitions": [],
+    "selections": (v0/*: any*/)
+  },
+  "params": {
+    "operationKind": "mutation",
+    "name": "LogoutMutation",
+    "id": null,
+    "text": "mutation LogoutMutation {\n  userLogout {\n    success\n  }\n}\n",
+    "metadata": {}
+  }
+};
+})();
+(node as any).hash = '6e2adec762cb01ecf93e79615badc4c0';
+export default node;

+ 5 - 2
app/javascript/src/context/User.tsx

@@ -5,7 +5,10 @@ import graphqlEnvironment from '../graphqlEnvironment';
 import {User_QueryResponse as UserResponse} from './__generated__/User_Query.graphql';
 
 export type User = UserResponse['me'];
-export const UserContext = React.createContext<User | null>(null);
+type SetUser = {
+  setUser: (User) => void,
+}
+export const UserContext = React.createContext<{user: User | null} & SetUser>(null);
 
 const query = graphql`
   query User_Query {
@@ -31,5 +34,5 @@ export const UserProvider: React.FC<Props> = ({children}) => {
     })();
   }, []);
 
-  return <UserContext.Provider value={user}>{children}</UserContext.Provider>;
+  return <UserContext.Provider value={{user, setUser}}>{children}</UserContext.Provider>;
 };

+ 3 - 1
app/javascript/src/graphqlEnvironment.ts

@@ -33,6 +33,8 @@ export const fetchQuery: FetchFunction = (
     return new Promise(resolve => resolve(cachedData));
   }
 
+  const query = operation.text.replace(/\s+/g, ' ');
+
   return fetch('/api/graphql', {
     credentials: 'same-origin',
     method: 'POST',
@@ -41,7 +43,7 @@ export const fetchQuery: FetchFunction = (
       ...csrf.headers,
     },
     body: JSON.stringify({
-      query: operation.text,
+      query,
       variables,
     }),
   }).then(response => {

+ 2 - 0
app/javascript/src/index.tsx

@@ -16,6 +16,7 @@ import PokemonIndex from './components/pages/pokemon/Index';
 import PokemonCreate from './components/pages/pokemon/Create';
 import Signup from './components/pages/Signup';
 import Login from './components/pages/Login';
+import Logout from './components/pages/Logout';
 import {UserProvider} from './context/User';
 
 function App(): React.ReactElement {
@@ -40,6 +41,7 @@ function App(): React.ReactElement {
       />
 
       <Route Component={PokemonCreate} path='pokemon/create' />
+      <Route Component={Logout} path='logout' />
       <Route Component={Login} path='login' />
       <Route Component={Signup} path='signup' />
       <Route Component={NotFound} path='*' />

+ 1 - 1
db/schema.graphql

@@ -29,7 +29,7 @@ type LoginPayload {
 Autogenerated return type of Logout
 """
 type LogoutPayload {
-  user: User!
+  success: Boolean!
 }
 
 type Mutation {