From b3cf2be29e4e75187995060e38567c79680043c0 Mon Sep 17 00:00:00 2001 From: Ansh Agrawal Date: Thu, 29 Aug 2024 13:29:24 -0400 Subject: [PATCH 01/11] Implement CSS dark mode --- tin/static/css/dark/base.css | 27 +++++++++++++++++++++++++++ tin/static/css/dark/edit.css | 3 +++ 2 files changed, 30 insertions(+) create mode 100644 tin/static/css/dark/base.css create mode 100644 tin/static/css/dark/edit.css diff --git a/tin/static/css/dark/base.css b/tin/static/css/dark/base.css new file mode 100644 index 00000000..3abdc90f --- /dev/null +++ b/tin/static/css/dark/base.css @@ -0,0 +1,27 @@ +#nav { + color: white; + background: #4fab4f; +} + +body { + background-color: #182c25; +} + +#footer { + background-color: #182c25; + border-top: #182c25; + color: white; +} + +#main { + color: white; +} + +ul#course-list > li a:not(.tin-btn), +ul#assignment-list > li a { + color: white; +} + +a:not(.tin-btn) { + color: white; +} diff --git a/tin/static/css/dark/edit.css b/tin/static/css/dark/edit.css new file mode 100644 index 00000000..7dbe4021 --- /dev/null +++ b/tin/static/css/dark/edit.css @@ -0,0 +1,3 @@ +.content .field { + color: black; +} From 8f0b573b8002b8c05ec8381c32ce1383b3c69d88 Mon Sep 17 00:00:00 2001 From: Ansh Agrawal Date: Tue, 3 Sep 2024 15:53:24 -0400 Subject: [PATCH 02/11] Implement dark mode --- tin/apps/users/forms.py | 4 ++ .../users/migrations/0003_user_dark_mode.py | 18 ++++++ tin/apps/users/models.py | 1 + tin/apps/users/urls.py | 11 ++++ tin/apps/users/views.py | 19 ++++++ tin/static/css/base.css | 17 ++++++ tin/templates/base.html | 59 +++++++++++++++++++ tin/urls.py | 1 + 8 files changed, 130 insertions(+) create mode 100644 tin/apps/users/migrations/0003_user_dark_mode.py create mode 100644 tin/apps/users/urls.py create mode 100644 tin/apps/users/views.py diff --git a/tin/apps/users/forms.py b/tin/apps/users/forms.py index 38c7ed1c..4d249155 100644 --- a/tin/apps/users/forms.py +++ b/tin/apps/users/forms.py @@ -6,3 +6,7 @@ class UserMultipleChoiceField(forms.ModelMultipleChoiceField): def label_from_instance(self, user): # pylint: disable=arguments-differ return f"{user.full_name} ({user.username})" + + +class ThemeForm(forms.Form): + dark_mode = forms.IntegerField() diff --git a/tin/apps/users/migrations/0003_user_dark_mode.py b/tin/apps/users/migrations/0003_user_dark_mode.py new file mode 100644 index 00000000..80174d87 --- /dev/null +++ b/tin/apps/users/migrations/0003_user_dark_mode.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.15 on 2024-09-03 19:43 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('users', '0002_remove_user_is_sysadmin'), + ] + + operations = [ + migrations.AddField( + model_name='user', + name='dark_mode', + field=models.IntegerField(default=0), + ), + ] diff --git a/tin/apps/users/models.py b/tin/apps/users/models.py index a5e90d56..3ed14353 100644 --- a/tin/apps/users/models.py +++ b/tin/apps/users/models.py @@ -30,6 +30,7 @@ class User(AbstractBaseUser, PermissionsMixin): is_teacher = models.BooleanField(default=False) is_student = models.BooleanField(default=False) date_joined = models.DateTimeField(default=timezone.now) + dark_mode = models.IntegerField(default=0) USERNAME_FIELD = "username" REQUIRED_FIELDS = ["email"] diff --git a/tin/apps/users/urls.py b/tin/apps/users/urls.py new file mode 100644 index 00000000..5d5e3334 --- /dev/null +++ b/tin/apps/users/urls.py @@ -0,0 +1,11 @@ +from __future__ import annotations + +from django.urls import path + +from . import views + +app_name = "users" + +urlpatterns = [ + path("theme/", views.change_theme, name="theme"), +] diff --git a/tin/apps/users/views.py b/tin/apps/users/views.py new file mode 100644 index 00000000..21568bbf --- /dev/null +++ b/tin/apps/users/views.py @@ -0,0 +1,19 @@ +from __future__ import annotations + +from django import http +from django.contrib.auth.decorators import login_required + +from tin.apps.users.forms import ThemeForm + + +@login_required +def change_theme(request): + if request.method == "POST": + form = ThemeForm(request.POST) + if form.is_valid(): + request.user.dark_mode = form.cleaned_data["dark_mode"] + request.user.save() + return http.JsonResponse({"success": True}) + else: + return http.JsonResponse({"success": False, "errors": form.errors}, status=400) + raise http.Http404 diff --git a/tin/static/css/base.css b/tin/static/css/base.css index b7c2a7f9..636a8e54 100644 --- a/tin/static/css/base.css +++ b/tin/static/css/base.css @@ -7,6 +7,7 @@ color: white; line-height: 40px; background: #4fab4f; + position: relative; } #nav ul { @@ -413,3 +414,19 @@ ul.errors { white-space: pre-wrap; } } + +#theme-toggle { + width: 30px; + height: 30px; +} + +.theme-toggle-button { + background: transparent; + border: none; + cursor: pointer; + padding: 0; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-85%, 12%); +} diff --git a/tin/templates/base.html b/tin/templates/base.html index 7629a206..99e89521 100644 --- a/tin/templates/base.html +++ b/tin/templates/base.html @@ -8,6 +8,10 @@ {% include "meta.html" %} + {% if request.user.dark_mode == 1 %} + + + {% endif %} @@ -44,8 +48,56 @@ $(".continuous-progress").css({height: "15px"}).progressbar({value: false}) }); + {% block head %}{% endblock %} +
+ + + Light mode + + + + + + + + + + + + + + + + Dark mode + + + + + + +
@@ -90,6 +142,13 @@ {% endif %}
  • Logout ({{ request.user.username }})
  • +
  • + +
  • {% endif %} diff --git a/tin/urls.py b/tin/urls.py index a7934de6..83a7f1d9 100644 --- a/tin/urls.py +++ b/tin/urls.py @@ -32,6 +32,7 @@ path("docs/", include("tin.apps.docs.urls", namespace="docs")), path("", include("tin.apps.auth.urls", namespace="auth")), path("", include("social_django.urls", namespace="social")), + path("", include("tin.apps.users.urls", namespace="users")), ] handler404 = handle_404_view From f92dcc8ac526094fead45531db245fe0e0403608 Mon Sep 17 00:00:00 2001 From: Ansh Agrawal Date: Wed, 4 Sep 2024 08:31:35 -0400 Subject: [PATCH 03/11] Use PositiveIntegerField and add docstrings --- tin/apps/users/migrations/0003_user_dark_mode.py | 5 +++-- tin/apps/users/models.py | 4 +++- tin/apps/users/views.py | 1 + tin/templates/base.html | 2 +- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/tin/apps/users/migrations/0003_user_dark_mode.py b/tin/apps/users/migrations/0003_user_dark_mode.py index 80174d87..dba18dff 100644 --- a/tin/apps/users/migrations/0003_user_dark_mode.py +++ b/tin/apps/users/migrations/0003_user_dark_mode.py @@ -1,5 +1,6 @@ -# Generated by Django 4.2.15 on 2024-09-03 19:43 +# Generated by Django 4.2.15 on 2024-09-04 12:28 +import django.core.validators from django.db import migrations, models @@ -13,6 +14,6 @@ class Migration(migrations.Migration): migrations.AddField( model_name='user', name='dark_mode', - field=models.IntegerField(default=0), + field=models.PositiveIntegerField(default=0, validators=[django.core.validators.MaxValueValidator(1)]), ), ] diff --git a/tin/apps/users/models.py b/tin/apps/users/models.py index 3ed14353..4701aa0a 100644 --- a/tin/apps/users/models.py +++ b/tin/apps/users/models.py @@ -5,6 +5,7 @@ import requests from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin from django.contrib.auth.models import UserManager as DjangoUserManager +from django.core.validators import MaxValueValidator from django.db import models from django.utils import timezone from social_django.utils import load_strategy @@ -30,7 +31,8 @@ class User(AbstractBaseUser, PermissionsMixin): is_teacher = models.BooleanField(default=False) is_student = models.BooleanField(default=False) date_joined = models.DateTimeField(default=timezone.now) - dark_mode = models.IntegerField(default=0) + # 0 = Light mode, 1 = Dark Mode + dark_mode = models.PositiveIntegerField(default=0, validators=[MaxValueValidator(1)]) USERNAME_FIELD = "username" REQUIRED_FIELDS = ["email"] diff --git a/tin/apps/users/views.py b/tin/apps/users/views.py index 21568bbf..d539075d 100644 --- a/tin/apps/users/views.py +++ b/tin/apps/users/views.py @@ -8,6 +8,7 @@ @login_required def change_theme(request): + """Sets the color theme""" if request.method == "POST": form = ThemeForm(request.POST) if form.is_valid(): diff --git a/tin/templates/base.html b/tin/templates/base.html index 99e89521..d33f30b7 100644 --- a/tin/templates/base.html +++ b/tin/templates/base.html @@ -8,7 +8,7 @@ {% include "meta.html" %} - {% if request.user.dark_mode == 1 %} + {% if request.user.dark_mode %} {% endif %} From be545e5f4cf28fe94559869fd0a5efdb3272b985 Mon Sep 17 00:00:00 2001 From: Ansh Agrawal <96297836+hsna674@users.noreply.github.com> Date: Thu, 12 Sep 2024 13:27:42 -0400 Subject: [PATCH 04/11] Return http response errors as json Co-authored-by: Aarush Deshpande <110117391+JasonGrace2282@users.noreply.github.com> --- tin/apps/users/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tin/apps/users/views.py b/tin/apps/users/views.py index d539075d..0a7ab4d4 100644 --- a/tin/apps/users/views.py +++ b/tin/apps/users/views.py @@ -16,5 +16,5 @@ def change_theme(request): request.user.save() return http.JsonResponse({"success": True}) else: - return http.JsonResponse({"success": False, "errors": form.errors}, status=400) + return http.JsonResponse({"success": False, "errors": form.errors.as_json()}, status=400) raise http.Http404 From ccdbfb5e2c0e097aa3be84185d676ac94b716b54 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 12 Sep 2024 17:28:30 +0000 Subject: [PATCH 05/11] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tin/apps/users/views.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tin/apps/users/views.py b/tin/apps/users/views.py index 0a7ab4d4..e4dc6aa2 100644 --- a/tin/apps/users/views.py +++ b/tin/apps/users/views.py @@ -16,5 +16,7 @@ def change_theme(request): request.user.save() return http.JsonResponse({"success": True}) else: - return http.JsonResponse({"success": False, "errors": form.errors.as_json()}, status=400) + return http.JsonResponse( + {"success": False, "errors": form.errors.as_json()}, status=400 + ) raise http.Http404 From 959f0d66115a79e17e2c909f8142eaf117055e7c Mon Sep 17 00:00:00 2001 From: Ansh Agrawal Date: Thu, 12 Sep 2024 21:20:18 -0400 Subject: [PATCH 06/11] Convert ThemeForm from forms.Form to forms.ModelForm --- tin/apps/users/forms.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tin/apps/users/forms.py b/tin/apps/users/forms.py index 4d249155..258653f9 100644 --- a/tin/apps/users/forms.py +++ b/tin/apps/users/forms.py @@ -2,11 +2,15 @@ from django import forms +from tin.apps.users.models import User + class UserMultipleChoiceField(forms.ModelMultipleChoiceField): def label_from_instance(self, user): # pylint: disable=arguments-differ return f"{user.full_name} ({user.username})" -class ThemeForm(forms.Form): - dark_mode = forms.IntegerField() +class ThemeForm(forms.ModelForm): + class Meta: + model = User + fields = ["dark_mode"] From 8939580c10b2ad63901efcade98a008491062888 Mon Sep 17 00:00:00 2001 From: Ansh Agrawal <96297836+hsna674@users.noreply.github.com> Date: Thu, 12 Sep 2024 21:23:29 -0400 Subject: [PATCH 07/11] Convert ThemeForm from forms.Form to forms.ModelForm --- tin/apps/users/forms.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tin/apps/users/forms.py b/tin/apps/users/forms.py index 4d249155..d9b3b87f 100644 --- a/tin/apps/users/forms.py +++ b/tin/apps/users/forms.py @@ -8,5 +8,7 @@ def label_from_instance(self, user): # pylint: disable=arguments-differ return f"{user.full_name} ({user.username})" -class ThemeForm(forms.Form): - dark_mode = forms.IntegerField() +class ThemeForm(forms.ModelForm): + class Meta: + model = User + fields = ["dark_mode"] From 526f33129da26031d3541158e8c0e5417ede0bb3 Mon Sep 17 00:00:00 2001 From: Ansh Agrawal <96297836+hsna674@users.noreply.github.com> Date: Thu, 12 Sep 2024 21:32:38 -0400 Subject: [PATCH 08/11] Fix User Model Import --- tin/apps/users/forms.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tin/apps/users/forms.py b/tin/apps/users/forms.py index d9b3b87f..258653f9 100644 --- a/tin/apps/users/forms.py +++ b/tin/apps/users/forms.py @@ -2,6 +2,8 @@ from django import forms +from tin.apps.users.models import User + class UserMultipleChoiceField(forms.ModelMultipleChoiceField): def label_from_instance(self, user): # pylint: disable=arguments-differ From fcf2385297f2843ccca55d5dfc77ba1cfeb38ed9 Mon Sep 17 00:00:00 2001 From: Ansh Agrawal Date: Tue, 8 Oct 2024 13:49:57 -0400 Subject: [PATCH 09/11] Add user test for dark mode --- tin/apps/users/tests.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 tin/apps/users/tests.py diff --git a/tin/apps/users/tests.py b/tin/apps/users/tests.py new file mode 100644 index 00000000..745ab947 --- /dev/null +++ b/tin/apps/users/tests.py @@ -0,0 +1,16 @@ +from __future__ import annotations + +import json + +from django.urls import reverse + +from tin.tests import login + + +@login("student") +def test_user_dark_mode(client, student): + assert student.dark_mode == 0 + response = client.post(reverse("users:theme"), {"dark_mode": 1}) + assert json.loads(response.content.decode("utf-8")).get("success") is True + student.refresh_from_db() + assert student.dark_mode == 1 From 5102744d3191a3fa80f7f230d7fab1db54d9ad66 Mon Sep 17 00:00:00 2001 From: Ansh Agrawal <96297836+hsna674@users.noreply.github.com> Date: Tue, 8 Oct 2024 13:53:28 -0400 Subject: [PATCH 10/11] Add user test for dark mode --- tin/apps/users/tests.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 tin/apps/users/tests.py diff --git a/tin/apps/users/tests.py b/tin/apps/users/tests.py new file mode 100644 index 00000000..745ab947 --- /dev/null +++ b/tin/apps/users/tests.py @@ -0,0 +1,16 @@ +from __future__ import annotations + +import json + +from django.urls import reverse + +from tin.tests import login + + +@login("student") +def test_user_dark_mode(client, student): + assert student.dark_mode == 0 + response = client.post(reverse("users:theme"), {"dark_mode": 1}) + assert json.loads(response.content.decode("utf-8")).get("success") is True + student.refresh_from_db() + assert student.dark_mode == 1 From b98034c470f8d4cb0ec3b1255648bf6930fd6ee0 Mon Sep 17 00:00:00 2001 From: Ansh Agrawal Date: Thu, 31 Oct 2024 18:05:44 -0400 Subject: [PATCH 11/11] CSS bug fix --- tin/static/css/dark/base.css | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tin/static/css/dark/base.css b/tin/static/css/dark/base.css index 3abdc90f..1e307f0e 100644 --- a/tin/static/css/dark/base.css +++ b/tin/static/css/dark/base.css @@ -25,3 +25,8 @@ ul#assignment-list > li a { a:not(.tin-btn) { color: white; } + +table.has-border th, +table.has-border td { + border: 1px solid #ffffff; +}