From 68aa108310eb8316ddded618646f4014bdd53983 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=AD=C3=B0ir=20Valberg=20Gu=C3=B0mundsson?= Date: Sun, 16 Apr 2017 02:10:24 +0200 Subject: [PATCH] Adding favoriting to the schedule. --- src/program/admin.py | 7 +++- src/program/consumers.py | 21 ++++++++-- src/program/migrations/0038_favorite.py | 26 +++++++++++++ src/program/models.py | 18 ++++++++- src/program/templates/schedule_overview.html | 40 ++++++++++++++++++-- 5 files changed, 103 insertions(+), 9 deletions(-) create mode 100644 src/program/migrations/0038_favorite.py diff --git a/src/program/admin.py b/src/program/admin.py index 81e2ee27..d72759d5 100644 --- a/src/program/admin.py +++ b/src/program/admin.py @@ -1,6 +1,6 @@ from django.contrib import admin -from .models import Event, Speaker, EventType, EventInstance, EventLocation, SpeakerProposal, EventProposal +from .models import Event, Speaker, EventType, EventInstance, EventLocation, SpeakerProposal, EventProposal, Favorite @admin.register(SpeakerProposal) @@ -43,6 +43,11 @@ class SpeakerAdmin(admin.ModelAdmin): pass +@admin.register(Favorite) +class FavoriteAdmin(admin.ModelAdmin): + raw_id_fields = ('event_instance',) + + class SpeakerInline(admin.StackedInline): model = Speaker.events.through diff --git a/src/program/consumers.py b/src/program/consumers.py index 3b814314..0501ea9f 100644 --- a/src/program/consumers.py +++ b/src/program/consumers.py @@ -1,6 +1,6 @@ from channels.generic.websockets import JsonWebsocketConsumer -from .models import EventInstance +from .models import EventInstance, Favorite class ScheduleConsumer(JsonWebsocketConsumer): @@ -12,7 +12,8 @@ class ScheduleConsumer(JsonWebsocketConsumer): def connect(self, message, **kwargs): self.send({"accept": True}) - def receive(self, content, **kwargs): + def raw_receive(self, message, **kwargs): + content = self.decode_json(message['text']) action = content.get('action') data = {} @@ -20,7 +21,21 @@ class ScheduleConsumer(JsonWebsocketConsumer): event_instance_id = content.get('event_instance_id') event_instance = EventInstance.objects.get(id=event_instance_id) data['action'] = 'event_instance' - data['event_instance'] = event_instance.to_json() + data['event_instance'] = event_instance.to_json(user=message.user) + + if action == 'favorite': + event_instance_id = content.get('event_instance_id') + event_instance = EventInstance.objects.get(id=event_instance_id) + Favorite.objects.create( + user=message.user, + event_instance=event_instance + ) + + if action == 'unfavorite': + event_instance_id = content.get('event_instance_id') + event_instance = EventInstance.objects.get(id=event_instance_id) + favorite = Favorite.objects.get(event_instance=event_instance, user=message.user) + favorite.delete() self.send(data) diff --git a/src/program/migrations/0038_favorite.py b/src/program/migrations/0038_favorite.py new file mode 100644 index 00000000..0b9b8334 --- /dev/null +++ b/src/program/migrations/0038_favorite.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11 on 2017-04-15 23:21 +from __future__ import unicode_literals + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('program', '0037_eventtype_include_in_event_list'), + ] + + operations = [ + migrations.CreateModel( + name='Favorite', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('event_instance', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='program.EventInstance')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='favorites', to=settings.AUTH_USER_MODEL)), + ], + ), + ] diff --git a/src/program/models.py b/src/program/models.py index a71b4278..d64892e3 100644 --- a/src/program/models.py +++ b/src/program/models.py @@ -449,12 +449,13 @@ class EventInstance(CampRelatedModel): ievent['location'] = icalendar.vText(self.location.name) return ievent - def to_json(self): + def to_json(self, user=None): parser = CommonMark.Parser() renderer = CommonMark.HtmlRenderer() ast = parser.parse(self.event.abstract) abstract = renderer.render(ast) - return { + + data = { 'title': self.event.title, 'event_slug': self.event.slug, 'abstract': abstract, @@ -464,6 +465,12 @@ class EventInstance(CampRelatedModel): 'id': self.id, } + if user: + is_favorited = user.favorites.filter(event_instance=self).exists() + data['is_favorited'] = is_favorited + + return data + def get_speaker_picture_upload_path(instance, filename): @@ -543,3 +550,10 @@ class Speaker(CampRelatedModel): return reverse_lazy('speaker_detail', kwargs={'camp_slug': self.camp.slug, 'slug': self.slug}) +class Favorite(models.Model): + user = models.ForeignKey('auth.User', related_name='favorites') + event_instance = models.ForeignKey('program.EventInstance') + + class Meta: + unique_together = ['user', 'event_instance'] + diff --git a/src/program/templates/schedule_overview.html b/src/program/templates/schedule_overview.html index ba87c03d..23f8cc7e 100644 --- a/src/program/templates/schedule_overview.html +++ b/src/program/templates/schedule_overview.html @@ -38,7 +38,7 @@ @@ -58,7 +58,31 @@ var event_elements = document.getElementsByClassName("event"); var modals = {}; - var events = {}; + + function toggleFavoriteButton(button) { + if(button.getAttribute('data-state') == 'true') { + favorite_button.classList.remove('btn-success'); + favorite_button.classList.add('btn-danger'); + favorite_button.innerHTML = ' Remove favorite'; + + favorite_button.onclick = function(e) { + button.setAttribute('data-state', 'false') + webSocketBridge.send({action: 'unfavorite', event_instance_id: event_instance_id}); + toggleFavoriteButton(button) + } + } else { + favorite_button.classList.remove('btn-danger'); + favorite_button.classList.add('btn-success'); + favorite_button.innerHTML = ' Favorite'; + + favorite_button.onclick = function(e) { + button.setAttribute('data-state', 'true') + webSocketBridge.send({action: 'favorite', event_instance_id: event_instance_id}); + toggleFavoriteButton(button) + } + + } + } webSocketBridge.connect('/schedule/'); webSocketBridge.socket.addEventListener('open', function() { @@ -74,12 +98,22 @@ modal_body.innerHTML = payload['event_instance']['abstract']; more_button = modal.getElementsByClassName('more-button')[0]; more_button.setAttribute('href', payload['event_instance']['url']); + favorite_button = modal.getElementsByClassName('favorite-button')[0]; + favorite_button.setAttribute('data-state', payload['event_instance']['is_favorited']) + toggleFavoriteButton(favorite_button) } }); function openModal(e) { e.preventDefault(); - event_instance_id = e.target.dataset['eventinstanceId']; + + // Avoid that clicking the text in the event will bring up an empty modal + target = e.target; + if (e.target !== this) { + target = e.target.parentElement + } + + event_instance_id = target.dataset['eventinstanceId']; modal = modals[event_instance_id];