├── config ├── __init__.py ├── settings │ ├── __init__.py │ ├── test.py │ └── local.py ├── wsgi.py └── urls.py ├── nomadgram ├── images │ ├── __init__.py │ ├── migrations │ │ ├── __init__.py │ │ ├── 0007_auto_20171014_2122.py │ │ ├── 0009_auto_20180118_1856.py │ │ ├── 0006_auto_20171014_2026.py │ │ ├── 0008_image_tags.py │ │ ├── 0004_auto_20171014_0001.py │ │ ├── 0003_auto_20171013_2247.py │ │ ├── 0005_auto_20171014_0019.py │ │ ├── 0001_initial.py │ │ └── 0002_auto_20171013_0110.py │ ├── tests.py │ ├── apps.py │ ├── admin.py │ ├── urls.py │ ├── models.py │ └── serializers.py ├── users │ ├── __init__.py │ ├── tests │ │ ├── __init__.py │ │ ├── factories.py │ │ ├── test_models.py │ │ ├── test_admin.py │ │ ├── test_urls.py │ │ └── test_views.py │ ├── management │ │ ├── __init__.py │ │ └── commands │ │ │ ├── __init__.py │ │ │ └── ebuser.py │ ├── migrations │ │ ├── __init__.py │ │ ├── 0006_user_push_token.py │ │ ├── 0004_user_profile_image.py │ │ ├── 0003_auto_20171013_0216.py │ │ ├── 0005_auto_20171015_0201.py │ │ ├── 0002_auto_20171013_0009.py │ │ └── 0001_initial.py │ ├── apps.py │ ├── signals.py │ ├── adapters.py │ ├── admin.py │ ├── models.py │ ├── urls.py │ └── serializers.py ├── static │ ├── fonts │ │ └── .gitkeep │ ├── sass │ │ ├── custom_bootstrap_vars.scss │ │ └── project.scss │ ├── images │ │ └── favicon.ico │ ├── css │ │ └── project.css │ └── js │ │ └── project.js ├── notifications │ ├── __init__.py │ ├── migrations │ │ ├── __init__.py │ │ ├── 0005_auto_20171015_2254.py │ │ ├── 0004_notification_comment.py │ │ ├── 0003_auto_20171015_1806.py │ │ ├── 0002_auto_20171015_1805.py │ │ └── 0001_initial.py │ ├── tests.py │ ├── apps.py │ ├── urls.py │ ├── admin.py │ ├── serializers.py │ ├── models.py │ └── views.py ├── templates │ ├── pages │ │ ├── about.html │ │ └── home.html │ ├── 404.html │ ├── 403_csrf.html │ ├── bootstrap4 │ │ ├── layout │ │ │ └── field_errors_block.html │ │ └── field.html │ ├── account │ │ ├── base.html │ │ ├── account_inactive.html │ │ ├── password_reset_from_key_done.html │ │ ├── signup_closed.html │ │ ├── verification_sent.html │ │ ├── password_set.html │ │ ├── password_reset_done.html │ │ ├── password_change.html │ │ ├── logout.html │ │ ├── signup.html │ │ ├── verified_email_required.html │ │ ├── password_reset.html │ │ ├── password_reset_from_key.html │ │ ├── email_confirm.html │ │ ├── login.html │ │ └── email.html │ ├── 500.html │ ├── users │ │ ├── user_list.html │ │ ├── user_form.html │ │ └── user_detail.html │ └── base.html ├── __init__.py ├── contrib │ ├── __init__.py │ └── sites │ │ ├── __init__.py │ │ └── migrations │ │ ├── __init__.py │ │ ├── 0002_alter_domain_unique.py │ │ ├── 0001_initial.py │ │ └── 0003_set_site_domain_and_name.py └── views.py ├── .gitattributes ├── requirements.txt ├── pytest.ini ├── frontend ├── build │ ├── favicon.ico │ ├── static │ │ └── media │ │ │ ├── logo.66710299.png │ │ │ ├── android.f06b9089.png │ │ │ └── phone.5d17c046.png │ ├── manifest.json │ ├── asset-manifest.json │ ├── index.html │ └── service-worker.js ├── public │ ├── favicon.ico │ ├── manifest.json │ └── index.html ├── src │ ├── images │ │ ├── ios.png │ │ ├── logo.png │ │ ├── phone.png │ │ ├── android.png │ │ ├── loading.png │ │ └── noPhoto.jpg │ ├── config │ │ ├── _variables.scss │ │ ├── _sizes.scss │ │ ├── _colors.scss │ │ └── _mixins.scss │ ├── components │ │ ├── TimeStamp │ │ │ ├── styles.scss │ │ │ └── index.js │ │ ├── UserDisplay │ │ │ ├── container.js │ │ │ ├── index.js │ │ │ ├── styles.scss │ │ │ └── presenter.js │ │ ├── Feed │ │ │ ├── styles.scss │ │ │ ├── index.js │ │ │ ├── presenter.js │ │ │ └── container.js │ │ ├── App │ │ │ ├── container.js │ │ │ ├── index.js │ │ │ ├── styles.scss │ │ │ └── presenter.js │ │ ├── PhotoComments │ │ │ ├── styles.scss │ │ │ └── index.js │ │ ├── Auth │ │ │ ├── index.js │ │ │ ├── container.js │ │ │ ├── styles.scss │ │ │ └── presenter.js │ │ ├── PhotoActions │ │ │ ├── container.js │ │ │ ├── styles.scss │ │ │ ├── index.js │ │ │ └── presenter.js │ │ ├── Loading │ │ │ ├── styles.scss │ │ │ └── index.js │ │ ├── Explore │ │ │ ├── styles.scss │ │ │ ├── index.js │ │ │ ├── container.js │ │ │ └── presenter.js │ │ ├── UserList │ │ │ ├── index.js │ │ │ ├── container.js │ │ │ ├── styles.scss │ │ │ └── presenter.js │ │ ├── Navigation │ │ │ ├── index.js │ │ │ ├── container.js │ │ │ ├── styles.scss │ │ │ └── presenter.js │ │ ├── FeedPhoto │ │ │ ├── index.js │ │ │ ├── container.js │ │ │ ├── styles.scss │ │ │ └── presenter.js │ │ ├── CommentBox │ │ │ ├── styles.scss │ │ │ ├── index.js │ │ │ ├── presenter.js │ │ │ └── container.js │ │ ├── LoginForm │ │ │ ├── index.js │ │ │ ├── container.js │ │ │ └── presenter.js │ │ ├── SignupForm │ │ │ ├── index.js │ │ │ ├── container.js │ │ │ └── presenter.js │ │ ├── Search │ │ │ ├── styles.scss │ │ │ ├── index.js │ │ │ ├── container.js │ │ │ └── presenter.js │ │ ├── PhotoDisplay │ │ │ ├── styles.scss │ │ │ └── index.js │ │ └── Footer │ │ │ ├── styles.scss │ │ │ └── index.js │ ├── ReactotronConfig.js │ ├── translations.js │ ├── index.js │ ├── redux │ │ ├── configureStore.js │ │ └── modules │ │ │ └── photos.js │ └── shared │ │ └── formStyles.scss ├── .gitignore ├── config │ ├── polyfills.js │ ├── paths.js │ ├── env.js │ └── webpackDevServer.config.js ├── locales │ └── es.po └── package.json ├── .ebextensions ├── 10_packages.config ├── wsgi.conf └── 20_python.config ├── .vscode └── settings.json ├── setup.cfg ├── README.md ├── requirements ├── test.txt ├── local.txt ├── production.txt └── base.txt ├── yarn.lock ├── .pylintrc ├── .editorconfig ├── LICENSE ├── manage.py ├── Pipfile ├── serving-react-app-django.recipe └── .gitignore /config/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /config/settings/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /nomadgram/images/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /nomadgram/users/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /nomadgram/static/fonts/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /nomadgram/users/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /nomadgram/notifications/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /nomadgram/users/management/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /nomadgram/users/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /nomadgram/images/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /nomadgram/notifications/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /nomadgram/static/sass/custom_bootstrap_vars.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /nomadgram/users/management/commands/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | -r requirements/production.txt -------------------------------------------------------------------------------- /nomadgram/templates/pages/about.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} -------------------------------------------------------------------------------- /nomadgram/templates/pages/home.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} -------------------------------------------------------------------------------- /pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | DJANGO_SETTINGS_MODULE=config.settings.test 3 | -------------------------------------------------------------------------------- /nomadgram/images/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /nomadgram/notifications/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /frontend/build/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nomadcoders/nomadgram/HEAD/frontend/build/favicon.ico -------------------------------------------------------------------------------- /frontend/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nomadcoders/nomadgram/HEAD/frontend/public/favicon.ico -------------------------------------------------------------------------------- /frontend/src/images/ios.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nomadcoders/nomadgram/HEAD/frontend/src/images/ios.png -------------------------------------------------------------------------------- /frontend/src/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nomadcoders/nomadgram/HEAD/frontend/src/images/logo.png -------------------------------------------------------------------------------- /frontend/src/images/phone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nomadcoders/nomadgram/HEAD/frontend/src/images/phone.png -------------------------------------------------------------------------------- /frontend/src/images/android.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nomadcoders/nomadgram/HEAD/frontend/src/images/android.png -------------------------------------------------------------------------------- /frontend/src/images/loading.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nomadcoders/nomadgram/HEAD/frontend/src/images/loading.png -------------------------------------------------------------------------------- /frontend/src/images/noPhoto.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nomadcoders/nomadgram/HEAD/frontend/src/images/noPhoto.jpg -------------------------------------------------------------------------------- /.ebextensions/10_packages.config: -------------------------------------------------------------------------------- 1 | packages: 2 | yum: 3 | git: [] 4 | postgresql94-devel: [] 5 | libjpeg-turbo-devel: [] -------------------------------------------------------------------------------- /frontend/src/config/_variables.scss: -------------------------------------------------------------------------------- 1 | @import "./_colors.scss"; 2 | @import "./_sizes.scss"; 3 | @import "./_mixins.scss"; 4 | -------------------------------------------------------------------------------- /nomadgram/static/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nomadcoders/nomadgram/HEAD/nomadgram/static/images/favicon.ico -------------------------------------------------------------------------------- /nomadgram/images/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class ImagesConfig(AppConfig): 5 | name = 'nomadgram.images' 6 | -------------------------------------------------------------------------------- /frontend/build/static/media/logo.66710299.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nomadcoders/nomadgram/HEAD/frontend/build/static/media/logo.66710299.png -------------------------------------------------------------------------------- /frontend/build/static/media/android.f06b9089.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nomadcoders/nomadgram/HEAD/frontend/build/static/media/android.f06b9089.png -------------------------------------------------------------------------------- /frontend/build/static/media/phone.5d17c046.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nomadcoders/nomadgram/HEAD/frontend/build/static/media/phone.5d17c046.png -------------------------------------------------------------------------------- /nomadgram/notifications/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class NotificationsConfig(AppConfig): 5 | name = 'nomadgram.notifications' 6 | -------------------------------------------------------------------------------- /frontend/src/config/_sizes.scss: -------------------------------------------------------------------------------- 1 | $max-page-width: 935px; 2 | $max-card-width: 600px; 3 | $small-font-size: 12px; 4 | $normal-font-size: 14px; 5 | $big-font-size: 16px; 6 | -------------------------------------------------------------------------------- /nomadgram/__init__.py: -------------------------------------------------------------------------------- 1 | __version__ = '0.1.0' 2 | __version_info__ = tuple([int(num) if num.isdigit() else num for num in __version__.replace('-', '.', 1).split('.')]) 3 | -------------------------------------------------------------------------------- /frontend/src/config/_colors.scss: -------------------------------------------------------------------------------- 1 | $bg-color: #fafafa; 2 | $dark-blue: #003569; 3 | $light-blue: #3897f0; 4 | $dark-grey: #999; 5 | $border-color: #e6e6e6; 6 | $light-grey: #efefef; 7 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "python.linting.pylintEnabled": false, 3 | "python.pythonPath": "/Library/Frameworks/Python.framework/Versions/2.7/Resources/Python.app/Contents/MacOS/Python" 4 | } -------------------------------------------------------------------------------- /frontend/src/components/TimeStamp/styles.scss: -------------------------------------------------------------------------------- 1 | .time { 2 | color: $dark-grey; 3 | font-size: $small-font-size - 2; 4 | text-transform: uppercase; 5 | margin-bottom: 10px; 6 | display: block; 7 | } 8 | -------------------------------------------------------------------------------- /frontend/src/components/UserDisplay/container.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import UserRow from "./presenter"; 3 | 4 | const Container = props => ; 5 | 6 | export default Container; 7 | -------------------------------------------------------------------------------- /nomadgram/contrib/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | To understand why this file is here, please read: 3 | 4 | http://cookiecutter-django.readthedocs.io/en/latest/faq.html#why-is-there-a-django-contrib-sites-directory-in-cookiecutter-django 5 | """ 6 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [flake8] 2 | max-line-length = 120 3 | exclude = .tox,.git,*/migrations/*,*/static/CACHE/*,docs,node_modules 4 | 5 | [pycodestyle] 6 | max-line-length = 120 7 | exclude=.tox,.git,*/migrations/*,*/static/CACHE/*,docs,node_modules 8 | -------------------------------------------------------------------------------- /frontend/src/components/Feed/styles.scss: -------------------------------------------------------------------------------- 1 | .feed { 2 | min-height: 500px; 3 | display: flex; 4 | flex-direction: column; 5 | width: 100%; 6 | max-width: $max-page-width; 7 | margin: 0 auto; 8 | align-items: center; 9 | } 10 | -------------------------------------------------------------------------------- /nomadgram/contrib/sites/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | To understand why this file is here, please read: 3 | 4 | http://cookiecutter-django.readthedocs.io/en/latest/faq.html#why-is-there-a-django-contrib-sites-directory-in-cookiecutter-django 5 | """ 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nomadgram 2 | Cloning Instagram with Python Django and React / React Native 3 | 4 | ## Learn how I did it by taking this awesome online course: 5 | 6 | https://academy.nomadcoders.co/courses/instagram-full-stack-front-end-back-end-ios 7 | -------------------------------------------------------------------------------- /frontend/src/components/App/container.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import App from "./presenter"; 3 | 4 | // TODO: If the user is logged in, call API 5 | 6 | const Container = props => ; 7 | 8 | export default Container; 9 | -------------------------------------------------------------------------------- /frontend/src/components/PhotoComments/styles.scss: -------------------------------------------------------------------------------- 1 | .comments { 2 | margin-bottom: 12px; 3 | .list { 4 | } 5 | } 6 | .comment { 7 | margin-bottom: 7px; 8 | .username { 9 | font-weight: 600; 10 | } 11 | .message { 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /nomadgram/contrib/sites/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | To understand why this file is here, please read: 3 | 4 | http://cookiecutter-django.readthedocs.io/en/latest/faq.html#why-is-there-a-django-contrib-sites-directory-in-cookiecutter-django 5 | """ 6 | -------------------------------------------------------------------------------- /nomadgram/notifications/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import url 2 | from . import views 3 | 4 | urlpatterns = [ 5 | url( 6 | regex=r'^$', 7 | view=views.Notifications.as_view(), 8 | name='notifications' 9 | ), 10 | ] 11 | -------------------------------------------------------------------------------- /nomadgram/templates/404.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}Page not found{% endblock %} 4 | 5 | {% block content %} 6 |

Page not found

7 | 8 |

This is not the page you were looking for.

9 | {% endblock content %} 10 | -------------------------------------------------------------------------------- /.ebextensions/wsgi.conf: -------------------------------------------------------------------------------- 1 | 2 | 3 | WSGIPassAuthorization On 4 | 5 | WSGIScriptAlias / /opt/python/current/app/config/wsgi.py 6 | 7 | 8 | 9 | Require all granted 10 | 11 | 12 | -------------------------------------------------------------------------------- /nomadgram/templates/403_csrf.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}Forbidden (403){% endblock %} 4 | 5 | {% block content %} 6 |

Forbidden (403)

7 | 8 |

CSRF verification failed. Request aborted.

9 | {% endblock content %} 10 | -------------------------------------------------------------------------------- /frontend/src/ReactotronConfig.js: -------------------------------------------------------------------------------- 1 | import Reactotron from "reactotron-react-js"; 2 | import { reactotronRedux } from "reactotron-redux"; 3 | 4 | Reactotron.configure({ name: "Nomadgram" }) 5 | .use(reactotronRedux()) 6 | .connect(); 7 | 8 | export default Reactotron; 9 | -------------------------------------------------------------------------------- /requirements/test.txt: -------------------------------------------------------------------------------- 1 | # Test dependencies go here. 2 | -r base.txt 3 | 4 | 5 | 6 | coverage==4.4.1 7 | flake8==3.4.1 # pyup: != 2.6.0 8 | django-test-plus==1.0.18 9 | factory-boy==2.9.2 10 | django-coverage-plugin==1.5.0 11 | 12 | # pytest 13 | pytest-django==3.1.2 14 | pytest-sugar==0.9.0 15 | -------------------------------------------------------------------------------- /frontend/src/components/Auth/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from "react-redux"; 2 | import Container from "./container"; 3 | 4 | // Add all the actions for: 5 | // Log in 6 | // Sign up 7 | // Recover Password 8 | // Check username 9 | // Check password 10 | 11 | export default connect()(Container); 12 | -------------------------------------------------------------------------------- /frontend/src/components/PhotoActions/container.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import PhotoActions from "./presenter"; 3 | 4 | class Container extends Component { 5 | render() { 6 | return ; 7 | } 8 | } 9 | 10 | export default Container; 11 | -------------------------------------------------------------------------------- /nomadgram/templates/bootstrap4/layout/field_errors_block.html: -------------------------------------------------------------------------------- 1 | 2 | {% if form_show_errors and field.errors %} 3 | {% for error in field.errors %} 4 |

{{ error }}

5 | {% endfor %} 6 | {% endif %} 7 | 8 | -------------------------------------------------------------------------------- /nomadgram/notifications/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from . import models 3 | 4 | 5 | @admin.register(models.Notification) 6 | class NotificationAdmin(admin.ModelAdmin): 7 | 8 | list_display = ( 9 | 'creator', 10 | 'to', 11 | 'notification_type' 12 | ) 13 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | redux-devtools-extension@^2.13.2: 6 | version "2.13.2" 7 | resolved "https://registry.yarnpkg.com/redux-devtools-extension/-/redux-devtools-extension-2.13.2.tgz#e0f9a8e8dfca7c17be92c7124958a3b94eb2911d" 8 | -------------------------------------------------------------------------------- /frontend/src/components/Loading/styles.scss: -------------------------------------------------------------------------------- 1 | .container { 2 | .spinner { 3 | height: 32px; 4 | animation: spinner 1.2s steps(12) infinite; 5 | } 6 | } 7 | 8 | @keyframes spinner { 9 | from { 10 | transform: rotate(0deg); 11 | } 12 | to { 13 | transform: rotate(360deg); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /.pylintrc: -------------------------------------------------------------------------------- 1 | [MASTER] 2 | load-plugins=pylint_common, pylint_django 3 | 4 | [FORMAT] 5 | max-line-length=120 6 | 7 | [MESSAGES CONTROL] 8 | disable=missing-docstring,invalid-name 9 | 10 | [DESIGN] 11 | max-parents=13 12 | 13 | [TYPECHECK] 14 | generated-members=REQUEST,acl_users,aq_parent,"[a-zA-Z]+_set{1,2}",save,delete 15 | -------------------------------------------------------------------------------- /nomadgram/templates/account/base.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% block title %}{% block head_title %}{% endblock head_title %}{% endblock title %} 3 | 4 | {% block content %} 5 |
6 |
7 | {% block inner %}{% endblock %} 8 |
9 |
10 | {% endblock %} 11 | -------------------------------------------------------------------------------- /frontend/src/components/Explore/styles.scss: -------------------------------------------------------------------------------- 1 | .explore { 2 | display: flex; 3 | flex-direction: column; 4 | width: 100%; 5 | max-width: $max-card-width; 6 | margin: 0 auto; 7 | align-items: center; 8 | @include boxBorder(yes); 9 | @include breakpoint("phone") { 10 | background-color: transparent; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /frontend/src/components/UserList/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from "react-redux"; 2 | import Container from "./container"; 3 | 4 | const mapStateToProps = (state, ownProps) => { 5 | const { user: { userList } } = state; 6 | return { 7 | userList 8 | }; 9 | }; 10 | 11 | export default connect(mapStateToProps)(Container); 12 | -------------------------------------------------------------------------------- /nomadgram/templates/account/account_inactive.html: -------------------------------------------------------------------------------- 1 | {% extends "account/base.html" %} 2 | 3 | {% load i18n %} 4 | 5 | {% block head_title %}{% trans "Account Inactive" %}{% endblock %} 6 | 7 | {% block inner %} 8 |

{% trans "Account Inactive" %}

9 | 10 |

{% trans "This account is inactive." %}

11 | {% endblock %} 12 | 13 | -------------------------------------------------------------------------------- /frontend/src/components/PhotoActions/styles.scss: -------------------------------------------------------------------------------- 1 | .actions { 2 | margin-bottom: 10px; 3 | .icons { 4 | margin-bottom: 5px; 5 | .icon { 6 | cursor: pointer; 7 | &:first-child { 8 | margin-right: 10px; 9 | } 10 | } 11 | } 12 | .likes { 13 | font-weight: 600; 14 | cursor: pointer; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /nomadgram/templates/account/password_reset_from_key_done.html: -------------------------------------------------------------------------------- 1 | {% extends "account/base.html" %} 2 | 3 | {% load i18n %} 4 | {% block head_title %}{% trans "Change Password" %}{% endblock %} 5 | 6 | {% block inner %} 7 |

{% trans "Change Password" %}

8 |

{% trans 'Your password is now changed.' %}

9 | {% endblock %} 10 | 11 | -------------------------------------------------------------------------------- /nomadgram/templates/account/signup_closed.html: -------------------------------------------------------------------------------- 1 | {% extends "account/base.html" %} 2 | 3 | {% load i18n %} 4 | 5 | {% block head_title %}{% trans "Sign Up Closed" %}{% endblock %} 6 | 7 | {% block inner %} 8 |

{% trans "Sign Up Closed" %}

9 | 10 |

{% trans "We are sorry, but the sign up is currently closed." %}

11 | {% endblock %} 12 | 13 | -------------------------------------------------------------------------------- /nomadgram/users/management/commands/ebuser.py: -------------------------------------------------------------------------------- 1 | from django.core.management.base import BaseCommand 2 | from django.contrib.auth import get_user_model 3 | 4 | class Command(BaseCommand): 5 | 6 | def handle(self, *args, **options): 7 | 8 | User = get_user_model() 9 | User.objects.create_superuser('admin', 'admin@example.com', 'pass') 10 | -------------------------------------------------------------------------------- /frontend/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # misc 10 | .DS_Store 11 | .env.local 12 | .env.development.local 13 | .env.test.local 14 | .env.production.local 15 | 16 | npm-debug.log* 17 | yarn-debug.log* 18 | yarn-error.log* 19 | -------------------------------------------------------------------------------- /frontend/src/components/Loading/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styles from "./styles.scss"; 3 | 4 | const Loading = props => ( 5 |
6 | loading 11 |
12 | ); 13 | 14 | export default Loading; 15 | -------------------------------------------------------------------------------- /frontend/build/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /frontend/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /nomadgram/users/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class UsersConfig(AppConfig): 5 | name = 'nomadgram.users' 6 | verbose_name = "Users" 7 | 8 | def ready(self): 9 | """Override this to put in: 10 | Users system checks 11 | Users signal registration 12 | """ 13 | 14 | from .signals import user_signed_up 15 | -------------------------------------------------------------------------------- /frontend/src/components/App/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from "react-redux"; 2 | import Container from "./container"; 3 | 4 | const mapStateToProps = (state, ownProps) => { 5 | const { user, routing: { location } } = state; 6 | return { 7 | isLoggedIn: user.isLoggedIn, 8 | pathname: location.pathname 9 | }; 10 | }; 11 | 12 | export default connect(mapStateToProps)(Container); 13 | -------------------------------------------------------------------------------- /nomadgram/templates/500.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}Server Error{% endblock %} 4 | 5 | {% block content %} 6 |

Ooops!!! 500

7 | 8 |

Looks like something went wrong!

9 | 10 |

We track these errors automatically, but if the problem persists feel free to contact us. In the meantime, try refreshing.

11 | {% endblock content %} 12 | 13 | 14 | -------------------------------------------------------------------------------- /requirements/local.txt: -------------------------------------------------------------------------------- 1 | # Local development dependencies go here 2 | -r base.txt 3 | 4 | coverage==4.4.1 5 | django-coverage-plugin==1.5.0 6 | 7 | Sphinx==1.6.4 8 | django-extensions==1.9.1 9 | Werkzeug==0.12.2 10 | django-test-plus==1.0.18 11 | factory-boy==2.9.2 12 | 13 | django-debug-toolbar==1.8 14 | 15 | # improved REPL 16 | ipdb==0.10.3 17 | 18 | pytest-django==3.1.2 19 | pytest-sugar==0.9.0 20 | -------------------------------------------------------------------------------- /frontend/src/components/Navigation/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from "react-redux"; 2 | import Container from "./container"; 3 | import { push } from "react-router-redux"; 4 | 5 | const mapDispatchToProps = (dispatch, ownProps) => { 6 | return { 7 | goToSearch: searchTerm => { 8 | dispatch(push(`/search/${searchTerm}`)); 9 | } 10 | }; 11 | }; 12 | 13 | export default connect(null, mapDispatchToProps)(Container); 14 | -------------------------------------------------------------------------------- /frontend/build/asset-manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "main.css": "static/css/main.5b13703d.css", 3 | "main.css.map": "static/css/main.5b13703d.css.map", 4 | "main.js": "static/js/main.22535e23.js", 5 | "main.js.map": "static/js/main.22535e23.js.map", 6 | "static/media/android.png": "static/media/android.f06b9089.png", 7 | "static/media/logo.png": "static/media/logo.66710299.png", 8 | "static/media/phone.png": "static/media/phone.5d17c046.png" 9 | } -------------------------------------------------------------------------------- /frontend/src/components/App/styles.scss: -------------------------------------------------------------------------------- 1 | @import "reset-css/reset.css"; 2 | @import url("https://fonts.googleapis.com/css?family=Roboto:300:400,500"); 3 | 4 | body { 5 | background-color: $bg-color; 6 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, 7 | Arial, sans-serif; 8 | font-size: $normal-font-size; 9 | } 10 | * { 11 | box-sizing: border-box; 12 | } 13 | 14 | img { 15 | max-width: 100%; 16 | } 17 | -------------------------------------------------------------------------------- /nomadgram/users/tests/factories.py: -------------------------------------------------------------------------------- 1 | import factory 2 | 3 | 4 | class UserFactory(factory.django.DjangoModelFactory): 5 | username = factory.Sequence(lambda n: 'user-{0}'.format(n)) 6 | email = factory.Sequence(lambda n: 'user-{0}@example.com'.format(n)) 7 | password = factory.PostGenerationMethodCall('set_password', 'password') 8 | 9 | class Meta: 10 | model = 'users.User' 11 | django_get_or_create = ('username', ) 12 | -------------------------------------------------------------------------------- /frontend/src/components/FeedPhoto/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from "react-redux"; 2 | import Container from "./container"; 3 | import { actionCreators as userActions } from "redux/modules/user"; 4 | 5 | const mapDispatchToProps = (dispatch, ownProps) => { 6 | return { 7 | getPhotoLikes: () => { 8 | dispatch(userActions.getPhotoLikes(ownProps.id)); 9 | } 10 | }; 11 | }; 12 | 13 | export default connect(null, mapDispatchToProps)(Container); 14 | -------------------------------------------------------------------------------- /frontend/src/components/TimeStamp/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | import styles from "./styles.scss"; 4 | 5 | const TimeStamp = (props, context) => ( 6 | {props.time} 7 | ); 8 | 9 | TimeStamp.propTypes = { 10 | time: PropTypes.string.isRequired 11 | }; 12 | 13 | TimeStamp.contextTypes = { 14 | t: PropTypes.func.isRequired 15 | }; 16 | 17 | export default TimeStamp; 18 | -------------------------------------------------------------------------------- /frontend/src/components/CommentBox/styles.scss: -------------------------------------------------------------------------------- 1 | .comment-box { 2 | border-top: 1px solid $light-grey; 3 | padding: 15px 0; 4 | @include breakpoint("phone") { 5 | display: none; 6 | } 7 | .input { 8 | border: 0; 9 | height: 20px; 10 | resize: none; 11 | width: 100%; 12 | font-size: $normal-font-size; 13 | &:focus { 14 | outline: none; 15 | } 16 | &::placeholder { 17 | color: $dark-grey; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /frontend/src/components/CommentBox/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from "react-redux"; 2 | import Container from "./container"; 3 | import { actionCreators as photoActions } from "redux/modules/photos"; 4 | 5 | const mapDispatchToProps = (dispatch, ownProps) => { 6 | return { 7 | submitComment: message => { 8 | dispatch(photoActions.commentPhoto(ownProps.photoId, message)); 9 | } 10 | }; 11 | }; 12 | 13 | export default connect(null, mapDispatchToProps)(Container); 14 | -------------------------------------------------------------------------------- /nomadgram/notifications/serializers.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers 2 | from . import models 3 | from nomadgram.users import serializers as user_serializers 4 | from nomadgram.images import serializers as image_serializers 5 | 6 | 7 | class NotificationSerializer(serializers.ModelSerializer): 8 | 9 | creator = user_serializers.ListUserSerializer() 10 | image = image_serializers.SmallImageSerializer() 11 | 12 | class Meta: 13 | model = models.Notification 14 | fields = '__all__' 15 | -------------------------------------------------------------------------------- /nomadgram/templates/account/verification_sent.html: -------------------------------------------------------------------------------- 1 | {% extends "account/base.html" %} 2 | 3 | {% load i18n %} 4 | 5 | {% block head_title %}{% trans "Verify Your E-mail Address" %}{% endblock %} 6 | 7 | {% block inner %} 8 |

{% trans "Verify Your E-mail Address" %}

9 | 10 |

{% blocktrans %}We have sent an e-mail to you for verification. Follow the link provided to finalize the signup process. Please contact us if you do not receive it within a few minutes.{% endblocktrans %}

11 | 12 | {% endblock %} 13 | 14 | -------------------------------------------------------------------------------- /nomadgram/templates/users/user_list.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% load static i18n %} 3 | {% block title %}Members{% endblock %} 4 | 5 | {% block content %} 6 |
7 |

Users

8 | 9 |
10 | {% for user in user_list %} 11 | 12 |

{{ user.username }}

13 |
14 | {% endfor %} 15 |
16 |
17 | {% endblock content %} 18 | -------------------------------------------------------------------------------- /frontend/src/translations.js: -------------------------------------------------------------------------------- 1 | export const translations = { 2 | 'es': { 3 | 'Have an account?': 'Tienes una cuenta?', 4 | 'Log in': 'Iniciar Sesión', 5 | 'Don\'t have an account?': 'No tienes una cuenta?', 6 | 'Sign up': '', 7 | 'Get the app': '', 8 | 'About Us': '', 9 | 'Support': '', 10 | 'Blog': '', 11 | 'Press': '', 12 | 'API': '', 13 | 'Jobs': '', 14 | 'Privacy': '', 15 | 'Terms': '', 16 | 'Directory': '', 17 | 'Language': '', 18 | }, 19 | 'options': { 20 | }, 21 | } 22 | -------------------------------------------------------------------------------- /nomadgram/images/migrations/0007_auto_20171014_2122.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.8 on 2017-10-14 12:22 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('images', '0006_auto_20171014_2026'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterModelOptions( 16 | name='image', 17 | options={'ordering': ['-created_at']}, 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /nomadgram/templates/users/user_form.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% load crispy_forms_tags %} 3 | 4 | {% block title %}{{ user.username }}{% endblock %} 5 | 6 | {% block content %} 7 |

{{ user.username }}

8 |
9 | {% csrf_token %} 10 | {{ form|crispy }} 11 |
12 |
13 | 14 |
15 |
16 |
17 | {% endblock %} 18 | -------------------------------------------------------------------------------- /nomadgram/users/tests/test_models.py: -------------------------------------------------------------------------------- 1 | from test_plus.test import TestCase 2 | 3 | 4 | class TestUser(TestCase): 5 | 6 | def setUp(self): 7 | self.user = self.make_user() 8 | 9 | def test__str__(self): 10 | self.assertEqual( 11 | self.user.__str__(), 12 | 'testuser' # This is the default username for self.make_user() 13 | ) 14 | 15 | def test_get_absolute_url(self): 16 | self.assertEqual( 17 | self.user.get_absolute_url(), 18 | '/users/testuser/' 19 | ) 20 | -------------------------------------------------------------------------------- /nomadgram/templates/account/password_set.html: -------------------------------------------------------------------------------- 1 | {% extends "account/base.html" %} 2 | 3 | {% load i18n %} 4 | {% load crispy_forms_tags %} 5 | 6 | {% block head_title %}{% trans "Set Password" %}{% endblock %} 7 | 8 | {% block inner %} 9 |

{% trans "Set Password" %}

10 | 11 |
12 | {% csrf_token %} 13 | {{ form|crispy }} 14 | 15 |
16 | {% endblock %} 17 | 18 | -------------------------------------------------------------------------------- /nomadgram/notifications/migrations/0005_auto_20171015_2254.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.8 on 2017-10-15 13:54 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('notifications', '0004_notification_comment'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterModelOptions( 16 | name='notification', 17 | options={'ordering': ['-created_at']}, 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /nomadgram/users/migrations/0006_user_push_token.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.8 on 2018-01-18 09:56 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('users', '0005_auto_20171015_0201'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='user', 17 | name='push_token', 18 | field=models.TextField(default=''), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /frontend/src/components/Feed/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from "react-redux"; 2 | import { actionCreators as photoActions } from "redux/modules/photos"; 3 | import Container from "./container"; 4 | 5 | const mapStateToProps = (state, ownProps) => { 6 | const { photos: { feed } } = state; 7 | return { 8 | feed 9 | }; 10 | }; 11 | 12 | const mapDispatchToProps = (dispatch, ownProps) => { 13 | return { 14 | getFeed: () => { 15 | dispatch(photoActions.getFeed()); 16 | } 17 | }; 18 | }; 19 | 20 | export default connect(mapStateToProps, mapDispatchToProps)(Container); 21 | -------------------------------------------------------------------------------- /frontend/src/components/LoginForm/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from "react-redux"; 2 | import Container from "./container"; 3 | import { actionCreators as userActions } from "redux/modules/user"; 4 | 5 | const mapDispatchToProps = (dispatch, ownProps) => { 6 | return { 7 | facebookLogin: access_token => { 8 | dispatch(userActions.facebookLogin(access_token)); 9 | }, 10 | usernameLogin: (email, password) => { 11 | dispatch(userActions.usernameLogin(email, password)); 12 | } 13 | }; 14 | }; 15 | 16 | export default connect(null, mapDispatchToProps)(Container); 17 | -------------------------------------------------------------------------------- /nomadgram/templates/account/password_reset_done.html: -------------------------------------------------------------------------------- 1 | {% extends "account/base.html" %} 2 | 3 | {% load i18n %} 4 | {% load account %} 5 | 6 | {% block head_title %}{% trans "Password Reset" %}{% endblock %} 7 | 8 | {% block inner %} 9 |

{% trans "Password Reset" %}

10 | 11 | {% if user.is_authenticated %} 12 | {% include "account/snippets/already_logged_in.html" %} 13 | {% endif %} 14 | 15 |

{% blocktrans %}We have sent you an e-mail. Please contact us if you do not receive it within a few minutes.{% endblocktrans %}

16 | {% endblock %} 17 | 18 | -------------------------------------------------------------------------------- /nomadgram/static/css/project.css: -------------------------------------------------------------------------------- 1 | /* These styles are generated from project.scss. */ 2 | 3 | .alert-debug { 4 | color: black; 5 | background-color: white; 6 | border-color: #d6e9c6; 7 | } 8 | 9 | .alert-error { 10 | color: #b94a48; 11 | background-color: #f2dede; 12 | border-color: #eed3d7; 13 | } 14 | 15 | /* Display django-debug-toolbar. 16 | See https://github.com/django-debug-toolbar/django-debug-toolbar/issues/742 17 | and https://github.com/pydanny/cookiecutter-django/issues/317 18 | */ 19 | [hidden][style="display: block;"] { 20 | display: block !important; 21 | } 22 | -------------------------------------------------------------------------------- /nomadgram/users/migrations/0004_user_profile_image.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.8 on 2017-10-14 12:22 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('users', '0003_auto_20171013_0216'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='user', 17 | name='profile_image', 18 | field=models.ImageField(null=True, upload_to=''), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /frontend/src/components/Explore/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from "react-redux"; 2 | import { actionCreators as userActions } from "redux/modules/user"; 3 | import Container from "./container"; 4 | 5 | const mapStateToProps = (state, ownProps) => { 6 | const { user: { userList } } = state; 7 | return { 8 | userList 9 | }; 10 | }; 11 | 12 | const mapDispatchToProps = (dispatch, ownProps) => { 13 | return { 14 | getExplore: () => { 15 | dispatch(userActions.getExplore()); 16 | } 17 | }; 18 | }; 19 | 20 | export default connect(mapStateToProps, mapDispatchToProps)(Container); 21 | -------------------------------------------------------------------------------- /frontend/src/components/PhotoActions/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from "react-redux"; 2 | import Container from "./container"; 3 | import { actionCreators as photoActions } from "redux/modules/photos"; 4 | 5 | const mapDispatchToProps = (dispatch, ownProps) => { 6 | return { 7 | handleHeartClick: () => { 8 | if (ownProps.isLiked) { 9 | dispatch(photoActions.unlikePhoto(ownProps.photoId)); 10 | } else { 11 | dispatch(photoActions.likePhoto(ownProps.photoId)); 12 | } 13 | } 14 | }; 15 | }; 16 | 17 | export default connect(null, mapDispatchToProps)(Container); 18 | -------------------------------------------------------------------------------- /frontend/src/components/UserDisplay/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from "react-redux"; 2 | import Container from "./container"; 3 | import { actionCreators as userAction } from "redux/modules/user"; 4 | 5 | const mapDispatchToProps = (dispatch, ownProps) => { 6 | const { user } = ownProps; 7 | return { 8 | handleClick: () => { 9 | if (user.following) { 10 | dispatch(userAction.unfollowUser(user.id)); 11 | } else { 12 | dispatch(userAction.followUser(user.id)); 13 | } 14 | } 15 | }; 16 | }; 17 | 18 | export default connect(null, mapDispatchToProps)(Container); 19 | -------------------------------------------------------------------------------- /nomadgram/templates/account/password_change.html: -------------------------------------------------------------------------------- 1 | {% extends "account/base.html" %} 2 | 3 | {% load i18n %} 4 | {% load crispy_forms_tags %} 5 | 6 | {% block head_title %}{% trans "Change Password" %}{% endblock %} 7 | 8 | {% block inner %} 9 |

{% trans "Change Password" %}

10 | 11 |
12 | {% csrf_token %} 13 | {{ form|crispy }} 14 | 15 |
16 | {% endblock %} 17 | 18 | -------------------------------------------------------------------------------- /.ebextensions/20_python.config: -------------------------------------------------------------------------------- 1 | container_commands: 2 | 01_migrate: 3 | command: "source /opt/python/run/venv/bin/activate && python manage.py migrate --noinput" 4 | leader_only: True 5 | 02_collectstatic: 6 | command: "source /opt/python/run/venv/bin/activate && python manage.py collectstatic --noinput" 7 | 03_wsgireplace: 8 | command: 'cp .ebextensions/wsgi.conf ../wsgi.conf' 9 | option_settings: 10 | "aws:elasticbeanstalk:application:environment": 11 | DJANGO_SETTINGS_MODULE: "config.settings.production" 12 | "aws:elasticbeanstalk:container:python": 13 | WSGIPath: "config/wsgi.py" -------------------------------------------------------------------------------- /nomadgram/notifications/migrations/0004_notification_comment.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.8 on 2017-10-15 09:22 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('notifications', '0003_auto_20171015_1806'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='notification', 17 | name='comment', 18 | field=models.TextField(blank=True, null=True), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /requirements/production.txt: -------------------------------------------------------------------------------- 1 | # Pro-tip: Try not to put anything here. Avoid dependencies in 2 | # production that aren't in development. 3 | -r base.txt 4 | 5 | 6 | 7 | # WSGI Handler 8 | # ------------------------------------------------ 9 | gevent==1.2.2 10 | gunicorn==19.7.1 11 | 12 | # Static and Media Storage 13 | # ------------------------------------------------ 14 | boto3==1.4.7 15 | django-storages==1.6.5 16 | Collectfast==0.5.2 17 | 18 | # Email backends for Mailgun, Postmark, SendGrid and more 19 | # ------------------------------------------------------- 20 | django-anymail==1.0 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /frontend/src/components/SignupForm/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from "react-redux"; 2 | import Container from "./container"; 3 | import { actionCreators as userActions } from "redux/modules/user"; 4 | 5 | const mapDispatchToProps = (dispatch, ownProps) => { 6 | return { 7 | facebookLogin: access_token => { 8 | dispatch(userActions.facebookLogin(access_token)); 9 | }, 10 | createAccount: (username, password, email, name) => { 11 | dispatch(userActions.createAccount(username, password, email, name)); 12 | } 13 | }; 14 | }; 15 | 16 | export default connect(null, mapDispatchToProps)(Container); 17 | -------------------------------------------------------------------------------- /nomadgram/images/migrations/0009_auto_20180118_1856.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.8 on 2018-01-18 09:56 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations 6 | import imagekit.models.fields 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [ 12 | ('images', '0008_image_tags'), 13 | ] 14 | 15 | operations = [ 16 | migrations.AlterField( 17 | model_name='image', 18 | name='file', 19 | field=imagekit.models.fields.ProcessedImageField(upload_to=''), 20 | ), 21 | ] 22 | -------------------------------------------------------------------------------- /nomadgram/views.py: -------------------------------------------------------------------------------- 1 | from django.views.generic import View 2 | from django.http import HttpResponse 3 | from django.conf import settings 4 | import os 5 | 6 | 7 | class ReactAppView(View): 8 | 9 | def get(self, request): 10 | try: 11 | with open(os.path.join(str(settings.ROOT_DIR), 'frontend', 'build', 'index.html')) as file: 12 | return HttpResponse(file.read()) 13 | except: 14 | return HttpResponse( 15 | """ 16 | index.html not found ! build your React app !! 17 | """, 18 | status=501, 19 | ) 20 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | charset = utf-8 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.{py,rst,ini}] 12 | indent_style = space 13 | indent_size = 4 14 | 15 | [*.py] 16 | line_length=120 17 | known_first_party=nomadgram 18 | multi_line_output=3 19 | default_section=THIRDPARTY 20 | 21 | [*.{html,css,scss,json,yml}] 22 | indent_style = space 23 | indent_size = 2 24 | 25 | [*.md] 26 | trim_trailing_whitespace = false 27 | 28 | [Makefile] 29 | indent_style = tab 30 | 31 | [nginx.conf] 32 | indent_style = space 33 | indent_size = 2 34 | -------------------------------------------------------------------------------- /nomadgram/contrib/sites/migrations/0002_alter_domain_unique.py: -------------------------------------------------------------------------------- 1 | import django.contrib.sites.models 2 | from django.db import migrations, models 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [ 8 | ('sites', '0001_initial'), 9 | ] 10 | 11 | operations = [ 12 | migrations.AlterField( 13 | model_name='site', 14 | name='domain', 15 | field=models.CharField( 16 | max_length=100, unique=True, validators=[django.contrib.sites.models._simple_domain_name_validator], 17 | verbose_name='domain name' 18 | ), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /nomadgram/templates/account/logout.html: -------------------------------------------------------------------------------- 1 | {% extends "account/base.html" %} 2 | 3 | {% load i18n %} 4 | 5 | {% block head_title %}{% trans "Sign Out" %}{% endblock %} 6 | 7 | {% block inner %} 8 |

{% trans "Sign Out" %}

9 | 10 |

{% trans 'Are you sure you want to sign out?' %}

11 | 12 |
13 | {% csrf_token %} 14 | {% if redirect_field_value %} 15 | 16 | {% endif %} 17 | 18 |
19 | 20 | 21 | {% endblock %} 22 | 23 | -------------------------------------------------------------------------------- /frontend/src/components/UserList/container.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import UserList from "./presenter"; 3 | 4 | class Container extends Component { 5 | state = { 6 | loading: true 7 | }; 8 | componentDidMount() { 9 | const { userList } = this.props; 10 | if (userList) { 11 | this.setState({ loading: false }); 12 | } 13 | } 14 | componentWillReceiveProps(nextProps) { 15 | if (nextProps.userList) { 16 | this.setState({ 17 | loading: false 18 | }); 19 | } 20 | } 21 | 22 | render() { 23 | return ; 24 | } 25 | } 26 | 27 | export default Container; 28 | -------------------------------------------------------------------------------- /frontend/src/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | import { Provider } from "react-redux"; 4 | import I18n from "redux-i18n"; 5 | import { ConnectedRouter } from "react-router-redux"; 6 | import store, { history } from "redux/configureStore"; 7 | import App from "components/App"; 8 | import { translations } from "translations"; 9 | 10 | ReactDOM.render( 11 | 12 | 13 | 14 | 15 | 16 | 17 | , 18 | document.getElementById("root") 19 | ); 20 | -------------------------------------------------------------------------------- /frontend/build/index.html: -------------------------------------------------------------------------------- 1 | Nomadgram
-------------------------------------------------------------------------------- /frontend/src/components/Search/styles.scss: -------------------------------------------------------------------------------- 1 | .search { 2 | display: flex; 3 | flex-direction: column; 4 | width: 100%; 5 | max-width: $max-page-width; 6 | margin: 0 auto; 7 | align-items: center; 8 | .section { 9 | width: 100%; 10 | margin-bottom: 80px; 11 | .title { 12 | text-align: left; 13 | color: $dark-grey; 14 | font-weight: 600; 15 | margin-bottom: 20px; 16 | } 17 | .not-found { 18 | text-align: center; 19 | width: 100%; 20 | display: block; 21 | font-weight: 600; 22 | } 23 | .content { 24 | display: flex; 25 | justify-content: space-between; 26 | flex-wrap: wrap; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /frontend/config/polyfills.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | if (typeof Promise === 'undefined') { 4 | // Rejection tracking prevents a common issue where React gets into an 5 | // inconsistent state due to an error, but it gets swallowed by a Promise, 6 | // and the user has no idea what causes React's erratic future behavior. 7 | require('promise/lib/rejection-tracking').enable(); 8 | window.Promise = require('promise/lib/es6-extensions.js'); 9 | } 10 | 11 | // fetch() polyfill for making API calls. 12 | require('whatwg-fetch'); 13 | 14 | // Object.assign() is commonly used with React. 15 | // It will use the native implementation if it's present and isn't buggy. 16 | Object.assign = require('object-assign'); 17 | -------------------------------------------------------------------------------- /nomadgram/notifications/migrations/0003_auto_20171015_1806.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.8 on 2017-10-15 09:06 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | import django.db.models.deletion 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [ 12 | ('notifications', '0002_auto_20171015_1805'), 13 | ] 14 | 15 | operations = [ 16 | migrations.AlterField( 17 | model_name='notification', 18 | name='image', 19 | field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='images.Image'), 20 | ), 21 | ] 22 | -------------------------------------------------------------------------------- /frontend/src/components/Search/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from "react-redux"; 2 | import { actionCreators as userActions } from "redux/modules/user"; 3 | import Container from "./container"; 4 | 5 | const mapStateToProps = (state, ownProps) => { 6 | const { user: { userList, imageList }, routing: { location } } = state; 7 | return { 8 | userList, 9 | imageList, 10 | location 11 | }; 12 | }; 13 | 14 | const mapDispatchToProps = (dispatch, ownProps) => { 15 | const { match: { params: { searchTerm } } } = ownProps; 16 | return { 17 | searchByTerm: () => { 18 | dispatch(userActions.searchByTerm(searchTerm)); 19 | } 20 | }; 21 | }; 22 | 23 | export default connect(mapStateToProps, mapDispatchToProps)(Container); 24 | -------------------------------------------------------------------------------- /nomadgram/images/migrations/0006_auto_20171014_2026.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.8 on 2017-10-14 11:26 3 | from __future__ import unicode_literals 4 | 5 | from django.conf import settings 6 | from django.db import migrations, models 7 | import django.db.models.deletion 8 | 9 | 10 | class Migration(migrations.Migration): 11 | 12 | dependencies = [ 13 | ('images', '0005_auto_20171014_0019'), 14 | ] 15 | 16 | operations = [ 17 | migrations.AlterField( 18 | model_name='image', 19 | name='creator', 20 | field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='images', to=settings.AUTH_USER_MODEL), 21 | ), 22 | ] 23 | -------------------------------------------------------------------------------- /nomadgram/images/migrations/0008_image_tags.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.8 on 2017-10-15 07:43 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations 6 | import taggit.managers 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [ 12 | ('taggit', '0002_auto_20150616_2121'), 13 | ('images', '0007_auto_20171014_2122'), 14 | ] 15 | 16 | operations = [ 17 | migrations.AddField( 18 | model_name='image', 19 | name='tags', 20 | field=taggit.managers.TaggableManager(help_text='A comma-separated list of tags.', through='taggit.TaggedItem', to='taggit.Tag', verbose_name='Tags'), 21 | ), 22 | ] 23 | -------------------------------------------------------------------------------- /frontend/src/components/Auth/container.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import Auth from "./presenter"; 3 | 4 | class Container extends Component { 5 | state = { 6 | action: "login" 7 | }; 8 | render() { 9 | const { action } = this.state; 10 | return ; 11 | } 12 | _changeAction = () => { 13 | this.setState(prevState => { 14 | const { action } = prevState; 15 | if (action === "login") { 16 | return { 17 | action: "signup" 18 | }; 19 | } else if (action === "signup") { 20 | return { 21 | action: "login" 22 | }; 23 | } 24 | }); 25 | }; 26 | } 27 | 28 | export default Container; 29 | -------------------------------------------------------------------------------- /nomadgram/templates/account/signup.html: -------------------------------------------------------------------------------- 1 | {% extends "account/base.html" %} 2 | 3 | {% load i18n %} 4 | {% load crispy_forms_tags %} 5 | 6 | {% block head_title %}{% trans "Signup" %}{% endblock %} 7 | 8 | {% block inner %} 9 |

{% trans "Sign Up" %}

10 | 11 |

{% blocktrans %}Already have an account? Then please sign in.{% endblocktrans %}

12 | 13 | 21 | 22 | {% endblock %} 23 | 24 | -------------------------------------------------------------------------------- /frontend/src/components/FeedPhoto/container.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import FeedPhoto from "./presenter"; 3 | 4 | class Container extends Component { 5 | state = { 6 | seeingLikes: false 7 | }; 8 | render() { 9 | return ( 10 | 16 | ); 17 | } 18 | _openLikes = () => { 19 | const { getPhotoLikes, likes } = this.props; 20 | this.setState({ 21 | seeingLikes: true 22 | }); 23 | if (!likes) { 24 | getPhotoLikes(); 25 | } 26 | }; 27 | _closeLikes = () => { 28 | this.setState({ 29 | seeingLikes: false 30 | }); 31 | }; 32 | } 33 | 34 | export default Container; 35 | -------------------------------------------------------------------------------- /frontend/src/components/Explore/container.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import PropTypes from "prop-types"; 3 | import Explore from "./presenter"; 4 | 5 | class Container extends Component { 6 | state = { 7 | loading: true 8 | }; 9 | static propTypes = { 10 | getExplore: PropTypes.func.isRequired, 11 | userList: PropTypes.array 12 | }; 13 | componentDidMount() { 14 | const { getExplore } = this.props; 15 | getExplore(); 16 | } 17 | componentWillReceiveProps = nextProps => { 18 | if (nextProps.userList) { 19 | this.setState({ 20 | loading: false 21 | }); 22 | } 23 | }; 24 | render() { 25 | const { userList } = this.props; 26 | return ; 27 | } 28 | } 29 | 30 | export default Container; 31 | -------------------------------------------------------------------------------- /nomadgram/users/signals.py: -------------------------------------------------------------------------------- 1 | from allauth.account.signals import user_signed_up 2 | from django.dispatch import receiver 3 | from io import BytesIO 4 | from urllib.request import urlopen 5 | from django.core.files import File 6 | 7 | 8 | @receiver(user_signed_up) 9 | def user_signed_up(request, user, **kwargs): 10 | if len(user.socialaccount_set.all()) > 0: 11 | social_account = user.socialaccount_set.all()[0] 12 | uid = social_account.uid 13 | gender = social_account.extra_data.get('gender', None) 14 | user.gender = gender 15 | avatar = social_account.get_avatar_url() 16 | avatar_image = urlopen(avatar) 17 | io = BytesIO(avatar_image.read()) 18 | user.profile_image.save('{}.jpg'.format(uid), File(io)) 19 | user.name = user.get_full_name() 20 | user.save() 21 | -------------------------------------------------------------------------------- /frontend/src/components/Feed/presenter.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | import styles from "./styles.scss"; 4 | import Loading from "components/Loading"; 5 | import FeedPhoto from "components/FeedPhoto"; 6 | 7 | const Feed = props => { 8 | if (props.loading) { 9 | return ; 10 | } else if (props.feed) { 11 | return ; 12 | } 13 | }; 14 | 15 | const LoadingFeed = props => ( 16 |
17 | 18 |
19 | ); 20 | 21 | const RenderFeed = props => ( 22 |
23 | {props.feed.map(photo => )} 24 |
25 | ); 26 | 27 | Feed.propTypes = { 28 | loading: PropTypes.bool.isRequired, 29 | feed: PropTypes.array 30 | }; 31 | 32 | export default Feed; 33 | -------------------------------------------------------------------------------- /frontend/src/components/CommentBox/presenter.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | import Textarea from "react-textarea-autosize"; 4 | import styles from "./styles.scss"; 5 | 6 | const CommentBox = (props, context) => ( 7 |
8 |