├── client
├── components
│ ├── Header.jsx
│ ├── Contents.jsx
│ ├── Nav.jsx
│ ├── UserCards.jsx
│ ├── Feed.jsx
│ ├── Login.jsx
│ ├── Profile.jsx
│ └── App.jsx
└── index.js
├── .gitignore
├── .DS_Store
├── server
├── .DS_Store
├── data
│ ├── .DS_Store
│ ├── images
│ │ └── axolotl.jpg
│ ├── userModel.js
│ └── testData.js
├── package.json
├── server.js
├── controllers
│ ├── loginController.js
│ └── userController.js
└── package-lock.json
├── css
├── images
│ ├── css.png
│ ├── html.png
│ ├── logo.png
│ ├── software.png
│ └── science-formulas-4.jpg
└── main.css
├── .eslintrc
├── webpack.config.js
├── index.html
├── package.json
└── README.md
/client/components/Header.jsx:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/client/components/Contents.jsx:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | build/bundle.js
3 |
--------------------------------------------------------------------------------
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axolotl-cs/axolotl/HEAD/.DS_Store
--------------------------------------------------------------------------------
/server/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axolotl-cs/axolotl/HEAD/server/.DS_Store
--------------------------------------------------------------------------------
/css/images/css.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axolotl-cs/axolotl/HEAD/css/images/css.png
--------------------------------------------------------------------------------
/css/images/html.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axolotl-cs/axolotl/HEAD/css/images/html.png
--------------------------------------------------------------------------------
/css/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axolotl-cs/axolotl/HEAD/css/images/logo.png
--------------------------------------------------------------------------------
/server/data/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axolotl-cs/axolotl/HEAD/server/data/.DS_Store
--------------------------------------------------------------------------------
/css/images/software.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axolotl-cs/axolotl/HEAD/css/images/software.png
--------------------------------------------------------------------------------
/server/data/images/axolotl.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axolotl-cs/axolotl/HEAD/server/data/images/axolotl.jpg
--------------------------------------------------------------------------------
/css/images/science-formulas-4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axolotl-cs/axolotl/HEAD/css/images/science-formulas-4.jpg
--------------------------------------------------------------------------------
/client/index.js:
--------------------------------------------------------------------------------
1 | // This is the entry point for the app
2 | import React from 'react';
3 | import { render } from 'react-dom';
4 | import App from '../client/components/App.jsx'; //don't erase the .jsx
5 | // import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
6 |
7 |
8 |
9 | render(
10 |
11 | , document.getElementById('content'),
12 | );
13 |
--------------------------------------------------------------------------------
/server/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "server",
3 | "version": "1.0.0",
4 | "description": "Server for Axolotl project.",
5 | "main": "server.js",
6 | "scripts": {
7 | "start": "nodemon server.js"
8 | },
9 | "author": "Axolotl",
10 | "license": "ISC",
11 | "dependencies": {
12 | "body-parser": "^1.18.2",
13 | "express": "^4.16.2",
14 | "json-odds-api": "^1.0.2",
15 | "mongoose": "^5.0.3",
16 | "nodemailer": "^4.4.2",
17 | "nodemon": "^1.14.12"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "airbnb",
3 | "parserOptions": {
4 | "ecmaVersion": 6,
5 | "sourceType": "module",
6 | "ecmaFeatures": {
7 | "jsx": true
8 | }
9 | },
10 | "env": {
11 | "browser": true,
12 | "node": true,
13 | "es6": true
14 | },
15 | "plugins": [
16 | "react",
17 | "jsx-a11y"
18 | ],
19 | "rules": {
20 | "no-underscore-dangle": 0,
21 | "import/extensions": 0,
22 | "indent": 0
23 | }
24 | }
--------------------------------------------------------------------------------
/client/components/Nav.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 |
3 | class Nav extends Component {
4 | render() {
5 | let buttonText = 'Feed';
6 | if (this.props.inFeed) buttonText = 'Profile';
7 | return (
8 |
9 |
Pair Programming Website!
10 |
11 |
12 |
13 |
14 |
15 | );
16 | }
17 | }
18 |
19 | export default Nav;
20 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const webpack = require('webpack');
2 | const path = require('path');
3 |
4 | const BUILD_DIR = path.resolve(__dirname, './build');
5 | const APP_DIR = path.resolve(__dirname, './client');
6 |
7 | const config = {
8 | entry: {
9 | main: APP_DIR + '/index.js'
10 | },
11 | output: {
12 | filename: 'bundle.js',
13 | path: BUILD_DIR
14 | },
15 | module: {
16 | rules: [
17 | {
18 | test: /\.(jsx|js)?$/,
19 | use: [{
20 | loader: 'babel-loader',
21 | options: {
22 | cacheDirectory: true,
23 | presets: ['react', 'es2015']
24 | }
25 | }]
26 | }]
27 | }
28 | };
29 |
30 | module.exports = config;
31 |
--------------------------------------------------------------------------------
/client/components/UserCards.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 |
3 | const UserCards = (props) => {
4 | console.log(props);
5 | return (
6 |
7 |

8 |
{props.user.username}
9 |
{props.user.location}
10 |
11 |
14 |
15 |
16 |
19 |
20 |
21 | );
22 | };
23 |
24 | export default UserCards;
25 |
--------------------------------------------------------------------------------
/server/data/userModel.js:
--------------------------------------------------------------------------------
1 | const mongoose = require('mongoose');
2 |
3 | const { Schema } = mongoose;
4 |
5 | // mongoose.connect('mongodb://localhost/axolotl');
6 |
7 | // Hosting db on mlab
8 | mongoose.connect('mongodb://axol:ilovetesting@ds231228.mlab.com:31228/axolotl');
9 | console.log('Connected to mongodb');
10 |
11 | const userSchema = new Schema({
12 | username: { type: String, unique: true, require: true },
13 | password: { type: String, require: true },
14 | location: { type: String, default: '' },
15 | email: String,
16 | invited: [String], // Store the userId of the people you've invited to pair
17 | requests: [String], // List of people who have requested to pair with you
18 | connected: [String], // Store the userId of the people you've connected with
19 | bio: { type: String, default: '' },
20 | skills: { type: String, default: '' },
21 | interests: { type: String, default: '' },
22 | image: { data: Buffer, contentType: String },
23 | });
24 |
25 | const User = mongoose.model('Users', userSchema);
26 |
27 | module.exports = User;
28 |
--------------------------------------------------------------------------------
/client/components/Feed.jsx:
--------------------------------------------------------------------------------
1 | import fetch from 'isomorphic-fetch';
2 | import React, { Component } from 'react';
3 | import UserCards from './UserCards.jsx';
4 |
5 |
6 | class Feed extends Component {
7 | constructor(props) {
8 | super(props);
9 | }
10 |
11 | render() {
12 | console.log('feed props', this.props);
13 | //console.log(props);
14 |
15 | // set to temp vars so they're accessible within map and filter
16 | let connect = this.props.connect;
17 | let userComp = this.props.user;
18 | let toProfile = this.props.toProfile;
19 |
20 | let feed = this.props.feed.filter(function(u){
21 | return (u.username !== userComp.username); // don't put current user in feed
22 | }).map(function(feedUser, index) {
23 | // create UserCard from each user in list
24 | return
25 | });
26 |
27 | return (
28 |
29 |
Feed
30 | {feed}
31 |
32 | );
33 | }
34 | }
35 |
36 | export default Feed;
37 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "src",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "webpack": "./node_modules/.bin/webpack -w",
8 | "test": "echo \"Error: no test specified\" && exit 1",
9 | "build": "./node_modules/.bin/webpack -w"
10 | },
11 | "author": "",
12 | "license": "ISC",
13 | "dependencies": {
14 | "bcryptjs": "^2.4.3",
15 | "body-parser": "^1.18.2",
16 | "bootstrap": "^4.0.0",
17 | "cheerio": "^1.0.0-rc.2",
18 | "cookie-parser": "^1.4.3",
19 | "ejs": "^2.5.7",
20 | "express": "^4.16.2",
21 | "material-ui": "^0.20.0",
22 | "mongoose": "^5.0.3",
23 | "react": "^16.2.0",
24 | "react-dom": "^16.2.0",
25 | "react-materialize": "^1.1.2",
26 | "redux": "^3.7.2",
27 | "request": "^2.83.0"
28 | },
29 | "devDependencies": {
30 | "babel": "^6.23.0",
31 | "babel-core": "^6.26.0",
32 | "babel-loader": "^7.1.2",
33 | "babel-preset-es2015": "^6.24.1",
34 | "babel-preset-react": "^6.24.1",
35 | "babel-register": "^6.26.0",
36 | "eslint": "^4.17.0",
37 | "eslint-config-airbnb": "^16.1.0",
38 | "eslint-plugin-import": "^2.8.0",
39 | "eslint-plugin-jsx-a11y": "^6.0.3",
40 | "eslint-plugin-react": "^7.6.1",
41 | "history": "^4.7.2",
42 | "react-router": "^4.2.0",
43 | "webpack": "^3.10.0"
44 | },
45 | "proxy": "http://localhost:5000/"
46 | }
47 |
--------------------------------------------------------------------------------
/server/server.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const path = require('path');
3 | const bodyParser = require('body-parser');
4 |
5 | // Controllers to handle login and user functions
6 | const loginController = require('./controllers/loginController.js');
7 | const userController = require('./controllers/userController.js');
8 |
9 | // Function to set test data in db
10 | const testData = require('./data/testData.js');
11 |
12 | const app = express();
13 |
14 | app.use(express.static(path.join(__dirname, '../')));
15 | app.use(bodyParser.json());
16 | // app.use((req, res, next) => {
17 | // res.header(‘Access-Control-Allow-Origin’, ‘*’);
18 | // res.header(‘Access-Control-Allow-Headers’, ‘Origin, X-Requested-With, Content-Type, Accept’);
19 | // next();
20 | // });
21 |
22 | // Send root react page
23 | app.get('/', (req, res) => { res.sendFile('index.html'); });
24 |
25 | // User logs in
26 | app.post('/login', loginController.login);
27 |
28 | // User signs up
29 | app.post('/signup', loginController.signup);
30 |
31 | // User sends an invite
32 | app.post('/invite', userController.invite);
33 |
34 | // User accepts an invite
35 | app.post('/connect', userController.connect);
36 |
37 | // Update user info
38 | app.post('/profile', userController.update);
39 |
40 | // Return list of users to render in feed
41 | app.get('/feed', userController.getUsers);
42 |
43 | // REMOVE ME!!!
44 | // I set fake data for testing
45 | // testData();
46 |
47 | const port = process.env.PORT || 5000;
48 | app.listen(port, () => console.log(`Listening on port ${port}`));
49 |
--------------------------------------------------------------------------------
/client/components/Login.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 |
3 |
4 | const Login = (props) => {
5 | console.log(props);
6 |
7 | //buttons change text when in signup / login
8 | let heading = (props.isSignup) ? 'Sign Up' : 'Log In';
9 | let buttonText = (props.isSignup) ? 'Sign Up' : 'Log In';
10 | let toggleText = (props.isSignup) ? 'Log In' : 'Sign Up';
11 |
12 | // only added while in signup mode
13 | let extra = (
14 |
15 |
19 |
20 |
21 | )
22 | extra = (props.isSignup) ? extra : '';
23 |
24 | return (
25 |
26 |
{heading}
27 |
31 |
32 |
36 |
37 | {extra}
38 | {
39 | let username = document.getElementById('userIn').value;
40 | let password = document.getElementById('passIn').value;
41 | let email = (props.isSignup) ? document.getElementById('emailIn').value : null;
42 | console.log(username, password, email);
43 | props.clickFun(username, password, email);
44 | }}/>
45 | {
46 | props.toggle();
47 | }}/>
48 |
49 | );
50 | };
51 |
52 | export default Login;
53 |
--------------------------------------------------------------------------------
/server/data/testData.js:
--------------------------------------------------------------------------------
1 | /**
2 | * File that sets up test data.
3 | */
4 |
5 | const User = require('./userModel.js');
6 | const fs = require('fs');
7 | const path = require('path');
8 |
9 | // Path to test image
10 | const imgPath = path.join(__dirname, 'images/axolotl.jpg');
11 |
12 | const image = {};
13 | image.data = fs.readFileSync(imgPath);
14 | image.contentType = 'image/jpg';
15 | // Clear database
16 | const testData = () => {
17 | User.remove({}, (err) => {
18 | if (err) throw err;
19 | })
20 | .catch((err) => {
21 | // Add error handling...
22 | console.log(err);
23 | });
24 |
25 | const andrew = new User({
26 | username: 'andrew',
27 | password: 'ilovetesting',
28 | location: 'Playa Vista',
29 | email: 'afuselier23@gmail.com',
30 | bio: 'I love programming!',
31 | skills: 'Javascript',
32 | });
33 |
34 | const eric = new User({
35 | username: 'eric',
36 | password: 'ilovetesting',
37 | location: 'Venice',
38 | email: 'eric.rudolph.carrillo@gmail.com',
39 | bio: 'I love surfing!',
40 | skills: 'Javascript',
41 | });
42 |
43 | const john = new User({
44 | username: 'john',
45 | password: 'ilovetesting',
46 | location: 'Playa Vista',
47 | email: 'john@gmail.com',
48 | bio: 'I love programming!',
49 | skills: 'Javascript',
50 | });
51 |
52 | const max = new User({
53 | username: 'max',
54 | password: 'ilovetesting',
55 | location: 'Playa Vista',
56 | email: 'max@gmail.com',
57 | bio: 'I love programming!',
58 | skills: 'Javascript',
59 | });
60 |
61 | const star = new User({
62 | username: 'star',
63 | password: 'ilovetesting',
64 | location: 'Playa Vista',
65 | email: 'star@gmail.com',
66 | bio: 'I love programming!',
67 | skills: 'Javascript',
68 | });
69 |
70 | andrew.save();
71 | eric.save();
72 | john.save();
73 | max.save();
74 | star.save();
75 | };
76 |
77 | module.exports = testData;
78 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Overview
2 |
3 | Axolotl* is a platform that brings coders together. It connects prospective pair-programmers, allowing them to search for similarly-inclined people in their area and choose potential partners based on their skills, interests and background. When matched, our app shares their contact information so they can meet up and start coding!
4 |
5 | ## Features
6 | - Persistent user profiles stored on a cloud database
7 | - Email notification on successful match
8 | - Stable user interface that allows users to:
9 | - Sign up, login and sign out
10 | - View and edit profile information
11 | - Scroll through other users and invite them to pair program
12 | - Navigate to other user profiles to read bios and get more info
13 | - Easily navigate between views in the app
14 |
15 | ## Getting Started
16 | - Fork repo to GitHub profile and clone files to your computer
17 | - Open two terminal windows
18 | - Navigate first window to main project folder (`axolotl`)
19 | - Navigate second window to `axolotl/server`
20 | - Run `npm install` in both folders (server has a separate package.json)
21 | - Run `npm run build` from within main project folder, keep running for auto rebuilds
22 | - Run `npm start` in server folder
23 | - Navigate to [localhost:5000](http://localhost:5000/) in browser to view application
24 | - Database (Mongo, using Mongoose) is hosted by [mLab](https://mlab.com/)
25 |
26 | ## Future Features
27 |
28 | Iterate on our project! It's stable and well-documented(ish), so you can jump right in and add the stuff you think would be cool!
29 |
30 | ### Iteration Ideas
31 | - Add support for user photos
32 | - Render a list of matched users in Profile
33 | - Separate matched, invited and nearby users in Feed
34 | - Rework how skills and interests are handled
35 | - Search users by skills / interests / location in Feed
36 | - Add support for chat between matched users
37 | - Could use web sockets for instant updates and save content to DB
38 | - Might show up when you navigate to a matched user's profile
39 | - Finish styling application
40 | - Add React Router support
41 |
--------------------------------------------------------------------------------
/server/controllers/loginController.js:
--------------------------------------------------------------------------------
1 | const User = require('../data/userModel.js');
2 |
3 | const loginController = {};
4 |
5 | // Handle user login
6 | loginController.login = (req, res, next) => {
7 | const user = {
8 | username: req.body.username,
9 | password: req.body.password,
10 | };
11 | // Object to respond to client with
12 | const data = {};
13 | // Look for the user with the given username and password
14 | User.find(user, (err, result) => {
15 | if (err) throw err;
16 | if (result[0]) {
17 | data.user = result[0];
18 | } else {
19 | res.status(200).json('Not found.');
20 | return next();
21 | }
22 | })
23 | .then(() => {
24 | User.find({}, (err, userList) => {
25 | if (err) throw err;
26 | data.list = userList;
27 | res.status(200).json(data);
28 | return next();
29 | });
30 | })
31 | .catch((err) => {
32 | // Add error handling...
33 | console.log(err);
34 | });
35 | };
36 |
37 | // Handle new user creation
38 | loginController.signup = (req, res, next) => {
39 | const { username, password, email } = req.body;
40 | // const newUser = new User({ username, password, email });
41 | // Object to respond to client with
42 | const data = {};
43 | // Save the user and send back the user object
44 | User.find({ username }, (err, result) => {
45 | if (err) throw err;
46 | if (result.length === 0) {
47 | const newUser = new User({ username, password, email });
48 | newUser.save()
49 | .then(() => {
50 | User.find({ username }, (err, result) => {
51 | if (err) throw err;
52 | data.user = result[0];
53 | });
54 | })
55 | .then(() => {
56 | User.find({}, (err, userList) => {
57 | if (err) throw err;
58 | data.list = userList;
59 | res.status(200).json(data);
60 | return next();
61 | });
62 | })
63 | .catch((err) => {
64 | // Add error handling...
65 | console.log(err);
66 | });
67 | } else {
68 | return res.json('Seats taken.');
69 | }
70 | })
71 | .catch((err) => {
72 | // Add error handling...
73 | console.log(err);
74 | });
75 | };
76 |
77 | module.exports = loginController;
78 |
--------------------------------------------------------------------------------
/css/main.css:
--------------------------------------------------------------------------------
1 | /* Profile STYLING */
2 | /*
3 | body {
4 | background: #1D212A;
5 | } */
6 |
7 | /* //PROFILE STYLING */
8 |
9 | /* .responsive-image {
10 | background-image: url(client/images/science-formulas-4.jpg);
11 | }
12 |
13 | @media screen and (min-width: 320px) {
14 | .responsive-image {
15 | background-image: url(http://placehold.it/640x400);
16 | }
17 | }
18 | @media screen and (min-width: 640px)
19 | {
20 | .responsive-image {
21 | background-image: url(http://placehold.it/1024x1024);
22 | }
23 | }
24 | @media screen and (min-width: 1024px) {
25 | .responsive-image {
26 | background-image: url(client/images/science-formulas-4.jpg);
27 | }
28 | } */
29 | body {
30 | background: #1D212A;
31 | color: whitesmoke;
32 | }
33 |
34 | .masthead {
35 | background-image: url("https://www.hongkiat.com/blog/wp-content/uploads/css3-circular-elliptical-gradient/ellipse-gradient-syntax.jpg");
36 | overflow: hidden;
37 | color: rgb(230, 225, 225);
38 | padding: 1em;
39 | /* border-top: 6px solid #9BA800; */
40 | background-color: #404040;
41 | height: 500px;
42 | width: 100%;
43 |
44 |
45 | &.responsive-image {
46 | background-position: center;
47 | background-attachment: scroll;
48 | background-repeat: no-repeat;
49 | -webkit-background-size: cover;
50 | -moz-background-size: cover;
51 | -o-background-size: cover;
52 | background-size: cover;
53 | }
54 | }
55 |
56 | .buttonConnect {
57 | position: relative;
58 | top: -22px;
59 | bottom: 5px;
60 | left: 986px;
61 | right: 14px;
62 | width: 261px;
63 | height: 73px;
64 | }
65 |
66 | .profile-Image {
67 | position: relative;
68 | top: -31px;
69 | height: 250px;
70 | width: 250px;
71 | border: 5px solid white;
72 | }
73 |
74 |
75 | textarea {
76 | margin-top: 11px;
77 | margin-left: -2px;
78 | width: 500px;
79 | height: 100px;
80 | background: none repeat scroll 0 0 whitesmoke;
81 | border-color: -moz-use-text-color rgb(194, 125, 125) #FFFFFF -moz-use-text-color;
82 | border-image: none;
83 | border-radius: 6px 6px 6px 6px;
84 | border-style: none solid solid none;
85 | border-width: medium 1px 1px medium;
86 | box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12) inset;
87 | color: black;
88 | /* font-family: "Helvetica Neue",Helvetica,Arial,sans-serif; */
89 | font-size: 1em;
90 | line-height: 1.4em;
91 | padding: 5px 8px;
92 | transition: background-color 0.2s ease 0s;
93 | }
94 |
95 |
96 | textarea:focus {
97 | background: none repeat scroll 0 0 #FFFFFF;
98 | outline-width: 0;
99 | }
100 |
101 | #bio {
102 | position: relative;
103 | top: 5px;
104 | left: 5px;
105 | right: 5px;
106 | bottom: 5px;
107 | }
108 |
109 | #userInfo {
110 | position: relative;
111 | top: 5px;
112 | left: 5px;
113 | right: 5px;
114 | bottom: 5px;
115 | }
116 |
117 | .languages {
118 | position: relative;
119 | float: right;
120 | top: -503px;
121 | right -49px;
122 | padding-top: 15px;
123 | }
--------------------------------------------------------------------------------
/server/controllers/userController.js:
--------------------------------------------------------------------------------
1 | const User = require('../data/userModel.js');
2 |
3 | // Email API
4 | const nodemailer = require('nodemailer');
5 |
6 | const transporter = nodemailer.createTransport({
7 | service: 'gmail',
8 | auth: {
9 | user: 'axolotl.codesmith@gmail.com',
10 | pass: 'ilovetesting',
11 | },
12 | });
13 |
14 | const userController = {};
15 |
16 | // Return the entire list of users
17 | userController.getUsers = (req, res) => {
18 | User.find({}, (err, userList) => {
19 | if (userList.length > 0) {
20 | res.status(200).json(userList);
21 | }
22 | });
23 | };
24 |
25 | // Add a user to the invite array
26 | userController.invite = (req, res) => {
27 | const { username, target } = req.body;
28 | User.update({ username }, { $push: { invited: target } })
29 | .then(() => {
30 | User.find({ username }, (err, user) => {
31 | if (err) throw err;
32 | res.status(200).json(user[0]);
33 | });
34 | })
35 | .then(() => {
36 | User.update({ username: target }, { $push: { requests: username } }, (err) => {
37 | if (err) throw err;
38 | });
39 | })
40 | .catch((err) => {
41 | // Add error handling...
42 | console.log(err);
43 | });
44 | };
45 |
46 | // Accept a request to pair
47 | userController.connect = (req, res) => {
48 | const { username, target } = req.body;
49 | let email = '';
50 | User.update({ username }, { $push: { connected: target } })
51 | .then(() => {
52 | User.find({ username }, (err, user) => {
53 | if (err) throw err;
54 | res.status(200).json(user[0]);
55 | });
56 | })
57 | .then(() => {
58 | User.update({ username: target }, { $push: { connected: username } }, (err) => {
59 | if (err) throw err;
60 | });
61 | })
62 | .then(() => {
63 | User.find({ username }, (err, result) => {
64 | if (err) throw err;
65 | email = result.email;
66 | });
67 | })
68 | .then(() => {
69 | User.find({ username: target }, (err, result) => {
70 | const mailOptions = {
71 | from: 'axolotl.codesmith@gmail.com',
72 | to: result[0].email,
73 | subject: 'New Pair Programming Connect',
74 | html: `Contact your potential abductor: ${username} (${email})`,
75 | };
76 | transporter.sendMail(mailOptions, (err, info) => {
77 | if (err) throw err;
78 | });
79 | });
80 | })
81 | .catch((err) => {
82 | // Add error handling...
83 | console.log(err);
84 | });
85 | };
86 |
87 | // Update user info
88 | userController.update = (req, res) => {
89 | // The post request includes a user property that stores the
90 | // contents of the user object with updates
91 | let user = req.body;
92 | const { password, location, bio, email, skills, interests } = user;
93 | const updates = { password, email, location, bio, skills, interests };
94 | User.update({ username: user.username }, updates)
95 | .then(() => {
96 | User.find({username: user.username }, (err, user) => {
97 | res.status(200).json(user[0]);
98 | });
99 | })
100 | .catch((err) => {
101 | // Add error handling...
102 | console.log(err);
103 | });
104 | }
105 |
106 | module.exports = userController;
107 |
--------------------------------------------------------------------------------
/client/components/Profile.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Button } from 'react-bootstrap';
3 | // import CardExampleWithAvatar from './CardExampleWithAvatar.jsx'
4 |
5 | // user object passed
6 | // edit: boolean
7 | // edit: function
8 | // sumbit : function
9 |
10 | class Profile extends Component {
11 | constructor(props) {
12 | super(props)
13 | }
14 |
15 | render() {
16 |
17 | let connectOrEdit = (!this.props.myProfile) ? 'Connect' : 'Edit';
18 | //let connectOrEdit = (this.props.myProfile) ? 'Edit' : 'Connect';
19 | let user = this.props.user;
20 | let clickFun = this.props.clickFun;
21 | console.log('user', user);
22 |
23 | let submit = (
24 | {
25 |
26 | // have to update new user obect with new info
27 | let bio = document.getElementById('bioIn').value;
28 | let skills = document.getElementById('skillsIn').value;
29 | let interests = document.getElementById('interestsIn').value;
30 | console.log(bio, skills, interests);
31 |
32 | let updatedUser = Object.assign(
33 | user,
34 | {bio, skills, interests}
35 | )
36 | this.props.submit(updatedUser);
37 | }}/>
38 | )
39 | submit = (this.props.edit) ? submit : '';
40 |
41 | return (
42 |
43 |
44 |
45 |
46 |
47 | {/* Headding Spaced with padding
48 | Some sort of lovely supporting title
*/}
49 |
50 |
51 |
52 |
{
53 | clickFun(user);
54 | }}/>
55 |
56 | {/*

*/}
57 |

58 |
59 |
60 |
61 |
{user.username}
62 |
63 |
64 |
Bio
65 |
67 |
68 |
Skills
69 |
70 |
71 |
72 |
Interests
73 |
74 |
75 |
76 |

77 |
78 |
79 |

80 |

81 |
82 |
83 | {submit}
84 |
85 |
86 |
87 |
88 | )
89 | }
90 |
91 | }
92 |
93 | export default Profile;
94 |
--------------------------------------------------------------------------------
/client/components/App.jsx:
--------------------------------------------------------------------------------
1 | import fetch from 'isomorphic-fetch';
2 | import React, { Component } from 'react';
3 | import Login from './Login.jsx';
4 | import Profile from './Profile.jsx';
5 | import Feed from './Feed.jsx';
6 | import UserCards from './UserCards.jsx';
7 | import Nav from './Nav.jsx';
8 |
9 |
10 |
11 | function getInitialState() {
12 | return {
13 | signup: false, // toggles between signup and login
14 | user: null, // contains data for current user, check userModel for properties
15 | feed: [], // list of all users (as user objects) in db
16 | myProfile: true, // whether the profile belongs to you or another user
17 | edit: false, // whether you are in edit mode (when in your profile)
18 | profile: null, // user object from which profile info is read
19 | };
20 | }
21 |
22 | class App extends Component {
23 | constructor(props) {
24 | super(props);
25 |
26 | //binding functions
27 | this.post = this.post.bind(this);
28 | this.login = this.login.bind(this);
29 | this.signup = this.signup.bind(this);
30 | this.toggleSignup = this.toggleSignup.bind(this);
31 | this.connect = this.connect.bind(this);
32 | this.toggleEdit = this.toggleEdit.bind(this);
33 | this.updateProfile = this.updateProfile.bind(this);
34 | this.viewProfile = this.viewProfile.bind(this);
35 | this.toFeed = this.toFeed.bind(this);
36 | this.signout = this.signout.bind(this);
37 |
38 | this.state = getInitialState();
39 | }
40 |
41 | // reusable post call to server, called within methods that interact with db
42 | post(route, body, callback) {
43 | fetch(route, {
44 | method: 'POST',
45 | headers: {
46 | Accept: 'application/json',
47 | 'Content-Type': 'application/json',
48 | },
49 | body: JSON.stringify(body),
50 | }).then(response => {
51 | if (response.status >= 200 && response.status < 300) {
52 | console.log(response)
53 | return response.json();
54 | }
55 | return Promise.reject(response.statusText);
56 | }).then((json) => {
57 | callback(json);
58 | }).catch(err => {
59 | console.log('ERROR!', err);
60 | });
61 | }
62 |
63 | login(username, password) {
64 | console.log('trying to login', username, password);
65 | let that = this; // loses reference to this when in post callback
66 | return this.post('/login', {username, password}, function(response) {
67 | console.log(response);
68 | that.setState(Object.assign(
69 | that.state,
70 | {
71 | user: response.user,
72 | feed: response.list,
73 | }
74 | ));
75 | });
76 | }
77 |
78 | toggleSignup() {
79 | console.log('switching login');
80 | this.setState(Object.assign(
81 | this.state,
82 | {signup: !this.state.signup}
83 | ));
84 | }
85 |
86 |
87 | signup(username, password, email) {
88 | console.log('trying to signup', username, password, email);
89 | let that = this;
90 | return this.post('/signup', {username, password, email}, function(response) {
91 | console.log(response);
92 | that.setState(Object.assign(
93 | that.state,
94 | {
95 | edit: true,
96 | profile: response.user,
97 | user: response.user,
98 | feed: response.list,
99 | myProfile: true
100 | }
101 | ));
102 | });
103 | }
104 |
105 | updateProfile(user) {
106 | console.log('Editing User Profile', user);
107 | let that = this;
108 | return this.post('/profile', user, function(response) {
109 | console.log(response);
110 |
111 | that.setState(Object.assign(
112 | that.state,
113 | {
114 | user: response,
115 | edit: false
116 | }
117 | ));
118 | });
119 | }
120 |
121 | getFeed() {
122 | return this.post('/feed', {}, function(response) {
123 | console.log(response);
124 | that.setState(Object.assign(
125 | that.state,
126 | {
127 | feed: response
128 | }
129 | ));
130 | });
131 | }
132 |
133 | // when connect button clicked on another user
134 | // invites (expresses interest in pair-programming) if they haven't already invited you
135 | // otherwise, it connects (completes the match, email will be sent)
136 | connect(user) {
137 | if (user.invited.indexOf(this.state.user.username) < 0) {
138 | let that = this;
139 | console.log(that.state);
140 | console.log('Inviting other user', that.state.user.username, user.username);
141 | console.log('Inviting other user', user);
142 | return this.post('/invite', {username: that.state.user.username, target: user.username }, function(response) {
143 | console.log('response', response);
144 |
145 | that.setState(Object.assign(
146 | that.state,
147 | {user: response}
148 | ));
149 | });
150 | } else {
151 | let that = this;
152 | console.log('Connecting with other user', that.state.user.username, user.username);
153 | return this.post('/connect', {username: that.state.user.username, target: user.username }, function(response) {
154 | console.log('response', response);
155 |
156 | that.setState(Object.assign(
157 | that.state,
158 | {user: response}
159 | ));
160 | });
161 | }
162 | }
163 |
164 | // changes state so we'll be directed to a user's profile
165 | viewProfile(user) {
166 | // console.log('usre', user);
167 | // console.log(typeof user.myProfile);
168 | console.log('Switching to Profile', this.state.user);
169 |
170 | // defaults to current user if user isn't a user object
171 | if (typeof user.username !== 'string') {user = this.state.user};
172 | console.log('Switching to Profile', user);
173 | let that = this;
174 | let mp = (this.state.user === user);
175 | this.setState(Object.assign(
176 | this.state,
177 | {
178 | profile: user,
179 | myProfile: mp,
180 | edit: false
181 | }
182 | ));
183 | }
184 |
185 | toFeed() {
186 | console.log('Going to the feed');
187 | this.setState(Object.assign(
188 | this.state,
189 | {profile: null}
190 | ));
191 | }
192 |
193 | signout() {
194 | console.log('Signing Up');
195 | this.setState(Object.assign(
196 | getInitialState()
197 | ));
198 | }
199 |
200 |
201 |
202 | toggleEdit(user) {
203 | if(!user) user = this.state.user;
204 | console.log('Going to edit mode', user);
205 | this.setState(Object.assign(
206 | this.state,
207 | {edit: !this.state.edit}
208 | ));
209 | }
210 |
211 | render() {
212 | console.log(this.state);
213 |
214 | // stores header nav bar, doesn't render if in login
215 | let header;
216 | if (!this.state.user) {
217 | header = ''
218 | } else {
219 | // callback takes you to profile if in feed, to feed if in profile
220 | let clickFun = (this.state.profile) ? this.toFeed : this.viewProfile;
221 | header = (
222 |