-
Notifications
You must be signed in to change notification settings - Fork 7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Initial implementation of Login and Registration components #1
Conversation
fix npm version typo in DEVELOPMENT.md file use babel-lint instead of eslint for stage2 features add scaffolding for redux store remove lib/index.js build file from repo basic styling for login widget
actions can now talk to graphql and get responses back serve webpack using middleware instead of dev server
registration action that talks to the server using graphql webpack dev hot reloading
break schema up into smaller files add password to register
fetch now passes the key along correctly
|
||
const app = express(); | ||
|
||
const PORT = 3000; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should we add a node_env for port so that other people can change it easily.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we want to do this, I would prefer to add it as an optional argument. --port=3000
for example.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now that I thought about this a little more, I find it unlikely that developers will want to change this port, at least when developing this project. On their own server, they definitely would want that capability.
app.use('/graphql', graphqlHTTP((req) => { | ||
return { | ||
schema: schema, | ||
rootValue: req, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we can do
app.use('/graphql', graphqlHTTP({
schema: MyGraphQLSchema,
graphiql: true
}));
The req
will be passed in as context
. I don't think we want to pass req as rootValue
here. rootValue
should be something totally customized, like root resolve functions
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ahh, I didn't realize the request
was provided automatically. Thanks!
|
||
// Login endpoint. This authenticates the session. We could instead do | ||
// this using GraphQL. | ||
app.post('/login', function (req, res) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I prefer using GraphQL for auth part as well, since we are going to use GraphQL. Not a fan of mixing both.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I considered doing this. However, authenticating the token in graphQL might get confusing. Personally, I'm okay with doing login using a normal endpoint and doing everything else in graphQL. I would also be okay with doing authentication in graphQL if it is relatively straightforward.
auth: { | ||
type: GraphQLBoolean, | ||
resolve (_, params, req) { | ||
console.log('session: ', req.session); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove
type: UserType, | ||
args: { | ||
uid: { | ||
name: 'uid', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rename the name
to uid
?
@@ -0,0 +1,10 @@ | |||
import {GraphQLInputObjectType, GraphQLNonNull, GraphQLString} from 'graphql'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggest we change the file name to user_input.js
, because this is the type for generic user, we can also use it for update_user
|
||
export default class App extends Component { | ||
static propTypes = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think the props children
needed to be declared here. prefer to remove it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You do actually need to declare children propTypes: jsx-eslint/eslint-plugin-react#7. If you meant that App doesn't need children, it does because the whole point is to wrap other components with the provider and base styles.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yea, I didn't know the children is required for eslint. it's okay then.
@@ -0,0 +1,8 @@ | |||
import {combineReducers} from 'redux'; | |||
import login from '../Login/reducer'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we can probably alias a root folder to src
, so instead, we write code like import login from '../Login/reducer'
, we write import login from 'Login/reducer'
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can do this, but it has downsides. Notably, it becomes more confusing which modules are internal vs external dependencies. Let's get other opinions on this. @dennismphil?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you don't really need that I guess, easy way to do is to set a alias in webpack file. like
{
resolve: {
root: ...
}
}
and we can separate the external/internal packages when we import than by put the external package in front.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree with @jessemao it's not pretty to look. But good point about the downside of being confused. I prefer to keep it as is.
@@ -0,0 +1,6 @@ | |||
export default { | |||
USERNAME: 'LOGIN_USERNAME', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I personally prefer to have action_types fields match up with its string value. Like LOGIN_USERNAME
: LOGIN_USERNAME
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The reason to do it this way is so that you can import all login actions together. I find importing individual action types to be a little cumbersome. It requires the imports to be changed every time you add a new action.
There's another method may be better.
export const USERNAME = 'LOGIN_USERNAME';
---
import * as LOGIN from './action_types';
I think this will still allow you to reference the action as LOGIN.USERNAME
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see.
<div className={theme.property}> | ||
<label className={theme.propertyLabel} htmlFor='uname'>{constants.USERNAME}</label> | ||
<input type='text' id='uname' placeholder={constants.USERNAME} required={true} value={username} onChange={(e) => { onUsernameChange(e.target.value); }}/> | ||
<input type='checkbox' id='remember' required={true} value={remember} onChange={(e) => { onRememberToggle(e.target.checked); }}/> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should be able to give our user the ability to change the id
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You mean developers right? I agree, but I think we should have sensible defaults and base the ids of subcomponents in a component off a base id. Like so:
const {id} = this.props;
<input type='text' id=`${id}-uname` />
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, we should totally have a default id for it with the ability to change them as we want.
</div> | ||
<div className={theme.property}> | ||
<label className={theme.propertyLabel} htmlFor='pword'>{constants.PASSWORD}</label> | ||
<input type='password' id='pword' placeholder={constants.PASSWORD} required={true} value={password} onChange={(e) => { onPasswordChange(e.target.value); }} /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same here.
@@ -0,0 +1,7 @@ | |||
# Component library | |||
|
|||
Source files are bundled using Webpack and exported in `../lib`. Included components are defined here. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
better to use abs path here.
@@ -0,0 +1,6 @@ | |||
export default { | |||
USERNAME: 'REGISTER_USERNAME', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same here, match up field name and value.
const res = await query({query: `mutation { | ||
createUser(data:{uid:'${name}', password:'${password}', fullName:'${fullName}'}) | ||
}`}); | ||
console.log('res: ', await res.json()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove
export function attemptRegister(name, password, fullName) { | ||
return async function (dispatch) { | ||
const res = await query({query: `mutation { | ||
createUser(data:{uid:'${name}', password:'${password}', fullName:'${fullName}'}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should use format like this if we need to pass in variables.
{
query: `mutation ($userId: ID, $tag: [String]) {
updateUser(data:{_id: $userId, tag: $tag}) {
_id,
tag
}
}`,
variables: JSON.stringify({
userId,
tag
})
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea.
<div className={theme.container}> | ||
<div className={theme.property}> | ||
<label className={theme.propertyLabel} htmlFor='uname'>{constants.USERNAME}</label> | ||
<input type='text' id='uname' placeholder={constants.USERNAME} required={true} value={username} onChange={(e) => { onUsernameChange(e.target.value); }}/> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same as input section
|
||
export default { | ||
// Localization | ||
USERNAME: 'User name', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should use Username
or just User
@@ -1,17 +1,11 @@ | |||
|
|||
const NODE_ENV = process.env.NODE_ENV; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we can probably have a webpack.config.base to include the duplicate codes in both dev and production config files.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could, but at least for now, the development and production webpack files are different in a lot of subtle but significant ways. For example, development uses hot middleware + react-hot
. At some point, production will probably use just a hash for CSS names rather than the BEM class name style. We could consider using webpack-merge.smart, but that is complicated enough that I would rather break it out.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know if you have read this yet, it's really good.
survival webpack
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
overall looks good!
console.log('initial session: ', req.session); | ||
const {uid, password} = req.body; | ||
const user = db.get('users').get(uid).value(); | ||
if (!user || user.password != password) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use !==
instead of !=
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should include this as an error in our eslint json
.
import Login from './Login'; | ||
import Register from './Register'; | ||
|
||
if (document.getElementById('root') == null){ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use strict equals
CONFIG = require("./webpack.config.development") | ||
} else if (NODE_ENV == "production") { | ||
CONFIG = require("./webpack.config.production") | ||
if (NODE_ENV == 'development'){ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Strict equals
CONFIG = require("./webpack.config.production") | ||
if (NODE_ENV == 'development'){ | ||
CONFIG = require('./webpack.config.development'); | ||
} else if (NODE_ENV == 'production') { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Strict equals
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great start!
@@ -15,6 +15,7 @@ import webpackHotMiddleware from 'webpack-hot-middleware'; | |||
|
|||
const app = express(); | |||
|
|||
// TODO: Provide as optional argument? | |||
const PORT = 3000; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what about let's just do const PORT = NODE_ENV.port || 3000;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm adding this, but I don't think anyone will use it.
This is a large batch of changes that provides a jumping off point for future development. Moving forward, features will be pulled in individually - however, early on there are so many things that are more experimental rather than specific features.
Changelog: