├── .flaskenv ├── dev-requirements.txt ├── migrations ├── README ├── script.py.mako ├── alembic.ini ├── versions │ ├── 0dc4227077a4_.py │ └── 9fcfa773ef84_.py └── env.py ├── react-app ├── .env.example ├── public │ ├── favicon.ico │ └── index.html ├── src │ ├── assets │ │ ├── redketchup.zip │ │ ├── git_frames_bmp.zip │ │ ├── ezgif.com-gif-maker.gif │ │ ├── git_frames_bmp (2).zip │ │ ├── dancing_stickman_test.gif │ │ ├── gif_frames_png │ │ │ ├── IMG00000.png │ │ │ ├── IMG00001.png │ │ │ ├── IMG00002.png │ │ │ ├── IMG00003.png │ │ │ ├── IMG00004.png │ │ │ ├── IMG00005.png │ │ │ ├── IMG00006.png │ │ │ ├── IMG00007.png │ │ │ ├── IMG00008.png │ │ │ ├── IMG00009.png │ │ │ ├── IMG00010.png │ │ │ ├── IMG00011.png │ │ │ ├── IMG00012.png │ │ │ ├── IMG00013.png │ │ │ ├── IMG00014.png │ │ │ ├── IMG00015.png │ │ │ ├── IMG00016.png │ │ │ ├── IMG00017.png │ │ │ ├── IMG00018.png │ │ │ ├── IMG00019.png │ │ │ ├── IMG00020.png │ │ │ ├── IMG00021.png │ │ │ ├── IMG00022.png │ │ │ ├── IMG00023.png │ │ │ ├── IMG00024.png │ │ │ ├── IMG00025.png │ │ │ ├── IMG00026.png │ │ │ ├── IMG00027.png │ │ │ ├── IMG00028.png │ │ │ ├── IMG00029.png │ │ │ ├── IMG00030.png │ │ │ ├── IMG00031.png │ │ │ ├── IMG00032.png │ │ │ ├── IMG00033.png │ │ │ ├── IMG00034.png │ │ │ ├── IMG00035.png │ │ │ ├── IMG00036.png │ │ │ ├── IMG00037.png │ │ │ ├── IMG00038.png │ │ │ ├── IMG00039.png │ │ │ ├── IMG00040.png │ │ │ ├── IMG00041.png │ │ │ ├── IMG00042.png │ │ │ ├── IMG00043.png │ │ │ ├── IMG00044.png │ │ │ ├── IMG00045.png │ │ │ ├── IMG00046.png │ │ │ ├── IMG00047.png │ │ │ ├── IMG00048.png │ │ │ ├── IMG00049.png │ │ │ ├── IMG00050.png │ │ │ ├── IMG00051.png │ │ │ ├── IMG00052.png │ │ │ ├── IMG00053.png │ │ │ ├── IMG00054.png │ │ │ ├── IMG00055.png │ │ │ ├── IMG00056.png │ │ │ ├── IMG00057.png │ │ │ ├── IMG00058.png │ │ │ ├── IMG00059.png │ │ │ ├── IMG00060.png │ │ │ └── IMG00061.png │ │ ├── git_frames_bmp │ │ │ ├── IMG00000.bmp │ │ │ ├── IMG00001.bmp │ │ │ ├── IMG00002.bmp │ │ │ ├── IMG00003.bmp │ │ │ ├── IMG00004.bmp │ │ │ ├── IMG00005.bmp │ │ │ ├── IMG00006.bmp │ │ │ ├── IMG00007.bmp │ │ │ ├── IMG00008.bmp │ │ │ ├── IMG00009.bmp │ │ │ ├── IMG00010.bmp │ │ │ ├── IMG00011.bmp │ │ │ ├── IMG00012.bmp │ │ │ ├── IMG00013.bmp │ │ │ ├── IMG00014.bmp │ │ │ ├── IMG00015.bmp │ │ │ ├── IMG00016.bmp │ │ │ ├── IMG00017.bmp │ │ │ ├── IMG00018.bmp │ │ │ ├── IMG00019.bmp │ │ │ ├── IMG00020.bmp │ │ │ ├── IMG00021.bmp │ │ │ ├── IMG00022.bmp │ │ │ ├── IMG00023.bmp │ │ │ ├── IMG00024.bmp │ │ │ ├── IMG00025.bmp │ │ │ ├── IMG00026.bmp │ │ │ ├── IMG00027.bmp │ │ │ ├── IMG00028.bmp │ │ │ ├── IMG00029.bmp │ │ │ ├── IMG00030.bmp │ │ │ ├── IMG00031.bmp │ │ │ ├── IMG00032.bmp │ │ │ ├── IMG00033.bmp │ │ │ ├── IMG00034.bmp │ │ │ ├── IMG00035.bmp │ │ │ ├── IMG00036.bmp │ │ │ ├── IMG00037.bmp │ │ │ ├── IMG00038.bmp │ │ │ ├── IMG00039.bmp │ │ │ ├── IMG00040.bmp │ │ │ ├── IMG00041.bmp │ │ │ ├── IMG00042.bmp │ │ │ ├── IMG00043.bmp │ │ │ ├── IMG00044.bmp │ │ │ ├── IMG00045.bmp │ │ │ ├── IMG00046.bmp │ │ │ ├── IMG00047.bmp │ │ │ ├── IMG00048.bmp │ │ │ ├── IMG00049.bmp │ │ │ ├── IMG00050.bmp │ │ │ ├── IMG00051.bmp │ │ │ ├── IMG00052.bmp │ │ │ ├── IMG00053.bmp │ │ │ ├── IMG00054.bmp │ │ │ ├── IMG00055.bmp │ │ │ ├── IMG00056.bmp │ │ │ ├── IMG00057.bmp │ │ │ ├── IMG00058.bmp │ │ │ ├── IMG00059.bmp │ │ │ ├── IMG00060.bmp │ │ │ ├── IMG00061.bmp │ │ │ └── frameslist.gsf │ │ └── frames.js │ ├── components │ │ ├── Footer │ │ │ └── index.js │ │ ├── Header │ │ │ └── index.js │ │ ├── auth │ │ │ ├── LogoutButton.js │ │ │ ├── ProtectedRoute.js │ │ │ ├── LoginForm.js │ │ │ └── SignUpForm.js │ │ ├── NavBanner │ │ │ └── index.js │ │ ├── SubComments │ │ │ └── index.js │ │ ├── UsersList.js │ │ ├── SplashPage │ │ │ └── index.js │ │ ├── User.js │ │ ├── NavBar.js │ │ ├── Comments │ │ │ └── index.js │ │ ├── Posts │ │ │ └── index.js │ │ └── Gif │ │ │ └── index.js │ ├── seed │ │ ├── seed.py │ │ └── gif_1.json │ ├── index.js │ ├── store │ │ ├── index.js │ │ ├── state_shape.txt │ │ ├── gif.js │ │ ├── session.js │ │ ├── comments.js │ │ └── posts.js │ ├── App.js │ ├── logo.svg │ └── index.css ├── .gitignore ├── README.md └── package.json ├── .gitignore ├── flask_backend ├── models │ ├── db.py │ ├── __init__.py │ ├── posts.py │ ├── subcomments.py │ ├── comments.py │ └── user.py ├── forms │ ├── __init__.py │ ├── signup_form.py │ └── login_form.py ├── api │ ├── __init__.py │ ├── gif.py │ ├── user_routes.py │ ├── auth_routes.py │ └── posts.py ├── seeds │ ├── __init__.py │ └── users.py ├── config.py └── __init__.py ├── .dockerignore ├── .env.example ├── Dockerfile ├── requirements.txt ├── Pipfile ├── .devcontainer ├── Dockerfile └── devcontainer.json ├── .github └── workflows │ └── main.yml ├── README.md └── Pipfile.lock /.flaskenv: -------------------------------------------------------------------------------- 1 | FLASK_APP=flask_backend 2 | -------------------------------------------------------------------------------- /dev-requirements.txt: -------------------------------------------------------------------------------- 1 | psycopg2-binary==2.8.6 2 | -------------------------------------------------------------------------------- /migrations/README: -------------------------------------------------------------------------------- 1 | Generic single-database configuration. -------------------------------------------------------------------------------- /react-app/.env.example: -------------------------------------------------------------------------------- 1 | REACT_APP_BASE_URL=http://localhost:5000 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | __pycache__/ 3 | *.py[cod] 4 | .venv 5 | .DS_Store 6 | .vscode/ 7 | -------------------------------------------------------------------------------- /flask_backend/models/db.py: -------------------------------------------------------------------------------- 1 | from flask_sqlalchemy import SQLAlchemy 2 | 3 | db = SQLAlchemy() 4 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | react-app/node_modules 2 | .venv 3 | Pipfile 4 | Pipfile.lock 5 | .env 6 | .DS_Store 7 | -------------------------------------------------------------------------------- /flask_backend/forms/__init__.py: -------------------------------------------------------------------------------- 1 | from .login_form import LoginForm 2 | from .signup_form import SignUpForm 3 | -------------------------------------------------------------------------------- /react-app/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/public/favicon.ico -------------------------------------------------------------------------------- /react-app/src/assets/redketchup.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/redketchup.zip -------------------------------------------------------------------------------- /react-app/src/assets/git_frames_bmp.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/git_frames_bmp.zip -------------------------------------------------------------------------------- /react-app/src/assets/ezgif.com-gif-maker.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/ezgif.com-gif-maker.gif -------------------------------------------------------------------------------- /react-app/src/assets/git_frames_bmp (2).zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/git_frames_bmp (2).zip -------------------------------------------------------------------------------- /react-app/src/assets/dancing_stickman_test.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/dancing_stickman_test.gif -------------------------------------------------------------------------------- /react-app/src/assets/gif_frames_png/IMG00000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/gif_frames_png/IMG00000.png -------------------------------------------------------------------------------- /react-app/src/assets/gif_frames_png/IMG00001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/gif_frames_png/IMG00001.png -------------------------------------------------------------------------------- /react-app/src/assets/gif_frames_png/IMG00002.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/gif_frames_png/IMG00002.png -------------------------------------------------------------------------------- /react-app/src/assets/gif_frames_png/IMG00003.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/gif_frames_png/IMG00003.png -------------------------------------------------------------------------------- /react-app/src/assets/gif_frames_png/IMG00004.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/gif_frames_png/IMG00004.png -------------------------------------------------------------------------------- /react-app/src/assets/gif_frames_png/IMG00005.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/gif_frames_png/IMG00005.png -------------------------------------------------------------------------------- /react-app/src/assets/gif_frames_png/IMG00006.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/gif_frames_png/IMG00006.png -------------------------------------------------------------------------------- /react-app/src/assets/gif_frames_png/IMG00007.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/gif_frames_png/IMG00007.png -------------------------------------------------------------------------------- /react-app/src/assets/gif_frames_png/IMG00008.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/gif_frames_png/IMG00008.png -------------------------------------------------------------------------------- /react-app/src/assets/gif_frames_png/IMG00009.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/gif_frames_png/IMG00009.png -------------------------------------------------------------------------------- /react-app/src/assets/gif_frames_png/IMG00010.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/gif_frames_png/IMG00010.png -------------------------------------------------------------------------------- /react-app/src/assets/gif_frames_png/IMG00011.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/gif_frames_png/IMG00011.png -------------------------------------------------------------------------------- /react-app/src/assets/gif_frames_png/IMG00012.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/gif_frames_png/IMG00012.png -------------------------------------------------------------------------------- /react-app/src/assets/gif_frames_png/IMG00013.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/gif_frames_png/IMG00013.png -------------------------------------------------------------------------------- /react-app/src/assets/gif_frames_png/IMG00014.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/gif_frames_png/IMG00014.png -------------------------------------------------------------------------------- /react-app/src/assets/gif_frames_png/IMG00015.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/gif_frames_png/IMG00015.png -------------------------------------------------------------------------------- /react-app/src/assets/gif_frames_png/IMG00016.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/gif_frames_png/IMG00016.png -------------------------------------------------------------------------------- /react-app/src/assets/gif_frames_png/IMG00017.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/gif_frames_png/IMG00017.png -------------------------------------------------------------------------------- /react-app/src/assets/gif_frames_png/IMG00018.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/gif_frames_png/IMG00018.png -------------------------------------------------------------------------------- /react-app/src/assets/gif_frames_png/IMG00019.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/gif_frames_png/IMG00019.png -------------------------------------------------------------------------------- /react-app/src/assets/gif_frames_png/IMG00020.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/gif_frames_png/IMG00020.png -------------------------------------------------------------------------------- /react-app/src/assets/gif_frames_png/IMG00021.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/gif_frames_png/IMG00021.png -------------------------------------------------------------------------------- /react-app/src/assets/gif_frames_png/IMG00022.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/gif_frames_png/IMG00022.png -------------------------------------------------------------------------------- /react-app/src/assets/gif_frames_png/IMG00023.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/gif_frames_png/IMG00023.png -------------------------------------------------------------------------------- /react-app/src/assets/gif_frames_png/IMG00024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/gif_frames_png/IMG00024.png -------------------------------------------------------------------------------- /react-app/src/assets/gif_frames_png/IMG00025.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/gif_frames_png/IMG00025.png -------------------------------------------------------------------------------- /react-app/src/assets/gif_frames_png/IMG00026.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/gif_frames_png/IMG00026.png -------------------------------------------------------------------------------- /react-app/src/assets/gif_frames_png/IMG00027.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/gif_frames_png/IMG00027.png -------------------------------------------------------------------------------- /react-app/src/assets/gif_frames_png/IMG00028.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/gif_frames_png/IMG00028.png -------------------------------------------------------------------------------- /react-app/src/assets/gif_frames_png/IMG00029.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/gif_frames_png/IMG00029.png -------------------------------------------------------------------------------- /react-app/src/assets/gif_frames_png/IMG00030.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/gif_frames_png/IMG00030.png -------------------------------------------------------------------------------- /react-app/src/assets/gif_frames_png/IMG00031.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/gif_frames_png/IMG00031.png -------------------------------------------------------------------------------- /react-app/src/assets/gif_frames_png/IMG00032.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/gif_frames_png/IMG00032.png -------------------------------------------------------------------------------- /react-app/src/assets/gif_frames_png/IMG00033.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/gif_frames_png/IMG00033.png -------------------------------------------------------------------------------- /react-app/src/assets/gif_frames_png/IMG00034.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/gif_frames_png/IMG00034.png -------------------------------------------------------------------------------- /react-app/src/assets/gif_frames_png/IMG00035.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/gif_frames_png/IMG00035.png -------------------------------------------------------------------------------- /react-app/src/assets/gif_frames_png/IMG00036.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/gif_frames_png/IMG00036.png -------------------------------------------------------------------------------- /react-app/src/assets/gif_frames_png/IMG00037.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/gif_frames_png/IMG00037.png -------------------------------------------------------------------------------- /react-app/src/assets/gif_frames_png/IMG00038.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/gif_frames_png/IMG00038.png -------------------------------------------------------------------------------- /react-app/src/assets/gif_frames_png/IMG00039.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/gif_frames_png/IMG00039.png -------------------------------------------------------------------------------- /react-app/src/assets/gif_frames_png/IMG00040.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/gif_frames_png/IMG00040.png -------------------------------------------------------------------------------- /react-app/src/assets/gif_frames_png/IMG00041.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/gif_frames_png/IMG00041.png -------------------------------------------------------------------------------- /react-app/src/assets/gif_frames_png/IMG00042.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/gif_frames_png/IMG00042.png -------------------------------------------------------------------------------- /react-app/src/assets/gif_frames_png/IMG00043.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/gif_frames_png/IMG00043.png -------------------------------------------------------------------------------- /react-app/src/assets/gif_frames_png/IMG00044.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/gif_frames_png/IMG00044.png -------------------------------------------------------------------------------- /react-app/src/assets/gif_frames_png/IMG00045.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/gif_frames_png/IMG00045.png -------------------------------------------------------------------------------- /react-app/src/assets/gif_frames_png/IMG00046.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/gif_frames_png/IMG00046.png -------------------------------------------------------------------------------- /react-app/src/assets/gif_frames_png/IMG00047.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/gif_frames_png/IMG00047.png -------------------------------------------------------------------------------- /react-app/src/assets/gif_frames_png/IMG00048.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/gif_frames_png/IMG00048.png -------------------------------------------------------------------------------- /react-app/src/assets/gif_frames_png/IMG00049.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/gif_frames_png/IMG00049.png -------------------------------------------------------------------------------- /react-app/src/assets/gif_frames_png/IMG00050.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/gif_frames_png/IMG00050.png -------------------------------------------------------------------------------- /react-app/src/assets/gif_frames_png/IMG00051.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/gif_frames_png/IMG00051.png -------------------------------------------------------------------------------- /react-app/src/assets/gif_frames_png/IMG00052.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/gif_frames_png/IMG00052.png -------------------------------------------------------------------------------- /react-app/src/assets/gif_frames_png/IMG00053.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/gif_frames_png/IMG00053.png -------------------------------------------------------------------------------- /react-app/src/assets/gif_frames_png/IMG00054.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/gif_frames_png/IMG00054.png -------------------------------------------------------------------------------- /react-app/src/assets/gif_frames_png/IMG00055.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/gif_frames_png/IMG00055.png -------------------------------------------------------------------------------- /react-app/src/assets/gif_frames_png/IMG00056.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/gif_frames_png/IMG00056.png -------------------------------------------------------------------------------- /react-app/src/assets/gif_frames_png/IMG00057.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/gif_frames_png/IMG00057.png -------------------------------------------------------------------------------- /react-app/src/assets/gif_frames_png/IMG00058.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/gif_frames_png/IMG00058.png -------------------------------------------------------------------------------- /react-app/src/assets/gif_frames_png/IMG00059.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/gif_frames_png/IMG00059.png -------------------------------------------------------------------------------- /react-app/src/assets/gif_frames_png/IMG00060.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/gif_frames_png/IMG00060.png -------------------------------------------------------------------------------- /react-app/src/assets/gif_frames_png/IMG00061.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/gif_frames_png/IMG00061.png -------------------------------------------------------------------------------- /react-app/src/assets/git_frames_bmp/IMG00000.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/git_frames_bmp/IMG00000.bmp -------------------------------------------------------------------------------- /react-app/src/assets/git_frames_bmp/IMG00001.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/git_frames_bmp/IMG00001.bmp -------------------------------------------------------------------------------- /react-app/src/assets/git_frames_bmp/IMG00002.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/git_frames_bmp/IMG00002.bmp -------------------------------------------------------------------------------- /react-app/src/assets/git_frames_bmp/IMG00003.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/git_frames_bmp/IMG00003.bmp -------------------------------------------------------------------------------- /react-app/src/assets/git_frames_bmp/IMG00004.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/git_frames_bmp/IMG00004.bmp -------------------------------------------------------------------------------- /react-app/src/assets/git_frames_bmp/IMG00005.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/git_frames_bmp/IMG00005.bmp -------------------------------------------------------------------------------- /react-app/src/assets/git_frames_bmp/IMG00006.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/git_frames_bmp/IMG00006.bmp -------------------------------------------------------------------------------- /react-app/src/assets/git_frames_bmp/IMG00007.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/git_frames_bmp/IMG00007.bmp -------------------------------------------------------------------------------- /react-app/src/assets/git_frames_bmp/IMG00008.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/git_frames_bmp/IMG00008.bmp -------------------------------------------------------------------------------- /react-app/src/assets/git_frames_bmp/IMG00009.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/git_frames_bmp/IMG00009.bmp -------------------------------------------------------------------------------- /react-app/src/assets/git_frames_bmp/IMG00010.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/git_frames_bmp/IMG00010.bmp -------------------------------------------------------------------------------- /react-app/src/assets/git_frames_bmp/IMG00011.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/git_frames_bmp/IMG00011.bmp -------------------------------------------------------------------------------- /react-app/src/assets/git_frames_bmp/IMG00012.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/git_frames_bmp/IMG00012.bmp -------------------------------------------------------------------------------- /react-app/src/assets/git_frames_bmp/IMG00013.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/git_frames_bmp/IMG00013.bmp -------------------------------------------------------------------------------- /react-app/src/assets/git_frames_bmp/IMG00014.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/git_frames_bmp/IMG00014.bmp -------------------------------------------------------------------------------- /react-app/src/assets/git_frames_bmp/IMG00015.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/git_frames_bmp/IMG00015.bmp -------------------------------------------------------------------------------- /react-app/src/assets/git_frames_bmp/IMG00016.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/git_frames_bmp/IMG00016.bmp -------------------------------------------------------------------------------- /react-app/src/assets/git_frames_bmp/IMG00017.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/git_frames_bmp/IMG00017.bmp -------------------------------------------------------------------------------- /react-app/src/assets/git_frames_bmp/IMG00018.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/git_frames_bmp/IMG00018.bmp -------------------------------------------------------------------------------- /react-app/src/assets/git_frames_bmp/IMG00019.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/git_frames_bmp/IMG00019.bmp -------------------------------------------------------------------------------- /react-app/src/assets/git_frames_bmp/IMG00020.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/git_frames_bmp/IMG00020.bmp -------------------------------------------------------------------------------- /react-app/src/assets/git_frames_bmp/IMG00021.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/git_frames_bmp/IMG00021.bmp -------------------------------------------------------------------------------- /react-app/src/assets/git_frames_bmp/IMG00022.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/git_frames_bmp/IMG00022.bmp -------------------------------------------------------------------------------- /react-app/src/assets/git_frames_bmp/IMG00023.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/git_frames_bmp/IMG00023.bmp -------------------------------------------------------------------------------- /react-app/src/assets/git_frames_bmp/IMG00024.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/git_frames_bmp/IMG00024.bmp -------------------------------------------------------------------------------- /react-app/src/assets/git_frames_bmp/IMG00025.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/git_frames_bmp/IMG00025.bmp -------------------------------------------------------------------------------- /react-app/src/assets/git_frames_bmp/IMG00026.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/git_frames_bmp/IMG00026.bmp -------------------------------------------------------------------------------- /react-app/src/assets/git_frames_bmp/IMG00027.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/git_frames_bmp/IMG00027.bmp -------------------------------------------------------------------------------- /react-app/src/assets/git_frames_bmp/IMG00028.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/git_frames_bmp/IMG00028.bmp -------------------------------------------------------------------------------- /react-app/src/assets/git_frames_bmp/IMG00029.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/git_frames_bmp/IMG00029.bmp -------------------------------------------------------------------------------- /react-app/src/assets/git_frames_bmp/IMG00030.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/git_frames_bmp/IMG00030.bmp -------------------------------------------------------------------------------- /react-app/src/assets/git_frames_bmp/IMG00031.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/git_frames_bmp/IMG00031.bmp -------------------------------------------------------------------------------- /react-app/src/assets/git_frames_bmp/IMG00032.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/git_frames_bmp/IMG00032.bmp -------------------------------------------------------------------------------- /react-app/src/assets/git_frames_bmp/IMG00033.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/git_frames_bmp/IMG00033.bmp -------------------------------------------------------------------------------- /react-app/src/assets/git_frames_bmp/IMG00034.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/git_frames_bmp/IMG00034.bmp -------------------------------------------------------------------------------- /react-app/src/assets/git_frames_bmp/IMG00035.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/git_frames_bmp/IMG00035.bmp -------------------------------------------------------------------------------- /react-app/src/assets/git_frames_bmp/IMG00036.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/git_frames_bmp/IMG00036.bmp -------------------------------------------------------------------------------- /react-app/src/assets/git_frames_bmp/IMG00037.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/git_frames_bmp/IMG00037.bmp -------------------------------------------------------------------------------- /react-app/src/assets/git_frames_bmp/IMG00038.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/git_frames_bmp/IMG00038.bmp -------------------------------------------------------------------------------- /react-app/src/assets/git_frames_bmp/IMG00039.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/git_frames_bmp/IMG00039.bmp -------------------------------------------------------------------------------- /react-app/src/assets/git_frames_bmp/IMG00040.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/git_frames_bmp/IMG00040.bmp -------------------------------------------------------------------------------- /react-app/src/assets/git_frames_bmp/IMG00041.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/git_frames_bmp/IMG00041.bmp -------------------------------------------------------------------------------- /react-app/src/assets/git_frames_bmp/IMG00042.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/git_frames_bmp/IMG00042.bmp -------------------------------------------------------------------------------- /react-app/src/assets/git_frames_bmp/IMG00043.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/git_frames_bmp/IMG00043.bmp -------------------------------------------------------------------------------- /react-app/src/assets/git_frames_bmp/IMG00044.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/git_frames_bmp/IMG00044.bmp -------------------------------------------------------------------------------- /react-app/src/assets/git_frames_bmp/IMG00045.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/git_frames_bmp/IMG00045.bmp -------------------------------------------------------------------------------- /react-app/src/assets/git_frames_bmp/IMG00046.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/git_frames_bmp/IMG00046.bmp -------------------------------------------------------------------------------- /react-app/src/assets/git_frames_bmp/IMG00047.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/git_frames_bmp/IMG00047.bmp -------------------------------------------------------------------------------- /react-app/src/assets/git_frames_bmp/IMG00048.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/git_frames_bmp/IMG00048.bmp -------------------------------------------------------------------------------- /react-app/src/assets/git_frames_bmp/IMG00049.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/git_frames_bmp/IMG00049.bmp -------------------------------------------------------------------------------- /react-app/src/assets/git_frames_bmp/IMG00050.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/git_frames_bmp/IMG00050.bmp -------------------------------------------------------------------------------- /react-app/src/assets/git_frames_bmp/IMG00051.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/git_frames_bmp/IMG00051.bmp -------------------------------------------------------------------------------- /react-app/src/assets/git_frames_bmp/IMG00052.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/git_frames_bmp/IMG00052.bmp -------------------------------------------------------------------------------- /react-app/src/assets/git_frames_bmp/IMG00053.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/git_frames_bmp/IMG00053.bmp -------------------------------------------------------------------------------- /react-app/src/assets/git_frames_bmp/IMG00054.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/git_frames_bmp/IMG00054.bmp -------------------------------------------------------------------------------- /react-app/src/assets/git_frames_bmp/IMG00055.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/git_frames_bmp/IMG00055.bmp -------------------------------------------------------------------------------- /react-app/src/assets/git_frames_bmp/IMG00056.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/git_frames_bmp/IMG00056.bmp -------------------------------------------------------------------------------- /react-app/src/assets/git_frames_bmp/IMG00057.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/git_frames_bmp/IMG00057.bmp -------------------------------------------------------------------------------- /react-app/src/assets/git_frames_bmp/IMG00058.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/git_frames_bmp/IMG00058.bmp -------------------------------------------------------------------------------- /react-app/src/assets/git_frames_bmp/IMG00059.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/git_frames_bmp/IMG00059.bmp -------------------------------------------------------------------------------- /react-app/src/assets/git_frames_bmp/IMG00060.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/git_frames_bmp/IMG00060.bmp -------------------------------------------------------------------------------- /react-app/src/assets/git_frames_bmp/IMG00061.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-state-15/react-redux-python-demo/HEAD/react-app/src/assets/git_frames_bmp/IMG00061.bmp -------------------------------------------------------------------------------- /flask_backend/api/__init__.py: -------------------------------------------------------------------------------- 1 | from .gif import gif_routes 2 | from .posts import posts_routes 3 | from .auth_routes import auth_routes 4 | from .user_routes import user_routes 5 | -------------------------------------------------------------------------------- /flask_backend/models/__init__.py: -------------------------------------------------------------------------------- 1 | from .db import db 2 | from .user import User 3 | from .posts import Post 4 | from .comments import Comment 5 | from .subcomments import SubComment 6 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | FLASK_APP=app 2 | FLASK_ENV=development 3 | SECRET_KEY=lkasjdf09ajsdkfljalsiorj12n3490re9485309irefvn,u90818734902139489230 4 | DATABASE_URL=postgresql://starter_app_dev@localhost/starter_app 5 | -------------------------------------------------------------------------------- /react-app/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Simple React App 6 | 7 | 8 |
9 | 10 | 11 | -------------------------------------------------------------------------------- /react-app/src/components/Footer/index.js: -------------------------------------------------------------------------------- 1 | export default function Footer() { 2 | return ( 3 |
4 |

React/Redux EOD series

5 |

created by Dan Purcell 2022

6 |
7 | ); 8 | } 9 | -------------------------------------------------------------------------------- /react-app/src/seed/seed.py: -------------------------------------------------------------------------------- 1 | 2 | import os 3 | import json 4 | 5 | 6 | cwd = os.getcwd() 7 | file_list = os.listdir(cwd + "/assets/gif_frames_png") 8 | file_list.sort() 9 | 10 | jsonified = json.dumps(file_list) 11 | 12 | new_file = open(cwd + "/seed/gif_1.json", "w") 13 | new_file.write(jsonified) 14 | new_file.close() 15 | -------------------------------------------------------------------------------- /react-app/src/components/Header/index.js: -------------------------------------------------------------------------------- 1 | import logo from "../../logo.svg"; 2 | import NavBar from "../NavBar" 3 | 4 | function Header() { 5 | return ( 6 |
7 | logo 8 |

WELCOME TO THE DYNAMIC REACT PROJECT

9 | 10 |
11 | ); 12 | } 13 | 14 | export default Header; 15 | -------------------------------------------------------------------------------- /react-app/src/components/auth/LogoutButton.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useDispatch } from 'react-redux'; 3 | import { logout } from '../../store/session'; 4 | 5 | const LogoutButton = () => { 6 | const dispatch = useDispatch() 7 | const onLogout = async (e) => { 8 | await dispatch(logout()); 9 | }; 10 | 11 | return ; 12 | }; 13 | 14 | export default LogoutButton; 15 | -------------------------------------------------------------------------------- /flask_backend/api/gif.py: -------------------------------------------------------------------------------- 1 | from flask import Blueprint 2 | 3 | gif_routes = Blueprint('gif', __name__) 4 | 5 | 6 | index = -1 7 | 8 | 9 | @gif_routes.route('/get-index', methods=['GET']) 10 | def get_index(): 11 | 12 | global index 13 | 14 | if index > 60: 15 | index = -1 16 | 17 | index += 1 18 | 19 | # convert = str(index) 20 | # padded = convert.zfill(5) # --> '00001' 21 | 22 | return {"frame_index": index} 23 | -------------------------------------------------------------------------------- /react-app/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | /node_modules 6 | /.pnp 7 | .pnp.js 8 | 9 | # testing 10 | /coverage 11 | 12 | # production 13 | /build 14 | 15 | # misc 16 | .DS_Store 17 | .env.local 18 | .env.development.local 19 | .env.test.local 20 | .env.production.local 21 | 22 | npm-debug.log* 23 | yarn-debug.log* 24 | yarn-error.log* 25 | -------------------------------------------------------------------------------- /react-app/src/components/auth/ProtectedRoute.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useSelector } from 'react-redux'; 3 | import { Route, Redirect } from 'react-router-dom'; 4 | 5 | const ProtectedRoute = props => { 6 | const user = useSelector(state => state.session.user) 7 | return ( 8 | 9 | {(user)? props.children : } 10 | 11 | ) 12 | }; 13 | 14 | 15 | export default ProtectedRoute; 16 | -------------------------------------------------------------------------------- /react-app/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import { Provider } from 'react-redux'; 4 | import './index.css'; 5 | import App from './App'; 6 | import configureStore from './store'; 7 | 8 | const store = configureStore(); 9 | 10 | ReactDOM.render( 11 | 12 | 13 | 14 | 15 | , 16 | document.getElementById('root') 17 | ); 18 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.9 2 | 3 | # Setup Flask environment 4 | ENV REACT_APP_BASE_URL=https://aa-react-eod-series.herokuapp.com 5 | ENV FLASK_APP=app 6 | ENV FLASK_ENV=production 7 | ENV SQLALCHEMY_ECHO=True 8 | 9 | WORKDIR /var/www 10 | COPY . . 11 | COPY /react-app/build/* flask_backend/static/ 12 | 13 | # Install Python Dependencies 14 | RUN pip install -r requirements.txt 15 | RUN pip install psycopg2 16 | 17 | # Run flask environment 18 | CMD gunicorn flask_backend:app 19 | -------------------------------------------------------------------------------- /flask_backend/api/user_routes.py: -------------------------------------------------------------------------------- 1 | from flask import Blueprint, jsonify 2 | from flask_login import login_required 3 | from flask_backend.models import User 4 | 5 | user_routes = Blueprint('users', __name__) 6 | 7 | 8 | @user_routes.route('/') 9 | @login_required 10 | def users(): 11 | users = User.query.all() 12 | return {'users': [user.to_dict() for user in users]} 13 | 14 | 15 | @user_routes.route('/') 16 | @login_required 17 | def user(id): 18 | user = User.query.get(id) 19 | return user.to_dict() 20 | -------------------------------------------------------------------------------- /react-app/src/components/NavBanner/index.js: -------------------------------------------------------------------------------- 1 | 2 | import { NavLink } from 'react-router-dom'; 3 | 4 | 5 | export default function NavBanner (){ 6 | return( 7 |
8 |

9 | 10 | Gif Demo 11 | 12 |

13 |

14 | 15 | Posts Demo 16 | 17 |

18 |
19 | ) 20 | } 21 | -------------------------------------------------------------------------------- /flask_backend/models/posts.py: -------------------------------------------------------------------------------- 1 | from .db import db 2 | 3 | 4 | class Post(db.Model): 5 | __tablename__ = 'posts' 6 | 7 | id = db.Column(db.Integer, primary_key=True) 8 | content = db.Column(db.Text, nullable=False) 9 | 10 | comments = db.relationship("Comment", back_populates="post", cascade="all, delete") 11 | 12 | 13 | def to_dict(self): 14 | return { 15 | 'id': self.id, 16 | 'content': self.content, 17 | 'comments': [c.to_dict() for c in self.comments] 18 | } 19 | -------------------------------------------------------------------------------- /flask_backend/seeds/__init__.py: -------------------------------------------------------------------------------- 1 | from flask.cli import AppGroup 2 | from .users import seed_users, undo_users 3 | 4 | # Creates a seed group to hold our commands 5 | # So we can type `flask seed --help` 6 | seed_commands = AppGroup('seed') 7 | 8 | 9 | # Creates the `flask seed all` command 10 | @seed_commands.command('all') 11 | def seed(): 12 | seed_users() 13 | # Add other seed functions here 14 | 15 | 16 | # Creates the `flask seed undo` command 17 | @seed_commands.command('undo') 18 | def undo(): 19 | undo_users() 20 | # Add other undo functions here 21 | -------------------------------------------------------------------------------- /react-app/README.md: -------------------------------------------------------------------------------- 1 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). 2 | 3 | Your React App will live here. While is development, run this application from this location using `npm start`. 4 | 5 | 6 | No environment variables are needed to run this application in development, but be sure to set the REACT_APP_BASE_URL environment variable in heroku! 7 | 8 | This app will be automatically built when you deploy to heroku, please see the `heroku-postbuild` script in your `express.js` applications `package.json` to see how this works. 9 | -------------------------------------------------------------------------------- /flask_backend/config.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | 4 | class Config: 5 | SECRET_KEY = os.environ.get('SECRET_KEY') 6 | SQLALCHEMY_TRACK_MODIFICATIONS = False 7 | # SQLAlchemy 1.4 no longer supports url strings that start with 'postgres' 8 | # (only 'postgresql') but heroku's postgres add-on automatically sets the 9 | # url in the hidden config vars to start with postgres. 10 | # so the connection uri must be updated here 11 | SQLALCHEMY_DATABASE_URI = os.environ.get( 12 | 'DATABASE_URL').replace('postgres://', 'postgresql://') 13 | SQLALCHEMY_ECHO = True 14 | -------------------------------------------------------------------------------- /flask_backend/models/subcomments.py: -------------------------------------------------------------------------------- 1 | from .db import db 2 | 3 | 4 | class SubComment(db.Model): 5 | __tablename__ = 'subcomments' 6 | 7 | id = db.Column(db.Integer, primary_key=True) 8 | content = db.Column(db.Text, nullable=False) 9 | 10 | comment_id = db.Column(db.Integer, db.ForeignKey('comments.id'), nullable=False) 11 | comment = db.relationship("Comment", back_populates="subcomments") 12 | 13 | 14 | def to_dict(self): 15 | return { 16 | 'id': self.id, 17 | 'comment_id': self.comment_id, 18 | 'content': self.content 19 | } 20 | -------------------------------------------------------------------------------- /migrations/script.py.mako: -------------------------------------------------------------------------------- 1 | """${message} 2 | 3 | Revision ID: ${up_revision} 4 | Revises: ${down_revision | comma,n} 5 | Create Date: ${create_date} 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | ${imports if imports else ""} 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = ${repr(up_revision)} 14 | down_revision = ${repr(down_revision)} 15 | branch_labels = ${repr(branch_labels)} 16 | depends_on = ${repr(depends_on)} 17 | 18 | 19 | def upgrade(): 20 | ${upgrades if upgrades else "pass"} 21 | 22 | 23 | def downgrade(): 24 | ${downgrades if downgrades else "pass"} 25 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | # 2 | # These requirements were autogenerated by pipenv 3 | # To regenerate from the project's Pipfile, run: 4 | # 5 | # pipenv lock --requirements 6 | # 7 | 8 | -i https://pypi.org/simple 9 | alembic==1.6.5 10 | click==7.1.2 11 | flask-cors==3.0.8 12 | flask-login==0.5.0 13 | flask-migrate==3.0.1 14 | flask-sqlalchemy==2.5.1 15 | flask-wtf==0.15.1 16 | flask==2.0.1 17 | greenlet==1.1.0 18 | gunicorn==20.1.0 19 | itsdangerous==2.0.1 20 | jinja2==3.0.1 21 | mako==1.1.4 22 | markupsafe==2.0.1 23 | python-dateutil==2.8.1 24 | python-dotenv==0.14.0 25 | python-editor==1.0.4 26 | six==1.15.0 27 | sqlalchemy==1.4.19 28 | werkzeug==2.0.1 29 | wtforms==2.3.3 30 | -------------------------------------------------------------------------------- /flask_backend/models/comments.py: -------------------------------------------------------------------------------- 1 | from .db import db 2 | 3 | 4 | class Comment(db.Model): 5 | __tablename__ = 'comments' 6 | 7 | id = db.Column(db.Integer, primary_key=True) 8 | content = db.Column(db.Text, nullable=False) 9 | 10 | post_id = db.Column(db.Integer, db.ForeignKey("posts.id"), nullable=False) 11 | post = db.relationship("Post", back_populates="comments") 12 | 13 | 14 | subcomments = db.relationship("SubComment", back_populates="comment", cascade="all, delete") 15 | 16 | 17 | def to_dict(self): 18 | return { 19 | 'id': self.id, 20 | 'post_id': self.post_id, 21 | 'content': self.content, 22 | 'subcomments': [s.to_dict() for s in self.subcomments] 23 | } 24 | -------------------------------------------------------------------------------- /react-app/src/components/SubComments/index.js: -------------------------------------------------------------------------------- 1 | import { useSelector } from "react-redux"; 2 | 3 | export default function SubComments({ props }) { 4 | const all_sub_comments = useSelector( 5 | (state) => state.posts[props.post_id].comments[props.comment_id].all 6 | ); 7 | 8 | const sub_comments = all_sub_comments 9 | ? all_sub_comments.map((sc) => { 10 | const ids = { subcomment_id: sc.id, post_id: props.post_id }; 11 | return ( 12 |
13 | {sc.content} 14 | 17 |
18 | ); 19 | }) 20 | : null; 21 | 22 | return
{sub_comments}
; 23 | } 24 | -------------------------------------------------------------------------------- /Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | url = "https://pypi.org/simple" 3 | verify_ssl = true 4 | name = "pypi" 5 | 6 | [packages] 7 | click = "==7.1.2" 8 | gunicorn = "==20.1.0" 9 | itsdangerous = "==2.0.1" 10 | python-dotenv = "==0.14.0" 11 | six = "==1.15.0" 12 | Flask = "==2.0.1" 13 | Flask-Cors = "==3.0.8" 14 | Flask-SQLAlchemy = "==2.5.1" 15 | Flask-WTF = "==0.15.1" 16 | Jinja2 = "==3.0.1" 17 | MarkupSafe = "==2.0.1" 18 | SQLAlchemy = "==1.4.19" 19 | Werkzeug = "==2.0.1" 20 | WTForms = "==2.3.3" 21 | Flask-Migrate = "==3.0.1" 22 | Flask-Login = "==0.5.0" 23 | alembic = "==1.6.5" 24 | python-dateutil = "==2.8.1" 25 | python-editor = "==1.0.4" 26 | greenlet = "==1.1.0" 27 | Mako = "==1.1.4" 28 | 29 | [dev-packages] 30 | psycopg2-binary = "==2.8.6" 31 | 32 | [requires] 33 | python_version = "3.9" 34 | -------------------------------------------------------------------------------- /react-app/src/components/UsersList.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | import { NavLink } from 'react-router-dom'; 3 | 4 | function UsersList() { 5 | const [users, setUsers] = useState([]); 6 | 7 | useEffect(() => { 8 | async function fetchData() { 9 | const response = await fetch('/api/users/'); 10 | const responseData = await response.json(); 11 | setUsers(responseData.users); 12 | } 13 | fetchData(); 14 | }, []); 15 | 16 | const userComponents = users.map((user) => { 17 | return ( 18 |
  • 19 | {user.username} 20 |
  • 21 | ); 22 | }); 23 | 24 | return ( 25 | <> 26 |

    User List:

    27 | 28 | 29 | ); 30 | } 31 | 32 | export default UsersList; 33 | -------------------------------------------------------------------------------- /flask_backend/seeds/users.py: -------------------------------------------------------------------------------- 1 | from flask_backend.models import db, User 2 | 3 | 4 | # Adds a demo user, you can add other users here if you want 5 | def seed_users(): 6 | demo = User( 7 | username='Demo', email='demo@aa.io', password='password') 8 | danp = User( 9 | username='danp', email='danp@aa.io', password='password') 10 | 11 | db.session.add(demo) 12 | db.session.add(danp) 13 | 14 | db.session.commit() 15 | 16 | 17 | # Uses a raw SQL query to TRUNCATE the users table. 18 | # SQLAlchemy doesn't have a built in function to do this 19 | # TRUNCATE Removes all the data from the table, and RESET IDENTITY 20 | # resets the auto incrementing primary key, CASCADE deletes any 21 | # dependent entities 22 | def undo_users(): 23 | db.session.execute('TRUNCATE users RESTART IDENTITY CASCADE;') 24 | db.session.commit() 25 | -------------------------------------------------------------------------------- /react-app/src/store/index.js: -------------------------------------------------------------------------------- 1 | import { createStore, combineReducers, applyMiddleware, compose } from 'redux'; 2 | import thunk from 'redux-thunk'; 3 | import session from './session'; 4 | import gif from './gif'; 5 | import posts from './posts'; 6 | 7 | const rootReducer = combineReducers({ 8 | session, 9 | gif, 10 | posts, 11 | }); 12 | 13 | 14 | let enhancer; 15 | 16 | if (process.env.NODE_ENV === 'production') { 17 | enhancer = applyMiddleware(thunk); 18 | } else { 19 | const logger = require('redux-logger').default; 20 | const composeEnhancers = 21 | window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; 22 | enhancer = composeEnhancers(applyMiddleware(thunk, logger)); 23 | } 24 | 25 | const configureStore = (preloadedState) => { 26 | return createStore(rootReducer, preloadedState, enhancer); 27 | }; 28 | 29 | export default configureStore; 30 | -------------------------------------------------------------------------------- /react-app/src/components/SplashPage/index.js: -------------------------------------------------------------------------------- 1 | 2 | import { Link } from 'react-router-dom'; 3 | 4 | 5 | export default function SplashPage(){ 6 | return ( 7 |
    8 |
    9 |

    10 | The gif animation demonstration displays the rendering power of the useSelector and useDispatch hooks. 11 |

    12 |
    13 |
    14 |

    15 | The posts demonstration samples a much more complex scenario that involves nested objects in state. Dynamic rerendering is still achieved through only the useSelector and useDispatch hooks. 16 |

    17 |
    18 |
    19 | ) 20 | } 21 | -------------------------------------------------------------------------------- /react-app/src/components/User.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import { useParams } from 'react-router-dom'; 3 | 4 | function User() { 5 | const [user, setUser] = useState({}); 6 | const { userId } = useParams(); 7 | 8 | useEffect(() => { 9 | if (!userId) { 10 | return; 11 | } 12 | (async () => { 13 | const response = await fetch(`/api/users/${userId}`); 14 | const user = await response.json(); 15 | setUser(user); 16 | })(); 17 | }, [userId]); 18 | 19 | if (!user) { 20 | return null; 21 | } 22 | 23 | return ( 24 | 35 | ); 36 | } 37 | export default User; 38 | -------------------------------------------------------------------------------- /react-app/src/store/state_shape.txt: -------------------------------------------------------------------------------- 1 | This file maps our state shape and paths we'll need in reducer 2 | Start at top level of state: 3 | 4 | 5 | state: { 6 | sessions: {}, 7 | gif: {}, 8 | posts: {} 9 | } 10 | 11 | --> state.posts 12 | posts: { 13 | id: {}, 14 | all_posts: [] 15 | } 16 | 17 | --> state.posts[id] 18 | posts[id] = { 19 | id: Number, 20 | content: String, 21 | comments: {} 22 | } 23 | 24 | --> state.posts[id].comments 25 | comments = { 26 | id: {}, 27 | all: [comment, comment] 28 | } 29 | 30 | --> state.posts[id].comments[id] 31 | comments[id] = { 32 | id: Number, 33 | post_id: Number, 34 | content: String, 35 | subcomments: {} 36 | } 37 | 38 | --> state.posts[id].comments[id].subcomments 39 | subcomments = { 40 | id: {}, 41 | all: [] 42 | } 43 | 44 | --> state.posts[id].comments[id].subcomments[id] 45 | subcomments[id] = { 46 | id: Number, 47 | comment_id: Number, 48 | content: String, 49 | } 50 | -------------------------------------------------------------------------------- /react-app/src/seed/gif_1.json: -------------------------------------------------------------------------------- 1 | ["IMG00000.png", "IMG00001.png", "IMG00002.png", "IMG00003.png", "IMG00004.png", "IMG00005.png", "IMG00006.png", "IMG00007.png", "IMG00008.png", "IMG00009.png", "IMG00010.png", "IMG00011.png", "IMG00012.png", "IMG00013.png", "IMG00014.png", "IMG00015.png", "IMG00016.png", "IMG00017.png", "IMG00018.png", "IMG00019.png", "IMG00020.png", "IMG00021.png", "IMG00022.png", "IMG00023.png", "IMG00024.png", "IMG00025.png", "IMG00026.png", "IMG00027.png", "IMG00028.png", "IMG00029.png", "IMG00030.png", "IMG00031.png", "IMG00032.png", "IMG00033.png", "IMG00034.png", "IMG00035.png", "IMG00036.png", "IMG00037.png", "IMG00038.png", "IMG00039.png", "IMG00040.png", "IMG00041.png", "IMG00042.png", "IMG00043.png", "IMG00044.png", "IMG00045.png", "IMG00046.png", "IMG00047.png", "IMG00048.png", "IMG00049.png", "IMG00050.png", "IMG00051.png", "IMG00052.png", "IMG00053.png", "IMG00054.png", "IMG00055.png", "IMG00056.png", "IMG00057.png", "IMG00058.png", "IMG00059.png", "IMG00060.png", "IMG00061.png"] -------------------------------------------------------------------------------- /flask_backend/models/user.py: -------------------------------------------------------------------------------- 1 | from .db import db 2 | from werkzeug.security import generate_password_hash, check_password_hash 3 | from flask_login import UserMixin 4 | 5 | 6 | class User(db.Model, UserMixin): 7 | __tablename__ = 'users' 8 | 9 | id = db.Column(db.Integer, primary_key=True) 10 | username = db.Column(db.String(40), nullable=False, unique=True) 11 | email = db.Column(db.String(255), nullable=False, unique=True) 12 | hashed_password = db.Column(db.String(255), nullable=False) 13 | 14 | @property 15 | def password(self): 16 | return self.hashed_password 17 | 18 | @password.setter 19 | def password(self, password): 20 | self.hashed_password = generate_password_hash(password) 21 | 22 | def check_password(self, password): 23 | return check_password_hash(self.password, password) 24 | 25 | def to_dict(self): 26 | return { 27 | 'id': self.id, 28 | 'username': self.username, 29 | 'email': self.email 30 | } 31 | -------------------------------------------------------------------------------- /flask_backend/forms/signup_form.py: -------------------------------------------------------------------------------- 1 | from flask_wtf import FlaskForm 2 | from wtforms import StringField 3 | from wtforms.validators import DataRequired, Email, ValidationError 4 | from flask_backend.models import User 5 | 6 | 7 | def user_exists(form, field): 8 | # Checking if user exists 9 | email = field.data 10 | user = User.query.filter(User.email == email).first() 11 | if user: 12 | raise ValidationError('Email address is already in use.') 13 | 14 | 15 | def username_exists(form, field): 16 | # Checking if username is already in use 17 | username = field.data 18 | user = User.query.filter(User.username == username).first() 19 | if user: 20 | raise ValidationError('Username is already in use.') 21 | 22 | 23 | class SignUpForm(FlaskForm): 24 | username = StringField( 25 | 'username', validators=[DataRequired(), username_exists]) 26 | email = StringField('email', validators=[DataRequired(), user_exists]) 27 | password = StringField('password', validators=[DataRequired()]) 28 | -------------------------------------------------------------------------------- /migrations/alembic.ini: -------------------------------------------------------------------------------- 1 | # A generic, single database configuration. 2 | 3 | [alembic] 4 | # template used to generate migration files 5 | # file_template = %%(rev)s_%%(slug)s 6 | 7 | # set to 'true' to run the environment during 8 | # the 'revision' command, regardless of autogenerate 9 | # revision_environment = false 10 | 11 | 12 | # Logging configuration 13 | [loggers] 14 | keys = root,sqlalchemy,alembic,flask_migrate 15 | 16 | [handlers] 17 | keys = console 18 | 19 | [formatters] 20 | keys = generic 21 | 22 | [logger_root] 23 | level = WARN 24 | handlers = console 25 | qualname = 26 | 27 | [logger_sqlalchemy] 28 | level = WARN 29 | handlers = 30 | qualname = sqlalchemy.engine 31 | 32 | [logger_alembic] 33 | level = INFO 34 | handlers = 35 | qualname = alembic 36 | 37 | [logger_flask_migrate] 38 | level = INFO 39 | handlers = 40 | qualname = flask_migrate 41 | 42 | [handler_console] 43 | class = StreamHandler 44 | args = (sys.stderr,) 45 | level = NOTSET 46 | formatter = generic 47 | 48 | [formatter_generic] 49 | format = %(levelname)-5.5s [%(name)s] %(message)s 50 | datefmt = %H:%M:%S 51 | -------------------------------------------------------------------------------- /react-app/src/components/NavBar.js: -------------------------------------------------------------------------------- 1 | 2 | import React from 'react'; 3 | import { NavLink } from 'react-router-dom'; 4 | import LogoutButton from './auth/LogoutButton'; 5 | 6 | const NavBar = () => { 7 | return ( 8 | 35 | ); 36 | } 37 | 38 | export default NavBar; 39 | -------------------------------------------------------------------------------- /flask_backend/forms/login_form.py: -------------------------------------------------------------------------------- 1 | from flask_wtf import FlaskForm 2 | from wtforms import StringField 3 | from wtforms.validators import DataRequired, Email, ValidationError 4 | from flask_backend.models import User 5 | 6 | 7 | def user_exists(form, field): 8 | # Checking if user exists 9 | email = field.data 10 | user = User.query.filter(User.email == email).first() 11 | if not user: 12 | raise ValidationError('Email provided not found.') 13 | 14 | 15 | def password_matches(form, field): 16 | # Checking if password matches 17 | password = field.data 18 | email = form.data['email'] 19 | user = User.query.filter(User.email == email).first() 20 | if not user: 21 | raise ValidationError('No such user exists.') 22 | if not user.check_password(password): 23 | raise ValidationError('Password was incorrect.') 24 | 25 | 26 | class LoginForm(FlaskForm): 27 | email = StringField('email', validators=[DataRequired(), user_exists]) 28 | password = StringField('password', validators=[ 29 | DataRequired(), password_matches]) 30 | -------------------------------------------------------------------------------- /react-app/src/components/Comments/index.js: -------------------------------------------------------------------------------- 1 | import { useSelector } from "react-redux"; 2 | 3 | 4 | export default function Comments({ props }) { 5 | 6 | //Knowing your state is shaped the way you want it, key into the specific object you need to render your component and pass into props, if needed: 7 | 8 | //NOTE: if your conditional is working properly in your parent, you'll know this path is valid and won't return the wrong type. 9 | const all_comments = useSelector( 10 | (state) => state.posts[props.post_id].comments.all 11 | ); 12 | 13 | const element = all_comments ? 14 | all_comments.map((comment) => { 15 | 16 | const ids = { post_id: props.post_id, comment_id: comment.id }; 17 | 18 | return ( 19 |
    20 | {comment.content} 21 | 24 |
    25 | ); 26 | }) 27 | : null; 28 | 29 | return !(all_comments == null) > 0 &&
    {element}
    ; 30 | } 31 | -------------------------------------------------------------------------------- /react-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-app", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.14.1", 7 | "@testing-library/react": "^11.2.7", 8 | "@testing-library/user-event": "^12.8.3", 9 | "http-proxy-middleware": "^1.0.5", 10 | "react": "^17.0.2", 11 | "react-dom": "^17.0.2", 12 | "react-redux": "^7.2.4", 13 | "react-router-dom": "^5.2.0", 14 | "react-scripts": "^4.0.3", 15 | "redux": "^4.1.0", 16 | "redux-logger": "^3.0.6", 17 | "redux-thunk": "^2.3.0" 18 | }, 19 | "scripts": { 20 | "start": "react-scripts start", 21 | "build": "react-scripts build", 22 | "test": "react-scripts test", 23 | "eject": "react-scripts eject" 24 | }, 25 | "eslintConfig": { 26 | "extends": "react-app" 27 | }, 28 | "browserslist": { 29 | "production": [ 30 | ">0.3%", 31 | "not ie 11", 32 | "not dead", 33 | "not op_mini all" 34 | ], 35 | "development": [ 36 | "last 1 chrome version", 37 | "last 1 firefox version", 38 | "last 1 safari version" 39 | ] 40 | }, 41 | "proxy": "http://localhost:5000" 42 | } 43 | -------------------------------------------------------------------------------- /migrations/versions/0dc4227077a4_.py: -------------------------------------------------------------------------------- 1 | """empty message 2 | 3 | Revision ID: 0dc4227077a4 4 | Revises: 9fcfa773ef84 5 | Create Date: 2022-02-27 09:14:39.162011 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = '0dc4227077a4' 14 | down_revision = '9fcfa773ef84' 15 | branch_labels = None 16 | depends_on = None 17 | 18 | 19 | def upgrade(): 20 | # ### commands auto generated by Alembic - please adjust! ### 21 | op.add_column('comments', sa.Column('post_id', sa.Integer(), nullable=False)) 22 | op.create_foreign_key(None, 'comments', 'posts', ['post_id'], ['id']) 23 | op.add_column('subcomments', sa.Column('comment_id', sa.Integer(), nullable=False)) 24 | op.create_foreign_key(None, 'subcomments', 'comments', ['comment_id'], ['id']) 25 | # ### end Alembic commands ### 26 | 27 | 28 | def downgrade(): 29 | # ### commands auto generated by Alembic - please adjust! ### 30 | op.drop_constraint(None, 'subcomments', type_='foreignkey') 31 | op.drop_column('subcomments', 'comment_id') 32 | op.drop_constraint(None, 'comments', type_='foreignkey') 33 | op.drop_column('comments', 'post_id') 34 | # ### end Alembic commands ### 35 | -------------------------------------------------------------------------------- /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | # See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.224.3/containers/python-3/.devcontainer/base.Dockerfile 2 | 3 | # [Choice] Python version (use -bullseye variants on local arm64/Apple Silicon): 3, 3.10, 3.9, 3.8, 3.7, 3.6, 3-bullseye, 3.10-bullseye, 3.9-bullseye, 3.8-bullseye, 3.7-bullseye, 3.6-bullseye, 3-buster, 3.10-buster, 3.9-buster, 3.8-buster, 3.7-buster, 3.6-buster 4 | ARG VARIANT="3.10-bullseye" 5 | FROM mcr.microsoft.com/vscode/devcontainers/python:0-${VARIANT} 6 | 7 | # [Choice] Node.js version: none, lts/*, 16, 14, 12, 10 8 | ARG NODE_VERSION="none" 9 | RUN if [ "${NODE_VERSION}" != "none" ]; then su vscode -c "umask 0002 && . /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1"; fi 10 | 11 | # Install Postgres 12 | RUN sudo apt update 13 | RUN sudo apt install postgresql -y && \ 14 | sudo service postgresql start && \ 15 | sudo -u postgres psql -c "CREATE USER vscode WITH PASSWORD 'password';" && \ 16 | sudo -u postgres psql -c "ALTER USER vscode WITH SUPERUSER;" && \ 17 | sudo -u postgres psql -c "CREATE DATABASE vscode WITH OWNER vscode" && \ 18 | sudo -u postgres psql -c "CREATE DATABASE python_project WITH OWNER vscode" 19 | 20 | # auto configure flask app to connect to internal postgres 21 | ENV DATABASE_URL=postgresql://vscode:password@localhost/python_project 22 | -------------------------------------------------------------------------------- /react-app/src/store/gif.js: -------------------------------------------------------------------------------- 1 | 2 | //switch cases 3 | const switch_frame = "frame/INCREMENT"; 4 | const set_error = "error/NEW_ERROR"; 5 | 6 | //actions 7 | const increment = (frame_index) => ({ 8 | type: switch_frame, 9 | frame_index, 10 | }); 11 | 12 | const new_error = (error) => ({ 13 | type: set_error, 14 | error, 15 | }); 16 | 17 | //thunks 18 | export const increment_frame = () => async (dispatch) => { 19 | const response = await fetch(`/api/gif/get-index`); 20 | 21 | if (response.ok) { 22 | const frame_index = await response.json(); 23 | dispatch(increment(frame_index)); 24 | } else { 25 | const error = { 26 | status_code: response.status, 27 | error_desc: "increment_frame failed", 28 | }; 29 | dispatch(new_error(error)); 30 | } 31 | }; 32 | 33 | //reducer 34 | export default function reducer(state = {}, action){ 35 | switch (action.type) { 36 | case switch_frame: 37 | return { 38 | ...state, 39 | frame: action.frame_index 40 | }; 41 | case set_error: 42 | return { ...state, error: action.error }; 43 | default: 44 | return state; 45 | } 46 | }; 47 | 48 | // //apply redux middlewares to make reducer work 49 | // const composedEnhancer = composeWithDevTools(applyMiddleware(thunkMiddleware)); 50 | 51 | // const store = createStore(rootReducer, composedEnhancer); 52 | 53 | // //sends store to react entrypoint 54 | // export default store; 55 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Deploy 2 | on: 3 | push: 4 | branches: 5 | - main 6 | 7 | jobs: 8 | BuildAndDeploy: 9 | runs-on: ubuntu-20.04 10 | steps: 11 | - uses: actions/checkout@v3 12 | # https://github.com/actions/setup-node 13 | - uses: actions/setup-node@v3 14 | with: 15 | node-version: '16' 16 | cache: 'npm' 17 | cache-dependency-path: react-app/package-lock.json 18 | - name: Build React App 19 | working-directory: react-app 20 | run: npm install && npm run build 21 | 22 | # https://github.com/actions/setup-python 23 | # If a student sees this, they could speed up their builds 24 | # by moving install from docker to cached GH action runner 25 | # Have fun (after you have a passing project!) :) 26 | - uses: actions/setup-python@v3 27 | with: 28 | python-version: '3.9' 29 | cache: 'pip' 30 | - run: pip install pipenv 31 | 32 | - name: Update requirments.txt for container install 33 | run: pipenv lock -r > requirements.txt 34 | 35 | - name: Login to Heroku Container registry 36 | env: 37 | HEROKU_API_KEY: ${{ secrets.HEROKU_API_KEY }} 38 | run: heroku container:login 39 | 40 | - name: Build Container and push 41 | env: 42 | HEROKU_API_KEY: ${{ secrets.HEROKU_API_KEY }} 43 | run: heroku container:push -a ${{ secrets.HEROKU_APP_NAME }} web 44 | 45 | - name: Release 46 | env: 47 | HEROKU_API_KEY: ${{ secrets.HEROKU_API_KEY }} 48 | run: heroku container:release -a ${{ secrets.HEROKU_APP_NAME }} web 49 | 50 | - name: Run DB Migration 51 | env: 52 | HEROKU_API_KEY: ${{ secrets.HEROKU_API_KEY }} 53 | run: heroku run -a ${{ secrets.HEROKU_APP_NAME }} flask run db upgrade 54 | -------------------------------------------------------------------------------- /migrations/versions/9fcfa773ef84_.py: -------------------------------------------------------------------------------- 1 | """empty message 2 | 3 | Revision ID: 9fcfa773ef84 4 | Revises: 5 | Create Date: 2022-02-26 16:15:47.236435 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = '9fcfa773ef84' 14 | down_revision = None 15 | branch_labels = None 16 | depends_on = None 17 | 18 | 19 | def upgrade(): 20 | # ### commands auto generated by Alembic - please adjust! ### 21 | op.create_table('comments', 22 | sa.Column('id', sa.Integer(), nullable=False), 23 | sa.Column('content', sa.Text(), nullable=False), 24 | sa.PrimaryKeyConstraint('id') 25 | ) 26 | op.create_table('posts', 27 | sa.Column('id', sa.Integer(), nullable=False), 28 | sa.Column('content', sa.Text(), nullable=False), 29 | sa.PrimaryKeyConstraint('id') 30 | ) 31 | op.create_table('subcomments', 32 | sa.Column('id', sa.Integer(), nullable=False), 33 | sa.Column('content', sa.Text(), nullable=False), 34 | sa.PrimaryKeyConstraint('id') 35 | ) 36 | op.create_table('users', 37 | sa.Column('id', sa.Integer(), nullable=False), 38 | sa.Column('username', sa.String(length=40), nullable=False), 39 | sa.Column('email', sa.String(length=255), nullable=False), 40 | sa.Column('hashed_password', sa.String(length=255), nullable=False), 41 | sa.PrimaryKeyConstraint('id'), 42 | sa.UniqueConstraint('email'), 43 | sa.UniqueConstraint('username') 44 | ) 45 | # ### end Alembic commands ### 46 | 47 | 48 | def downgrade(): 49 | # ### commands auto generated by Alembic - please adjust! ### 50 | op.drop_table('users') 51 | op.drop_table('subcomments') 52 | op.drop_table('posts') 53 | op.drop_table('comments') 54 | # ### end Alembic commands ### 55 | -------------------------------------------------------------------------------- /react-app/src/components/auth/LoginForm.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { useSelector, useDispatch } from 'react-redux'; 3 | import { Redirect } from 'react-router-dom'; 4 | import { login } from '../../store/session'; 5 | 6 | const LoginForm = () => { 7 | const [errors, setErrors] = useState([]); 8 | const [email, setEmail] = useState(''); 9 | const [password, setPassword] = useState(''); 10 | const user = useSelector(state => state.session.user); 11 | const dispatch = useDispatch(); 12 | 13 | const onLogin = async (e) => { 14 | e.preventDefault(); 15 | const data = await dispatch(login(email, password)); 16 | if (data) { 17 | setErrors(data); 18 | } 19 | }; 20 | 21 | const updateEmail = (e) => { 22 | setEmail(e.target.value); 23 | }; 24 | 25 | const updatePassword = (e) => { 26 | setPassword(e.target.value); 27 | }; 28 | 29 | if (user) { 30 | return ; 31 | } 32 | 33 | return ( 34 |
    35 |
    36 | {errors.map((error, ind) => ( 37 |
    {error}
    38 | ))} 39 |
    40 |
    41 | 42 | 49 |
    50 |
    51 | 52 | 59 | 60 |
    61 |
    62 | ); 63 | }; 64 | 65 | export default LoginForm; 66 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | // For format details, see https://aka.ms/devcontainer.json. For config options, see the README at: 2 | // https://github.com/microsoft/vscode-dev-containers/tree/v0.224.3/containers/python-3 3 | { 4 | "name": "Python 3", 5 | "build": { 6 | "dockerfile": "Dockerfile", 7 | "context": "..", 8 | "args": { 9 | "VARIANT": "3.9", 10 | "NODE_VERSION": "16" 11 | } 12 | }, 13 | 14 | // Set *default* container specific settings.json values on container create. 15 | "settings": { 16 | // "python.defaultInterpreterPath": "/usr/local/bin/python", 17 | // "python.linting.enabled": true, 18 | // "python.linting.pylintEnabled": true, 19 | // "python.formatting.autopep8Path": "/usr/local/py-utils/bin/autopep8", 20 | // "python.formatting.blackPath": "/usr/local/py-utils/bin/black", 21 | // "python.formatting.yapfPath": "/usr/local/py-utils/bin/yapf", 22 | // "python.linting.banditPath": "/usr/local/py-utils/bin/bandit", 23 | // "python.linting.flake8Path": "/usr/local/py-utils/bin/flake8", 24 | // "python.linting.mypyPath": "/usr/local/py-utils/bin/mypy", 25 | // "python.linting.pycodestylePath": "/usr/local/py-utils/bin/pycodestyle", 26 | // "python.linting.pydocstylePath": "/usr/local/py-utils/bin/pydocstyle", 27 | // "python.linting.pylintPath": "/usr/local/py-utils/bin/pylint" 28 | }, 29 | 30 | // Add the IDs of extensions you want installed when the container is created. 31 | "extensions": [ 32 | // "ms-python.python", 33 | // "ms-python.vscode-pylance" 34 | ], 35 | 36 | // Use 'forwardPorts' to make a list of ports inside the container available locally. 37 | "forwardPorts": [5000, 5432, 3000], 38 | 39 | // Use 'postCreateCommand' to run commands after the container is created. 40 | "postCreateCommand": "pipenv install --dev && cd react-app && npm install", 41 | // Flask python-dotenv SQLAlchemy Flask-SQLAlchemy psycopg2-binary WTForms Flask-WTF Flask-Login 42 | 43 | "postStartCommand": "sudo service postgresql start", 44 | 45 | // Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. 46 | "remoteUser": "vscode" 47 | } 48 | -------------------------------------------------------------------------------- /react-app/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from "react"; 2 | import { BrowserRouter, Route, Switch } from "react-router-dom"; 3 | import { useDispatch } from "react-redux"; 4 | 5 | import LoginForm from "./components/auth/LoginForm"; 6 | import SignUpForm from "./components/auth/SignUpForm"; 7 | import ProtectedRoute from "./components/auth/ProtectedRoute"; 8 | import NavBanner from "./components/NavBanner"; 9 | import SplashPage from "./components/SplashPage"; 10 | import UsersList from "./components/UsersList"; 11 | import User from "./components/User"; 12 | import Header from "./components/Header"; 13 | import Posts from "./components/Posts"; 14 | import Footer from "./components/Footer"; 15 | import Gif from "./components/Gif"; 16 | 17 | import { authenticate } from "./store/session"; 18 | import { get_all_posts } from "./store/posts"; 19 | 20 | function App() { 21 | const [loaded, setLoaded] = useState(false); 22 | const dispatch = useDispatch(); 23 | 24 | useEffect(() => { 25 | (async () => { 26 | await dispatch(authenticate()); 27 | dispatch(get_all_posts()); 28 | setLoaded(true); 29 | })(); 30 | }, [dispatch]); 31 | 32 | if (!loaded) { 33 | return null; 34 | } 35 | 36 | return ( 37 | 38 |
    39 | 40 |
    41 |
    42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 |
    66 |
    67 |