171 |
--------------------------------------------------------------------------------
/build.js:
--------------------------------------------------------------------------------
1 | const webpack = require( 'webpack' );
2 |
3 | const plugins = [
4 | new webpack.DefinePlugin( {
5 | "process.env": {
6 | NODE_ENV: JSON.stringify( "production" )
7 | }
8 | } )
9 | ];
10 |
11 | if ( process.env.NODE_ENV == 'production' ) {
12 | plugins.push( new webpack.optimize.UglifyJsPlugin( {
13 | compress: {
14 | warnings: false
15 | }
16 | } ) );
17 | }
18 |
19 | const conf = {
20 | entry: [ 'babel-polyfill', __dirname + '/src/shell.js' ],
21 | output: {
22 | path: __dirname + '/public/',
23 | filename: 'bundle.js'
24 | },
25 | plugins: plugins,
26 | module: {
27 | loaders: [ {
28 | test: /\.js$/,
29 | loader: 'babel-loader',
30 | query: {
31 | presets: [ [ 'es2015' ], [ 'react' ], [ 'stage-2' ] ],
32 | plugins: [ 'transform-async-to-generator' ]
33 | }
34 | } ]
35 | }
36 | };
37 |
38 | if ( require.main == module ) {
39 | webpack( conf, function ( err, info ) {
40 | if ( err ) {
41 | console.log( err );
42 | }
43 | if ( info && info.compilation.errors.length ) {
44 | console.log( info.compilation.errors );
45 | }
46 | } );
47 | } else {
48 | module.exports = require( 'webpack-dev-middleware' )( webpack( conf ), {
49 | watchOptions: {
50 | aggregateTimeout: 300
51 | },
52 | publicPath: '/'
53 | } );
54 | }
55 |
--------------------------------------------------------------------------------
/bundle-server.js:
--------------------------------------------------------------------------------
1 | const express = require( 'express' );
2 | const app = express();
3 |
4 | app.use( require( './build' ) );
5 |
6 | app.listen( 8081, () => {
7 | console.log( 'Ready to compile and serve bundle.js' );
8 | } );
9 |
--------------------------------------------------------------------------------
/config/setupDb.sql:
--------------------------------------------------------------------------------
1 | DROP TABLE IF EXISTS messages;
2 | DROP TABLE IF EXISTS friendships;
3 | DROP TABLE IF EXISTS users;
4 |
5 | CREATE TABLE users(
6 | uid SERIAL PRIMARY KEY,
7 | email VARCHAR(300) NOT NULL UNIQUE,
8 | password VARCHAR(200) NOT NULL,
9 | "firstName" VARCHAR(200) NOT NULL,
10 | "lastName" VARCHAR (200) NOT NULL,
11 | "profilePic" VARCHAR(300),
12 | bio VARCHAR(300),
13 | timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
14 | );
15 |
16 | CREATE TABLE friendships(
17 | fid SERIAL PRIMARY KEY,
18 | "fromUserId" INTEGER NOT NULL REFERENCES users(uid),
19 | status VARCHAR (200) NOT NULL,
20 | "toUserId" INTEGER NOT NULL REFERENCES users(uid)
21 | );
22 |
23 | CREATE TABLE messages(
24 | mid SERIAL PRIMARY KEY,
25 | "fromUserId" INTEGER NOT NULL REFERENCES users(uid),
26 | "toUserId" INTEGER REFERENCES users(uid),
27 | "toAll" BIT DEFAULT B'0'::"bit",
28 | "messageBody" VARCHAR (300) NOT NULL,
29 | timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
30 | read BIT DEFAULT B'0'::"bit"
31 | );
32 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | P2P-Chat
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | // REQUIRED MODULES_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2 | const express = require( 'express' ),
3 | // morgan = require( 'morgan' ),
4 | path = require( 'path' ),
5 | bodyParser = require( 'body-parser' ),
6 | cookieSession = require( 'cookie-session' ),
7 | csrf = require( 'csurf' ),
8 | compression = require( 'compression' );
9 | // db = require( './modules/dbQuery' );
10 | // favicon = require( 'serve-favicon' );
11 |
12 |
13 | // EXPRESS
14 | const app = express();
15 |
16 | // SOCKETio
17 | const server = require( 'http' ).Server( app );
18 | const io = require( 'socket.io' )( server );
19 | module.exports.io = io;
20 |
21 | // PEERJS
22 | const ExpressPeerServer = require( 'peer' ).ExpressPeerServer;
23 | const options = { debug: true };
24 |
25 | // MIDDLEWARE __________________________________________________________________
26 |
27 | // HTTP request logger middleware
28 | // app.use( morgan( 'dev' ) );
29 | // _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
30 |
31 |
32 | // compression gZip response before sending them
33 | app.use( compression() );
34 | // _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
35 |
36 |
37 | // BODY PARSER
38 | app.use( bodyParser.json() );
39 | // _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
40 |
41 |
42 | // COOKIE SESSION
43 | app.use( cookieSession( {
44 | secret: require( './config/secrets.json' ).sessionSecret,
45 | maxAge: 1000 * 60 * 60 * 24 * 14
46 | } ) );
47 | // _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
48 |
49 |
50 | if ( process.env.NODE_ENV != 'production' ) {
51 | app.use( '/bundle.js', require( 'http-proxy-middleware' )( {
52 | target: 'http://localhost:8081/'
53 | } ) );
54 | }
55 |
56 |
57 | // set the public folder where client stuff lives
58 | // app.use( express.static( './public' ) );
59 | app.use( express.static( path.join( __dirname, '/public' ) ) );
60 |
61 |
62 | // CSURF
63 | // app.use( csrf() );
64 |
65 | app.use( ( req, res, next ) => {
66 | // res.cookie( '__csrf__', req.csrfToken() );
67 | next();
68 | } );
69 | // _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
70 |
71 |
72 | // ROUTING _____________________________________________________________________
73 | // Connect all our routes to our application
74 | app.use( '/', require( './routes/root' ) );
75 | app.use( '/api/', require( './routes/api' ) );
76 | // load webSocket.js and pass it the socket.io object
77 | app.use( '/ws/', require( './routes/webSocket' ) );
78 |
79 |
80 | // PEERJS ROUTING
81 | app.use( '/peerjs', ExpressPeerServer( server, options ) );
82 |
83 | // if no route match then..
84 | app.get( '*', function ( req, res ) {
85 | res.sendFile( path.join( __dirname, 'index.html' ) );
86 | } );
87 |
88 |
89 |
90 | // PEERJS EVENTS LISTENING
91 | server.on( 'connection', peer => {
92 | console.log( 'Peerjs - server - Event - "connection" - id:', peer.id );
93 | } );
94 | server.on( 'disconnect', peer => {
95 | console.log( 'Peerjs - server - Event - "disconnect" - id:', peer.id );
96 | } );
97 |
98 |
99 |
100 | // ERROR:
101 | // catch 404 and forward to error handler
102 | app.use( function ( req, res, next ) {
103 | var err = new Error( 'Not Found' );
104 | err.status = 404;
105 | next( err );
106 | } );
107 |
108 | app.use( ( err, req, res, next ) => {
109 | // set locals, only providing error in development
110 | res.locals.message = err.message;
111 | res.locals.error = req.app.get( 'env' ) === 'development' ? err : {};
112 |
113 | // render the error page
114 | res.status( err.status || 500 );
115 | console.log(err);
116 | res.send( 'error' );
117 | } );
118 | // _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
119 |
120 |
121 | // SERVER ______________________________________________________________________
122 | const listener = server.listen( process.env.PORT || 8080, () => {
123 | console.log( `listening on port ${listener.address().port}.` );
124 | } );
125 |
--------------------------------------------------------------------------------
/modules/hasher.js:
--------------------------------------------------------------------------------
1 | const bcrypt = require( 'bcryptjs' );
2 |
3 | const salt = process.env.BCRYPTSALT || require( '../config/secrets.json' ).bcryptSalt;
4 |
5 | function hashPassword( plainTextPassword ) {
6 | return new Promise( function ( resolve, reject ) {
7 | bcrypt.genSalt( function ( err, salt ) {
8 | if ( err ) {
9 | return reject( err );
10 | }
11 | bcrypt.hash( plainTextPassword, salt, function ( err, hash ) {
12 | if ( err ) {
13 | return reject( err );
14 | }
15 | resolve( hash );
16 | } );
17 | } );
18 | } );
19 | }
20 |
21 | function checkPassword( textEnteredInLoginForm, hashedPasswordFromDatabase ) {
22 | return new Promise( function ( resolve, reject ) {
23 | bcrypt.compare( textEnteredInLoginForm, hashedPasswordFromDatabase, function ( err, doesMatch ) {
24 | if ( err ) {
25 | reject( err );
26 | } else {
27 | resolve( doesMatch );
28 | }
29 | } );
30 | } );
31 | }
32 |
33 | module.exports.hashPassword = hashPassword;
34 | module.exports.checkPassword = checkPassword;
35 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "p2p-chat",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "build": "node build.js",
8 | "start": "npm run build && node index.js",
9 | "test": "echo \"Error: no test specified\" && exit 1"
10 | },
11 | "engines": {
12 | "node": "8.4.0"
13 | },
14 | "nodemonConfig": {
15 | "ignore": [
16 | "src/*"
17 | ],
18 | "delay": "2500"
19 | },
20 | "dependencies": {
21 | "axios": "^0.15.3",
22 | "babel-cli": "^6.18.0",
23 | "babel-core": "^6.20.0",
24 | "babel-loader": "^6.2.9",
25 | "babel-plugin-transform-async-to-generator": "^6.24.1",
26 | "babel-polyfill": "^6.26.0",
27 | "babel-preset-es2015": "^6.18.0",
28 | "babel-preset-latest": "^6.16.0",
29 | "babel-preset-react": "^6.16.0",
30 | "babel-preset-stage-2": "^6.24.1",
31 | "bcryptjs": "^2.4.3",
32 | "body-parser": "^1.15.2",
33 | "compression": "^1.7.0",
34 | "cookie-parser": "^1.4.3",
35 | "cookie-session": "^2.0.0-alpha.2",
36 | "csurf": "^1.9.0",
37 | "express": "^4.14.0",
38 | "express-session": "^1.15.5",
39 | "http-proxy-middleware": "^0.17.4",
40 | "knox": "^0.9.2",
41 | "material-design-icons": "^3.0.1",
42 | "material-ui": "^0.19.2",
43 | "morgan": "^1.8.2",
44 | "multer": "^1.3.0",
45 | "peer": "^0.2.8",
46 | "react": "^15.4.1",
47 | "react-dom": "^15.4.1",
48 | "react-redux": "^5.0.5",
49 | "react-router": "^3.0.0",
50 | "react-tap-event-plugin": "^2.0.1",
51 | "redux": "^3.7.1",
52 | "redux-devtools-extension": "^2.13.2",
53 | "redux-promise": "^0.5.3",
54 | "socket.io": "^2.0.3",
55 | "spiced-pg": "spicedacademy/spiced-pg",
56 | "styled-components": "^2.1.2",
57 | "tern-node-express": "^0.4.0",
58 | "uid-safe": "^2.1.4",
59 | "webpack": "^1.14.0",
60 | "webpack-dev-middleware": "^1.8.4"
61 | },
62 | "keywords": [],
63 | "author": "",
64 | "license": "ISC",
65 | "devDependencies": {
66 | "eslint": "^4.6.1",
67 | "eslint-plugin-react": "^7.3.0"
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/public/ico-security.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/profile_pic_def/def_profilePic_1.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
85 |
--------------------------------------------------------------------------------
/public/profile_pic_def/def_profilePic_10.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
77 |
--------------------------------------------------------------------------------
/public/profile_pic_def/def_profilePic_11.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
124 |
--------------------------------------------------------------------------------
/public/profile_pic_def/def_profilePic_12.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
100 |
--------------------------------------------------------------------------------
/public/profile_pic_def/def_profilePic_2.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
77 |
--------------------------------------------------------------------------------
/public/profile_pic_def/def_profilePic_3.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
73 |
--------------------------------------------------------------------------------
/public/profile_pic_def/def_profilePic_4.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
85 |
--------------------------------------------------------------------------------
/public/profile_pic_def/def_profilePic_5.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
92 |
--------------------------------------------------------------------------------
/public/profile_pic_def/def_profilePic_6.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
87 |
--------------------------------------------------------------------------------
/public/profile_pic_def/def_profilePic_7.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
123 |
--------------------------------------------------------------------------------
/public/profile_pic_def/def_profilePic_8.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
79 |
--------------------------------------------------------------------------------
/public/profile_pic_def/def_profilePic_9.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
94 |
--------------------------------------------------------------------------------
/readme/p2p_chat-find_friends.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/suddenlyGiovanni/spiced_academy--p2p_chat/a64d852d68888778f407edd741eb922be90749cb/readme/p2p_chat-find_friends.gif
--------------------------------------------------------------------------------
/readme/p2p_chat-group_chat.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/suddenlyGiovanni/spiced_academy--p2p_chat/a64d852d68888778f407edd741eb922be90749cb/readme/p2p_chat-group_chat.gif
--------------------------------------------------------------------------------
/readme/p2p_chat-login.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/suddenlyGiovanni/spiced_academy--p2p_chat/a64d852d68888778f407edd741eb922be90749cb/readme/p2p_chat-login.gif
--------------------------------------------------------------------------------
/readme/p2p_chat-manage_friendship .gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/suddenlyGiovanni/spiced_academy--p2p_chat/a64d852d68888778f407edd741eb922be90749cb/readme/p2p_chat-manage_friendship .gif
--------------------------------------------------------------------------------
/readme/p2p_chat-private_chat.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/suddenlyGiovanni/spiced_academy--p2p_chat/a64d852d68888778f407edd741eb922be90749cb/readme/p2p_chat-private_chat.gif
--------------------------------------------------------------------------------
/readme/p2p_chat-profile_pic.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/suddenlyGiovanni/spiced_academy--p2p_chat/a64d852d68888778f407edd741eb922be90749cb/readme/p2p_chat-profile_pic.gif
--------------------------------------------------------------------------------
/readme/p2p_chat-register.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/suddenlyGiovanni/spiced_academy--p2p_chat/a64d852d68888778f407edd741eb922be90749cb/readme/p2p_chat-register.gif
--------------------------------------------------------------------------------
/routes/root.js:
--------------------------------------------------------------------------------
1 | // ROUTE: --> /
2 | const router = require( 'express' ).Router();
3 | const path = require( 'path' );
4 |
5 |
6 |
7 | // router.get( '/', ( req, res ) => {
8 | // res.sendFile( path.join( __dirname, '../index.html' ) );
9 | // } );
10 |
11 |
12 |
13 | router.get( '/', function ( req, res ) {
14 | if ( !req.session.user ) {
15 | return res.redirect( '/welcome/' );
16 | }
17 | res.sendFile( path.join( __dirname, '../index.html' ) );
18 | } );
19 |
20 |
21 |
22 | router.get( '/welcome/', function ( req, res ) {
23 | if ( req.session.user ) {
24 | return res.redirect( '/' );
25 | }
26 | res.sendFile( path.join( __dirname, '../index.html' ) );
27 | } );
28 |
29 |
30 |
31 | /* MODULE EXPORTS */
32 | module.exports = router;
33 |
--------------------------------------------------------------------------------
/routes/webSocket-bk.js:
--------------------------------------------------------------------------------
1 | // ROUTE: --> /ws/
2 | const router = require( 'express' ).Router(),
3 | db = require( '../modules/dbQuery' ),
4 | io = require( '../index' ).io;
5 |
6 |
7 | // SOCKET.IO WS:
8 | // define constructor function that gets `io` send to it
9 | // function( io ) {
10 | // };
11 | io.on( 'connection', socket => {
12 | console.log( `socket with the id ${socket.id} is now connected` );
13 |
14 | socket.on( 'disconnect', () => {
15 | console.log( `SocketIo - on: "disconnect" - socket with the id ${socket.id} is now disconnected` );
16 | // remove here the disconnected socket from the list of online users:
17 | /*
18 | it is possible for a single user to appear in the list more than once.
19 | it is important to only remove the item from the list that has the
20 | matching socket id when 'disconnect' event occurs
21 | */
22 | const disconnectedUserSocket = onlineUsers.find( user => user.socketId == socket.id );
23 | // console.log( 'disconnectedUserSocket: ', disconnectedUserSocket );
24 |
25 | onlineUsers.splice( onlineUsers.indexOf( disconnectedUserSocket ), 1 );
26 | // console.log( 'onlineUsers - after removing disconnectedUserSocket: ', onlineUsers );
27 | /*
28 | When a user disconnects, after removing the user with the id of the
29 | socket that disconnected from the list, you should determine if that
30 | user is no longer in your list at all. If the user is gone, the server
31 | should send a message to all connected clients with the id of the
32 | disconnected user in the payload so that they can update their lists of
33 | online users accordingly. Let's call this event 'userLeft'.
34 | */
35 | const disconnectedUserId = onlineUsers.filter( user => {
36 | // remember that filter return an array
37 | return user.userId == disconnectedUserSocket.userId;
38 | } );
39 | // console.log( 'disconnectedUserId: ', disconnectedUserId );
40 |
41 | if ( disconnectedUserId.length === 0 ) {
42 | const userId = disconnectedUserSocket.userId;
43 | console.log(`last user with id ${userId} has gone offline`);
44 | io.sockets.emit( 'userLeft', userId );
45 | }
46 |
47 | } );
48 |
49 | socket.on( 'chatMessage', ( messageBody ) => {
50 | const messengerId = onlineUsers.find( user => user.socketId == socket.id ).userId;
51 | console.log( `SocketIo - on: "chatMessage" - messengerId: ${messengerId} - payload:`, messageBody );
52 | /* When the server receives this event, it should broadcast
53 | a 'chatMessage' event to all of the connected sockets.
54 | The payload for this event should include the message the user sent
55 | as well as the user's id, first name, last name, and profile pic.*/
56 | return db.createPublicMessage( messengerId, messageBody )
57 | .then( newPublicMessage => io.sockets.emit( 'chatMessage', newPublicMessage ) )
58 | .catch( err => console.error( err.stack ) );
59 | } );
60 |
61 |
62 | socket.on( 'chatMessagePrivate', privateMessage => {
63 | const fromUserId = onlineUsers.find( user => user.socketId == socket.id ).userId;
64 | const { toUserId, messageBody } = privateMessage;
65 |
66 | const toUserIdOnline = onlineUsers.find( user => user.userId == toUserId );
67 |
68 | const fromUserSocketId = socket.id;
69 | const toUserSocketId = toUserIdOnline && toUserIdOnline.socketId;
70 |
71 | console.log( `SocketIo - on: "chatMessagePrivate"
72 | - fromUserId: ${fromUserId} - toUserId: ${toUserId}
73 | - payload:`, privateMessage );
74 | return db.createPrivateMessage( fromUserId, toUserId, messageBody )
75 |
76 | .then( newPrivateMessage => {
77 | console.log( 'emitting private messaging - fromUserSocketId', fromUserSocketId );
78 | io.sockets.sockets[ fromUserSocketId ].emit( 'privateChatMessage', newPrivateMessage );
79 | toUserSocketId && io.sockets.sockets[ toUserSocketId ].emit( 'privateChatMessage', newPrivateMessage );
80 | } )
81 |
82 | .catch( err => console.error( err.stack ) );
83 | } );
84 |
85 | } );
86 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
87 |
88 | function makeSureUserIsLoggedIn( req, res, next ) {
89 | console.log( 'webSocket.js - fn: makeSureUserIsLoggedIn' );
90 | next();
91 | }
92 |
93 |
94 | let onlineUsers = [];
95 | let mapUsersIdToPeerId = [];
96 | /* exemple...
97 | onlineUsers = [
98 | {
99 | socketId: 'wJdwDQAKhUuXxZ2vAAAA',
100 | userId: 1
101 | }
102 | ];
103 |
104 | mapUsersIdToPeerId = [
105 | {
106 | peerId: 'lololololllolloo',
107 | userId: 1
108 | }
109 | ]
110 | */
111 |
112 | // SOCKET.IO ROUTES
113 | /* make a route that the client can hit after the socket connects.
114 | The /ws/connected/:socketId route can then read the user's id from
115 | the session and the socket id from req.params */
116 | router.post( '/connected/:socketId', makeSureUserIsLoggedIn, ( req, res ) => {
117 | const uid = req.session.user.uid;
118 | const socketId = req.params.socketId;
119 |
120 | const socketAlreadyThere = onlineUsers.find( user => user.socketId == socketId );
121 | const userAlreadyThere = onlineUsers.find( user => user.userId == uid );
122 |
123 | console.log( `API: method: POST /ws/connected/:${socketId} - uid: ${uid} -
124 | onlineUsers: `, onlineUsers, '\n' );
125 |
126 | if ( !socketAlreadyThere && io.sockets.sockets[ socketId ] ) {
127 | /* Every time a logged in user makes a request, push
128 | an object representing that user to an array after confirming that
129 | that user is not already in the list */
130 | onlineUsers.push( { userId: uid, socketId } );
131 |
132 | /* When a user is added to the list of online users, the server should
133 | send a message to that user with the list of all online users
134 | as the payload: event 'onlineUsers' */
135 | return db.readAllUsersByIds( onlineUsers.map( user => user.userId ) )
136 |
137 | .then( onlineUsers => io.sockets.sockets[ socketId ].emit( 'onlineUsers', onlineUsers ) )
138 |
139 | .then( () => {
140 | return db.readAllPublicMessages()
141 | .then( publicMessageList => io.sockets.sockets[ socketId ].emit( 'publicChatMessages', publicMessageList ) );
142 | } )
143 |
144 | .then( () => {
145 | return db.readAllPrivateMessages( uid )
146 | .then( privateMessageList => io.sockets.sockets[ socketId ].emit( 'privateChatMessages', privateMessageList ) );
147 | } )
148 |
149 | .then( () => {
150 | res.json( { success: true } );
151 | /* Also when a user is added to the list of online users,
152 | the server should send a message to all online users with information
153 | about the user who just came online as the payload, allowing all clients
154 | to keep their list of online users updated: event 'userJoined' */
155 | if ( !userAlreadyThere ) {
156 | return db.readUser( uid )
157 | .then( userJoined => {
158 | // FIXME: this should not be done here!!!!!
159 | userJoined.online = true;
160 | return io.sockets.emit( 'userJoined', userJoined );
161 | } );
162 | }
163 | } )
164 |
165 | .catch( err => console.log( err ) );
166 | }
167 | } );
168 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
169 |
170 | // PEER.JS ROUTES
171 | /* make a route that the client can hit after the socket connects.
172 | The /ws/storeIdToServer/:peerId route can then read the user's id from
173 | the session and the socket id from req.params
174 | */
175 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
176 |
177 | router.post( '/storeIdToServer/:peerId', makeSureUserIsLoggedIn, ( req, res ) => {
178 | const uid = req.session.user.uid;
179 | const peerId = req.params.peerId;
180 |
181 | // FIXME: eventually in the future send the peerId info to just the clients of the the user's friend
182 |
183 | const peerAlreadyThere = mapUsersIdToPeerId.find( user => user.peerId == peerId );
184 | const userAlreadyThere = mapUsersIdToPeerId.find( user => user.userId == uid );
185 |
186 | console.log( `API: method: POST /ws/storeIdToServer/:${peerId} - uid: ${uid} -
187 | onlineUsers: `, onlineUsers, '\n' );
188 |
189 | if ( !peerAlreadyThere ) {
190 | /* Every time a logged in user makes a request, push
191 | an object representing that user to an array after confirming that
192 | that user is not already in the list */
193 | mapUsersIdToPeerId.push( { userId: uid, peerId } );
194 |
195 | /* When a user is added to the list of mapUsersIdToPeerId, the server should
196 | send a message to that user with the list of all online users linked to their peerId
197 | as the payload: event 'onlineUsers' */
198 | return db.readAllUsersByIds( mapUsersIdToPeerId.map( user => user.userId ) )
199 |
200 | .then( onlinePeers => {
201 | // find the socket that belongs to the user
202 | const socketId = onlineUsers.find( user => user.userId == uid ).socketId;
203 | // set update the user list with their respecting peerId
204 | const updatedUsers = onlinePeers.map( user => {
205 | const peerIdToAdd = mapUsersIdToPeerId.find( peer => peer.userId === user.uid ).peerId;
206 | return { ...user, peerId: peerIdToAdd };
207 | } );
208 | return io.sockets.sockets[ socketId ].emit( 'onlinePeers', updatedUsers );
209 | } )
210 |
211 | .then( () => {
212 | res.json( { success: true } );
213 | /* Also when a user is added to the list of online users,
214 | the server should send a message to all online users with information
215 | about the user who just came online as the payload, allowing all clients
216 | to keep their list of online users updated: event 'userJoined' */
217 | if ( !userAlreadyThere ) {
218 | return db.readUser( uid )
219 | .then( userData => {
220 | // FIXME: this should not be done here!!!!!
221 | // userData.peerId = peerId;
222 | return io.sockets.emit( 'peerJoined', { ...userData, peerId: peerId } );
223 | } );
224 | }
225 | } )
226 |
227 | .catch( err => console.log( err ) );
228 | }
229 | } );
230 |
231 |
232 | /* MODULE EXPORTS */
233 | module.exports = router;
234 | // module.exports.io = io;
235 |
--------------------------------------------------------------------------------
/routes/webSocket.js:
--------------------------------------------------------------------------------
1 | // ROUTE: --> /ws/
2 | const router = require( 'express' ).Router(),
3 | db = require( '../modules/dbQuery' ),
4 | io = require( '../index' ).io;
5 |
6 | let onlineUsers = {};
7 | /* example...
8 | onlineUsers = {
9 | uid:{
10 | socketId: ['wJdwDQAKhUuXxZ2vAAAA', 'yolloyllyo'],
11 | peerId: 'lololololllolloo',
12 | }
13 | }
14 | */
15 |
16 | // SOCKET.IO WS:
17 | // define constructor function that gets `io` send to it
18 | // function( io ) {
19 | // };
20 | io.on( 'connection', socket => {
21 | console.log( `socket with the id ${socket.id} is now connected` );
22 |
23 | socket.on( 'disconnect', () => {
24 | console.log( `SocketIo - on: "disconnect" - socket with the id ${socket.id} is now disconnected` );
25 | // remove here the disconnected socket from the list of online users:
26 | /*
27 | it is possible for a single user to appear in the list more than once.
28 | it is important to only remove the item from the list that has the
29 | matching socket id when 'disconnect' event occurs
30 | */
31 |
32 | let userId;
33 |
34 | for ( let uid in onlineUsers ) {
35 | const socketIds = onlineUsers[ uid ].socketIds;
36 | const matchSocket = socketIds.find( socketId => socketId === socket.id );
37 | if ( matchSocket ) {
38 | userId = uid;
39 | }
40 | }
41 |
42 | if ( !onlineUsers[ userId ] ) {
43 | return
44 | }
45 |
46 | const socketIdIndex = onlineUsers[ userId ].socketIds.indexOf( socket.id );
47 | onlineUsers[ userId ].socketIds.splice( socketIdIndex, 1 );
48 |
49 |
50 | // console.log( 'onlineUsers - after removing disconnectedUserSocket: ', onlineUsers );
51 | /*
52 | When a user disconnects, after removing the user with the id of the
53 | socket that disconnected from the list, you should determine if that
54 | user is no longer in your list at all. If the user is gone, the server
55 | should send a message to all connected clients with the id of the
56 | disconnected user in the payload so that they can update their lists of
57 | online users accordingly. Let's call this event 'userLeft'.
58 | */
59 |
60 | if ( onlineUsers[ userId ].socketIds.length === 0 ) {
61 | console.log( `last user with id ${userId} has gone offline` );
62 | io.sockets.emit( 'userLeft', userId );
63 | delete onlineUsers[ userId ];
64 | }
65 |
66 | } );
67 |
68 | socket.on( 'chatMessage', messageBody => {
69 | let userId;
70 | for ( let uid in onlineUsers ) {
71 | const socketIds = onlineUsers[ uid ].socketIds;
72 | const matchSocket = socketIds.find( socketId => socketId === socket.id );
73 | if ( matchSocket ) {
74 | userId = uid;
75 | }
76 | }
77 |
78 | console.log( `SocketIo - on: "chatMessage" - messengerId: ${userId} - payload:`, messageBody );
79 | /* When the server receives this event, it should broadcast
80 | a 'chatMessage' event to all of the connected sockets.
81 | The payload for this event should include the message the user sent
82 | as well as the user's id, first name, last name, and profile pic.*/
83 | return db.createPublicMessage( userId, messageBody )
84 | .then( newPublicMessage => io.sockets.emit( 'chatMessage', newPublicMessage ) )
85 | .catch( err => console.error( err.stack ) );
86 | } );
87 |
88 |
89 | socket.on( 'chatMessagePrivate', privateMessage => {
90 | let fromUserId;
91 | for ( let uid in onlineUsers ) {
92 | const socketIds = onlineUsers[ uid ].socketIds;
93 | const matchSocket = socketIds.find( socketId => socketId === socket.id );
94 | if ( matchSocket ) {
95 | fromUserId = uid;
96 | }
97 | }
98 | const { toUserId, messageBody } = privateMessage;
99 | const toUserIdOnline = Object.keys( onlineUsers ).find( userId => userId == toUserId );
100 |
101 | const fromUserSocketId = socket.id;
102 | const toUserSocketId = toUserIdOnline && onlineUsers[ toUserIdOnline ].socketIds.slice( -1 )[ 0 ];
103 |
104 | console.log( `SocketIo - on: "chatMessagePrivate"
105 | - fromUserId: ${fromUserId} - toUserId: ${toUserId}
106 | - payload:`, privateMessage );
107 | return db.createPrivateMessage( fromUserId, toUserId, messageBody )
108 |
109 | .then( newPrivateMessage => {
110 | console.log( 'emitting private messaging - fromUserSocketId', fromUserSocketId );
111 | io.sockets.sockets[ fromUserSocketId ].emit( 'privateChatMessage', newPrivateMessage );
112 | toUserSocketId && io.sockets.sockets[ toUserSocketId ].emit( 'privateChatMessage', newPrivateMessage );
113 | } )
114 |
115 | .catch( err => console.error( err.stack ) );
116 | } );
117 |
118 | } );
119 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
120 |
121 | function makeSureUserIsLoggedIn( req, res, next ) {
122 | console.log( 'webSocket.js - fn: makeSureUserIsLoggedIn' );
123 | next();
124 | }
125 |
126 |
127 |
128 |
129 | // SOCKET.IO ROUTES
130 | /* make a route that the client can hit after the socket connects.
131 | The /ws/connected/:socketId route can then read the user's id from
132 | the session and the socket id from req.params */
133 | router.post( '/connected/:socketId', makeSureUserIsLoggedIn, ( req, res ) => {
134 | const uid = req.session.user.uid;
135 | const socketId = req.params.socketId;
136 | console.log( `API: method: POST /ws/connected/:${socketId} - uid: ${uid} -
137 | onlineUsers: `, onlineUsers, '\n' );
138 | let userAlreadyThere = true;
139 | if ( !onlineUsers[ uid ] ) {
140 | userAlreadyThere = false;
141 | onlineUsers[ uid ] = {
142 | socketIds: [ socketId ]
143 | };
144 | }
145 |
146 | const socketAlreadyThere = onlineUsers[ uid ].socketIds.find( socket => socket == socketId );
147 |
148 | /* Every time a logged in user makes a request, push
149 | an object representing that user to an array after confirming that
150 | that user is not already in the list */
151 |
152 | // if the obj with key of uid is present => then update it's property
153 |
154 | // if the obj with key of uid is present => then update it's property
155 | // if the socket is not in the user's socketsIds list, than add it to he list.
156 | if ( !socketAlreadyThere && io.sockets.sockets[ socketId ] ) {
157 | onlineUsers[ uid ].socketIds.push( socketId );
158 | }
159 | // else, there can't be any sockets ids duplication's, so break
160 |
161 |
162 | // THEN SEND the list of all online users TO THE USER HIMSELF
163 | /* When a user is added to the list of online users, the server should
164 | send a message to that user with the list of all online users
165 | as the payload: event 'onlineUsers' */
166 | return db.readAllUsersByIds( Object.keys( onlineUsers ) )
167 |
168 | .then( onlineUsers => io.sockets.sockets[ socketId ].emit( 'onlineUsers', onlineUsers ) )
169 |
170 | .then( () => {
171 | return db.readAllPublicMessages().then( publicMessageList => io.sockets.sockets[ socketId ].emit( 'publicChatMessages', publicMessageList ) );
172 | } )
173 |
174 | .then( () => {
175 | return db.readAllPrivateMessages( uid ).then( privateMessageList => io.sockets.sockets[ socketId ].emit( 'privateChatMessages', privateMessageList ) );
176 | } )
177 |
178 | // then send TO ALL ONLINE USERS the data and socketId of the user himself.
179 | .then( () => {
180 | res.json( { success: true } );
181 | /* Also when a user is added to the list of online users,
182 | the server should send a message to all online users with information
183 | about the user who just came online as the payload, allowing all clients
184 | to keep their list of online users updated: event 'userJoined' */
185 | if ( !userAlreadyThere ) {
186 | return db.readUser( uid )
187 | .then( userJoined => {
188 | // FIXME: this should not be done here!!!!!
189 | userJoined.online = true;
190 | return io.sockets.emit( 'userJoined', userJoined );
191 | } );
192 | }
193 | } )
194 |
195 | .catch( err => console.log( err ) );
196 |
197 | } );
198 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
199 |
200 | // PEER.JS ROUTES
201 | /* make a route that the client can hit after the socket connects.
202 | The /ws/storeIdToServer/:peerId route can then read the user's id from
203 | the session and the socket id from req.params
204 | */
205 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
206 |
207 | router.post( '/storeIdToServer/:peerId', makeSureUserIsLoggedIn, ( req, res ) => {
208 | const uid = req.session.user.uid;
209 | const peerId = req.params.peerId;
210 |
211 | // FIXME: eventually in the future send the peerId info to just the clients of the the user's friend
212 |
213 | // const peerAlreadyThere = mapUsersIdToPeerId.find( user => user.peerId == peerId );
214 | const peerAlreadyThere = onlineUsers[ uid ].peerId === peerId;
215 | // const userAlreadyThere = mapUsersIdToPeerId.find( user => user.userId == uid );
216 | // const userAlreadyThere = onlineUsers.hasOwnProperty( uid );
217 |
218 | console.log( `API: method: POST /ws/storeIdToServer/:${peerId} - uid: ${uid} -
219 | onlineUsers: `, onlineUsers, '\n' );
220 |
221 | /* Every time a logged in user makes a request, push
222 | an object representing that user to an array after confirming that
223 | that user is not already in the list */
224 |
225 | // if user is not yet a prop of the onlineUsers obj
226 | if ( !onlineUsers.hasOwnProperty( uid ) ) {
227 | // then add a new user to the onlineUsers and set it's peerId prop accordingly
228 | onlineUsers[ uid ] = { peerId: peerId };
229 | } else if ( onlineUsers.hasOwnProperty( uid ) ) {
230 | if ( !peerAlreadyThere ) {
231 | // if the obj with key of uid is present => then update it's property
232 | // if the peerId is not already there, than add it to the prop.
233 | onlineUsers[ uid ].peerId = peerId;
234 | }
235 | }
236 |
237 | /* When a user is added to the list of mapUsersIdToPeerId, the server should
238 | send a message to that user with the list of all online users linked to their peerId
239 | as the payload: event 'onlineUsers' */
240 | return db.readAllUsersByIds( Object.keys( onlineUsers ) )
241 |
242 | .then( onlinePeers => {
243 | // find the socket that belongs to the user
244 | const socketId = onlineUsers[ uid ].socketIds.slice( -1 )[ 0 ];
245 |
246 | // set update the user list with their respecting peerId
247 | const updatedUsers = onlinePeers.map( user => {
248 | // const peerIdToAdd = mapUsersIdToPeerId.find( peer => peer.userId === user.uid ).peerId;
249 | for ( let uid in onlineUsers ) {
250 | if ( uid == user.uid ) {
251 | return { ...user, peerId: onlineUsers[ uid ].peerId };
252 | }
253 | }
254 | } );
255 | return io.sockets.sockets[ socketId ].emit( 'onlinePeers', updatedUsers );
256 | } )
257 |
258 | .then( () => {
259 | res.json( { success: true } );
260 | /* Also when a user is added to the list of online users,
261 | the server should send a message to all online users with information
262 | about the user who just came online as the payload, allowing all clients
263 | to keep their list of online users updated: event 'userJoined' */
264 | return db.readUser( uid )
265 | .then( userData => {
266 | // FIXME: this should not be done here!!!!!
267 | // userData.peerId = peerId;
268 | return io.sockets.emit( 'peerJoined', { ...userData, peerId: peerId } );
269 | } );
270 |
271 | } )
272 |
273 | .catch( err => console.log( err ) );
274 | } );
275 |
276 |
277 | /* MODULE EXPORTS */
278 | module.exports = router;
279 | // module.exports.io = io;
280 |
--------------------------------------------------------------------------------
/src/actions/actions.js:
--------------------------------------------------------------------------------
1 | import axios from '../utils/axios';
2 |
3 |
4 |
5 |
6 | export function logOutUser() {
7 | console.log( 'REDUX - ACTION - fn: logOutUser' );
8 | return axios.get( '/api/logout' )
9 |
10 | .then( result => {
11 | console.log( 'REDUX - ACTION - fn: logOutUser - data', result.data );
12 | if ( result.data.success ) {
13 | window.location.href = '/';
14 | }
15 | } )
16 |
17 | .catch( err => console.log( err ) );
18 | }
19 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
20 |
21 |
22 | export function loadUserData() {
23 | console.log( 'REDUX - ACTION - fn: loadUserData' );
24 | return axios.get( '/api/user' )
25 | .then( result => {
26 | console.log( 'REDUX - ACTION - fn: loadUserData - data', result.data );
27 | return {
28 | type: 'LOAD_USER_DATA',
29 | user: result.data.userData
30 | };
31 |
32 | } )
33 |
34 | .catch( err => {
35 | console.log( err );
36 | return { type: 'ERROR' };
37 | } );
38 | }
39 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
40 |
41 |
42 | export function updateProfilePic( formData ) {
43 | console.log( 'REDUX - ACTION - fn: updateProfilePic' );
44 | return axios.put( '/api/user/profile_pic', formData )
45 |
46 | .then( result => {
47 | if ( result.data.success ) {
48 | return {
49 | type: 'UPDATE_USER_DATA',
50 | user: result.data.userData
51 | };
52 | }
53 | } )
54 |
55 | .catch( err => console.log( err ) );
56 |
57 | }
58 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
59 |
60 |
61 | export function loadLatestUsers() {
62 | console.log( 'REDUX - ACTION - fn: loadLatestUsers' );
63 | return axios.get( '/api/users' )
64 |
65 | .then( result => {
66 | console.log( 'REDUX - ACTION - fn: loadLatestUsers - data', result.data );
67 | return {
68 | type: 'LOAD_LATEST_USERS',
69 | users: result.data.users
70 | };
71 | } )
72 |
73 | .catch( err => console.log( err ) );
74 | }
75 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
76 |
77 | export function loadSearchedUsers( search ) {
78 | console.log( 'REDUX - ACTION - fn: loadSearchedUsers', search );
79 | // TODO: switch to socketio at some point
80 |
81 | return axios.post( '/api/users/search', { search } )
82 | .then( results => {
83 | console.log( 'REDUX - ACTION - fn: loadSearchedUsers - data', results.data.users );
84 | return {
85 | type: 'LOAD_SEARCHED_USERS',
86 | users: results.data.users
87 | };
88 | } )
89 |
90 | .catch( err => console.log( err ) );
91 | }
92 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
93 |
94 |
95 |
96 |
97 | export function clearSearchedUsers() {
98 | console.log( 'REDUX - ACTION - fn: clearSearchedUsers' );
99 | return { type: 'CLEAR_SEARCHED_USERS' };
100 | }
101 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
102 |
103 |
104 |
105 |
106 | export function loadFriends() {
107 | console.log( 'REDUX - ACTION - fn: loadFriends' );
108 |
109 | return axios.get( '/api/friends' )
110 |
111 | .then( result => {
112 | console.log( 'REDUX - ACTION - fn: loadFriends - data', result.data.friends );
113 | return {
114 | type: 'LOAD_FRIENDS',
115 | friends: result.data.friends
116 | };
117 | } )
118 |
119 | .catch( err => {
120 | console.log( err );
121 | return { type: 'ERROR' };
122 | } );
123 | }
124 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
125 |
126 |
127 |
128 | export function requestFriendship( fromUserId, toUserId, status ) {
129 |
130 | console.log( `REDUX - ACTION - fn: requestFriendship
131 | fromUserId: ${fromUserId},
132 | toUserId: ${toUserId},
133 | status: ${status}` );
134 |
135 | return axios.post( `/api/friends/${fromUserId}/${toUserId}/new`, { status: status } )
136 |
137 | .then( result => {
138 | console.log( 'REDUX - ACTION - fn: requestFriendship - data', result.data );
139 | return {
140 | type: 'REQUEST_FRIENDSHIP',
141 | newFriendshipStatus: result.data.newFriendshipStatus
142 | };
143 | } )
144 |
145 | .catch( err => {
146 | console.log( err );
147 | return { type: 'ERROR' };
148 | } );
149 | }
150 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
151 |
152 |
153 |
154 |
155 | export function updateFriendship( fromUserId, toUserId, status ) {
156 |
157 | console.log( `REDUX - ACTION - fn: updateFriendship
158 | fromUserId: ${fromUserId},
159 | toUserId: ${toUserId},
160 | status: ${status}` );
161 |
162 | return axios.put( `/api/friends/${fromUserId}/${toUserId}`, { status: status } )
163 |
164 | .then( result => {
165 | console.log( 'REDUX - ACTION - fn: updateFriendship - data', result.data );
166 | return {
167 | type: 'UPDATE_FRIENDSHIP',
168 | newFriendshipStatus: result.data.newFriendshipStatus
169 | };
170 | } )
171 |
172 | .catch( err => {
173 | console.log( err );
174 | return { type: 'ERROR' };
175 | } );
176 | }
177 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
178 |
179 |
180 |
181 |
182 | export function connectUser( socketId ) {
183 | console.log( 'REDUX - ACTION - fn: connectUser' );
184 | return axios.post( `/ws/connected/${socketId}` )
185 |
186 | .then( () => { return { type: 'CONNECT_USER' }; } )
187 |
188 | .catch( err => console.log( err ) );
189 | }
190 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
191 |
192 |
193 |
194 |
195 | export function sendPeerIdToServer( peerId ) {
196 | console.log( 'REDUX - ACTION - fn: sendPeerIdToServer' );
197 | return axios.post( `/ws/storeIdToServer/${peerId}` )
198 | .then( () => {
199 | return { type: 'ADD_PEERID_TO_USER_DATA', peerId };
200 | } )
201 | .catch( err => console.log( err ) );
202 | }
203 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
204 |
205 |
206 |
207 |
208 | export function createOnlineUsers( onlineUsers ) {
209 | console.log( 'REDUX - ACTION - fn: createOnlineUsers' );
210 | return {
211 | type: 'CREATE_ONLINE_USERS',
212 | onlineUsers
213 | };
214 | }
215 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
216 |
217 |
218 |
219 |
220 | export function createOnlinePeers( onlinePeers ) {
221 | console.log( 'REDUX - ACTION - fn: createOnlinePeers' );
222 | return {
223 | type: 'CREATE_ONLINE_PEERS',
224 | onlinePeers
225 | };
226 | }
227 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
228 |
229 |
230 |
231 |
232 | export function addOnlineUser( userJoined ) {
233 | console.log( 'REDUX - ACTION - fn: addOnlineUser' );
234 | return {
235 | type: 'ADD_ONLINE_USER',
236 | userJoined
237 | };
238 | }
239 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
240 |
241 |
242 |
243 |
244 | export function addOnlinePeer( peerJoined ) {
245 | console.log( 'REDUX - ACTION - fn: addOnlinePeer' );
246 | return {
247 | type: 'ADD_ONLINE_PEER',
248 | peerJoined
249 | };
250 | }
251 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
252 |
253 |
254 |
255 |
256 | export function removeOnlineUser( uid ) {
257 | console.log( 'REDUX - ACTION - fn: removeOnlineUser - uid:', uid );
258 | return {
259 | type: 'REMOVE_ONLINE_USER',
260 | uid
261 | };
262 | }
263 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
264 |
265 |
266 |
267 |
268 | export function createPublicMessageList( publicMessageList ) {
269 | console.log( 'REDUX - ACTION - fn: createPublicMessageList' );
270 | return {
271 | type: 'CREATE_PUBLIC_MESSAGE_LIST',
272 | publicMessageList
273 | };
274 | }
275 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
276 |
277 |
278 |
279 |
280 | export function createPrivateMessageList( privateMessageList ) {
281 | console.log( 'REDUX - ACTION - fn: createPrivateMessageList' );
282 | return {
283 | type: 'CREATE_PRIVATE_MESSAGE_LIST',
284 | privateMessageList
285 | };
286 | }
287 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
288 |
289 |
290 |
291 |
292 | export function addNewPublicMessage( newPublicMessage ) {
293 | console.log( 'REDUX - ACTION - fn: addNewPublicMessage' );
294 | return {
295 | type: 'ADD_NEW_PUBLIC_MESSAGE',
296 | newPublicMessage
297 | };
298 | }
299 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
300 |
301 |
302 |
303 |
304 | export function addNewPrivateMessage( newPrivateMessage ) {
305 | console.log( 'REDUX - ACTION - fn: addNewPrivateMessage', newPrivateMessage );
306 | return {
307 | type: 'ADD_NEW_PRIVATE_MESSAGE',
308 | newPrivateMessage
309 | };
310 | }
311 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
312 |
313 |
314 |
315 |
316 | export function persistOtherUid( otherUid ) {
317 | console.log( 'REDUX - ACTION - fn: persistOtherUid' );
318 | return {
319 | type: 'PERSIST_OTHER_UID',
320 | otherUid
321 | };
322 | }
323 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
324 |
325 |
326 |
327 |
328 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
329 |
--------------------------------------------------------------------------------
/src/components/chat-private.js:
--------------------------------------------------------------------------------
1 | // REACT
2 | import React, { Component } from 'react';
3 | // UTILS
4 | import getSocket from '../utils/socketIo';
5 |
6 | // MATERIAL UI
7 | import { Card, CardHeader, CardTitle, CardText } from 'material-ui/Card';
8 | import Avatar from 'material-ui/Avatar';
9 | import { Toolbar } from 'material-ui/Toolbar';
10 | import TextField from 'material-ui/TextField';
11 |
12 |
13 | export default class ChatPrivate extends Component {
14 | constructor( props ) {
15 | super( props );
16 | }
17 |
18 | handleSubmit( e ) {
19 | if ( e.keyCode === 13 ) {
20 | e.preventDefault();
21 | console.log( e.target.value );
22 | getSocket().emit( 'chatMessagePrivate', {
23 | toUserId: this.props.otherUser.uid,
24 | messageBody: e.target.value
25 | } );
26 | e.target.value = '';
27 | }
28 | }
29 |
30 | componentDidUpdate() {
31 | this.messageArea.scrollTop = this.messageArea.scrollHeight;
32 | }
33 |
34 | render() {
35 | console.log( 'ChatPrivate - RENDER - this.props: ', this.props );
36 | const { currentUser, otherUser, messages } = this.props;
37 |
38 | const chatMessages = messages && messages.map( message => {
39 | const { mid, sender, msg, timestamp } = message;
40 |
41 |
42 |
43 | if ( sender ) {
44 | // MESSAGE FROM THE USER
45 | const avatar =
46 | return (
47 |
55 |
60 | {msg}
61 |
62 | );
63 | } else {
64 | // MESSAGE FORM THE OTHE USER
65 | const avatar =
66 | return (
67 |
75 |
79 | {msg}
80 |
81 | );
82 | }
83 |
84 | } );
85 |
86 | return (
87 |
88 |
this.messageArea = elem}
90 | style={{
91 | overflow: 'scroll',
92 | height: 400,
93 | display: 'flex',
94 | flexDirection: 'column',
95 | justifyContent: 'space-evenly',
96 | alignItems: 'center'
97 | }}>
98 |
99 | {chatMessages}
100 |
101 |
102 |
103 | this.newMessage = newMessage}
109 | onKeyDown={ e => this.handleSubmit(e) }
110 | />
111 |
112 |
113 | );
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/src/components/chat-public.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import getSocket from '../utils/socketIo';
3 | import ProfilePicOther from './profilePicOther';
4 |
5 | // MATERIAL UI
6 | import { Card, CardHeader, CardTitle, CardText } from 'material-ui/Card';
7 | import Avatar from 'material-ui/Avatar';
8 | import { Toolbar } from 'material-ui/Toolbar';
9 | import TextField from 'material-ui/TextField';
10 |
11 |
12 |
13 | export default class ChatPublic extends Component {
14 | constructor( props ) {
15 | super( props );
16 | }
17 |
18 | handleSubmit( e ) {
19 | if ( e.keyCode === 13 ) {
20 | e.preventDefault();
21 | console.log( e.target.value );
22 | getSocket().emit( 'chatMessage', e.target.value );
23 | e.target.value = '';
24 | }
25 | }
26 |
27 | componentDidUpdate() {
28 | this.messageArea.scrollTop = this.messageArea.scrollHeight;
29 | }
30 |
31 | render() {
32 | console.log( 'ChatPublic - RENDER - this.props: ', this.props );
33 | const { currentUser } = this.props;
34 |
35 | const chatMessages = this.props.publicMessagesList && this.props.publicMessagesList.map( message => {
36 | const {
37 | mid,
38 | fromUserId,
39 | firstName,
40 | lastName,
41 | profilePic,
42 | messageBody,
43 | timestamp
44 | } = message;
45 |
46 | const sender = ( currentUser.uid == message.uid ) ? true : false
47 |
48 | const avatar =
49 |
50 | if ( sender ) {
51 | return (
52 |
53 |
61 |
66 | {messageBody}
67 |
68 | );
69 | } else {
70 | return (
71 |
79 |
83 | {messageBody}
84 |
85 | );
86 | }
87 |
88 |
89 | } );
90 |
91 | return (
92 |
93 |
this.messageArea = elem}
95 | style={{
96 | overflow: 'scroll',
97 | height: 400,
98 | display: 'flex',
99 | flexDirection: 'column',
100 | justifyContent: 'space-evenly',
101 | alignItems: 'center'
102 | }}>
103 |
104 | {chatMessages}
105 |
106 |
107 |
108 | this.newMessage = newMessage}
114 | onKeyDown={ e => this.handleSubmit(e) }
115 | />
116 |
117 |
118 | );
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/src/components/chat-secure.js:
--------------------------------------------------------------------------------
1 | // REACT
2 | import React, { Component } from 'react';
3 | // UTILS
4 | import getSocket from '../utils/socketIo';
5 | import getPeers from '../utils/peer';
6 |
7 | // MATERIAL UI
8 | import { Card, CardHeader, CardTitle, CardText } from 'material-ui/Card';
9 | import Avatar from 'material-ui/Avatar';
10 | import { Toolbar } from 'material-ui/Toolbar';
11 | import TextField from 'material-ui/TextField';
12 |
13 | let conn;
14 |
15 | export default class ChatSecure extends Component {
16 | constructor( props ) {
17 | super( props );
18 | }
19 |
20 | componentDidMount() {
21 | conn = getPeers().connect( this.props.otherUser.peerId );
22 | console.log( 'ChatSecure - componentDidMount - conn: ', conn );
23 | }
24 |
25 | handleSubmit( e ) {
26 | if ( e.keyCode === 13 ) {
27 | e.preventDefault();
28 | console.log( e.target.value );
29 | conn.send( {
30 | fromUserId: this.props.currentUser.uid,
31 | toUserId: this.props.otherUser.uid,
32 | messageBody: e.target.value
33 | } );
34 | // getSocket().emit( 'chatMessagePrivate', {
35 | // toUserId: this.props.otherUser.uid,
36 | // messageBody: e.target.value
37 | // } );
38 | e.target.value = '';
39 | }
40 | }
41 |
42 | componentDidUpdate() {
43 | this.messageArea.scrollTop = this.messageArea.scrollHeight;
44 | }
45 |
46 | render() {
47 | console.log( 'ChatSecure - RENDER - this.props: ', this.props );
48 | const { currentUser, otherUser, messages } = this.props;
49 |
50 | const chatMessages = messages && messages.map( message => {
51 | const { mid, sender, msg, timestamp } = message;
52 |
53 | let uid,
54 | firstName,
55 | lastName,
56 | profilePic;
57 |
58 |
59 | if ( sender ) {
60 | uid = otherUser.uid;
61 | firstName = otherUser.firstName;
62 | lastName = otherUser.lastName;
63 | profilePic = otherUser.profilePic;
64 | } else {
65 | uid = currentUser.uid;
66 | firstName = currentUser.firstName;
67 | lastName = currentUser.lastName;
68 | profilePic = currentUser.profilePic;
69 | }
70 |
71 | const avatar =
72 |
73 |
74 | if ( sender ) {
75 |
76 |
81 |
82 | {msg}
83 |
84 | }
85 |
86 | return (
87 |
88 |
89 |
93 | {msg}
94 |
95 |
96 | );
97 | } );
98 |
99 | return (
100 |
101 |
this.messageArea = elem}
103 | style={{
104 | overflow: 'scroll',
105 | height: 400
106 | }}>
107 | messages go here
108 |
109 | {chatMessages}
110 |
111 |
112 |
113 | this.newMessage = newMessage}
119 | onKeyDown={ e => this.handleSubmit(e) }
120 | />
121 |
122 |
123 | );
124 | }
125 | }
126 |
--------------------------------------------------------------------------------
/src/components/friendshipButton.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const FriendshipButton = (props) => {
4 | return(
5 |
6 | );
7 | };
8 |
9 | export default FriendshipButton;
10 |
--------------------------------------------------------------------------------
/src/components/friendships-blocked.js:
--------------------------------------------------------------------------------
1 | // REACT
2 | import React from 'react';
3 |
4 | // MATERIAL-UI
5 | import Avatar from 'material-ui/Avatar';
6 | import { List, ListItem } from 'material-ui/List';
7 | import Subheader from 'material-ui/Subheader';
8 | import Divider from 'material-ui/Divider';
9 | import { grey400 } from 'material-ui/styles/colors';
10 | import IconButton from 'material-ui/IconButton';
11 | import IconMenu from 'material-ui/IconMenu';
12 | import MenuItem from 'material-ui/MenuItem';
13 |
14 | // ICONS
15 | import SocialPersonAdd from 'material-ui/svg-icons/social/person-add';
16 | import MoreVertIcon from 'material-ui/svg-icons/navigation/more-vert';
17 |
18 | const iconButtonElement = (
19 |
24 |
25 |
26 | );
27 |
28 | const BlockedFriendships = ( props ) => {
29 | console.log( 'BlockedFriendships - RENDER - this.props: ', props );
30 | const { handleFriendshipChange } = props;
31 |
32 | const listBlockedFriendships = props.blockedFriendships.map( ( blockedFriend ) => {
33 | const { uid, firstName, lastName, profilePic } = blockedFriend;
34 |
35 | const rightIconMenu = (
36 | handleFriendshipChange( uid, 'PENDING' ) }>
39 | }/>
43 |
44 | );
45 |
46 | return (
47 |
48 |
}
51 | rightIconButton={rightIconMenu}
52 | />
53 |
54 |
55 | );
56 | } );
57 |
58 | return (
59 |
60 | These people have been blocked
61 | {listBlockedFriendships}
62 |
63 | );
64 |
65 | };
66 |
67 | export default BlockedFriendships;
68 |
--------------------------------------------------------------------------------
/src/components/friendships-current.js:
--------------------------------------------------------------------------------
1 | // REACT
2 | import React from 'react';
3 | import ProfilePicOther from './profilePicOther';
4 | import { browserHistory } from 'react-router';
5 |
6 | // MATERIAL-UI
7 | import Avatar from 'material-ui/Avatar';
8 | import { List, ListItem } from 'material-ui/List';
9 | import Subheader from 'material-ui/Subheader';
10 | import Divider from 'material-ui/Divider';
11 | import { grey400 } from 'material-ui/styles/colors';
12 | import IconButton from 'material-ui/IconButton';
13 | import IconMenu from 'material-ui/IconMenu';
14 | import MenuItem from 'material-ui/MenuItem';
15 | import MoreVertIcon from 'material-ui/svg-icons/navigation/more-vert';
16 | // ICONS
17 | import ContentRemoveCircle from 'material-ui/svg-icons/content/remove-circle';
18 |
19 |
20 |
21 | const handleTouchCurrentFriend = ( uid ) => {
22 | console.log( 'App - fn: handleTouchCurrentFriend' );
23 | browserHistory.push( `/chat/private/${uid}` );
24 | };
25 |
26 |
27 | const iconButtonElement = (
28 |
33 |
34 |
35 | );
36 |
37 |
38 | const CurrentFriendship = ( props ) => {
39 | console.log( 'CurrentFriendship - RENDER - this.props: ', props );
40 | const { handleFriendshipChange, uidSelf } = props;
41 |
42 |
43 | const listCurrentFriendships = props.currentFriendships.map( ( currentFriend ) => {
44 | const { uid, firstName, lastName, profilePic } = currentFriend;
45 | const rightIconMenu = (
46 | handleFriendshipChange( uid, 'TERMINATED' ) }>
49 | }/>
53 |
54 | );
55 |
56 | return (
57 |
58 |
}
61 | rightIconButton={rightIconMenu}
62 | onClick={ () => handleTouchCurrentFriend( uid ) }
63 | />
64 |
65 |
66 |
67 | );
68 | } );
69 |
70 | return (
71 |
72 | These people are currently your friend
73 | {listCurrentFriendships}
74 |
75 | );
76 |
77 | };
78 |
79 | export default CurrentFriendship;
80 |
--------------------------------------------------------------------------------
/src/components/friendships-pending.js:
--------------------------------------------------------------------------------
1 | // REACT
2 | import React from 'react';
3 |
4 | // MATERIAL-UI
5 | import Avatar from 'material-ui/Avatar';
6 | import { List, ListItem } from 'material-ui/List';
7 | import Subheader from 'material-ui/Subheader';
8 | import Divider from 'material-ui/Divider';
9 | import { grey400 } from 'material-ui/styles/colors';
10 | import IconButton from 'material-ui/IconButton';
11 | import IconMenu from 'material-ui/IconMenu';
12 | import MenuItem from 'material-ui/MenuItem';
13 | import MoreVertIcon from 'material-ui/svg-icons/navigation/more-vert';
14 | // ICONS
15 | import SocialPersonAdd from 'material-ui/svg-icons/social/person-add';
16 |
17 |
18 | const iconButtonElement = (
19 |
24 |
25 |
26 | );
27 |
28 | const PendingFriendships = props => {
29 | console.log( 'PendingFriendships - RENDER - this.props: ', props );
30 | const { handleFriendshipChange } = props;
31 |
32 | const listPendingFriendships = props.pendingFriendships.map( ( pendingFriend ) => {
33 | const { uid, firstName, lastName, profilePic } = pendingFriend;
34 | const rightIconMenu = (
35 | handleFriendshipChange( uid, 'ACCEPTED' ) }>
38 | }/>
42 |
43 | );
44 | return (
45 |
46 |
}
49 | rightIconButton={rightIconMenu}
50 | />
51 |
52 |
53 | );
54 | } );
55 |
56 |
57 | return (
58 |
59 | These people want to be your friend
60 | {listPendingFriendships}
61 |
62 | );
63 | };
64 |
65 |
66 | export default PendingFriendships;
67 |
--------------------------------------------------------------------------------
/src/components/login.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import { Link } from 'react-router';
4 | import FormWrapper from '../utils/formWrapper';
5 | // MATERIAL-UI
6 | import TextField from 'material-ui/TextField';
7 | import RaisedButton from 'material-ui/RaisedButton';
8 |
9 | const style = { margin: 12 };
10 |
11 | const LoginForm = ( { handleInput, handleSubmit, error } ) => {
12 |
13 | console.log( 'LoginForm - RENDER');
14 |
15 | return (
16 |
17 |
Log in
18 | { error &&
Something went wrong. Please try again!
}
19 |
51 | {/*
Not a member? Register
*/}
52 |
53 | );
54 | };
55 |
56 |
57 | LoginForm.propTypes = {
58 | handleInput : PropTypes.func,
59 | handleSubmit: PropTypes.func,
60 | error: PropTypes.bool,
61 | // success: PropTypes.bool
62 | };
63 |
64 | export default FormWrapper(LoginForm, '/api/login');
65 |
--------------------------------------------------------------------------------
/src/components/logo.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const Logo = () => {
4 | return (
5 | Logo
6 | );
7 | };
8 |
9 | export default Logo;
10 |
--------------------------------------------------------------------------------
/src/components/online-friends.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Link, browserHistory } from 'react-router';
3 |
4 |
5 | // MATERIAL-UI
6 | import Avatar from 'material-ui/Avatar';
7 | import { List, ListItem } from 'material-ui/List';
8 | import Subheader from 'material-ui/Subheader';
9 | import Divider from 'material-ui/Divider';
10 |
11 | const handleTouchOnlineFriend = ( uid ) => {
12 | console.log( 'OnlineFriend - fn: handleTouchOnlineFriend' );
13 | browserHistory.push( `/chat/secure/${uid}` );
14 | };
15 |
16 | const OnlineFriend = props => {
17 | console.log( 'OnlineFriend - RENDER - this.props: ', props );
18 |
19 | const listOnlineFriends = props.onlineFriends.map( onlineFriend => {
20 | const { uid, firstName, lastName, profilePic } = onlineFriend;
21 |
22 | const avatar = (
23 |
24 |
25 |
26 | );
27 |
28 | return (
29 |
30 |
handleTouchOnlineFriend( uid ) }
34 | />
35 |
36 |
37 | );
38 | } );
39 |
40 | return (
41 |
42 | Online Friends you can secure chat with:
43 | {listOnlineFriends}
44 |
45 | );
46 | };
47 |
48 | export default OnlineFriend;
49 |
--------------------------------------------------------------------------------
/src/components/profile-pic-upload.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export default class ProfilePicUpload extends React.Component {
4 |
5 | constructor( props ) {
6 | super( props );
7 | this.state = {
8 | hover: false
9 | };
10 | this.toggleHover = this.toggleHover.bind( this );
11 | }
12 |
13 |
14 | toggleHover() {
15 | this.setState( { hover: !this.state.hover } );
16 | }
17 |
18 | render() {
19 | console.log( 'ProfilePicUpload - RENDER - this.prop: ', this.prop );
20 |
21 | const {
22 | hideProfilePicUpload,
23 | uploadProfilePic
24 | } = this.props;
25 |
26 | const modalStyle = {
27 | position: 'absolute',
28 | top: '50%',
29 | left: '50%',
30 | transform: 'translate(-50%, -50%)',
31 | zIndex: '9999',
32 | background: '#fff'
33 | };
34 |
35 | const backdropStyle = {
36 | position: 'absolute',
37 | width: '100%',
38 | height: '100%',
39 | top: '0px',
40 | left: '0px',
41 | zIndex: '9998',
42 | background: 'rgba(0, 0, 0, 0.3)'
43 | };
44 |
45 | const inputStyle = {
46 | width: '0.1px',
47 | height: '0.1px',
48 | opacity: '0',
49 | overflow: 'hidden',
50 | position: 'absolute',
51 | zIndex: '-1'
52 | };
53 |
54 | let labelStyle;
55 |
56 | if ( this.state.hover ) {
57 | labelStyle = {
58 | fontSize: '1.25em',
59 | fontWeight: '700',
60 | color: 'white',
61 | backgroundColor: 'red',
62 | display: 'inline-block',
63 | cursor: 'pointer'
64 | };
65 | } else {
66 | labelStyle = {
67 | fontSize: '1.25em',
68 | fontWeight: '700',
69 | color: 'white',
70 | backgroundColor: 'black',
71 | display: 'inline-block',
72 | cursor: 'pointer'
73 | };
74 | }
75 |
76 | return (
77 |
78 |
79 |
80 |
81 |
82 |
83 |
ProfilePicUploader
84 |
85 |
89 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 | );
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/src/components/profile-pic.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 |
4 | const ProfilePic = ( props ) => {
5 | // console.log( 'ProfilePic - RENDER - this.props: ', props );
6 | const { showProfilePicUpload, src, alt } = props;
7 | return (
8 |
11 |
12 |

16 |
17 |
18 | );
19 | };
20 |
21 |
22 | ProfilePic.propTypes = {
23 | showProfilePicUpload: PropTypes.func,
24 | src: PropTypes.string,
25 | alt: PropTypes.string
26 | };
27 |
28 | export default ProfilePic;
29 |
--------------------------------------------------------------------------------
/src/components/profile-self-bio.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import axios from '../utils/axios';
4 |
5 | export default class ProfileSelfBio extends React.Component {
6 |
7 | constructor( props ) {
8 | super( props );
9 | this.state = {
10 | bio: '',
11 | editBioIsVisible: false
12 |
13 | };
14 | }
15 |
16 | componentDidMount() {
17 | if ( this.props.bio ) {
18 | this.setState( { bio: this.props.bio } );
19 | }
20 | }
21 |
22 |
23 |
24 | handleInput( e ) {
25 | this.setState( {
26 | [ e.target.name ]: e.target.value
27 | } );
28 | }
29 |
30 | handleSubmit( e ) {
31 | e.preventDefault();
32 | // make POST request to this.url and handle response
33 | console.log( 'ProfileSelfBio - fn: Axios.put - data: ', this.state );
34 |
35 | axios.put( `/api/user/${this.props.uid}/bio`, this.state )
36 |
37 | .then( resp => {
38 | const data = resp.data;
39 | !data.success ?
40 | this.setState( { error: true } ) :
41 | this.setState( { data, editBioIsVisible: false } );
42 | } )
43 |
44 | .catch( err => console.error( err.stack ) );
45 | }
46 |
47 | setBioVisibility( boolean ) {
48 | console.log( 'fn: editBioIsVisible ' );
49 | this.setState( { editBioIsVisible: boolean } );
50 | }
51 |
52 |
53 | render() {
54 | console.log( 'React Component: ProfileSelfBio - RENDER - this.props: ', JSON.stringify( this.props ) );
55 |
56 | const { bio, editBioIsVisible, error } = this.state;
57 | // const { bio } = this.props;
58 |
59 | // if no bio found..
60 | const noBioData = (
61 | this.setBioVisibility(true)}>Add your bio now
62 | );
63 |
64 | // if bio is found..
65 | const bioData = (
66 |
67 |
68 |
71 | this.setBioVisibility(true)}>Edit
72 |
73 | );
74 |
75 | // to edit the bio..
76 | const editBio = (
77 |
90 | );
91 |
92 | // what to var to render..
93 | const bioRender = () => {
94 | if ( editBioIsVisible ) {
95 | return editBio;
96 | } else if ( bio ) {
97 | return bioData;
98 | } else {
99 | return noBioData;
100 | }
101 | };
102 |
103 | return (
104 | {bioRender()}
105 | );
106 | }
107 |
108 | }
109 |
110 |
111 | ProfileSelfBio.propTypes = {
112 | bio: PropTypes.string,
113 | uid: PropTypes.number
114 | };
115 |
--------------------------------------------------------------------------------
/src/components/profile-self.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | // REDUX
3 | import { connect } from 'react-redux';
4 | // MY COMPONENTS
5 | import ProfilePic from './profile-pic';
6 | import PropTypes from 'prop-types';
7 | import ProfileSelfBio from './profile-self-bio';
8 |
9 | import Avatar from 'material-ui/Avatar';
10 |
11 |
12 | const ProfileSelf = ( props ) => {
13 | console.log( 'ProfileSelf - RENDER - this.props: ', props );
14 |
15 | if (!props.user) {
16 | return null;
17 | }
18 | const {
19 | uid,
20 | firstName,
21 | lastName,
22 | email,
23 | bio,
24 | profilePic
25 | } = props.user;
26 |
27 | return (
28 |
29 |
30 |
ProfileSelf
31 |
32 |
33 |
34 |
35 |
39 |
40 |
41 |
45 |
46 |
47 |
51 |
52 |
53 |
54 |
55 |
56 | );
57 | };
58 |
59 | ProfileSelf.propTypes = {
60 | uid: PropTypes.number,
61 | firstName: PropTypes.string,
62 | lastName: PropTypes.string,
63 | email: PropTypes.string,
64 | bio: PropTypes.string,
65 | profilePic: PropTypes.string
66 | };
67 |
68 | // REDUX - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
69 | const mapStateToProps = ( state ) => {
70 | console.log( 'App - fn: ProfileSelf' );
71 | return { user: state.user };
72 | };
73 |
74 |
75 | export default connect( mapStateToProps )( ProfileSelf );
76 |
--------------------------------------------------------------------------------
/src/components/profilePicOther.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Link } from 'react-router';
3 |
4 | const ProfilePicOther = ( props ) => {
5 | const { src, alt, uid, chat } = props;
6 | const url = chat == true ? `/chat/private/${uid}` : `/user/${uid}`
7 | return (
8 |
9 |
10 |

12 |
13 |
14 | );
15 | };
16 |
17 | export default ProfilePicOther;
18 |
--------------------------------------------------------------------------------
/src/components/registration.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import { Link } from 'react-router';
4 | import FormWrapper from '../utils/formWrapper';
5 | // MATERIAL-UI
6 | import TextField from 'material-ui/TextField';
7 | import RaisedButton from 'material-ui/RaisedButton';
8 |
9 | const style = { margin: 12 };
10 |
11 |
12 | const RegistrationForm = ( { handleInput, handleSubmit, error } ) => {
13 | console.log( 'RegistrationForm - RENDER ' );
14 |
15 | return (
16 |
17 |
18 |
Register
19 | {error &&
Something went wrong. Please try again!
}
20 |
74 |
75 | {/*
Already a member? Log In
*/}
76 |
77 | );
78 | };
79 |
80 |
81 | RegistrationForm.propTypes = {
82 | handleInput: PropTypes.func,
83 | handleSubmit: PropTypes.func,
84 | error: PropTypes.string
85 | };
86 |
87 | export default FormWrapper( RegistrationForm, '/api/register' );
88 |
--------------------------------------------------------------------------------
/src/components/users.js:
--------------------------------------------------------------------------------
1 | // REACT
2 | import React from 'react';
3 |
4 | // MATERIAL-UI
5 | import Avatar from 'material-ui/Avatar';
6 | import { List, ListItem } from 'material-ui/List';
7 | import Subheader from 'material-ui/Subheader';
8 | import Divider from 'material-ui/Divider';
9 | import { grey400, pinkA200 } from 'material-ui/styles/colors';
10 | import IconButton from 'material-ui/IconButton';
11 | import IconMenu from 'material-ui/IconMenu';
12 | import MenuItem from 'material-ui/MenuItem';
13 | import MoreVertIcon from 'material-ui/svg-icons/navigation/more-vert';
14 |
15 |
16 | // ICONS
17 | import SocialPersonAdd from 'material-ui/svg-icons/social/person-add';
18 | import ComunicationChat from 'material-ui/svg-icons/communication/chat';
19 |
20 |
21 | const iconButtonElement = (
22 |
27 |
28 |
29 | );
30 |
31 | const Users = props => {
32 | console.log( 'Users - RENDER - this.props: ', props );
33 | const { handleFriendshipChange } = props;
34 |
35 | const listUsers = props.users.map( user => {
36 | const { uid, firstName, lastName, profilePic, online, status } = user;
37 |
38 | const rightIconMenu = (
39 | handleFriendshipChange( uid, 'PENDING' ) }>
42 | {
43 | status == 'ACCEPTED' &&
44 | }/>
48 | }
49 | {
50 | status != 'ACCEPTED' &&
51 | }/>
55 | }
56 |
57 | );
58 |
59 |
60 | return (
61 |
62 |
}
64 | primaryText={`${firstName} ${lastName}`}
65 | rightIconButton={rightIconMenu}
66 | />
67 |
68 |
69 | );
70 | } );
71 |
72 |
73 | return (
74 |
75 | People that you might know
76 | {listUsers}
77 |
78 | );
79 | };
80 |
81 |
82 | export default Users;
83 |
--------------------------------------------------------------------------------
/src/components/welcome.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import { Link } from 'react-router';
4 |
5 | // MATERIAL-UI:
6 | import AppBar from 'material-ui/AppBar';
7 | import FlatButton from 'material-ui/FlatButton';
8 |
9 | const style = {
10 | flex: '0 0 auto',
11 | color: 'rgba(255, 255, 255, 1)',
12 | display: 'flex',
13 | minHeight: '100vh',
14 | alignItems: 'center',
15 | justifyContent: 'center',
16 | backgroundColor: '#2196f3'
17 | };
18 |
19 | const Welcome = ( props ) => {
20 | console.log( 'Welcome - RENDER - this.props: ', props );
21 | let action;
22 | let url;
23 | props.location.pathname === '/' ?
24 | ( action = 'login' ) && ( url = 'login' ) :
25 | ( action = 'register' ) && ( url = '' );
26 | return (
27 |
28 |
33 |
34 | }
35 | />
36 | {props.children}
37 |
38 | );
39 | };
40 |
41 | Welcome.propTypes = { children: PropTypes.element };
42 |
43 | export default Welcome;
44 |
--------------------------------------------------------------------------------
/src/containers/app.js:
--------------------------------------------------------------------------------
1 | // REACT
2 | import React, { Component } from 'react';
3 | import { Link, browserHistory } from 'react-router';
4 |
5 | // REDUX
6 | import { connect } from 'react-redux';
7 | import { logOutUser, updateProfilePic, loadUserData, loadFriends } from '../actions/actions';
8 |
9 | // SOCKETIO
10 | import getSocket from '../utils/socketIo';
11 | import getPeer from '../utils/peer';
12 |
13 | // MATERIAL-UI:
14 | import AppBar from 'material-ui/AppBar';
15 | import FlatButton from 'material-ui/FlatButton';
16 | import Drawer from 'material-ui/Drawer';
17 | import Avatar from 'material-ui/Avatar';
18 | import { List, ListItem } from 'material-ui/List';
19 | import Subheader from 'material-ui/Subheader';
20 | import Divider from 'material-ui/Divider';
21 | // import FontIcon from 'material-ui/FontIcon';
22 | import { BottomNavigation, BottomNavigationItem } from 'material-ui/BottomNavigation';
23 |
24 | // ICONS
25 | // import CommunicationChatBubble from 'material-ui/svg-icons/communication/chat-bubble';
26 | // import ContentSend from 'material-ui/svg-icons/content/send';
27 | import ActionInfo from 'material-ui/svg-icons/action/info';
28 | import ComunicationChat from 'material-ui/svg-icons/communication/chat';
29 | import ActionHome from 'material-ui/svg-icons/action/home';
30 | import SocialGroup from 'material-ui/svg-icons/social/group';
31 | import SocialGroupAdd from 'material-ui/svg-icons/social/group-add';
32 |
33 |
34 |
35 |
36 | // MY COMPONENTS
37 | import ProfilePicUpload from '../components/profile-pic-upload';
38 |
39 | class App extends Component {
40 |
41 | constructor( props ) {
42 | super( props );
43 | getSocket();
44 | getPeer();
45 |
46 | this.state = {
47 | uploaderIsVisible: false,
48 | open: false,
49 | selectedIndex: null
50 | };
51 | this.showProfilePicUpload = this.showProfilePicUpload.bind( this );
52 | }
53 |
54 | componentDidMount() {
55 | console.log( 'App - fn: componentDidMount - this.props: ', this.props );
56 | this.props.loadUserData();
57 | this.props.loadFriends();
58 | browserHistory.push( '/chat' );
59 | }
60 |
61 | componentWillReceiveProps( nextProps ) {
62 | const { pathname } = nextProps.location;
63 | switch ( pathname ) {
64 | case '/chat':
65 | this.setState( { selectedIndex: 0 } );
66 | break;
67 | case '/friends':
68 | this.setState( { selectedIndex: 1 } );
69 | break;
70 | case '/users':
71 | this.setState( { selectedIndex: 2 } );
72 | break;
73 | }
74 | }
75 |
76 | showProfilePicUpload( e ) {
77 | e.stopPropagation();
78 | console.log( 'App - fn: showProfilePicUpload' );
79 | this.setState( { uploaderIsVisible: true } );
80 | }
81 |
82 | hideProfilePicUpload( e ) {
83 | e.stopPropagation();
84 | console.log( 'App - fn: hideProfilePicUpload' );
85 | this.setState( { uploaderIsVisible: false } );
86 | }
87 |
88 | uploadProfilePic( e ) {
89 | console.log( 'App - fn: uploadProfilePic' );
90 | e.stopPropagation();
91 | const formData = new FormData;
92 | formData.append( 'file', e.target.files[ 0 ] );
93 | this.props.updateProfilePic( formData );
94 | }
95 |
96 | handleLogOut() {
97 | console.log( 'App - fn: handleLogOut' );
98 | this.props.logOutUser();
99 | }
100 |
101 | handleTouchTitle() {
102 | console.log( 'App - fn: handleTouchTitle' );
103 | browserHistory.push( '/chat' );
104 | }
105 |
106 |
107 | // DRAWER:
108 | handleToggle() {
109 | this.setState( { open: !this.state.open } );
110 | }
111 |
112 | handleClose() {
113 | this.setState( { open: false } );
114 | }
115 |
116 | // BOTTOM NAVIGATION
117 | handleNavigation( href ) {
118 | browserHistory.push( href );
119 | switch ( href ) {
120 | case '/chat':
121 | this.setState( { selectedIndex: 0 } );
122 | break;
123 | case '/friends':
124 | this.setState( { selectedIndex: 1 } );
125 | break;
126 | case '/users':
127 | this.setState( { selectedIndex: 2 } );
128 | break;
129 | }
130 | }
131 |
132 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
133 | render() {
134 | console.log( 'App - RENDER - this.props: ', this.props );
135 |
136 | const { error, uploaderIsVisible } = this.state;
137 |
138 |
139 | const titleStyle = { cursor: 'pointer' };
140 |
141 | if ( !this.props.user ) {
142 | return null;
143 | }
144 |
145 | const {
146 | uid,
147 | firstName,
148 | lastName,
149 | email,
150 | bio,
151 | profilePic
152 | } = this.props.user;
153 |
154 |
155 | return (
156 |
159 |
p2pChat}
161 | onTitleTouchTap={ () => this.handleTouchTitle() }
162 | iconClassNameRight='muidocs-icon-navigation-expand-more'
163 | iconElementRight={}
164 | onRightIconButtonTouchTap={ () => this.handleLogOut() }
165 | onLeftIconButtonTouchTap={ () => this.handleToggle() }>
166 |
167 |
168 |
169 |
170 |
171 | }
174 | onClick={ e => this.handleNavigation('/chat')}/>
175 | }
178 | onClick={ e => this.handleNavigation('/friends')}/>
179 | }
182 | onClick={ e => this.handleNavigation('/users')}/>
183 |
184 |
185 |
186 |
187 |
188 |
this.setState( { open } )}>
193 |
194 | User Data
195 | }
198 | rightIcon={}
199 | onClick={ (e) => this.showProfilePicUpload(e) }
200 | />
201 |
202 |
203 | Navigation
204 | }
207 | onClick={ e => this.handleNavigation('/chat')}/>
208 | }
211 | onClick={ e => this.handleNavigation('/friends')}/>
212 | }
215 | onClick={ e => this.handleNavigation('/users')}/>
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 | {
224 | uploaderIsVisible &&
225 |
this.uploadProfilePic(e) }
227 | hideProfilePicUpload={ (e) => this.hideProfilePicUpload(e) }/>
228 | }
229 |
230 |
231 | { error && { error }
}
232 |
233 |
234 | {this.props.children}
235 |
236 |
237 |
238 |
239 |
240 | );
241 | }
242 |
243 | }
244 | // REDUX - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
245 | const mapStateToProps = ( state ) => {
246 | console.log( 'App - fn: mapStateToProps' );
247 | return { user: state.user };
248 | };
249 |
250 | const mapDispatchToProps = ( dispatch ) => ( {
251 | loadUserData: () => dispatch( loadUserData() ),
252 | loadFriends: () => dispatch( loadFriends() ),
253 | logOutUser: () => dispatch( logOutUser() ),
254 | updateProfilePic: ( formData ) => dispatch( updateProfilePic( formData ) ),
255 | } );
256 |
257 | export default connect( mapStateToProps, mapDispatchToProps )( App );
258 |
--------------------------------------------------------------------------------
/src/containers/chat-children-container.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const ChatChildrenContainer = (props) => {
4 | return {props.children}
;
5 | };
6 |
7 | export default ChatChildrenContainer;
8 |
--------------------------------------------------------------------------------
/src/containers/chat-list-container.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { connect } from 'react-redux';
3 | import { Link, browserHistory } from 'react-router';
4 | import OnlineFriendsContainer from './online-friends-container';
5 |
6 | // MATERIAL-UI
7 | import Avatar from 'material-ui/Avatar';
8 | import { List, ListItem } from 'material-ui/List';
9 | import Subheader from 'material-ui/Subheader';
10 | import Divider from 'material-ui/Divider';
11 | import { darkBlack } from 'material-ui/styles/colors';
12 |
13 | // MATERIAL-UI
14 | import SocialPublic from 'material-ui/svg-icons/social/public';
15 |
16 | class ChatListContainer extends Component {
17 | constructor( props ) {
18 | super( props );
19 | }
20 |
21 | handleTouchConversation( uid ) {
22 | console.log( 'ChatListContainer - fn: handleTouchConversation' );
23 | browserHistory.push( `/chat/private/${uid}` );
24 | }
25 |
26 | handleTouchConversationGlobal() {
27 | console.log( 'ChatListContainer - fn: handleTouchConversationGlobal' );
28 | browserHistory.push( '/chat/public' );
29 | }
30 |
31 | render() {
32 | console.log( 'ChatListContainer - RENDER - this.props: ', this.props );
33 | const { privateConversation } = this.props;
34 |
35 | const privateConversations = privateConversation && privateConversation.map( conversation => {
36 | const { uid, firstName, lastName, profilePic } = conversation;
37 | const avatar = (
38 |
39 |
40 |
41 | );
42 | return (
43 |
44 |
this.handleTouchConversation(uid) }
48 | />
49 |
50 |
51 |
52 |
53 | );
54 | } );
55 |
56 |
57 |
58 | if (!this.props.user && !this.props.privateConversation && !this.props.privateConversation ) {
59 | return null;
60 | }
61 | return (
62 |
63 |
64 | ChatListContainer.js
65 |
66 | Global chat
67 | }
70 | onClick={ () => this.handleTouchConversationGlobal() }
71 | />
72 |
73 |
74 | Private
75 | {privateConversations}
76 |
77 |
78 | );
79 | }
80 | }
81 |
82 | const mapStateToProps = ( state ) => {
83 | console.log( 'ChatListContainer - fn: mapStateToProps' );
84 | return {
85 | user: state.user,
86 | privateConversation: state.users && state.users.filter( user => {
87 | return user.privateMessages;
88 | } ),
89 | latestPublicMessage: state.publicMessages && state.publicMessages[ state.publicMessages.length - 1 ]
90 |
91 | };
92 | };
93 |
94 | export default connect( mapStateToProps )( ChatListContainer );
95 |
--------------------------------------------------------------------------------
/src/containers/chat-private-container.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { connect } from 'react-redux';
3 | import ChatPrivate from '../components/chat-private';
4 | import { store } from '../shell';
5 | import { persistOtherUid } from '../actions/actions';
6 |
7 | class ChatPrivateContainer extends Component {
8 | constructor( props ) {
9 | super( props );
10 | }
11 |
12 | componentDidMount() {
13 | const otherUid = this.props.routeParams.otherUid;
14 | store.dispatch( persistOtherUid( otherUid ) );
15 | }
16 |
17 | render() {
18 | console.log( 'ChatPrivateContainer - RENDER - this.props: ', this.props );
19 | const { currentUser, otherUser, messages } = this.props;
20 | return (
21 |
22 | ChatPrivateContainer.js
23 |
24 |
29 |
30 |
31 | );
32 | }
33 | }
34 |
35 | const mapStateToProps = ( state ) => {
36 | console.log( 'ChatPrivateContainer - fn: mapStateToProps' );
37 | const otherUser = state.users && state.users.find( user => user.uid == state.otherUid );
38 | const messages = otherUser && otherUser.privateMessages;
39 | return {
40 | otherUid: state.otherUid,
41 | currentUser: state.user,
42 | otherUser: otherUser,
43 | messages: messages
44 | };
45 | };
46 |
47 | export default connect( mapStateToProps )( ChatPrivateContainer );
48 |
--------------------------------------------------------------------------------
/src/containers/chat-public-container.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { connect } from 'react-redux';
3 | import ChatPublic from '../components/chat-public';
4 |
5 | class ChatPublicContainer extends Component {
6 | constructor( props ) {
7 | super( props );
8 | }
9 |
10 | render() {
11 | console.log( 'ChatPublicContainer - RENDER - this.props: ', this.props );
12 | return (
13 |
14 | ChatPublicContainer.js
15 | {
16 | this.props.publicMessages &&
17 |
21 | }
22 |
23 | );
24 | }
25 | }
26 |
27 | const mapStateToProps = ( state ) => {
28 | console.log( 'ChatPublicContainer - fn: mapStateToProps' );
29 | return {
30 | publicMessages: state.publicMessages && state.publicMessages,
31 | currentUser: state.user
32 | };
33 | };
34 |
35 | export default connect( mapStateToProps )( ChatPublicContainer );
36 |
--------------------------------------------------------------------------------
/src/containers/chat-secure-container.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { connect } from 'react-redux';
3 | import ChatSecure from '../components/chat-secure';
4 | import { store } from '../shell';
5 | import { persistOtherUid } from '../actions/actions';
6 |
7 | class ChatSecureContainer extends Component {
8 | constructor( props ) {
9 | super( props );
10 | }
11 |
12 | componentDidMount() {
13 | const otherUid = this.props.routeParams.otherUid;
14 | persistOtherUid( otherUid );
15 | }
16 |
17 | render() {
18 | console.log( 'ChatSecureContainer - RENDER - this.props: ', this.props );
19 | const { currentUser, otherUser, messages } = this.props;
20 | return (
21 |
22 | ChatSecureContainer.js
23 | {
24 | messages &&
25 |
29 | }
30 |
31 | );
32 | }
33 | }
34 |
35 | const mapStateToProps = ( state ) => {
36 | console.log( 'ChatSecureContainer - fn: mapStateToProps' );
37 | const otherUser = state.users && state.users.find( user => user.uid == state.otherUid );
38 | const messages = otherUser && otherUser.privateMessages;
39 | return {
40 | otherUid: state.otherUid,
41 | currentUser: state.user,
42 | otherUser: otherUser,
43 | messages: messages
44 | };
45 | };
46 |
47 | const mapDispatchToProps = dispatch => ( {
48 | persistOtherUid: otherUid => dispatch( persistOtherUid( otherUid ) ),
49 | } );
50 |
51 | export default connect( mapStateToProps, mapDispatchToProps )( ChatSecureContainer );
52 |
--------------------------------------------------------------------------------
/src/containers/friends-container.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { connect } from 'react-redux';
3 | // import { bindActionCreators } from 'redux';
4 | import { loadFriends, updateFriendship } from '../actions/actions';
5 | import FriendshipsPending from '../components/friendships-pending';
6 | import FriendshipsCurrent from '../components/friendships-current';
7 | import FriendshipsBlocked from '../components/friendships-blocked';
8 |
9 | class FriendsContainer extends Component {
10 | constructor( props ) {
11 | super( props );
12 | this.state = {};
13 | // this.handleFriendshipChange = this.handleFriendshipChange.bind( this );
14 | }
15 |
16 | componentDidMount() {
17 | this.props.loadFriends();
18 | }
19 |
20 | handleFriendshipChange( toUserId, status ) {
21 | console.log('handleFriendshipChange', toUserId, status);
22 | const { updateFriendship, user } = this.props;
23 | updateFriendship( user.uid, toUserId, status );
24 | }
25 |
26 | render() {
27 | console.log( 'FriendsContainer - RENDER - this.props: ', this.props );
28 | const { pendingFriendships, currentFriendships, blockedFriendships, user } = this.props;
29 | const fromUserId = user.uid;
30 |
31 | if ( !pendingFriendships && currentFriendships ) {
32 | return Loading friendships...
;
33 | }
34 |
35 | return (
36 |
37 | {
38 | pendingFriendships &&
39 | this.handleFriendshipChange( toUserId, status )
43 | }/>
44 | }
45 | {
46 | currentFriendships &&
47 | this.handleFriendshipChange( toUserId, status )
52 | }/>
53 | }
54 | {
55 | blockedFriendships &&
56 | this.handleFriendshipChange( toUserId, status )
60 | }/>
61 | }
62 |
63 | );
64 | }
65 | }
66 |
67 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
68 | const mapStateToProps = ( state ) => {
69 | console.log( 'FriendsContainer - fn: mapStateToProps' );
70 | return {
71 | user: state.user,
72 | pendingFriendships: state.users &&
73 | state.users.filter( friend => friend.status === 'PENDING' ),
74 | currentFriendships: state.users &&
75 | state.users.filter( friend => friend.status === 'ACCEPTED' ),
76 | blockedFriendships: state.users &&
77 | state.users.filter( friend => friend.status === 'TERMINATED' || friend.status === 'CANCELED' )
78 | };
79 | };
80 |
81 | // Get actions and pass them as props to to FriendsContainer
82 | const mapDispatchToProps = ( dispatch ) => ( {
83 | loadFriends: () => dispatch( loadFriends() ),
84 | updateFriendship: ( fromUserId, toUserId, status ) => dispatch( updateFriendship( fromUserId, toUserId, status ) )
85 | } );
86 |
87 | export default connect( mapStateToProps, mapDispatchToProps )( FriendsContainer );
88 |
--------------------------------------------------------------------------------
/src/containers/friendshipButtonContainer.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import axios from '../utils/axios';
3 | import FriendshipButton from '../components/friendshipButton';
4 |
5 | export default class FriendshipButtonContainer extends React.Component {
6 | constructor( props ) {
7 | super( props );
8 | this.state = {
9 | currentUserId: props.currentUserId,
10 | otherUserId: props.otherUserId,
11 | fromUserId: '',
12 | toUserId: '',
13 | status: '',
14 | btnAction: ''
15 | };
16 | }
17 |
18 | componentDidMount() {
19 | console.log( 'FriendshipButtonContainer - fn: componentDidMount - this.props: ', this.props );
20 | // this.setState( {
21 | // fromUserId: this.props.fromUserId,
22 | // toUserId: this.props.toUserId
23 | // } );
24 | // R: READ - GET - /api/friends/:fromUserId/:toUserI SEE FRIENDSHIP STATUS OF TWO USERS
25 | axios.get( `/api/friends/${this.state.currentUserId}/${this.props.otherUserId}` )
26 | .then( resp => {
27 | if ( resp.data.success ) {
28 | this.setState( resp.data );
29 | return this.setBtnAction();
30 | } else {
31 | throw 'ERR: FriendshipButtonContainer - fn: componentDidMount - unable to retrieve data';
32 | }
33 | } )
34 | .catch( err => console.error( err ) );
35 | }
36 |
37 | setBtnAction() {
38 | const { status, currentUserId, fromUserId, toUserId } = this.state;
39 | if ( !status ) {
40 | return this.setState( { btnAction: 'MAKE FRIEND REQ' } );
41 | } else if ( status == 'CANCELED' ) {
42 | return this.setState( { btnAction: 'MAKE FRIEND REQ' } );
43 | } else if ( status == 'PENDING' && currentUserId == fromUserId ) {
44 | return this.setState( { btnAction: 'CANCEL' } );
45 | } else if ( status == 'PENDING' && currentUserId == toUserId ) {
46 | return this.setState( { btnAction: 'ACCEPT' } );
47 | } else if ( status == 'ACCEPTED' ) {
48 | return this.setState( { btnAction: 'TERMINATE' } );
49 | } else if ( status == 'TERMINATED' ) {
50 | return this.setState( { btnAction: 'DISABLED' } );
51 | }
52 | }
53 |
54 | // axios.put( `/api/friends/${currentUserId}/${otherUserId}/delete`, { status: 'CANCEL' } )
55 | // .then( resp => console.log( resp.data ) )
56 | // .catch( err => console.error( err.stack ) );
57 |
58 | handleClick() {
59 | const { currentUserId, otherUserId, btnAction } = this.state;
60 | console.log( 'FriendshipButtonContainer - fn: handleClick' );
61 |
62 | // status == '' && btnAction === 'MAKE FRIEND' && newStatus === 'PENDING'
63 | if ( !status && btnAction === 'MAKE FRIEND REQ' ) {
64 | axios.post( `/api/friends/${currentUserId}/${otherUserId}`, { status: 'PENDING' } )
65 |
66 | .then( resp => {
67 | if ( resp.data.success ) {
68 | this.setState( resp.data );
69 | return this.setBtnAction();
70 | } else {
71 | throw 'ERR: FriendshipButtonContainer - fn: handleClick - unable MAKE friendship';
72 | }
73 | } )
74 |
75 | .catch( err => console.error( err ) );
76 |
77 | }
78 |
79 | // status == 'CANCELED' && btnAction === 'MAKE FRIEND' && newStatus === 'PENDING'
80 | if ( btnAction === 'MAKE FRIEND REQ' ) {
81 | axios.put( `/api/friends/${currentUserId}/${otherUserId}`, { status: 'PENDING' } )
82 |
83 | .then( resp => {
84 | if ( resp.data.success ) {
85 | this.setState( resp.data );
86 | return this.setBtnAction();
87 | } else {
88 | throw 'ERR: FriendshipButtonContainer - fn: handleClick - unable MAKE friendship';
89 | }
90 | } )
91 |
92 | .catch( err => console.error( err ) );
93 |
94 | }
95 |
96 | // status == 'PENDING' && btnAction === 'CANCEL' && newStatus === 'CANCELED'
97 | else if ( btnAction === 'CANCEL' ) {
98 | axios.put( `/api/friends/${currentUserId}/${otherUserId}`, { status: 'CANCELED' } )
99 |
100 | .then( resp => {
101 | if ( resp.data.success ) {
102 | this.setState( resp.data );
103 | return this.setBtnAction();
104 | } else {
105 | throw 'ERR: FriendshipButtonContainer - fn: handleClick - unable CANCEL friendship';
106 | }
107 | } )
108 |
109 | .catch( err => console.error( err ) );
110 | }
111 |
112 | // status == 'PENDING' && btnAction === 'ACCEPT' && newStatus === 'ACCEPTED'
113 | else if ( btnAction === 'ACCEPT' ) {
114 | axios.put( `/api/friends/${currentUserId}/${otherUserId}`, { status: 'ACCEPTED' } )
115 |
116 | .then( resp => {
117 | if ( resp.data.success ) {
118 | this.setState( resp.data );
119 | return this.setBtnAction();
120 | } else {
121 | throw 'ERR: FriendshipButtonContainer - fn: handleClick - unable ACCEPT friendship';
122 | }
123 | } )
124 |
125 | .catch( err => console.error( err ) );
126 | }
127 |
128 | // status == 'ACCEPTED' && btnAction === 'TERMINATE' && newStatus === 'TERMINATED'
129 | else if ( btnAction === 'TERMINATE' ) {
130 | axios.put( `/api/friends/${currentUserId}/${otherUserId}`, { status: 'TERMINATED' } )
131 |
132 | .then( resp => {
133 | if ( resp.data.success ) {
134 | this.setState( resp.data );
135 | return this.setBtnAction();
136 | } else {
137 | throw 'ERR: FriendshipButtonContainer - fn: handleClick - unable TERMINATE friendship';
138 | }
139 | } )
140 |
141 | .catch( err => console.error( err ) );
142 | }
143 |
144 | // status == 'TERMINATED' && btnAction === 'DISABLED'
145 | else if ( btnAction === 'DISABLED' ) {
146 | throw 'ERR: FriendshipButtonContainer - fn: handleClick - btn is DISABLED: deal with it!';
147 | }
148 | }
149 |
150 | render() {
151 | console.log( 'FriendshipButtonContainer - RENDER - this.state: ', this.state );
152 | return this.handleClick(e) }
154 | btnAction={this.state.btnAction} />;
155 | }
156 | }
157 |
--------------------------------------------------------------------------------
/src/containers/online-friends-container.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { connect } from 'react-redux';
3 | import OnlineFriends from '../components/online-friends';
4 |
5 | class OnlineFriendsContainer extends Component {
6 | constructor( props ) {
7 | super( props );
8 | }
9 | render() {
10 | const { onlineFriends } = this.props;
11 | console.log( 'OnlineUsersContainer - RENDER - this.props: ', this.props );
12 |
13 | if ( !onlineFriends ) {
14 | return Loading online friends
;
15 | }
16 | return ;
17 | }
18 | }
19 |
20 | const mapStateToProps = ( state ) => {
21 | console.log( 'OnlineFriendsContainer - fn: mapStateToProps' );
22 | return {
23 | user: state.user,
24 | onlineFriends: state.users && state.users.filter( user => {
25 | return (user.online == true && user.status == 'ACCEPTED' && user.uid != state.user.uid);
26 | } )
27 | };
28 | };
29 |
30 |
31 | export default connect( mapStateToProps )( OnlineFriendsContainer );
32 |
--------------------------------------------------------------------------------
/src/containers/profileOther.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { browserHistory } from 'react-router';
3 | import axios from '../utils/axios';
4 | // import PropTypes from 'prop-types';
5 | import ProfilePic from '../components/profile-pic';
6 | import FriendshipButtonContainer from './friendshipButtonContainer';
7 |
8 | export default class ProfileOther extends Component {
9 |
10 | constructor( props ) {
11 | super( props );
12 | this.state = {};
13 | }
14 |
15 | componentDidMount() {
16 | // if the uid (this.props.params.uid) passed to this comp is the same as
17 | // the logged in user's (this.props.uid).
18 | if ( this.props.uid == this.props.params.uid ) {
19 | browserHistory.push( '/' );
20 | }
21 |
22 | // console.log( 'ProfileOther - fn: componentDidMount', `/api/user/${this.props.params.uid}` );
23 |
24 | axios.get( `/api/user/${this.props.params.uid}` )
25 |
26 | .then( resp => {
27 | if ( resp.data.success ) {
28 | this.setState( resp.data.otherUserData );
29 | console.log( 'ProfileOther - fn: componentDidMount - this.state', this.state );
30 | }
31 | this.setState( { error: 'Something went wrong. Please try again!' } );
32 | } )
33 |
34 | .catch( err => {
35 | this.setState( { error: 'Something went wrong. Please try again!' } );
36 | console.err( err );
37 | } );
38 | }
39 |
40 | render() {
41 | console.log( 'ProfileOther - RENDER - this.state: ', this.state );
42 |
43 | const {
44 | uid,
45 | firstName,
46 | lastName,
47 | email,
48 | bio,
49 | profilePic
50 | } = this.state;
51 |
52 | return (
53 |
54 |
ProfileOther
55 |
56 |
59 |
60 |
61 |
62 |
66 |
67 |
68 |
72 |
73 |
74 |
78 | {
79 | bio && (
80 |
81 |
82 |
83 |
86 |
)
87 | }
88 | {
89 | this.props.uid && this.state.uid &&
90 |
93 | }
94 |
95 | );
96 | }
97 |
98 | }
99 |
--------------------------------------------------------------------------------
/src/containers/users-container.js:
--------------------------------------------------------------------------------
1 | // REACT
2 | import React, { Component } from 'react';
3 | // REDUX
4 | import { connect } from 'react-redux';
5 | import {
6 | loadLatestUsers,
7 | loadSearchedUsers,
8 | clearSearchedUsers,
9 | updateFriendship,
10 | requestFriendship
11 | } from '../actions/actions';
12 |
13 | // MY COMPONENTS
14 | import Users from '../components/users';
15 |
16 | // MATERIAL-UI
17 | import { Toolbar, ToolbarGroup } from 'material-ui/Toolbar';
18 | import TextField from 'material-ui/TextField';
19 |
20 | // ICONS
21 | import ActionSearch from 'material-ui/svg-icons/action/search';
22 |
23 | class UsersContainer extends Component {
24 | constructor( props ) {
25 | super( props );
26 | this.state = { showSearchedUsersList: false }
27 | }
28 |
29 | componentDidMount() {
30 | this.props.loadLatestUsers();
31 | }
32 |
33 | handleFriendshipChange( toUserId, status ) {
34 | console.log( 'handleFriendshipChange', toUserId, status );
35 | const { updateFriendship, requestFriendship, user } = this.props;
36 | // updateFriendship( user.uid, toUserId, status );
37 | requestFriendship( user.uid, toUserId, status );
38 | }
39 |
40 | handleUpdateInput( e ) {
41 | console.log( 'handleUpdateInput: ', e.target.value );
42 | if ( !e.target.value ) {
43 | console.log( 'fn: handleUpdateInput: value is blank' );
44 | this.setState( { showSearchedUsersList: false } );
45 | this.props.clearSearchedUsers();
46 | } else {
47 | this.props.loadSearchedUsers( e.target.value );
48 | this.setState( { showSearchedUsersList: true } );
49 | }
50 |
51 | }
52 |
53 | render() {
54 | console.log( 'UsersContainer - RENDER - this.props: ', this.props );
55 | const { searchedUsersList, users } = this.props;
56 | return (
57 |
58 |
59 | this.handleUpdateInput(e)}
63 | />
64 |
65 |
66 | {
67 | this.state.showSearchedUsersList && searchedUsersList &&
68 |
this.handleFriendshipChange( toUserId, status )}/>
71 | }
72 |
73 | {
74 | !this.state.showSearchedUsersList && users &&
75 | this.handleFriendshipChange( toUserId, status )}/>
78 | }
79 |
80 | );
81 | }
82 | }
83 |
84 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
85 | const mapStateToProps = state => {
86 | console.log( 'UsersContainer - fn: mapStateToProps' );
87 | return {
88 | user: state.user,
89 | users: state.users && state.users.filter( user => {
90 | if ( !user.status ) {
91 | return user;
92 | } else {
93 | if ( user.status == 'TERMINATED' || user.status == 'CANCELED' ) {
94 | return user;
95 | }
96 | }
97 |
98 | } ),
99 | searchedUsersList: state.searchedUsersList
100 | };
101 | };
102 |
103 | const mapDispatchToProps = dispatch => ( {
104 | loadLatestUsers: () => dispatch( loadLatestUsers() ),
105 | updateFriendship: ( fromUserId, toUserId, status ) => dispatch( updateFriendship( fromUserId, toUserId, status ) ),
106 | requestFriendship: ( fromUserId, toUserId, status ) => dispatch( requestFriendship( fromUserId, toUserId, status ) ),
107 | loadSearchedUsers: search => dispatch( loadSearchedUsers( search ) ),
108 | clearSearchedUsers: () => dispatch( clearSearchedUsers() )
109 | } );
110 |
111 | export default connect( mapStateToProps, mapDispatchToProps )( UsersContainer );
112 |
--------------------------------------------------------------------------------
/src/shell.js:
--------------------------------------------------------------------------------
1 | // REACT components
2 | import React from 'react';
3 | import ReactDOM from 'react-dom';
4 | import { Router, Route, IndexRoute, hashHistory, browserHistory } from 'react-router';
5 | import injectTapEventPlugin from 'react-tap-event-plugin';
6 | injectTapEventPlugin();
7 |
8 | // REDUX components
9 | import { Provider } from 'react-redux';
10 | import { createStore, applyMiddleware } from 'redux';
11 | import reduxPromise from 'redux-promise';
12 | // import allReducers from './reducers';
13 | import reducers from './reducers/reducers';
14 | import { composeWithDevTools } from 'redux-devtools-extension';
15 |
16 |
17 | // Containers:
18 | import App from './containers/app';
19 | import FriendsContainer from './containers/friends-container';
20 | import ProfileOther from './containers/profileOther';
21 | import ChatChildrenContainer from './containers/chat-children-container';
22 | import ChatListContainer from './containers/chat-list-container';
23 | import ChatPublicContainer from './containers/chat-public-container';
24 | import ChatPrivateContainer from './containers/chat-private-container';
25 | import ChatSecureContainer from './containers/chat-secure-container';
26 | import UsersContainer from './containers/users-container';
27 |
28 | // Components
29 | import Welcome from './components/welcome';
30 | import Registration from './components/registration';
31 | import Login from './components/login';
32 | import ProfileSelf from './components/profile-self';
33 |
34 | // MATERIAL-UI:
35 | import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
36 |
37 | export const store = createStore( reducers, composeWithDevTools( applyMiddleware( reduxPromise ) ) );
38 |
39 | // REACT Router
40 | let router;
41 |
42 | if ( location.pathname === '/welcome/' ) {
43 | console.log( 'Shell: ', location.pathname );
44 | router = (
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 | );
54 | } else if ( location.pathname !== '/welcome/' ) {
55 | console.log( 'Shell: ', location.pathname );
56 | router = (
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 | );
76 | }
77 |
78 |
79 |
80 | ReactDOM.render(
81 | router,
82 | document.querySelector( 'main' )
83 | );
84 |
--------------------------------------------------------------------------------
/src/utils/axios.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 |
3 | const instance = axios.create( {
4 | xsrfCookieName: '__csrf__',
5 | xsrfHeaderName: 'csrf-token'
6 | } );
7 |
8 | export default instance;
9 |
--------------------------------------------------------------------------------
/src/utils/formWrapper.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import axios from './axios';
3 |
4 | export default ( Component, url ) => {
5 |
6 | return class AuthForm extends React.Component {
7 |
8 | constructor( props ) {
9 | super( props );
10 | this.state = {};
11 | this.url = url;
12 | }
13 |
14 | handleInput( e ) {
15 | // gather value from changed form field
16 | this.setState( { [ e.target.name ]: e.target.value } );
17 | }
18 |
19 | handleSubmit( e ) {
20 | e.preventDefault();
21 | // make POST request to this.url and handle response
22 | axios.post( this.url, this.state )
23 | .then( resp => {
24 | const data = resp.data;
25 | // console.log( 'formWrapper - fn: Axios.post - data: ', data );
26 | if ( !data.success ) {
27 | this.setState( { error: true } );
28 | }
29 |
30 | // else {
31 | // this.setState({ success: true });
32 | // }
33 |
34 | location.replace( '/' );
35 | } )
36 |
37 | .catch( err => {
38 | console.error( err.stack );
39 | this.setState( { error: true } );
40 | } );
41 | }
42 |
43 | render() {
44 | return (
45 | this.handleInput(e)}
48 | handleSubmit={(e)=>this.handleSubmit(e)}/>
49 | );
50 | }
51 | };
52 | };
53 |
--------------------------------------------------------------------------------
/src/utils/peer-bk.js:
--------------------------------------------------------------------------------
1 | import { store } from '../shell';
2 | import { sendPeerIdToServer } from '../actions/actions';
3 |
4 | export const getPeer = () => {
5 | const peer = new Peer( {
6 | host: 'localhost',
7 | port: 8080,
8 | path: '/peerjs',
9 | debug: 3
10 | } );
11 |
12 | // Once the initialization succeeds:
13 | // Log the ID that allows other user to connect to your session.
14 | peer.on( 'open', () => {
15 | console.log( 'PeerJs - client - Event - "open" - id:', peer.id );
16 | store.dispatch( sendPeerIdToServer( peer.id ) )
17 | .then(() => peer.connect(peer.id));
18 | } );
19 |
20 | return peer;
21 |
22 |
23 | };
24 |
25 | export default getPeer;
26 |
--------------------------------------------------------------------------------
/src/utils/peer.js:
--------------------------------------------------------------------------------
1 | import { store } from '../shell';
2 | import { sendPeerIdToServer } from '../actions/actions';
3 |
4 | let peer;
5 |
6 | export default function getPeers() {
7 | if ( !peer ) {
8 |
9 | peer = new Peer( {
10 | host: 'localhost',
11 | port: 8080,
12 | path: '/peerjs',
13 | debug: 3
14 | } );
15 |
16 |
17 | // Once the initialization succeeds:
18 | // Log the ID that allows other user to connect to your session.
19 |
20 | peer.on( 'open', () => {
21 | console.log( 'PeerJs - client - Event - "open" - id:', peer.id );
22 | store.dispatch( sendPeerIdToServer( peer.id ) )
23 | .then( () => peer.connect( peer.id ) );
24 | } );
25 |
26 |
27 | peer.on( 'connection', dataConnection => {
28 | console.log( 'PeerJs - client - Event - "connection" - dataConnection:', dataConnection );
29 | } );
30 |
31 |
32 | peer.on( 'data', data => {
33 | console.log( 'PeerJs - client - Event - "data" - data:', data );
34 | } );
35 |
36 | return peer;
37 |
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/utils/socketIo.js:
--------------------------------------------------------------------------------
1 | // SOCKETio.js
2 | import * as io from 'socket.io-client';
3 | import { store } from '../shell';
4 | import {
5 | connectUser,
6 | createOnlineUsers,
7 | createOnlinePeers,
8 | addOnlineUser,
9 | addOnlinePeer,
10 | removeOnlineUser,
11 | createPublicMessageList,
12 | createPrivateMessageList,
13 | addNewPublicMessage,
14 | addNewPrivateMessage
15 | } from '../actions/actions';
16 |
17 | let socket;
18 |
19 | const getSocket = () => {
20 | if ( !socket ) {
21 | socket = io.connect();
22 |
23 |
24 | socket.on( 'connect', () => {
25 | console.log( `Socket.io Event: connect - socketId: ${socket.id}` );
26 | store.dispatch( connectUser( socket.id ) );
27 | } );
28 |
29 |
30 | socket.on( 'onlineUsers', ( onlineUsers ) => {
31 | console.log( 'Socket.io Event: onlineUsers', onlineUsers );
32 | store.dispatch( createOnlineUsers( onlineUsers ) );
33 | } );
34 |
35 |
36 | socket.on( 'onlinePeers', ( onlinePeers ) => {
37 | console.log( 'Socket.io Event: onlinePeers', onlinePeers );
38 | store.dispatch( createOnlinePeers( onlinePeers ) );
39 | } );
40 |
41 |
42 | socket.on( 'userJoined', ( userJoined ) => {
43 | console.log( 'Socket.io Event: userJoined', userJoined );
44 | store.dispatch( addOnlineUser( userJoined ) );
45 | } );
46 |
47 |
48 | socket.on( 'peerJoined', ( peerJoined ) => {
49 | console.log( 'Socket.io Event: peerJoined', peerJoined );
50 | store.dispatch( addOnlinePeer( peerJoined ) );
51 | } );
52 |
53 |
54 | socket.on( 'userLeft', ( uid ) => {
55 | console.log( 'Socket.io Event: userLeft', uid );
56 | store.dispatch( removeOnlineUser( uid ) );
57 | } );
58 |
59 |
60 | socket.on( 'publicChatMessages', ( publicMessageList ) => {
61 | console.log( 'Socket.io Event: publicChatMessages', publicMessageList );
62 | store.dispatch( createPublicMessageList( publicMessageList ) );
63 | } );
64 |
65 |
66 | socket.on( 'privateChatMessages', privateMessageList => {
67 | console.log( 'Socket.io Event: privateChatMessages', privateMessageList );
68 | store.dispatch( createPrivateMessageList( privateMessageList ) );
69 | } );
70 |
71 |
72 | socket.on( 'chatMessage', ( newPublicMessage ) => {
73 | console.log( 'Socket.io Event: chatMessage' );
74 | store.dispatch( addNewPublicMessage( newPublicMessage ) );
75 | } );
76 |
77 | socket.on( 'privateChatMessage', ( newPrivateMessage ) => {
78 | console.log( 'Socket.io Event: privateChatMessage' );
79 | store.dispatch( addNewPrivateMessage( newPrivateMessage ) );
80 | } );
81 |
82 | }
83 | return socket;
84 | };
85 |
86 | export default getSocket;
87 |
--------------------------------------------------------------------------------
/uploads/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/users/04ec7e10980947.560efb9b62284.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/suddenlyGiovanni/spiced_academy--p2p_chat/a64d852d68888778f407edd741eb922be90749cb/users/04ec7e10980947.560efb9b62284.png
--------------------------------------------------------------------------------
/users/4a2d8e10980947.560ef9a45fc34.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/suddenlyGiovanni/spiced_academy--p2p_chat/a64d852d68888778f407edd741eb922be90749cb/users/4a2d8e10980947.560ef9a45fc34.png
--------------------------------------------------------------------------------
/users/8e44e310980947.560efbe769094.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/suddenlyGiovanni/spiced_academy--p2p_chat/a64d852d68888778f407edd741eb922be90749cb/users/8e44e310980947.560efbe769094.png
--------------------------------------------------------------------------------
/users/Aquaman - .png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/suddenlyGiovanni/spiced_academy--p2p_chat/a64d852d68888778f407edd741eb922be90749cb/users/Aquaman - .png
--------------------------------------------------------------------------------
/users/Batman - Bruce Wayne.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/suddenlyGiovanni/spiced_academy--p2p_chat/a64d852d68888778f407edd741eb922be90749cb/users/Batman - Bruce Wayne.png
--------------------------------------------------------------------------------
/users/Black Widow - Natasha Romanoff.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/suddenlyGiovanni/spiced_academy--p2p_chat/a64d852d68888778f407edd741eb922be90749cb/users/Black Widow - Natasha Romanoff.png
--------------------------------------------------------------------------------
/users/Captain America.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/suddenlyGiovanni/spiced_academy--p2p_chat/a64d852d68888778f407edd741eb922be90749cb/users/Captain America.png
--------------------------------------------------------------------------------
/users/Flash - Barry Allen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/suddenlyGiovanni/spiced_academy--p2p_chat/a64d852d68888778f407edd741eb922be90749cb/users/Flash - Barry Allen.png
--------------------------------------------------------------------------------
/users/Green Lantern - .png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/suddenlyGiovanni/spiced_academy--p2p_chat/a64d852d68888778f407edd741eb922be90749cb/users/Green Lantern - .png
--------------------------------------------------------------------------------
/users/Hell Boy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/suddenlyGiovanni/spiced_academy--p2p_chat/a64d852d68888778f407edd741eb922be90749cb/users/Hell Boy.png
--------------------------------------------------------------------------------
/users/Hulk.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/suddenlyGiovanni/spiced_academy--p2p_chat/a64d852d68888778f407edd741eb922be90749cb/users/Hulk.png
--------------------------------------------------------------------------------
/users/Invisible Woman.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/suddenlyGiovanni/spiced_academy--p2p_chat/a64d852d68888778f407edd741eb922be90749cb/users/Invisible Woman.png
--------------------------------------------------------------------------------
/users/Iron Man - Tony Stark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/suddenlyGiovanni/spiced_academy--p2p_chat/a64d852d68888778f407edd741eb922be90749cb/users/Iron Man - Tony Stark.png
--------------------------------------------------------------------------------
/users/Litz - .png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/suddenlyGiovanni/spiced_academy--p2p_chat/a64d852d68888778f407edd741eb922be90749cb/users/Litz - .png
--------------------------------------------------------------------------------
/users/Mr Fantastic - .png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/suddenlyGiovanni/spiced_academy--p2p_chat/a64d852d68888778f407edd741eb922be90749cb/users/Mr Fantastic - .png
--------------------------------------------------------------------------------
/users/Nick Fury.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/suddenlyGiovanni/spiced_academy--p2p_chat/a64d852d68888778f407edd741eb922be90749cb/users/Nick Fury.png
--------------------------------------------------------------------------------
/users/Superman -Clark Kent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/suddenlyGiovanni/spiced_academy--p2p_chat/a64d852d68888778f407edd741eb922be90749cb/users/Superman -Clark Kent.png
--------------------------------------------------------------------------------
/users/The Human Torch - .png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/suddenlyGiovanni/spiced_academy--p2p_chat/a64d852d68888778f407edd741eb922be90749cb/users/The Human Torch - .png
--------------------------------------------------------------------------------
/users/The Thing - .png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/suddenlyGiovanni/spiced_academy--p2p_chat/a64d852d68888778f407edd741eb922be90749cb/users/The Thing - .png
--------------------------------------------------------------------------------
/users/Thor - .png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/suddenlyGiovanni/spiced_academy--p2p_chat/a64d852d68888778f407edd741eb922be90749cb/users/Thor - .png
--------------------------------------------------------------------------------
/users/Wonder Woman.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/suddenlyGiovanni/spiced_academy--p2p_chat/a64d852d68888778f407edd741eb922be90749cb/users/Wonder Woman.png
--------------------------------------------------------------------------------
/users/db151110980947.560efb243ae6e.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/suddenlyGiovanni/spiced_academy--p2p_chat/a64d852d68888778f407edd741eb922be90749cb/users/db151110980947.560efb243ae6e.png
--------------------------------------------------------------------------------
/users/e3afac10980947.560ef9a483f53.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/suddenlyGiovanni/spiced_academy--p2p_chat/a64d852d68888778f407edd741eb922be90749cb/users/e3afac10980947.560ef9a483f53.png
--------------------------------------------------------------------------------