Skip to content

Commit

Permalink
Here's some fixed UI and logic that can be pushed:
Browse files Browse the repository at this point in the history
- Added password change functionality.
- vitorfs#167 Restored the old crop profile image section and fixed profile images aspect ratio.
- Changed Notifications and News UI to a better looking.
- Added Bell notification with number count.
- Changed delete news posts to be removed by Ajax request.
- Updated migrations so ImageField is not anymore in User model (since upload mage has changed)
- vitorfs#222 Added links in Notifications and fixed 'replied to' messages (still have to put the right addres to the links)
  • Loading branch information
Gustavo Bakker authored and Gustavo Bakker committed Apr 13, 2020
1 parent 59c2cb8 commit e157163
Show file tree
Hide file tree
Showing 48 changed files with 1,305 additions and 742 deletions.
Empty file modified bootcamp/articles/migrations/0001_initial.py
100755 → 100644
Empty file.
Empty file modified bootcamp/contrib/sites/migrations/0001_initial.py
100755 → 100644
Empty file.
Empty file modified bootcamp/contrib/sites/migrations/0002_alter_domain_unique.py
100755 → 100644
Empty file.
Empty file.
Empty file modified bootcamp/messager/migrations/0001_initial.py
100755 → 100644
Empty file.
4 changes: 2 additions & 2 deletions bootcamp/messager/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,6 @@ def send_message(sender, recipient, message):
"recipient": str(recipient),
}
transaction.on_commit(
lambda: async_to_sync(channel_layer.group_send)(recipient.username,
payload))
lambda: async_to_sync(channel_layer.group_send)(recipient.username, payload)
)
return new_message
Empty file modified bootcamp/news/migrations/0001_initial.py
100755 → 100644
Empty file.
2 changes: 1 addition & 1 deletion bootcamp/news/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ def reply_this(self, user, text):
user,
parent.user,
Notification.REPLY,
action_object=reply_news,
action_object=reply_news.parent,
id_value=str(parent.uuid_id),
key="social_update",
)
Expand Down
7 changes: 4 additions & 3 deletions bootcamp/news/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@
app_name = "news"
urlpatterns = [
url(r"^$", views.NewsListView.as_view(), name="list"),
url(
r"^delete/(?P<pk>[-\w]+)/$", views.NewsDeleteView.as_view(), name="delete_news"
),
# url(
# r"^delete/(?P<pk>[-\w]+)/$", views.NewsDeleteView.as_view(), name="delete_news"
# ),
url(r"^remove/$", views.remove_news, name="remove_news"),
url(r"^post-news/$", views.post_news, name="post_news"),
url(r"^like/$", views.like, name="like_post"),
url(r"^get-thread/$", views.get_thread, name="get_thread"),
Expand Down
28 changes: 25 additions & 3 deletions bootcamp/news/views.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin
from django.http import HttpResponse, HttpResponseBadRequest, JsonResponse
from django.http import HttpResponse, HttpResponseBadRequest, JsonResponse, HttpResponseForbidden
from django.template.loader import render_to_string
from django.urls import reverse_lazy
from django.utils.translation import ugettext_lazy as _
from django.views.decorators.http import require_http_methods
from django.views.generic import ListView, DeleteView

from django.template.context_processors import csrf
from bootcamp.helpers import ajax_required, AuthorRequiredMixin
from bootcamp.news.models import News

Expand Down Expand Up @@ -52,6 +52,28 @@ def post_news(request):
)


@login_required
@ajax_required
@require_http_methods(["POST"])
def remove_news(request):
try:
news_id = request.POST["news"]
feed = News.objects.get(pk=news_id)
if feed.user == request.user:
parent = feed.parent
feed.delete()
if parent:
parent.count_thread()

return HttpResponse()

else:
return HttpResponseForbidden()

except Exception:
return HttpResponseBadRequest()


@login_required
@ajax_required
@require_http_methods(["POST"])
Expand All @@ -70,7 +92,7 @@ def like(request):
@require_http_methods(["GET"])
def get_thread(request):
"""Returns a list of news with the given news as parent."""
news_id = request.GET["news"]
news_id = request.GET.get("news")
news = News.objects.get(pk=news_id)
news_html = render_to_string("news/news_single.html", {"news": news})
thread_html = render_to_string(
Expand Down
Empty file modified bootcamp/notifications/migrations/0001_initial.py
100755 → 100644
Empty file.
123 changes: 119 additions & 4 deletions bootcamp/notifications/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from django.contrib.contenttypes.models import ContentType
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.utils.html import escape

from asgiref.sync import async_to_sync

Expand Down Expand Up @@ -85,7 +86,7 @@ class Notification(models.Model):
NOTIFICATION_TYPES = (
(LIKED, _("liked")),
(COMMENTED, _("commented")),
(FAVORITED, _("cavorited")),
(FAVORITED, _("favorited")),
(ANSWERED, _("answered")),
(ACCEPTED_ANSWER, _("accepted")),
(EDITED_ARTICLE, _("edited")),
Expand All @@ -97,6 +98,18 @@ class Notification(models.Model):
(SIGNUP, _("created an account")),
(REPLY, _("replied to")),
)
_LIKED_TEMPLATE = '<a href="/{0}/">{1}</a> {2} <a href="/news/{3}/">{4}</a>' # noqa: E501
_COMMENTED_TEMPLATE = '<a href="/{0}/">{1}</a> {2} <a href="/news/{3}/">{4}</a>' # noqa: E501
_FAVORITED_TEMPLATE = '<a href="/{0}/">{1}</a> {2} <a href="/qa/{3}/">{4}</a>' # noqa: E501
_ANSWERED_TEMPLATE = '<a href="/{0}/">{1}</a> {2} <a href="/qa/{3}/">{4}</a>' # noqa: E501
_ACCEPTED_ANSWER_TEMPLATE = '<a href="/{0}/">{1}</a> {2} <a href="/qa/{3}/">{4}</a>' # noqa: E501
_UPVOTED_QUESTION_TEMPLATE = '<a href="/{0}/">{1}</a> {2} <a href="/qa/{3}/">{4}</a>' # noqa: E501
_UPVOTED_ANSWER_TEMPLATE = '<a href="/{0}/">{1}</a> {2} <a href="/qa/{3}/">{4}</a>' # noqa: E501
_EDITED_ARTICLE_TEMPLATE = '<a href="/{0}/">{1}</a> {2} <a href="/articles/{3}/">{4}</a>' # noqa: E501
_ALSO_COMMENTED_TEMPLATE = '<a href="/{0}/">{1}</a> {2} <a href="/news/{3}/">{4}</a>' # noqa: E501
_USER_LOGIN_TEMPLATE = '<a href="/{0}/">{1}</a> has just logged in.' # noqa: E501
_USER_LOGOUT_TEMPLATE = '<a href="/{0}/">{1}</a> has just logged out.' # noqa: E501

actor = models.ForeignKey(
settings.AUTH_USER_MODEL, related_name="notify_actor", on_delete=models.CASCADE
)
Expand Down Expand Up @@ -131,9 +144,111 @@ class Meta:

def __str__(self):
if self.action_object:
return f"{self.actor} {self.get_verb_display()} {self.action_object} {self.time_since()} ago"

return f"{self.actor} {self.get_verb_display()} {self.time_since()} ago"
if self.verb == self.LIKED:
return self._LIKED_TEMPLATE.format(
escape(self.actor),
escape(self.actor),
escape(self.get_verb_display()),
self.action_object_object_id,
escape(self.get_summary(self.action_object.content))
)

elif self.verb == self.REPLY:
return self._COMMENTED_TEMPLATE.format(
escape(self.actor),
escape(self.actor),
escape(self.get_verb_display()),
self.action_object_object_id,
escape(self.get_summary(self.action_object.content))
)

elif self.verb == self.COMMENTED:
return self._COMMENTED_TEMPLATE.format(
escape(self.actor),
escape(self.actor),
escape(self.get_verb_display()),
self.action_object_object_id,
escape(self.get_summary(self.action_object.content))
)

elif self.verb == self.FAVORITED:
return self._FAVORITED_TEMPLATE.format(
escape(self.actor),
escape(self.actor),
escape(self.get_verb_display()),
self.action_object_object_id,
escape(self.get_summary(self.action_object.content))
)

elif self.verb == self.ANSWERED:
return self._ANSWERED_TEMPLATE.format(
escape(self.actor),
escape(self.actor),
escape(self.get_verb_display()),
self.action_object_object_id,
escape(self.get_summary(self.action_object.content))
)

elif self.verb == self.ACCEPTED_ANSWER:
return self._ACCEPTED_ANSWER_TEMPLATE.format(
escape(self.actor),
escape(self.actor),
escape(self.get_verb_display()),
self.action_object_object_id,
escape(self.get_summary(self.action_object.content))
)

elif self.verb == self.EDITED_ARTICLE:
return self._EDITED_ARTICLE_TEMPLATE.format(
escape(self.actor),
escape(self.actor),
escape(self.get_verb_display()),
self.action_object_object_id,
escape(self.get_summary(self.action_object.content))
)

elif self.verb == self.ALSO_COMMENTED:
return self._ALSO_COMMENTED_TEMPLATE.format(
escape(self.actor),
escape(self.actor),
escape(self.get_verb_display()),
self.action_object_object_id,
escape(self.get_summary(self.action_object.content))
)

elif self.verb == self.LOGGED_IN:
return self._USER_LOGIN_TEMPLATE.format(
escape(self.actor),
escape(self.actor)
)

elif self.verb == self.LOGGED_OUT:
return self._USER_LOGOUT_TEMPLATE.format(
escape(self.actor),
escape(self.actor)
)

elif self.verb == self.VOTED:
return self._UPVOTED_QUESTION_TEMPLATE.format(
escape(self.actor),
escape(self.actor),
escape(self.get_verb_display()),
self.action_object_object_id,
escape(self.get_summary(self.action_object))
)

else:
return 'Ooops! Something went wrong.'
else:
return f"{self.actor} {self.get_verb_display()} {self.time_since()} ago"

def get_summary(self, value):
summary_size = 50
if len(value) > summary_size:
return '{0}...'.format(value[:summary_size])

else:
return value

def save(self, *args, **kwargs):
if not self.slug:
Expand Down
6 changes: 6 additions & 0 deletions bootcamp/notifications/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,16 @@
urlpatterns = [
url(r"^$", views.NotificationUnreadListView.as_view(), name="unread"),
url(r"^mark-as-read/(?P<slug>[-\w]+)/$", views.mark_as_read, name="mark_as_read"),
url(r"^mark_as_read_ajax/$", views.mark_as_read_ajax, name="mark_as_read_ajax"),
url(r"^mark-all-as-read/$", views.mark_all_as_read, name="mark_all_read"),
url(
r"^latest-notifications/$",
views.get_latest_notifications,
name="latest_notifications",
),
url(
r"^unread-notifications/$",
views.get_unread_notifications,
name="unread_notifications",
),
]
25 changes: 25 additions & 0 deletions bootcamp/notifications/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@
from django.contrib.auth.mixins import LoginRequiredMixin
from django.shortcuts import get_object_or_404, redirect, render
from django.utils.translation import ugettext_lazy as _
from django.views.decorators.http import require_http_methods
from django.views.generic import ListView
from django.http import JsonResponse, HttpResponseForbidden, HttpResponse, HttpResponseBadRequest

from bootcamp.helpers import ajax_required
from bootcamp.notifications.models import Notification


Expand Down Expand Up @@ -59,9 +62,31 @@ def mark_as_read(request, slug=None):
return redirect("notifications:unread")


@login_required
@ajax_required
@require_http_methods(["POST"])
def mark_as_read_ajax(request):
try:
slug = request.POST["slug"]
if slug:
notification = get_object_or_404(Notification, slug=slug)
notification.mark_as_read()

return HttpResponse()

except Exception:
return HttpResponseBadRequest()


@login_required
def get_latest_notifications(request):
notifications = request.user.notifications.get_most_recent()
return render(
request, "notifications/most_recent.html", {"notifications": notifications}
)


@login_required
def get_unread_notifications(request):
notifications = request.user.notifications.unread()
return JsonResponse({"unread_notifications": str(len(notifications))})
Empty file modified bootcamp/qa/migrations/0001_initial.py
100755 → 100644
Empty file.
40 changes: 35 additions & 5 deletions bootcamp/static/css/bootcamp.css
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ header .navbar-brand {
.page-header {
margin-top: 15px;
margin-bottom: 15px;
padding-bottom: 9px;
border-bottom: 1px solid #eee;
}

.page-header h1 {
Expand All @@ -32,16 +34,43 @@ header .navbar-brand {
padding: 1em 0;
}

#notifications {
.bell-notifications {
font-size: 1.5em;
padding: 13px;
color: #dddddd;
color: white;
text-decoration: none;
position: relative;
display: inline-block;
padding: 4px;
margin-right: 10px;
}

#notifications.new-notifications {
color: #428bca;
.bell-notifications .badge {
font-size: 50%;
position: absolute;
right: -5px;
border-radius: 50%;
background-color: red;
color: white;
}

.inbox-notifications {
font-size: 1.5em;
color: white;
text-decoration: none;
position: relative;
display: inline-block;
padding: 4px;
margin-right: 10px;
}

.inbox-notifications .badge {
font-size: 50%;
position: absolute;
right: -5px;
border-radius: 50%;
background-color: red;
color: white;
}

.alert-debug {
color: black;
Expand Down Expand Up @@ -86,3 +115,4 @@ header .navbar-brand {
margin-left: 45px;
margin-bottom: 0;
}

30 changes: 30 additions & 0 deletions bootcamp/static/css/jquery.Jcrop.min.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/* jquery.Jcrop.min.css v0.9.12 (build:20130126) */
.jcrop-holder{direction:ltr;text-align:left;}
.jcrop-vline,.jcrop-hline{background:#FFF url(../img/Jcrop.gif);font-size:0;position:absolute;}
.jcrop-vline{height:100%;width:1px!important;}
.jcrop-vline.right{right:0;}
.jcrop-hline{height:1px!important;width:100%;}
.jcrop-hline.bottom{bottom:0;}
.jcrop-tracker{-webkit-tap-highlight-color:transparent;-webkit-touch-callout:none;-webkit-user-select:none;height:100%;width:100%;}
.jcrop-handle{background-color:#333;border:1px #EEE solid;font-size:1px;height:7px;width:7px;}
.jcrop-handle.ord-n{left:50%;margin-left:-4px;margin-top:-4px;top:0;}
.jcrop-handle.ord-s{bottom:0;left:50%;margin-bottom:-4px;margin-left:-4px;}
.jcrop-handle.ord-e{margin-right:-4px;margin-top:-4px;right:0;top:50%;}
.jcrop-handle.ord-w{left:0;margin-left:-4px;margin-top:-4px;top:50%;}
.jcrop-handle.ord-nw{left:0;margin-left:-4px;margin-top:-4px;top:0;}
.jcrop-handle.ord-ne{margin-right:-4px;margin-top:-4px;right:0;top:0;}
.jcrop-handle.ord-se{bottom:0;margin-bottom:-4px;margin-right:-4px;right:0;}
.jcrop-handle.ord-sw{bottom:0;left:0;margin-bottom:-4px;margin-left:-4px;}
.jcrop-dragbar.ord-n,.jcrop-dragbar.ord-s{height:7px;width:100%;}
.jcrop-dragbar.ord-e,.jcrop-dragbar.ord-w{height:100%;width:7px;}
.jcrop-dragbar.ord-n{margin-top:-4px;}
.jcrop-dragbar.ord-s{bottom:0;margin-bottom:-4px;}
.jcrop-dragbar.ord-e{margin-right:-4px;right:0;}
.jcrop-dragbar.ord-w{margin-left:-4px;}
.jcrop-light .jcrop-vline,.jcrop-light .jcrop-hline{background:#FFF;filter:alpha(opacity=70)!important;opacity:.70!important;}
.jcrop-light .jcrop-handle{-moz-border-radius:3px;-webkit-border-radius:3px;background-color:#000;border-color:#FFF;border-radius:3px;}
.jcrop-dark .jcrop-vline,.jcrop-dark .jcrop-hline{background:#000;filter:alpha(opacity=70)!important;opacity:.7!important;}
.jcrop-dark .jcrop-handle{-moz-border-radius:3px;-webkit-border-radius:3px;background-color:#FFF;border-color:#000;border-radius:3px;}
.solid-line .jcrop-vline,.solid-line .jcrop-hline{background:#FFF;}
.jcrop-holder img,img.jcrop-preview{max-width:none;}
.jcrop-keymgr{display:none!important;}
Loading

0 comments on commit e157163

Please sign in to comment.