├── .yo-rc.json
├── .babelrc
├── src
├── config
│ └── index.js
├── styles
│ ├── Login.css
│ ├── ChatApp.css
│ └── App.css
├── index.js
├── index.html
└── components
│ ├── Message.js
│ ├── Messages.js
│ ├── ChatInput.js
│ ├── App.js
│ └── ChatApp.js
├── .editorconfig
├── karma.conf.js
├── server.js
├── .gitignore
├── README.md
├── .eslintrc
├── webpack.config.js
├── cfg
├── dev.js
├── dist.js
├── base.js
├── test.js
└── defaults.js
├── LICENSE
└── package.json
/.yo-rc.json:
--------------------------------------------------------------------------------
1 | {
2 | "generator-react-webpack": {}
3 | }
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "es2015",
4 | "stage-0",
5 | "react"
6 | ]
7 | }
8 |
--------------------------------------------------------------------------------
/src/config/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // Settings configured here will be merged into the final config object.
4 | export default {
5 | api: 'http://localhost:4008'
6 | }
7 |
--------------------------------------------------------------------------------
/src/styles/Login.css:
--------------------------------------------------------------------------------
1 | .username-container {
2 | max-width: 400px;
3 | margin: 0 auto;
4 | text-align: center;
5 | margin-top: 10%;
6 | }
7 |
8 | h1 {
9 | display: block;
10 | }
11 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | indent_style = space
5 | indent_size = 2
6 | end_of_line = lf
7 | charset = utf-8
8 | trim_trailing_whitespace = true
9 | insert_final_newline = true
10 |
11 | [*.md]
12 | trim_trailing_whitespace = false
13 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import 'core-js/fn/object/assign';
2 | import React from 'react';
3 | import ReactDOM from 'react-dom';
4 | import App from './components/App';
5 |
6 | // Render the main component into the dom
7 | ReactDOM.render(, document.getElementById('app'));
8 |
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | var webpackCfg = require('./webpack.config');
2 |
3 | // Set node environment to testing
4 | process.env.NODE_ENV = 'test';
5 |
6 | module.exports = function(config) {
7 | config.set({
8 | basePath: '',
9 | browsers: [ 'PhantomJS' ],
10 | files: [
11 | 'test/loadtests.js'
12 | ],
13 | port: 8000,
14 | captureTimeout: 60000,
15 | singleRun: true,
16 | preprocessors: {
17 | 'test/loadtests.js': [ 'webpack', 'sourcemap' ]
18 | },
19 | webpack: webpackCfg,
20 | webpackServer: {
21 | noInfo: true
22 | }
23 | });
24 | };
25 |
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | React Instant Chat
6 |
7 |
8 |
9 |
10 |
11 | APPLICATION CONTENT
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/server.js:
--------------------------------------------------------------------------------
1 | /*eslint no-console:0 */
2 | 'use strict';
3 | require('core-js/fn/object/assign');
4 | const webpack = require('webpack');
5 | const WebpackDevServer = require('webpack-dev-server');
6 | const config = require('./webpack.config');
7 | const open = require('open');
8 |
9 | new WebpackDevServer(webpack(config), config.devServer)
10 | .listen(config.port, 'localhost', (err) => {
11 | if (err) {
12 | console.log(err);
13 | }
14 | console.log('Listening at localhost:' + config.port);
15 | console.log('Opening your system browser...');
16 | open('http://localhost:' + config.port + '/webpack-dev-server/');
17 | });
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 |
5 | # Runtime data
6 | pids
7 | *.pid
8 | *.seed
9 |
10 | # Directory for instrumented libs generated by jscoverage/JSCover
11 | lib-cov
12 |
13 | # Coverage directory used by tools like istanbul
14 | coverage
15 |
16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
17 | .grunt
18 |
19 | # node-waf configuration
20 | .lock-wscript
21 |
22 | # Compiled binary addons (http://nodejs.org/api/addons.html)
23 | build/Release
24 |
25 | # Dependency directory
26 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
27 | node_modules
28 |
29 | # Bower
30 | bower_components/
31 |
--------------------------------------------------------------------------------
/src/components/Message.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | class Message extends React.Component {
4 | render() {
5 | // Was the message sent by the current user. If so, add a css class
6 | const fromMe = this.props.fromMe ? 'from-me' : '';
7 |
8 | return (
9 |
10 |
11 | { this.props.username }
12 |
13 |
14 | { this.props.message }
15 |
16 |
17 | );
18 | }
19 | }
20 |
21 | Message.defaultProps = {
22 | message: '',
23 | username: '',
24 | fromMe: false
25 | };
26 |
27 | export default Message;
28 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # react-instant-chat
2 | A simple Facebook messenger style chat application using socket.io and React
3 |
4 | See the tutorial here [https://medium.com/@coderacademy/you-can-build-an-fb-messenger-style-chat-app-with-reactjs-heres-how-intermediate-211b523838ad](https://medium.com/@coderacademy/you-can-build-an-fb-messenger-style-chat-app-with-reactjs-heres-how-intermediate-211b523838ad)
5 |
6 |
7 | To start the app, run:
8 | ```
9 | npm install
10 |
11 | npm start
12 | ```
13 |
14 | Once the webpack server has started you can access it in a browser at http://localhost:8000
15 |
16 | Ensure that you also have the [simple-chat-api](https://github.com/kentandlime/simple-chat-api) running as well
17 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "parser": "babel-eslint",
3 | "plugins": [
4 | "react"
5 | ],
6 | "parserOptions": {
7 | "ecmaVersion": 6,
8 | "sourceType": "module",
9 | "ecmaFeatures": {
10 | "jsx": true
11 | }
12 | },
13 | "env": {
14 | "browser": true,
15 | "amd": true,
16 | "es6": true,
17 | "node": true,
18 | "mocha": true
19 | },
20 | "rules": {
21 | "comma-dangle": 1,
22 | "quotes": [ 1, "single" ],
23 | "no-undef": 1,
24 | "global-strict": 0,
25 | "no-extra-semi": 1,
26 | "no-underscore-dangle": 0,
27 | "no-console": 1,
28 | "no-unused-vars": 1,
29 | "no-trailing-spaces": [1, { "skipBlankLines": true }],
30 | "no-unreachable": 1,
31 | "no-alert": 0,
32 | "react/jsx-uses-react": 1,
33 | "react/jsx-uses-vars": 1
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const path = require('path');
4 | const args = require('minimist')(process.argv.slice(2));
5 |
6 | // List of allowed environments
7 | const allowedEnvs = ['dev', 'dist', 'test'];
8 |
9 | // Set the correct environment
10 | let env;
11 | if (args._.length > 0 && args._.indexOf('start') !== -1) {
12 | env = 'test';
13 | } else if (args.env) {
14 | env = args.env;
15 | } else {
16 | env = 'dev';
17 | }
18 | process.env.REACT_WEBPACK_ENV = env;
19 |
20 | /**
21 | * Build the webpack configuration
22 | * @param {String} wantedEnv The wanted environment
23 | * @return {Object} Webpack config
24 | */
25 | function buildConfig(wantedEnv) {
26 | let isValid = wantedEnv && wantedEnv.length > 0 && allowedEnvs.indexOf(wantedEnv) !== -1;
27 | let validEnv = isValid ? wantedEnv : 'dev';
28 | let config = require(path.join(__dirname, 'cfg/' + validEnv));
29 | return config;
30 | }
31 |
32 | module.exports = buildConfig(env);
33 |
--------------------------------------------------------------------------------
/src/components/Messages.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import Message from './Message';
4 |
5 | class Messages extends React.Component {
6 | componentDidUpdate() {
7 | // There is a new message in the state, scroll to bottom of list
8 | const objDiv = document.getElementById('messageList');
9 | objDiv.scrollTop = objDiv.scrollHeight;
10 | }
11 |
12 | render() {
13 | // Loop through all the messages in the state and create a Message component
14 | const messages = this.props.messages.map((message, i) => {
15 | return (
16 |
21 | );
22 | });
23 |
24 | return (
25 |
26 | { messages }
27 |
28 | );
29 | }
30 | }
31 |
32 | Messages.defaultProps = {
33 | messages: []
34 | };
35 |
36 | export default Messages;
37 |
--------------------------------------------------------------------------------
/cfg/dev.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | let path = require('path');
4 | let webpack = require('webpack');
5 | let baseConfig = require('./base');
6 | let defaultSettings = require('./defaults');
7 |
8 | // Add needed plugins here
9 | let BowerWebpackPlugin = require('bower-webpack-plugin');
10 |
11 | let config = Object.assign({}, baseConfig, {
12 | entry: [
13 | 'webpack-dev-server/client?http://127.0.0.1:' + defaultSettings.port,
14 | 'webpack/hot/only-dev-server',
15 | './src/index'
16 | ],
17 | cache: true,
18 | devtool: 'eval-source-map',
19 | plugins: [
20 | new webpack.HotModuleReplacementPlugin(),
21 | new webpack.NoErrorsPlugin(),
22 | new BowerWebpackPlugin({
23 | searchResolveModulesDirectories: false
24 | })
25 | ],
26 | module: defaultSettings.getDefaultModules()
27 | });
28 |
29 | // Add needed loaders to the defaults here
30 | config.module.loaders.push({
31 | test: /\.(js|jsx)$/,
32 | loader: 'react-hot!babel-loader',
33 | include: [].concat(
34 | config.additionalPaths,
35 | [ path.join(__dirname, '/../src') ]
36 | )
37 | });
38 |
39 | module.exports = config;
40 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016 Kent and Lime
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/cfg/dist.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | let path = require('path');
4 | let webpack = require('webpack');
5 |
6 | let baseConfig = require('./base');
7 | let defaultSettings = require('./defaults');
8 |
9 | // Add needed plugins here
10 | let BowerWebpackPlugin = require('bower-webpack-plugin');
11 |
12 | let config = Object.assign({}, baseConfig, {
13 | entry: path.join(__dirname, '../src/index'),
14 | cache: false,
15 | devtool: 'sourcemap',
16 | plugins: [
17 | new webpack.optimize.DedupePlugin(),
18 | new webpack.DefinePlugin({
19 | 'process.env.NODE_ENV': '"production"'
20 | }),
21 | new BowerWebpackPlugin({
22 | searchResolveModulesDirectories: false
23 | }),
24 | new webpack.optimize.UglifyJsPlugin(),
25 | new webpack.optimize.OccurenceOrderPlugin(),
26 | new webpack.optimize.AggressiveMergingPlugin(),
27 | new webpack.NoErrorsPlugin()
28 | ],
29 | module: defaultSettings.getDefaultModules()
30 | });
31 |
32 | // Add needed loaders to the defaults here
33 | config.module.loaders.push({
34 | test: /\.(js|jsx)$/,
35 | loader: 'babel',
36 | include: [].concat(
37 | config.additionalPaths,
38 | [ path.join(__dirname, '/../src') ]
39 | )
40 | });
41 |
42 | module.exports = config;
43 |
--------------------------------------------------------------------------------
/src/components/ChatInput.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | class ChatInput extends React.Component {
4 | constructor(props) {
5 | super(props);
6 | this.state = { chatInput: '' };
7 |
8 | // React ES6 does not bind 'this' to event handlers by default
9 | this.submitHandler = this.submitHandler.bind(this);
10 | this.textChangeHandler = this.textChangeHandler.bind(this);
11 | }
12 |
13 | submitHandler(event) {
14 | // Stop the form from refreshing the page on submit
15 | event.preventDefault();
16 |
17 | // Clear the input box
18 | this.setState({ chatInput: '' });
19 |
20 | // Call the onSend callback with the chatInput message
21 | this.props.onSend(this.state.chatInput);
22 | }
23 |
24 | textChangeHandler(event) {
25 | this.setState({ chatInput: event.target.value });
26 | }
27 |
28 | render() {
29 | return (
30 |
37 | );
38 | }
39 | }
40 |
41 | ChatInput.defaultProps = {
42 | };
43 |
44 | export default ChatInput;
45 |
--------------------------------------------------------------------------------
/src/styles/ChatApp.css:
--------------------------------------------------------------------------------
1 | .container {
2 | display: flex;
3 | flex-direction: column;
4 | }
5 |
6 | .messages {
7 | overflow-y: scroll;
8 | overflow-x: hidden;
9 | flex-grow: 1;
10 | padding: 20px;
11 | }
12 |
13 | h3 {
14 | text-align: center;
15 | padding: 20px 0;
16 | margin: 0;
17 | border-bottom: 1px solid #ddd;
18 | background-color: #eee;
19 | }
20 |
21 | .chat-input {
22 | position: relative;
23 | overflow: hidden;
24 | padding: 0 40px;
25 | flex-shrink: 0;
26 | }
27 |
28 | .chat-input input[type="text"] {
29 | width: 100%;
30 | margin-left: -20px;
31 | margin-right: -20px;
32 | }
33 |
34 | .message.from-me .username {
35 | display: none;
36 | }
37 |
38 | .message.from-me {
39 | display: flex;
40 | justify-content: flex-end;
41 | margin-bottom: 5px;
42 | }
43 |
44 | .message.from-me .message-body {
45 | background-color: #af9570;
46 | color: white;
47 | }
48 |
49 | .message {
50 | margin-bottom: 20px;
51 | }
52 | .message-body {
53 | max-width: 80%;
54 | display: inline-block;
55 | padding: 20px;
56 | background-color: #eee;
57 | border: 1px;
58 | border-radius: 5px;
59 | padding-right: 50px;
60 | }
61 |
62 | .username {
63 | font-weight: bold;
64 | font-size: 0.9rem;
65 | color: #999;
66 | margin-bottom: 5px;
67 | }
--------------------------------------------------------------------------------
/cfg/base.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | let path = require('path');
3 | let defaultSettings = require('./defaults');
4 |
5 | // Additional npm or bower modules to include in builds
6 | // Add all foreign plugins you may need into this array
7 | // @example:
8 | // let npmBase = path.join(__dirname, '../node_modules');
9 | // let additionalPaths = [ path.join(npmBase, 'react-bootstrap') ];
10 | let additionalPaths = [];
11 |
12 | module.exports = {
13 | additionalPaths: additionalPaths,
14 | port: defaultSettings.port,
15 | debug: true,
16 | devtool: 'eval',
17 | output: {
18 | path: path.join(__dirname, '/../dist/assets'),
19 | filename: 'app.js',
20 | publicPath: defaultSettings.publicPath
21 | },
22 | devServer: {
23 | contentBase: './src/',
24 | historyApiFallback: true,
25 | hot: true,
26 | port: defaultSettings.port,
27 | publicPath: defaultSettings.publicPath,
28 | noInfo: false
29 | },
30 | resolve: {
31 | extensions: ['', '.js', '.jsx'],
32 | alias: {
33 | actions: `${defaultSettings.srcPath}/actions/`,
34 | components: `${defaultSettings.srcPath}/components/`,
35 | sources: `${defaultSettings.srcPath}/sources/`,
36 | stores: `${defaultSettings.srcPath}/stores/`,
37 | styles: `${defaultSettings.srcPath}/styles/`,
38 | config: `${defaultSettings.srcPath}/config/` + process.env.REACT_WEBPACK_ENV
39 | }
40 | },
41 | module: {}
42 | };
43 |
--------------------------------------------------------------------------------
/cfg/test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | let path = require('path');
4 | let srcPath = path.join(__dirname, '/../src/');
5 |
6 | let baseConfig = require('./base');
7 |
8 | // Add needed plugins here
9 | let BowerWebpackPlugin = require('bower-webpack-plugin');
10 |
11 | module.exports = {
12 | devtool: 'eval',
13 | module: {
14 | preLoaders: [
15 | {
16 | test: /\.(js|jsx)$/,
17 | loader: 'isparta-instrumenter-loader',
18 | include: [
19 | path.join(__dirname, '/../src')
20 | ]
21 | }
22 | ],
23 | loaders: [
24 | {
25 | test: /\.(png|jpg|gif|woff|woff2|css|sass|scss|less|styl)$/,
26 | loader: 'null-loader'
27 | },
28 | {
29 | test: /\.(js|jsx)$/,
30 | loader: 'babel-loader',
31 | include: [].concat(
32 | baseConfig.additionalPaths,
33 | [
34 | path.join(__dirname, '/../src'),
35 | path.join(__dirname, '/../test')
36 | ]
37 | )
38 | }
39 | ]
40 | },
41 | resolve: {
42 | extensions: [ '', '.js', '.jsx' ],
43 | alias: {
44 | actions: srcPath + 'actions/',
45 | helpers: path.join(__dirname, '/../test/helpers'),
46 | components: srcPath + 'components/',
47 | sources: srcPath + 'sources/',
48 | stores: srcPath + 'stores/',
49 | styles: srcPath + 'styles/',
50 | config: srcPath + 'config/' + process.env.REACT_WEBPACK_ENV
51 | }
52 | },
53 | plugins: [
54 | new BowerWebpackPlugin({
55 | searchResolveModulesDirectories: false
56 | })
57 | ]
58 | };
59 |
--------------------------------------------------------------------------------
/src/components/App.js:
--------------------------------------------------------------------------------
1 | require('../styles/App.css');
2 | require('../styles/Login.css');
3 |
4 | import React from 'react';
5 | import ChatApp from './ChatApp';
6 |
7 | class App extends React.Component {
8 | constructor(props) {
9 | super(props);
10 | this.state = { username: '' };
11 |
12 | // Bind 'this' to event handlers. React ES6 does not do this by default
13 | this.usernameChangeHandler = this.usernameChangeHandler.bind(this);
14 | this.usernameSubmitHandler = this.usernameSubmitHandler.bind(this);
15 | }
16 |
17 | usernameChangeHandler(event) {
18 | this.setState({ username: event.target.value });
19 | }
20 |
21 | usernameSubmitHandler(event) {
22 | event.preventDefault();
23 | this.setState({ submitted: true, username: this.state.username });
24 | }
25 |
26 | render() {
27 | if (this.state.submitted) {
28 | // Form was submitted, now show the main App
29 | return (
30 |
31 | );
32 | }
33 |
34 | // Initial page load, show a simple login form
35 | return (
36 |
47 | );
48 | }
49 |
50 | }
51 | App.defaultProps = {
52 | };
53 |
54 | export default App;
55 |
--------------------------------------------------------------------------------
/src/components/ChatApp.js:
--------------------------------------------------------------------------------
1 | require('../styles/ChatApp.css');
2 |
3 | import React from 'react';
4 | import io from 'socket.io-client';
5 | import config from '../config';
6 |
7 | import Messages from './Messages';
8 | import ChatInput from './ChatInput';
9 |
10 | class ChatApp extends React.Component {
11 | socket = {};
12 | constructor(props) {
13 | super(props);
14 | this.state = { messages: [] };
15 | this.sendHandler = this.sendHandler.bind(this);
16 |
17 | // Connect to the server
18 | this.socket = io(config.api, { query: `username=${props.username}` }).connect();
19 |
20 | // Listen for messages from the server
21 | this.socket.on('server:message', message => {
22 | this.addMessage(message);
23 | });
24 | }
25 |
26 | sendHandler(message) {
27 | const messageObject = {
28 | username: this.props.username,
29 | message
30 | };
31 |
32 | // Emit the message to the server
33 | this.socket.emit('client:message', messageObject);
34 |
35 | messageObject.fromMe = true;
36 | this.addMessage(messageObject);
37 | }
38 |
39 | addMessage(message) {
40 | // Append the message to the component state
41 | const messages = this.state.messages;
42 | messages.push(message);
43 | this.setState({ messages });
44 | }
45 |
46 | render() {
47 | return (
48 |
49 |
React Chat App
50 |
51 |
52 |
53 | );
54 | }
55 |
56 | }
57 | ChatApp.defaultProps = {
58 | username: 'Anonymous'
59 | };
60 |
61 | export default ChatApp;
62 |
--------------------------------------------------------------------------------
/cfg/defaults.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Function that returns default values.
3 | * Used because Object.assign does a shallow instead of a deep copy.
4 | * Using [].push will add to the base array, so a require will alter
5 | * the base array output.
6 | */
7 | 'use strict';
8 |
9 | const path = require('path');
10 | const srcPath = path.join(__dirname, '/../src');
11 | const dfltPort = 8000;
12 |
13 | /**
14 | * Get the default modules object for webpack
15 | * @return {Object}
16 | */
17 | function getDefaultModules() {
18 | return {
19 | preLoaders: [
20 | {
21 | test: /\.(js|jsx)$/,
22 | include: srcPath,
23 | loader: 'eslint-loader'
24 | }
25 | ],
26 | loaders: [
27 | {
28 | test: /\.css$/,
29 | loader: 'style-loader!css-loader'
30 | },
31 | {
32 | test: /\.sass/,
33 | loader: 'style-loader!css-loader!sass-loader?outputStyle=expanded&indentedSyntax'
34 | },
35 | {
36 | test: /\.scss/,
37 | loader: 'style-loader!css-loader!sass-loader?outputStyle=expanded'
38 | },
39 | {
40 | test: /\.less/,
41 | loader: 'style-loader!css-loader!less-loader'
42 | },
43 | {
44 | test: /\.styl/,
45 | loader: 'style-loader!css-loader!stylus-loader'
46 | },
47 | {
48 | test: /\.(png|jpg|gif|woff|woff2)$/,
49 | loader: 'url-loader?limit=8192'
50 | },
51 | {
52 | test: /\.(mp4|ogg|svg)$/,
53 | loader: 'file-loader'
54 | }
55 | ]
56 | };
57 | }
58 |
59 | module.exports = {
60 | srcPath: srcPath,
61 | publicPath: '/assets/',
62 | port: dfltPort,
63 | getDefaultModules: getDefaultModules
64 | };
65 |
--------------------------------------------------------------------------------
/src/styles/App.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: 'proxima-nova', sans-serif;
3 | margin: 0;
4 | }
5 |
6 | html, body, #app, .container {
7 | min-height: 100vh;
8 | max-height: 100vh;
9 | max-width: 100%;
10 | }
11 |
12 | input[type="text"] {
13 | margin: 10px 0px;
14 | height: 60px;
15 | -webkit-box-shadow: 0 1px 2px rgba(0,0,0,0.15);
16 | -moz-box-shadow: 0 1px 2px rgba(0,0,0,0.15);
17 | box-shadow: 0 1px 2px rgba(0,0,0,0.15);
18 | color: #1e1e1e;
19 | font: 400 1rem "proxima-nova", sans-serif;
20 | width: 80%;
21 | border: 1px solid #dee0e0;
22 | padding: 0 20px;
23 | -webkit-transition: border-color 0.2s ease-in-out;
24 | -moz-transition: border-color 0.2s ease-in-out;
25 | transition: border-color 0.2s ease-in-out;
26 | }
27 |
28 | input[type="text"]:focus {
29 | border: 1px solid #AF9570;
30 | outline: none !important;
31 | }
32 |
33 | input[type="submit"] {
34 | background-color: #323232;
35 | color: white;
36 | font: 700 1rem "proxima-nova",sans-serif;
37 | border: none;
38 | position: relative;
39 | -webkit-transition: 0.3s ease-in-out;
40 | -moz-transition: 0.3s ease-in-out;
41 | transition: 0.3s ease-in-out;
42 | text-decoration: none;
43 | padding: 20px 80px;
44 | text-align: center;
45 | cursor: pointer;
46 | -webkit-box-shadow: -7px 7px 0px rgba(50,50,50,0.15);
47 | -moz-box-shadow: -7px 7px 0px rgba(50,50,50,0.15);
48 | box-shadow: -7px 7px 0px rgba(50,50,50,0.15);
49 | margin-top: 20px;
50 | }
51 |
52 | input[type="submit"]:hover {
53 | -webkit-box-shadow: -10px 10px 0px rgba(50,50,50,0.15);
54 | -moz-box-shadow: -10px 10px 0px rgba(50,50,50,0.15);
55 | box-shadow: -10px 10px 0px rgba(50,50,50,0.15);
56 | background-color: #1e1e1e;
57 | }
58 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "1.0.0",
3 | "description": "A simple instant message client built for a CoderFactory blog article",
4 | "main": "",
5 | "scripts": {
6 | "start": "node server.js --env=dev",
7 | "serve": "node server.js --env=dev",
8 | "serve:dist": "node server.js --env=dist",
9 | "dist": "npm run copy & webpack --env=dist",
10 | "lint": "eslint ./src",
11 | "copy": "copyfiles -f ./src/index.html ./src/favicon.ico ./dist",
12 | "clean": "rimraf dist/*"
13 | },
14 | "repository": "",
15 | "keywords": [],
16 | "license": "MIT",
17 | "author": "Paul Pagnan ",
18 | "devDependencies": {
19 | "babel-core": "^6.0.0",
20 | "babel-eslint": "^6.0.0",
21 | "babel-loader": "^6.0.0",
22 | "babel-polyfill": "^6.3.14",
23 | "babel-preset-es2015": "^6.0.15",
24 | "babel-preset-react": "^6.0.15",
25 | "babel-preset-stage-0": "^6.5.0",
26 | "bower-webpack-plugin": "^0.1.9",
27 | "copyfiles": "^1.0.0",
28 | "css-loader": "^0.23.0",
29 | "eslint": "^3.0.0",
30 | "eslint-loader": "^1.0.0",
31 | "eslint-plugin-react": "^6.0.0",
32 | "file-loader": "^0.9.0",
33 | "glob": "^7.0.0",
34 | "isparta-instrumenter-loader": "^1.0.0",
35 | "karma": "^1.0.0",
36 | "karma-phantomjs-launcher": "^1.0.0",
37 | "karma-sourcemap-loader": "^0.3.5",
38 | "karma-webpack": "^1.7.0",
39 | "minimist": "^1.2.0",
40 | "null-loader": "^0.1.1",
41 | "open": "0.0.5",
42 | "phantomjs-prebuilt": "^2.0.0",
43 | "react-addons-test-utils": "^15.0.0",
44 | "react-hot-loader": "^1.2.9",
45 | "rimraf": "^2.4.3",
46 | "style-loader": "^0.13.0",
47 | "url-loader": "^0.5.6",
48 | "webpack": "^1.12.0",
49 | "webpack-dev-server": "^1.12.0"
50 | },
51 | "dependencies": {
52 | "core-js": "^2.0.0",
53 | "normalize.css": "^4.0.0",
54 | "react": "^15.0.0",
55 | "react-dom": "^15.0.0",
56 | "socket.io-client": "^1.4.8"
57 | }
58 | }
59 |
--------------------------------------------------------------------------------