├── src ├── styles.scss ├── Candidates.jsx ├── App.js ├── index.js ├── WaitingRoom.jsx ├── timer.jsx ├── GameOver.jsx ├── Winner.jsx ├── Voting.jsx ├── Ideation.jsx ├── Home.jsx └── templates.js ├── Procfile ├── .prettierrc.js ├── .babelrc ├── README.md ├── .eslintrc.json ├── index.html ├── LICENSE ├── webpack.config.js ├── package.json ├── .gitignore └── server └── server.js /src/styles.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: node server/server.js -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | semi: true, 3 | trailingComma: 'all', 4 | arrowParens: 'avoid', 5 | singleQuote: true, 6 | printWidth: 100, 7 | tabWidth: 2, 8 | }; 9 | -------------------------------------------------------------------------------- /src/Candidates.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default function Candidates({ memes }) { 4 | const images = memes.map(url => {}); 5 | 6 | return
hi
; 7 | } 8 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "modules": false 7 | } 8 | ], 9 | "@babel/preset-react" 10 | ], 11 | "plugins": [ 12 | "react-hot-loader/babel" 13 | ] 14 | } -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Home from './Home'; 3 | import io from 'socket.io-client'; 4 | 5 | export default function App() { 6 | const socket = io.connect('http://localhost:3001'); 7 | 8 | return ( 9 |
10 | 11 |
12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | import { BrowserRouter as Router } from 'react-router-dom'; 5 | import './styles.scss'; 6 | 7 | const mountNode = document.getElementById('app'); 8 | ReactDOM.render( 9 | 10 | 11 | , 12 | mountNode, 13 | ); 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MEME HOURS 2 | 3 | Meme Hours is a real-time multiplayer game in which users are prompted to write captions for a randomly given meme template. At the end of each round, users give likes to each meme and the round winner is displayed. At the end of the game, all round winners are displayed. 4 | 5 | 6 | First: 7 | `npm run build` 8 | 9 | Then: 10 | `npm start` for hot module reloading! 11 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": {}, 3 | "env": { 4 | "es6": true, 5 | "browser": true 6 | }, 7 | "parserOptions": { 8 | "ecmaVersion": 2018, 9 | "sourceType": "module", 10 | "ecmaFeatures": { 11 | "jsx": true 12 | } 13 | }, 14 | "extends": [ 15 | "plugin:prettier/recommended" 16 | ], 17 | "globals": { 18 | "Atomics": "readonly", 19 | "SharedArrayBuffer": "readonly" 20 | }, 21 | "plugins": [ 22 | "react" 23 | ] 24 | } -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Meme Hours 6 | 7 | 8 | 9 | 10 | 11 |
12 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/WaitingRoom.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components'; 3 | 4 | const Wrapper = styled.main` 5 | display: flex; 6 | flex-direction: column; 7 | justify-content: center; 8 | align-items: center; 9 | text-align: center; 10 | `; 11 | 12 | const Div = styled.div` 13 | display: flex; 14 | flex-direction: column; 15 | align-items: center; 16 | border: dotted 0.5px; 17 | width: 50%; 18 | padding: 10px; 19 | font-size: 50px; 20 | box-shadow: 9px 9px 40px -12px rgba(0, 0, 0, 0.75); 21 | `; 22 | 23 | export default function WaitingRoom(props) { 24 | return {props.name} is in the room!; 25 | } 26 | -------------------------------------------------------------------------------- /src/timer.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect, useRef } from 'react'; 2 | 3 | export default function Timer({ mins, secs, setTimesUp }) { 4 | const [minutes, setMinutes] = useState(mins); 5 | const [seconds, setSeconds] = useState(secs); 6 | 7 | useEffect(() => { 8 | let myInterval = setInterval(() => { 9 | if (seconds > 0) { 10 | setSeconds(seconds => seconds - 1); 11 | } 12 | if (seconds === 0) { 13 | if (minutes === 0) { 14 | clearInterval(myInterval); 15 | setTimesUp(true); 16 | } else { 17 | setMinutes(minutes => minutes - 1); 18 | setSeconds(59); 19 | } 20 | } 21 | }, 1000); 22 | return () => { 23 | clearInterval(myInterval); 24 | }; 25 | }); 26 | 27 | return ( 28 |
29 | {minutes === 0 && seconds == 0 ? null : ( 30 |

31 | Time Remaining: {minutes}: {seconds < 10 ? `0${seconds}` : seconds} 32 |

33 | )} 34 |
35 | ); 36 | } 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 squirtle-squared 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/GameOver.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import { Switch, Route, Link, useHistory } from 'react-router-dom'; 3 | 4 | export default function GameOver({ socket, setRound, winners, setWinners, self }) { 5 | const history = useHistory(); 6 | const handleClick = e => { 7 | e.preventDefault(); 8 | socket.emit('reset'); 9 | }; 10 | 11 | useEffect(() => { 12 | socket.emit('getWinners'); 13 | socket.on('getWinners', memes => { 14 | setWinners(memes); 15 | }); 16 | socket.on('reset', () => { 17 | setRound(1); 18 | history.push('/'); 19 | }); 20 | }, []); 21 | return ( 22 |
23 | {winners.length && 24 | winners.map((meme, i) => ( 25 |
26 |
27 |

Round: {meme.round}

28 |

Creator: {meme.name}

29 |

Points: {meme.likes}

30 |
31 | 32 |
33 | ))} 34 | {self.isHost && } 35 |
36 | ); 37 | } 38 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | entry: ['react-hot-loader/patch', './src/index.js'], 5 | output: { 6 | path: path.resolve(__dirname, 'dist'), 7 | filename: 'bundle.js', 8 | publicPath: '/', 9 | }, 10 | module: { 11 | rules: [ 12 | { 13 | test: /\.(js|jsx)$/, 14 | use: 'babel-loader', 15 | exclude: /node_modules/, 16 | }, 17 | { 18 | test: /\.css$/, 19 | use: ['style-loader', 'css-loader'], 20 | }, 21 | { 22 | test: /\.scss$/, 23 | use: ['style-loader', 'css-loader', 'sass-loader'], 24 | }, 25 | { 26 | test: /\.(eot|woff|woff2|ttf|svg|png|jpg)$/, 27 | include: [path.resolve(__dirname + '/src')], 28 | loader: 'url-loader?limit=50000&name=[name].[ext]', 29 | }, 30 | ], 31 | }, 32 | resolve: { 33 | extensions: ['.js', '.jsx'], 34 | alias: { 35 | 'react-dom': '@hot-loader/react-dom', 36 | }, 37 | }, 38 | devServer: { 39 | host: 'localhost', 40 | port: 8080, 41 | contentBase: path.resolve(__dirname, 'dist'), 42 | historyApiFallback: true, 43 | headers: { 'Access-Control-Allow-Origin': '*' }, 44 | proxy: { 45 | '/': { 46 | target: 'http://localhost:3001/', 47 | secure: false, 48 | }, 49 | }, 50 | }, 51 | }; 52 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "memehours", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "server/server.js", 6 | "engines": { 7 | "node": "^12" 8 | }, 9 | "scripts": { 10 | "clean": "rm -rf dist", 11 | "build": "webpack -p --mode=production && cp index.html dist/index.html", 12 | "start": "webpack-dev-server --open --hot & nodemon server/server.js", 13 | "test": "echo \"Error: no test specified\" && exit 1", 14 | "start-server": "node server/server.js", 15 | "build-all": "rm -rf dist && npm i && npm run build", 16 | "heroku-postbuild": "npm run build-all" 17 | }, 18 | "repository": { 19 | "type": "git", 20 | "url": "git+https://github.com/heatherfriedman/MemeHours.git" 21 | }, 22 | "author": "Heather Friedman, Will Hack, Kadir Gundogdu, Rebecca Miller", 23 | "license": "MIT", 24 | "bugs": { 25 | "url": "https://github.com/heatherfriedman/MemeHours/issues" 26 | }, 27 | "homepage": "https://github.com/heatherfriedman/MemeHours#readme", 28 | "dependencies": { 29 | "express": "^4.17.1", 30 | "react": "^16.13.1", 31 | "react-dom": "^16.13.1", 32 | "react-hook-form": "^6.8.6", 33 | "react-hot-loader": "^4.13.0", 34 | "react-router-dom": "^5.2.0", 35 | "socket.io": "^2.3.0", 36 | "socket.io-client": "^2.3.0", 37 | "styled-components": "^5.2.0", 38 | "stylis": "^4.0.3" 39 | }, 40 | "devDependencies": { 41 | "@babel/core": "^7.11.6", 42 | "@babel/preset-env": "^7.11.5", 43 | "@babel/preset-react": "^7.10.4", 44 | "@hot-loader/react-dom": "^17.0.0-rc.2", 45 | "babel-loader": "^8.1.0", 46 | "css-loader": "^4.3.0", 47 | "eslint": "^7.10.0", 48 | "eslint-config-prettier": "^6.12.0", 49 | "eslint-plugin-prettier": "^3.1.4", 50 | "eslint-plugin-react": "^7.21.2", 51 | "file-loader": "^6.1.0", 52 | "node-sass": "^4.14.1", 53 | "prettier": "^2.1.2", 54 | "sass-loader": "^10.0.2", 55 | "style-loader": "^1.2.1", 56 | "url-loader": "^4.1.0", 57 | "webpack": "^4.44.2", 58 | "webpack-cli": "^3.3.12", 59 | "webpack-dev-server": "^3.11.0" 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | package-lock.json 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # DS_STORE 104 | .DS_Store 105 | 106 | # TernJS port file 107 | .tern-port 108 | 109 | # Dist file 110 | dist 111 | 112 | # Package-lock.json 113 | package-lock.json -------------------------------------------------------------------------------- /src/Winner.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import { Switch, Route, useHistory } from 'react-router-dom'; 3 | import Timer from './Timer.jsx'; 4 | 5 | export default function Winner({ 6 | socket, 7 | setSubmitClicked, 8 | setIdeationTimesUp, 9 | setAllSubmitted, 10 | round, 11 | setRound, 12 | winner, 13 | setWinner, 14 | winners, 15 | setWinners, 16 | }) { 17 | const history = useHistory(); 18 | const [timesUp, setTimesUp] = useState(false); 19 | 20 | useEffect(() => { 21 | socket.emit('getCandidates'); 22 | socket.on('memeCandidates', candidates => { 23 | let max = -1; 24 | let winningMeme; 25 | for (let candidate of candidates) { 26 | if (candidate.likes > max) { 27 | max = candidate.likes; 28 | winningMeme = candidate; 29 | } 30 | } 31 | socket.emit('roundWinner', winningMeme); 32 | }); 33 | }, []); 34 | 35 | useEffect(() => { 36 | if (timesUp) { 37 | socket.emit('newRound', round + 1); 38 | if (round + 1 === 3) { 39 | socket.emit('gameOver'); 40 | } else { 41 | socket.emit('ideate'); 42 | } 43 | } 44 | // socket.emit('newRound', round + 1); 45 | }, [timesUp]); 46 | 47 | // useEffect(() => { 48 | // socket.emit('roundWinner', winner); 49 | // }, [winner]); 50 | 51 | useEffect(() => { 52 | socket.on('roundWinner', meme => { 53 | setWinner(meme); 54 | }); 55 | socket.on('roundWinners', roundWinners => { 56 | setWinners(roundWinners); 57 | }); 58 | socket.on('newRound', round => { 59 | setSubmitClicked(false); 60 | setIdeationTimesUp(false); 61 | setAllSubmitted(false); 62 | setRound(round); 63 | }); 64 | socket.on('ideate', () => { 65 | history.push('/ideation'); 66 | }); 67 | socket.on('gameOver', () => { 68 | history.push('/gameOver'); 69 | }); 70 | }, []); 71 | 72 | return ( 73 |
74 | 75 |

The Winning Meme of Round {round}

76 | {winner && ( 77 |
78 |

Creator: {winner.name}

79 |

Points: {winner.likes}

80 | 81 |
82 | )} 83 | {!winner && ( 84 |
85 |

Nobody submitted a meme!

86 | 87 |
88 | )} 89 |
90 | ); 91 | } 92 | -------------------------------------------------------------------------------- /src/Voting.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import { useHistory } from 'react-router-dom'; 3 | import Candidates from './Candidates'; 4 | import Timer from './Timer.jsx'; 5 | 6 | export default function Voting({ socket, self }) { 7 | const [candidates, setCandidates] = useState([]); 8 | const history = useHistory(); 9 | const [likes, setLikes] = useState([]); 10 | const [timesUp, setTimesUp] = useState(false); 11 | const handleClick = (e, i) => { 12 | e.preventDefault(); 13 | const newCandidates = [...candidates]; 14 | if (likes[i] < 10 || !likes[i]) { 15 | newCandidates[i].likes += 1; 16 | if (!likes[i]) { 17 | const newLikes = [...likes]; 18 | newLikes[i] = 1; 19 | setLikes(newLikes); 20 | } else { 21 | const newLikes = [...likes]; 22 | newLikes[i]++; 23 | setLikes(newLikes); 24 | } 25 | socket.emit('updateCandidates', newCandidates); 26 | } 27 | }; 28 | const handleDislike = (e, i) => { 29 | e.preventDefault(); 30 | const dislikedCandidates = [...candidates]; 31 | if (likes[i] > 0) { 32 | dislikedCandidates[i].likes -= 1; 33 | const updatedLikes = [...likes]; 34 | updatedLikes[i]--; 35 | setLikes(updatedLikes); 36 | } 37 | socket.emit('updateCandidates', dislikedCandidates); 38 | }; 39 | 40 | let memes; 41 | 42 | //another timer to track voting - 1 min 43 | //same conditional rendering as ideation 44 | useEffect(() => { 45 | socket.emit('getCandidates'); 46 | socket.on('memeCandidates', memes => { 47 | setCandidates(memes); 48 | }); 49 | socket.on('updateCandidates', memes => { 50 | setCandidates(memes); 51 | }); 52 | }, []); 53 | 54 | useEffect(() => { 55 | if (timesUp) history.push('/winner'); 56 | }, [timesUp]); 57 | 58 | return ( 59 |
60 | 61 | {candidates.length && 62 | candidates.map((meme, i) => ( 63 |
64 |

Meme {i + 1}

65 | {self.id !== meme.id && ( 66 |
67 | 68 | {likes[i] > 0 && } 69 | {likes[i] > 0 && You have liked this meme {likes[i]} times} 70 |
71 | )} 72 | {self.id === meme.id &&

Your meme

} 73 |
74 | 75 |
76 |
77 | ))} 78 | {!candidates.length && ( 79 |
80 |

Nobody submitted a meme!

81 | 82 |
83 | )} 84 |
85 | ); 86 | } 87 | -------------------------------------------------------------------------------- /server/server.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const app = express(); 3 | const socketIO = require('socket.io'); 4 | const path = require('path'); 5 | 6 | const port = process.env.PORT || 3001; 7 | 8 | app.use(express.static(path.join(__dirname, '../dist'))); 9 | app.get('*', (_request, response) => 10 | response.sendFile(path.resolve(`${__dirname}/../dist/index.html`)), 11 | ); 12 | const server = app.listen(port, () => console.log(`server listening on ${port}.`)); 13 | const io = socketIO(server); 14 | 15 | const players = []; 16 | let submissions = []; 17 | let roundWinners = []; 18 | let round; 19 | 20 | io.on('connection', socket => { 21 | console.log('new client connected'); 22 | socket.on('newPlayer', playerName => { 23 | const playerObject = { 24 | name: playerName, 25 | id: socket.id, 26 | isHost: players.length < 1, 27 | }; 28 | players.push(playerObject); 29 | io.emit('updatePlayers', players); 30 | socket.emit('getSelf', playerObject); 31 | }); 32 | socket.on('ideate', () => { 33 | io.emit('ideate'); 34 | io.emit('randomNumber', Math.floor(Math.random() * 100)); 35 | }); 36 | 37 | // socket.on('randomNumber', () => { 38 | // io.emit('randomNumber', Math.floor(Math.random() * 100)); 39 | // }); 40 | 41 | socket.on('submitImage', ([name, id, memeUrl, round]) => { 42 | // console.log(name, memeUrl); 43 | // once submissions line 16 length === players length move on 44 | submissions.push({ name, id, memeUrl, likes: 0, round }); 45 | // console.log(submissions); 46 | if (submissions.length === players.length) io.emit('voting'); 47 | }); 48 | 49 | socket.on('getCandidates', () => { 50 | socket.emit('memeCandidates', submissions); 51 | }); 52 | socket.on('updateCandidates', memes => { 53 | submissions = memes; 54 | io.emit('updateCandidates', memes); 55 | }); 56 | 57 | socket.on('roundWinner', meme => { 58 | if (meme) { 59 | if (meme.name) { 60 | let isInList = false; 61 | for (let winner of roundWinners) { 62 | if (meme.memeUrl === winner.memeUrl) isInList = true; 63 | } 64 | if (!isInList) { 65 | roundWinners.push(meme); 66 | io.emit('roundWinner', meme); 67 | io.emit('roundWinners', roundWinners); 68 | } 69 | } 70 | } 71 | }); 72 | 73 | socket.on('newRound', newRound => { 74 | round = newRound; 75 | submissions = []; 76 | io.emit('newRound', round); 77 | }); 78 | socket.on('getWinners', () => { 79 | socket.emit('getWinners', roundWinners); 80 | }); 81 | socket.on('gameOver', () => { 82 | round = 1; 83 | io.emit('gameOver'); 84 | }); 85 | socket.on('reset', () => { 86 | roundWinners = []; 87 | round = 1; 88 | io.emit('reset'); 89 | }); 90 | 91 | socket.on('disconnect', sckt => { 92 | // removal from users array 93 | for (let i = 0; i < players.length; i++) { 94 | if (players[i].id === socket.id) { 95 | players.splice(i, 1); 96 | break; 97 | } 98 | } 99 | if (players.length) players[0].isHost = true; 100 | io.emit('updatePlayers', players); 101 | console.log('a user disconnected'); 102 | // removal from submissions array 103 | for (let i = 0; i < submissions.length; i += 1) { 104 | if (submissions[i].id === socket.id) { 105 | submissions.splice(i, 1); 106 | break; 107 | } 108 | } 109 | if (submissions.length === players.length) io.emit('voting'); 110 | }); 111 | }); 112 | -------------------------------------------------------------------------------- /src/Ideation.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect, useRef } from 'react'; 2 | import { useHistory } from 'react-router-dom'; 3 | import Timer from './Timer.jsx'; 4 | const temps = require('./templates.js'); 5 | 6 | export default function Ideation({ 7 | socket, 8 | name, 9 | id, 10 | round, 11 | submitClicked, 12 | setSubmitClicked, 13 | ideationTimesUp, 14 | setIdeationTimesUp, 15 | allSubmitted, 16 | setAllSubmitted, 17 | winners, 18 | setWinners, 19 | }) { 20 | const [texts, setTexts] = useState([1, 2, 3, 4, 5, 6, 7]); 21 | const [templates, setTemplates] = useState(temps); 22 | const [currentMeme, setCurrentMeme] = useState(null); 23 | const imgRef = useRef(null); 24 | const history = useHistory(); 25 | 26 | const handleChange = e => { 27 | const inputFields = [...texts]; 28 | inputFields[e.target.id] = e.target.value; 29 | setTexts(inputFields); 30 | }; 31 | 32 | const handleSubmit = e => { 33 | e.preventDefault(); 34 | socket.emit('submitImage', [name, id, imgRef.current.src, round]); 35 | setSubmitClicked(true); 36 | }; 37 | 38 | const handlePreview = e => { 39 | e.preventDefault(); 40 | var myHeaders = new Headers(); 41 | // myHeaders.append("Cookie", "__cfduid=d5b06b2df9eda0d63076d8ba50f5642121601326806; iflipsess=dv537v2andm5mkrns95s4se369; claim_key=bz13Qn7QBuqPCCqtTDLZz2Mierh_HXNx"); 42 | var formdata = new FormData(); 43 | formdata.append('template_id', currentMeme.id); 44 | formdata.append('username', 'memehours'); 45 | formdata.append('password', 'csny2020'); 46 | for (let i = 0; i < currentMeme.box_count; i++) { 47 | formdata.append(`boxes[${i}][text]`, texts[i]); 48 | } 49 | 50 | var requestOptions = { 51 | method: 'POST', 52 | headers: myHeaders, 53 | body: formdata, 54 | redirect: 'follow', 55 | }; 56 | 57 | fetch('https://api.imgflip.com/caption_image', requestOptions) 58 | .then(res => res.json()) 59 | .then(res => { 60 | imgRef.current.src = res.data.url; 61 | }) 62 | .catch(error => console.log('error', error)); 63 | }; 64 | 65 | const mockPreview = () => { 66 | var myHeaders = new Headers(); 67 | // myHeaders.append("Cookie", "__cfduid=d5b06b2df9eda0d63076d8ba50f5642121601326806; iflipsess=dv537v2andm5mkrns95s4se369; claim_key=bz13Qn7QBuqPCCqtTDLZz2Mierh_HXNx"); 68 | var formdata = new FormData(); 69 | formdata.append('template_id', currentMeme.id); 70 | formdata.append('username', 'memehours'); 71 | formdata.append('password', 'csny2020'); 72 | for (let i = 0; i < currentMeme.box_count; i++) { 73 | formdata.append(`boxes[${i}][text]`, texts[i]); 74 | } 75 | 76 | var requestOptions = { 77 | method: 'POST', 78 | headers: myHeaders, 79 | body: formdata, 80 | redirect: 'follow', 81 | }; 82 | 83 | fetch('https://api.imgflip.com/caption_image', requestOptions) 84 | .then(res => res.json()) 85 | .then(res => { 86 | imgRef.current.src = res.data.url; 87 | }) 88 | .catch(error => console.log('error', error)); 89 | }; 90 | 91 | // const initialFetch = () => { 92 | // var myHeaders = new Headers(); 93 | // // myHeaders.append("Cookie", "__cfduid=d5b06b2df9eda0d63076d8ba50f5642121601326806; iflipsess=dv537v2andm5mkrns95s4se369; claim_key=bz13Qn7QBuqPCCqtTDLZz2Mierh_HXNx"); 94 | // var formdata = new FormData(); 95 | // formdata.append('template_id', currentMeme.id); 96 | // formdata.append('username', 'memehours'); 97 | // formdata.append('password', 'csny2020'); 98 | // for (let i = 0; i < currentMeme.box_count; i++) { 99 | // formdata.append(`boxes[${i}][text]`, i + 1); 100 | // } 101 | // var requestOptions = { 102 | // method: 'POST', 103 | // headers: myHeaders, 104 | // body: formdata, 105 | // redirect: 'follow', 106 | // }; 107 | 108 | // fetch('https://api.imgflip.com/caption_image', requestOptions) 109 | // .then(res => res.json()) 110 | // .then(res => { 111 | // imgRef.current.src = res.data.url; 112 | // }) 113 | // .catch(error => console.log('error', error)); 114 | // }; 115 | 116 | useEffect(() => { 117 | if (currentMeme) { 118 | mockPreview(); 119 | } 120 | }, [currentMeme]); 121 | 122 | useEffect(() => { 123 | socket.on('randomNumber', num => { 124 | setCurrentMeme(templates[num]); 125 | }); 126 | socket.on('voting', () => { 127 | setAllSubmitted(true); 128 | }); 129 | setWinners([]); 130 | }, []); 131 | 132 | useEffect(() => { 133 | if (allSubmitted || ideationTimesUp) history.push('/voting'); 134 | }, [allSubmitted, ideationTimesUp]); 135 | 136 | const textBoxes = []; 137 | if (currentMeme) { 138 | for (let i = 0; i < currentMeme.box_count; i++) { 139 | textBoxes.push( 140 | , 148 | ); 149 | } 150 | } 151 | return ( 152 |
153 |

HELLO MEME HOURS!!!

154 |

Round {round}

155 | 156 | {currentMeme && ( 157 |
158 | {currentMeme.name} 159 |
160 | 161 |
162 |
163 | )} 164 | {!ideationTimesUp && !submitClicked && ( 165 |
166 | {textBoxes} 167 | 168 | 169 |
170 | )} 171 | {!ideationTimesUp && submitClicked && ( 172 |
173 |

174 | You have successfully submitted your meme! Please wait for others to submit or the time 175 | to run out to continue! 176 |

177 |
178 | )} 179 |
180 | ); 181 | } 182 | -------------------------------------------------------------------------------- /src/Home.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import { Switch, Route, useHistory } from 'react-router-dom'; 3 | import styled from 'styled-components'; 4 | import Ideation from './Ideation'; 5 | import WaitingRoom from './WaitingRoom'; 6 | import Voting from './Voting'; 7 | import Winner from './Winner'; 8 | import GameOver from './GameOver'; 9 | 10 | const Wrapper = styled.main` 11 | display: flex; 12 | flex-direction: column; 13 | justify-content: center; 14 | align-items: center; 15 | text-align: center; 16 | `; 17 | 18 | const Title = styled.h1` 19 | font-size: 60px; 20 | text-align: center; 21 | color: black; 22 | `; 23 | 24 | const SmallerText = styled.div` 25 | font-size: 30px; 26 | `; 27 | 28 | const FormStyled = styled.form` 29 | display: flex; 30 | flex-direction: column; 31 | align-items: center; 32 | border: dotted 0.5px; 33 | width: 50%; 34 | padding: 10px; 35 | font-size: 50px; 36 | box-shadow: 9px 9px 40px -12px rgba(0, 0, 0, 0.75); 37 | `; 38 | 39 | const StyledInput = styled.input` 40 | padding: 20px; 41 | margin: 10px; 42 | width: 75%; 43 | border: dotted; 44 | outline: none; 45 | font-size: 30px; 46 | `; 47 | 48 | const StyledInputButton = styled.input` 49 | border: solid 1px; 50 | padding: 10px; 51 | margin: 10px; 52 | background-color: inherit; 53 | font-size: 30px; 54 | border-radius: 10px; 55 | 56 | &:hover { 57 | background-color: lightblue; 58 | cursor: pointer; 59 | } 60 | `; 61 | 62 | const Div = styled.div` 63 | display: flex; 64 | flex-direction: column; 65 | align-items: center; 66 | border: dotted 0.5px; 67 | width: 50%; 68 | padding: 10px; 69 | font-size: 50px; 70 | box-shadow: 9px 9px 40px -12px rgba(0, 0, 0, 0.75); 71 | `; 72 | 73 | const Button = styled.button` 74 | border: solid 1px; 75 | padding: 10px; 76 | margin: 10px; 77 | background-color: inherit; 78 | font-size: 30px; 79 | border-radius: 10px; 80 | 81 | &:hover { 82 | background-color: lightblue; 83 | cursor: pointer; 84 | } 85 | `; 86 | 87 | export default function Home({ socket }) { 88 | const [name, setName] = useState(''); 89 | const [newName, setNewName] = useState('start'); 90 | const [players, setPlayers] = useState([]); 91 | const [isClicked, setIsClicked] = useState(false); 92 | const [submitClicked, setSubmitClicked] = useState(false); 93 | const [ideationTimesUp, setIdeationTimesUp] = useState(false); 94 | const [allSubmitted, setAllSubmitted] = useState(false); 95 | const [winners, setWinners] = useState([]); 96 | const [winner, setWinner] = useState({}); 97 | const [self, setSelf] = useState({}); 98 | const [round, setRound] = useState(1); 99 | const [winningMemes, setWinningMemes] = useState([]); 100 | const history = useHistory(); 101 | 102 | // when submitted, player's name is pushed into player's array to be stored in state 103 | const handleSubmit = e => { 104 | e.preventDefault(); 105 | if (!name) { 106 | setNewName(''); 107 | } 108 | if (name) { 109 | setNewName(name); 110 | socket.emit('newPlayer', name); 111 | setIsClicked(true); 112 | } 113 | }; 114 | 115 | // logic for what happens when start game is clicked 116 | const handleClick = e => { 117 | e.preventDefault(); 118 | socket.emit('ideate'); 119 | }; 120 | 121 | useEffect(() => { 122 | socket.on('updatePlayers', newPlayers => { 123 | setPlayers(newPlayers); 124 | }); 125 | socket.on('getSelf', newSelf => { 126 | setSelf(newSelf); 127 | }); 128 | socket.on('ideate', () => { 129 | history.push('/ideation'); 130 | }); 131 | }, [players]); 132 | 133 | return ( 134 | 135 | 136 | 137 | Meme Hours 138 | {!isClicked && ( 139 | 140 |
141 | Name 142 | { 148 | setName(e.target.value); 149 | }} 150 | > 151 |
152 | {/* if correct is false, show this message */} 153 | {!newName &&
Please enter a name
} 154 | {/* if unique is false, show this message */} 155 | {/* {!unique &&
Someone else has that name, please pick a new one!
} */} 156 | 157 |
158 | )} 159 | {/* when you submit, you render the waiting room from all the current players in state */} 160 | {isClicked && ( 161 |
162 | {players.map((player, index) => ( 163 | 164 | ))} 165 | {players.length} player(s) are ready to play! 166 | {self.isHost ? ( 167 | 168 | ) : ( 169 | Waiting for the host to start game... 170 | )} 171 |
172 | )} 173 |
174 |
175 | 176 | 190 | 191 | 192 | 193 | 194 | 195 | 207 | 208 | 209 | 216 | 217 |
218 | ); 219 | } 220 | -------------------------------------------------------------------------------- /src/templates.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | { 3 | id: '181913649', 4 | name: 'Drake Hotline Bling', 5 | url: 'https://i.imgflip.com/30b1gx.jpg', 6 | width: 1200, 7 | height: 1200, 8 | box_count: 2, 9 | }, 10 | { 11 | id: '112126428', 12 | name: 'Distracted Boyfriend', 13 | url: 'https://i.imgflip.com/1ur9b0.jpg', 14 | width: 1200, 15 | height: 800, 16 | box_count: 3, 17 | }, 18 | { 19 | id: '87743020', 20 | name: 'Two Buttons', 21 | url: 'https://i.imgflip.com/1g8my4.jpg', 22 | width: 600, 23 | height: 908, 24 | box_count: 2, 25 | }, 26 | { 27 | id: '129242436', 28 | name: 'Change My Mind', 29 | url: 'https://i.imgflip.com/24y43o.jpg', 30 | width: 482, 31 | height: 361, 32 | box_count: 2, 33 | }, 34 | { 35 | id: '131087935', 36 | name: 'Running Away Balloon', 37 | url: 'https://i.imgflip.com/261o3j.jpg', 38 | width: 761, 39 | height: 1024, 40 | box_count: 5, 41 | }, 42 | { 43 | id: '217743513', 44 | name: 'UNO Draw 25 Cards', 45 | url: 'https://i.imgflip.com/3lmzyx.jpg', 46 | width: 500, 47 | height: 494, 48 | box_count: 2, 49 | }, 50 | { 51 | id: '124822590', 52 | name: 'Left Exit 12 Off Ramp', 53 | url: 'https://i.imgflip.com/22bdq6.jpg', 54 | width: 804, 55 | height: 767, 56 | box_count: 3, 57 | }, 58 | { 59 | id: '102156234', 60 | name: 'Mocking Spongebob', 61 | url: 'https://i.imgflip.com/1otk96.jpg', 62 | width: 502, 63 | height: 353, 64 | box_count: 2, 65 | }, 66 | { 67 | id: '438680', 68 | name: 'Batman Slapping Robin', 69 | url: 'https://i.imgflip.com/9ehk.jpg', 70 | width: 400, 71 | height: 387, 72 | box_count: 2, 73 | }, 74 | { 75 | id: '93895088', 76 | name: 'Expanding Brain', 77 | url: 'https://i.imgflip.com/1jwhww.jpg', 78 | width: 857, 79 | height: 1202, 80 | box_count: 4, 81 | }, 82 | { 83 | id: '188390779', 84 | name: 'Woman Yelling At Cat', 85 | url: 'https://i.imgflip.com/345v97.jpg', 86 | width: 680, 87 | height: 438, 88 | box_count: 2, 89 | }, 90 | { 91 | id: '222403160', 92 | name: 'Bernie I Am Once Again Asking For Your Support', 93 | url: 'https://i.imgflip.com/3oevdk.jpg', 94 | width: 750, 95 | height: 750, 96 | box_count: 2, 97 | }, 98 | { 99 | id: '148909805', 100 | name: 'Monkey Puppet', 101 | url: 'https://i.imgflip.com/2gnnjh.jpg', 102 | width: 923, 103 | height: 768, 104 | box_count: 2, 105 | }, 106 | { 107 | id: '1035805', 108 | name: 'Boardroom Meeting Suggestion', 109 | url: 'https://i.imgflip.com/m78d.jpg', 110 | width: 500, 111 | height: 649, 112 | box_count: 4, 113 | }, 114 | { 115 | id: '119139145', 116 | name: 'Blank Nut Button', 117 | url: 'https://i.imgflip.com/1yxkcp.jpg', 118 | width: 600, 119 | height: 446, 120 | box_count: 2, 121 | }, 122 | { 123 | id: '4087833', 124 | name: 'Waiting Skeleton', 125 | url: 'https://i.imgflip.com/2fm6x.jpg', 126 | width: 298, 127 | height: 403, 128 | box_count: 2, 129 | }, 130 | { 131 | id: '226297822', 132 | name: 'Panik Kalm Panik', 133 | url: 'https://i.imgflip.com/3qqcim.png', 134 | width: 640, 135 | height: 881, 136 | box_count: 3, 137 | }, 138 | { 139 | id: '100777631', 140 | name: 'Is This A Pigeon', 141 | url: 'https://i.imgflip.com/1o00in.jpg', 142 | width: 1587, 143 | height: 1425, 144 | box_count: 3, 145 | }, 146 | { 147 | id: '97984', 148 | name: 'Disaster Girl', 149 | url: 'https://i.imgflip.com/23ls.jpg', 150 | width: 500, 151 | height: 375, 152 | box_count: 2, 153 | }, 154 | { 155 | id: '91538330', 156 | name: 'X, X Everywhere', 157 | url: 'https://i.imgflip.com/1ihzfe.jpg', 158 | width: 2118, 159 | height: 1440, 160 | box_count: 2, 161 | }, 162 | { 163 | id: '135256802', 164 | name: 'Epic Handshake', 165 | url: 'https://i.imgflip.com/28j0te.jpg', 166 | width: 900, 167 | height: 645, 168 | box_count: 3, 169 | }, 170 | { 171 | id: '80707627', 172 | name: 'Sad Pablo Escobar', 173 | url: 'https://i.imgflip.com/1c1uej.jpg', 174 | width: 720, 175 | height: 709, 176 | box_count: 3, 177 | }, 178 | { 179 | id: '178591752', 180 | name: 'Tuxedo Winnie The Pooh', 181 | url: 'https://i.imgflip.com/2ybua0.png', 182 | width: 800, 183 | height: 582, 184 | box_count: 2, 185 | }, 186 | { 187 | id: '114585149', 188 | name: 'Inhaling Seagull', 189 | url: 'https://i.imgflip.com/1w7ygt.jpg', 190 | width: 1269, 191 | height: 2825, 192 | box_count: 4, 193 | }, 194 | { 195 | id: '155067746', 196 | name: 'Surprised Pikachu', 197 | url: 'https://i.imgflip.com/2kbn1e.jpg', 198 | width: 1893, 199 | height: 1893, 200 | box_count: 3, 201 | }, 202 | { 203 | id: '123999232', 204 | name: 'The Scroll Of Truth', 205 | url: 'https://i.imgflip.com/21tqf4.jpg', 206 | width: 1280, 207 | height: 1236, 208 | box_count: 2, 209 | }, 210 | { 211 | id: '89370399', 212 | name: 'Roll Safe Think About It', 213 | url: 'https://i.imgflip.com/1h7in3.jpg', 214 | width: 702, 215 | height: 395, 216 | box_count: 2, 217 | }, 218 | { 219 | id: '27813981', 220 | name: 'Hide the Pain Harold', 221 | url: 'https://i.imgflip.com/gk5el.jpg', 222 | width: 480, 223 | height: 601, 224 | box_count: 2, 225 | }, 226 | { 227 | id: '61579', 228 | name: 'One Does Not Simply', 229 | url: 'https://i.imgflip.com/1bij.jpg', 230 | width: 568, 231 | height: 335, 232 | box_count: 2, 233 | }, 234 | { 235 | id: '134797956', 236 | name: 'American Chopper Argument', 237 | url: 'https://i.imgflip.com/2896ro.jpg', 238 | width: 640, 239 | height: 1800, 240 | box_count: 5, 241 | }, 242 | { 243 | id: '180190441', 244 | name: "They're The Same Picture", 245 | url: 'https://i.imgflip.com/2za3u1.jpg', 246 | width: 1363, 247 | height: 1524, 248 | box_count: 3, 249 | }, 250 | { 251 | id: '135678846', 252 | name: 'Who Killed Hannibal', 253 | url: 'https://i.imgflip.com/28s2gu.jpg', 254 | width: 1280, 255 | height: 1440, 256 | box_count: 3, 257 | }, 258 | { 259 | id: '101470', 260 | name: 'Ancient Aliens', 261 | url: 'https://i.imgflip.com/26am.jpg', 262 | width: 500, 263 | height: 437, 264 | box_count: 2, 265 | }, 266 | { 267 | id: '175540452', 268 | name: 'Unsettled Tom', 269 | url: 'https://i.imgflip.com/2wifvo.jpg', 270 | width: 680, 271 | height: 550, 272 | box_count: 2, 273 | }, 274 | { 275 | id: '91545132', 276 | name: 'Trump Bill Signing', 277 | url: 'https://i.imgflip.com/1ii4oc.jpg', 278 | width: 1866, 279 | height: 1529, 280 | box_count: 2, 281 | }, 282 | { 283 | id: '21735', 284 | name: 'The Rock Driving', 285 | url: 'https://i.imgflip.com/grr.jpg', 286 | width: 568, 287 | height: 700, 288 | box_count: 2, 289 | }, 290 | { 291 | id: '196652226', 292 | name: 'Spongebob Ight Imma Head Out', 293 | url: 'https://i.imgflip.com/392xtu.jpg', 294 | width: 822, 295 | height: 960, 296 | box_count: 2, 297 | }, 298 | { 299 | id: '235589', 300 | name: 'Evil Toddler', 301 | url: 'https://i.imgflip.com/51s5.jpg', 302 | width: 500, 303 | height: 332, 304 | box_count: 2, 305 | }, 306 | { 307 | id: '124055727', 308 | name: "Y'all Got Any More Of That", 309 | url: 'https://i.imgflip.com/21uy0f.jpg', 310 | width: 600, 311 | height: 471, 312 | box_count: 2, 313 | }, 314 | { 315 | id: '6235864', 316 | name: 'Finding Neverland', 317 | url: 'https://i.imgflip.com/3pnmg.jpg', 318 | width: 423, 319 | height: 600, 320 | box_count: 3, 321 | }, 322 | { 323 | id: '61520', 324 | name: 'Futurama Fry', 325 | url: 'https://i.imgflip.com/1bgw.jpg', 326 | width: 552, 327 | height: 414, 328 | box_count: 2, 329 | }, 330 | { 331 | id: '28251713', 332 | name: 'Oprah You Get A', 333 | url: 'https://i.imgflip.com/gtj5t.jpg', 334 | width: 620, 335 | height: 465, 336 | box_count: 2, 337 | }, 338 | { 339 | id: '161865971', 340 | name: 'Marked Safe From', 341 | url: 'https://i.imgflip.com/2odckz.jpg', 342 | width: 618, 343 | height: 499, 344 | box_count: 2, 345 | }, 346 | { 347 | id: '3218037', 348 | name: "This Is Where I'd Put My Trophy If I Had One", 349 | url: 'https://i.imgflip.com/1wz1x.jpg', 350 | width: 300, 351 | height: 418, 352 | box_count: 2, 353 | }, 354 | { 355 | id: '132769734', 356 | name: 'Hard To Swallow Pills', 357 | url: 'https://i.imgflip.com/271ps6.jpg', 358 | width: 680, 359 | height: 979, 360 | box_count: 2, 361 | }, 362 | { 363 | id: '101288', 364 | name: 'Third World Skeptical Kid', 365 | url: 'https://i.imgflip.com/265k.jpg', 366 | width: 426, 367 | height: 426, 368 | box_count: 2, 369 | }, 370 | { 371 | id: '101287', 372 | name: 'Third World Success Kid', 373 | url: 'https://i.imgflip.com/265j.jpg', 374 | width: 500, 375 | height: 500, 376 | box_count: 2, 377 | }, 378 | { 379 | id: '61556', 380 | name: 'Grandma Finds The Internet', 381 | url: 'https://i.imgflip.com/1bhw.jpg', 382 | width: 640, 383 | height: 480, 384 | box_count: 2, 385 | }, 386 | { 387 | id: '8072285', 388 | name: 'Doge', 389 | url: 'https://i.imgflip.com/4t0m5.jpg', 390 | width: 620, 391 | height: 620, 392 | box_count: 5, 393 | }, 394 | { 395 | id: '14371066', 396 | name: 'Star Wars Yoda', 397 | url: 'https://i.imgflip.com/8k0sa.jpg', 398 | width: 620, 399 | height: 714, 400 | box_count: 2, 401 | }, 402 | { 403 | id: '55311130', 404 | name: 'This Is Fine', 405 | url: 'https://i.imgflip.com/wxica.jpg', 406 | width: 580, 407 | height: 282, 408 | box_count: 2, 409 | }, 410 | { 411 | id: '84341851', 412 | name: 'Evil Kermit', 413 | url: 'https://i.imgflip.com/1e7ql7.jpg', 414 | width: 700, 415 | height: 325, 416 | box_count: 2, 417 | }, 418 | { 419 | id: '101511', 420 | name: "Don't You Squidward", 421 | url: 'https://i.imgflip.com/26br.jpg', 422 | width: 500, 423 | height: 333, 424 | box_count: 2, 425 | }, 426 | { 427 | id: '61532', 428 | name: 'The Most Interesting Man In The World', 429 | url: 'https://i.imgflip.com/1bh8.jpg', 430 | width: 550, 431 | height: 690, 432 | box_count: 2, 433 | }, 434 | { 435 | id: '5496396', 436 | name: 'Leonardo Dicaprio Cheers', 437 | url: 'https://i.imgflip.com/39t1o.jpg', 438 | width: 600, 439 | height: 400, 440 | box_count: 2, 441 | }, 442 | { 443 | id: '16464531', 444 | name: "But That's None Of My Business", 445 | url: 'https://i.imgflip.com/9sw43.jpg', 446 | width: 600, 447 | height: 600, 448 | box_count: 2, 449 | }, 450 | { 451 | id: '61539', 452 | name: 'First World Problems', 453 | url: 'https://i.imgflip.com/1bhf.jpg', 454 | width: 552, 455 | height: 367, 456 | box_count: 2, 457 | }, 458 | { 459 | id: '61527', 460 | name: 'Y U No', 461 | url: 'https://i.imgflip.com/1bh3.jpg', 462 | width: 500, 463 | height: 500, 464 | box_count: 2, 465 | }, 466 | { 467 | id: '922147', 468 | name: 'Laughing Men In Suits', 469 | url: 'https://i.imgflip.com/jrj7.jpg', 470 | width: 500, 471 | height: 333, 472 | box_count: 2, 473 | }, 474 | { 475 | id: '563423', 476 | name: 'That Would Be Great', 477 | url: 'https://i.imgflip.com/c2qn.jpg', 478 | width: 526, 479 | height: 440, 480 | box_count: 2, 481 | }, 482 | { 483 | id: '460541', 484 | name: 'Jack Sparrow Being Chased', 485 | url: 'https://i.imgflip.com/9vct.jpg', 486 | width: 500, 487 | height: 375, 488 | box_count: 2, 489 | }, 490 | { 491 | id: '99683372', 492 | name: 'Sleeping Shaq', 493 | url: 'https://i.imgflip.com/1nck6k.jpg', 494 | width: 640, 495 | height: 631, 496 | box_count: 2, 497 | }, 498 | { 499 | id: '101910402', 500 | name: 'Who Would Win?', 501 | url: 'https://i.imgflip.com/1ooaki.jpg', 502 | width: 802, 503 | height: 500, 504 | box_count: 2, 505 | }, 506 | { 507 | id: '4173692', 508 | name: 'Scared Cat', 509 | url: 'https://i.imgflip.com/2hgfw.jpg', 510 | width: 620, 511 | height: 464, 512 | box_count: 2, 513 | }, 514 | { 515 | id: '61546', 516 | name: 'Brace Yourselves X is Coming', 517 | url: 'https://i.imgflip.com/1bhm.jpg', 518 | width: 622, 519 | height: 477, 520 | box_count: 2, 521 | }, 522 | { 523 | id: '6531067', 524 | name: 'See Nobody Cares', 525 | url: 'https://i.imgflip.com/3vzej.jpg', 526 | width: 620, 527 | height: 676, 528 | box_count: 2, 529 | }, 530 | { 531 | id: '163573', 532 | name: 'Imagination Spongebob', 533 | url: 'https://i.imgflip.com/3i7p.jpg', 534 | width: 500, 535 | height: 366, 536 | box_count: 2, 537 | }, 538 | { 539 | id: '285870', 540 | name: 'Squidward', 541 | url: 'https://i.imgflip.com/64ku.jpg', 542 | width: 500, 543 | height: 750, 544 | box_count: 2, 545 | }, 546 | { 547 | id: '61582', 548 | name: 'Creepy Condescending Wonka', 549 | url: 'https://i.imgflip.com/1bim.jpg', 550 | width: 550, 551 | height: 545, 552 | box_count: 2, 553 | }, 554 | { 555 | id: '157978092', 556 | name: 'Presidential Alert', 557 | url: 'https://i.imgflip.com/2m20oc.jpg', 558 | width: 920, 559 | height: 534, 560 | box_count: 2, 561 | }, 562 | { 563 | id: '61585', 564 | name: 'Bad Luck Brian', 565 | url: 'https://i.imgflip.com/1bip.jpg', 566 | width: 475, 567 | height: 562, 568 | box_count: 2, 569 | }, 570 | { 571 | id: '109765', 572 | name: "I'll Just Wait Here", 573 | url: 'https://i.imgflip.com/2cp1.jpg', 574 | width: 491, 575 | height: 550, 576 | box_count: 2, 577 | }, 578 | { 579 | id: '53764', 580 | name: 'Peter Parker Cry', 581 | url: 'https://i.imgflip.com/15hg.jpg', 582 | width: 400, 583 | height: 992, 584 | box_count: 4, 585 | }, 586 | { 587 | id: '170715647', 588 | name: 'Well Yes, But Actually No', 589 | url: 'https://i.imgflip.com/2tn11b.jpg', 590 | width: 1600, 591 | height: 1218, 592 | box_count: 2, 593 | }, 594 | { 595 | id: '14230520', 596 | name: 'Black Girl Wat', 597 | url: 'https://i.imgflip.com/8h0c8.jpg', 598 | width: 599, 599 | height: 626, 600 | box_count: 2, 601 | }, 602 | { 603 | id: '61544', 604 | name: 'Success Kid', 605 | url: 'https://i.imgflip.com/1bhk.jpg', 606 | width: 500, 607 | height: 500, 608 | box_count: 2, 609 | }, 610 | { 611 | id: '40945639', 612 | name: 'Dr Evil Laser', 613 | url: 'https://i.imgflip.com/odluv.jpg', 614 | width: 500, 615 | height: 405, 616 | box_count: 2, 617 | }, 618 | { 619 | id: '61533', 620 | name: 'X All The Y', 621 | url: 'https://i.imgflip.com/1bh9.jpg', 622 | width: 500, 623 | height: 355, 624 | box_count: 2, 625 | }, 626 | { 627 | id: '28034788', 628 | name: 'Marvel Civil War 1', 629 | url: 'https://i.imgflip.com/govs4.jpg', 630 | width: 423, 631 | height: 734, 632 | box_count: 2, 633 | }, 634 | { 635 | id: '405658', 636 | name: 'Grumpy Cat', 637 | url: 'https://i.imgflip.com/8p0a.jpg', 638 | width: 500, 639 | height: 617, 640 | box_count: 2, 641 | }, 642 | { 643 | id: '29617627', 644 | name: 'Look At Me', 645 | url: 'https://i.imgflip.com/hmt3v.jpg', 646 | width: 300, 647 | height: 300, 648 | box_count: 2, 649 | }, 650 | { 651 | id: '27920', 652 | name: 'Surprised Koala', 653 | url: 'https://i.imgflip.com/ljk.jpg', 654 | width: 500, 655 | height: 667, 656 | box_count: 2, 657 | }, 658 | { 659 | id: '1509839', 660 | name: 'Captain Picard Facepalm', 661 | url: 'https://i.imgflip.com/wczz.jpg', 662 | width: 500, 663 | height: 324, 664 | box_count: 2, 665 | }, 666 | { 667 | id: '71428573', 668 | name: 'Say it Again, Dexter', 669 | url: 'https://i.imgflip.com/16iyn1.jpg', 670 | width: 698, 671 | height: 900, 672 | box_count: 2, 673 | }, 674 | { 675 | id: '21604248', 676 | name: 'Mugatu So Hot Right Now', 677 | url: 'https://i.imgflip.com/cv1y0.jpg', 678 | width: 620, 679 | height: 497, 680 | box_count: 2, 681 | }, 682 | { 683 | id: '176908', 684 | name: 'Shut Up And Take My Money Fry', 685 | url: 'https://i.imgflip.com/3si4.jpg', 686 | width: 500, 687 | height: 281, 688 | box_count: 2, 689 | }, 690 | { 691 | id: '444501', 692 | name: 'Maury Lie Detector', 693 | url: 'https://i.imgflip.com/9iz9.jpg', 694 | width: 381, 695 | height: 378, 696 | box_count: 2, 697 | }, 698 | { 699 | id: '89655', 700 | name: 'Uncle Sam', 701 | url: 'https://i.imgflip.com/1x6f.jpg', 702 | width: 620, 703 | height: 833, 704 | box_count: 2, 705 | }, 706 | { 707 | id: '184801100', 708 | name: 'Me And The Boys', 709 | url: 'https://i.imgflip.com/320xfw.jpg', 710 | width: 720, 711 | height: 476, 712 | box_count: 2, 713 | }, 714 | { 715 | id: '61580', 716 | name: 'Too Damn High', 717 | url: 'https://i.imgflip.com/1bik.jpg', 718 | width: 420, 719 | height: 316, 720 | box_count: 2, 721 | }, 722 | { 723 | id: '1367068', 724 | name: 'I Should Buy A Boat Cat', 725 | url: 'https://i.imgflip.com/tau4.jpg', 726 | width: 500, 727 | height: 368, 728 | box_count: 2, 729 | }, 730 | { 731 | id: '61581', 732 | name: 'Put It Somewhere Else Patrick', 733 | url: 'https://i.imgflip.com/1bil.jpg', 734 | width: 343, 735 | height: 604, 736 | box_count: 2, 737 | }, 738 | { 739 | id: '195389', 740 | name: 'Sparta Leonidas', 741 | url: 'https://i.imgflip.com/46rh.jpg', 742 | width: 500, 743 | height: 264, 744 | box_count: 2, 745 | }, 746 | { 747 | id: '101716', 748 | name: 'Yo Dawg Heard You', 749 | url: 'https://i.imgflip.com/26hg.jpg', 750 | width: 500, 751 | height: 323, 752 | box_count: 2, 753 | }, 754 | { 755 | id: '142921050', 756 | name: 'Car Salesman Slaps Roof Of Car', 757 | url: 'https://i.imgflip.com/2d3al6.jpg', 758 | width: 800, 759 | height: 450, 760 | box_count: 2, 761 | }, 762 | { 763 | id: '1202623', 764 | name: 'Keep Calm And Carry On Red', 765 | url: 'https://i.imgflip.com/pry7.jpg', 766 | width: 500, 767 | height: 704, 768 | box_count: 2, 769 | }, 770 | { 771 | id: '259680', 772 | name: 'Am I The Only One Around Here', 773 | url: 'https://i.imgflip.com/5kdc.jpg', 774 | width: 500, 775 | height: 348, 776 | box_count: 2, 777 | }, 778 | { 779 | id: '100947', 780 | name: 'Matrix Morpheus', 781 | url: 'https://i.imgflip.com/25w3.jpg', 782 | width: 500, 783 | height: 303, 784 | box_count: 2, 785 | }, 786 | { 787 | id: '61516', 788 | name: 'Philosoraptor', 789 | url: 'https://i.imgflip.com/1bgs.jpg', 790 | width: 500, 791 | height: 500, 792 | box_count: 2, 793 | }, 794 | { 795 | id: '9440985', 796 | name: 'Face You Make Robert Downey Jr', 797 | url: 'https://i.imgflip.com/5mcpl.jpg', 798 | width: 460, 799 | height: 523, 800 | box_count: 2, 801 | }, 802 | ]; 803 | --------------------------------------------------------------------------------