Skip to content

Commit

Permalink
Implement dark mode
Browse files Browse the repository at this point in the history
  • Loading branch information
hsna674 committed Sep 3, 2024
1 parent b3cf2be commit 8f0b573
Show file tree
Hide file tree
Showing 8 changed files with 130 additions and 0 deletions.
4 changes: 4 additions & 0 deletions tin/apps/users/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
18 changes: 18 additions & 0 deletions tin/apps/users/migrations/0003_user_dark_mode.py
Original file line number Diff line number Diff line change
@@ -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),
),
]
1 change: 1 addition & 0 deletions tin/apps/users/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand Down
11 changes: 11 additions & 0 deletions tin/apps/users/urls.py
Original file line number Diff line number Diff line change
@@ -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"),
]
19 changes: 19 additions & 0 deletions tin/apps/users/views.py
Original file line number Diff line number Diff line change
@@ -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
17 changes: 17 additions & 0 deletions tin/static/css/base.css
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
color: white;
line-height: 40px;
background: #4fab4f;
position: relative;
}

#nav ul {
Expand Down Expand Up @@ -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%);
}
59 changes: 59 additions & 0 deletions tin/templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@
{% include "meta.html" %}

<link rel="stylesheet" href="{% static 'css/base.css' %}">
{% if request.user.dark_mode == 1 %}
<link rel="stylesheet" href="{% static 'css/dark/base.css' %}">
<link rel="stylesheet" href="{% static 'css/dark/edit.css' %}">
{% endif %}
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"/>
<link rel="stylesheet" type="text/css"
href="https://fonts.googleapis.com/css?family=Open+Sans:100italic,400italic,700italic,100,400,700"/>
Expand Down Expand Up @@ -44,8 +48,56 @@
$(".continuous-progress").css({height: "15px"}).progressbar({value: false})
});
</script>
<script>
function changeTheme() {
const $themeToggle = $('#theme-toggle use');
const isDarkMode = $themeToggle.attr('href') === '#svg-moon';

$themeToggle.attr('href', isDarkMode ? '#svg-sun' : '#svg-moon');

$.post(
"{% url 'users:theme' %}",
{
dark_mode: isDarkMode ? 1 : 0,
csrfmiddlewaretoken: "{{ csrf_token }}"
},
function () {
location.reload();
}
);
}
</script>
{% block head %}{% endblock %}
</head>
<div style="display: none">
<svg>
<symbol id="svg-sun" viewBox="0 0 24 24">
<title>Light mode</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="white"
stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="feather-sun">
<circle cx="12" cy="12" r="5"></circle>
<line x1="12" y1="1" x2="12" y2="3"></line>
<line x1="12" y1="21" x2="12" y2="23"></line>
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
<line x1="1" y1="12" x2="3" y2="12"></line>
<line x1="21" y1="12" x2="23" y2="12"></line>
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
</svg>
</symbol>
</svg>
<svg>
<symbol id="svg-moon" viewBox="0 0 24 24">
<title>Dark mode</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="white"
stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="icon-tabler-moon">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z" />
</svg>
</symbol>
</svg>
</div>

<body>

Expand Down Expand Up @@ -90,6 +142,13 @@
{% endif %}
<li class="right"><i class="fa fa-sign-out"></i><a href="{% url 'auth:logout' %}">Logout
({{ request.user.username }})</a></li>
<li class="right">
<button class="theme-toggle-button" onclick="changeTheme()">
<svg id="theme-toggle">
<use href="{% if request.user.dark_mode == 1 %}#svg-sun{% else %}#svg-moon{% endif %}"></use>
</svg>
</button>
</li>
{% endif %}
</ul>
</div>
Expand Down
1 change: 1 addition & 0 deletions tin/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 8f0b573

Please sign in to comment.