'theme_switcher,' 'theme-picker' & 'theme' renamed everywhere to simply 'palette'; new urls & views & their corresponding ITs ensure applet menu checkbox functionality

This commit is contained in:
Disco DeDisco
2026-03-05 14:45:55 -05:00
parent ca835059c2
commit c099479740
16 changed files with 154 additions and 85 deletions

View File

@@ -82,7 +82,7 @@ class AppletModelTest(TestCase):
class UserAppletModelTest(TestCase):
def setUp(self):
self.user = User.objects.create(email="a@b.cde")
self.applet = Applet.objects.create(slug="username", name="Username")
self.applet, _ = Applet.objects.get_or_create(slug="username", defaults={"name": "Username"})
def test_user_applet_links_user_to_applet(self):
ua = UserApplet.objects.create(user=self.user, applet=self.applet, visible=True)

View File

@@ -9,7 +9,7 @@ from apps.dashboard.forms import (
DUPLICATE_ITEM_ERROR,
EMPTY_ITEM_ERROR,
)
from apps.dashboard.models import Item, List
from apps.dashboard.models import Applet, Item, List, UserApplet
from apps.lyric.models import User
@@ -256,45 +256,45 @@ class ViewAuthListTest(TestCase):
response = self.client.get(reverse("view_list", args=[self.our_list.id]))
self.assertEqual(response.status_code, 200)
class SetThemeTest(TestCase):
class SetPaletteTest(TestCase):
def setUp(self):
self.user = User.objects.create(email="a@b.cde")
self.client.force_login(self.user)
self.url = reverse("home")
def test_anonymous_user_is_redirected_home(self):
response = self.client.post("/dashboard/set_theme")
response = self.client.post("/dashboard/set_palette")
self.assertRedirects(response, "/")
def test_set_theme_updates_user_theme(self):
User.objects.filter(pk=self.user.pk).update(theme="theme-sheol")
self.client.post("/dashboard/set_theme", data={"theme": "theme-default"})
def test_set_palette_updates_user_palette(self):
User.objects.filter(pk=self.user.pk).update(palette="palette-sheol")
self.client.post("/dashboard/set_palette", data={"palette": "palette-default"})
self.user.refresh_from_db()
self.assertEqual(self.user.theme, "theme-default")
self.assertEqual(self.user.palette, "palette-default")
def test_locked_theme_is_rejected(self):
response = self.client.post("/dashboard/set_theme", data={"theme": "theme-nirvana"})
def test_locked_palette_is_rejected(self):
response = self.client.post("/dashboard/set_palette", data={"palette": "palette-nirvana"})
self.user.refresh_from_db()
self.assertEqual(self.user.theme, "theme-default")
self.assertEqual(self.user.palette, "palette-default")
self.assertRedirects(response, "/")
def test_set_theme_redirects_home(self):
response = self.client.post("/dashboard/set_theme", data={"theme": "theme-default"})
def test_set_palette_redirects_home(self):
response = self.client.post("/dashboard/set_palette", data={"palette": "palette-default"})
self.assertRedirects(response, "/")
def test_my_lists_contains_set_theme_form(self):
def test_my_lists_contains_set_palette_form(self):
response = self.client.get(self.url)
parsed = lxml.html.fromstring(response.content)
forms = parsed.cssselect('form[action="/dashboard/set_theme"]')
forms = parsed.cssselect('form[action="/dashboard/set_palette"]')
self.assertEqual(len(forms), 1)
def test_active_theme_swatch_has_active_class(self):
def test_active_palette_swatch_has_active_class(self):
response = self.client.get(self.url)
parsed = lxml.html.fromstring(response.content)
[active] = parsed.cssselect(".swatch.active")
self.assertIn("theme-default", active.classes)
self.assertIn("palette-default", active.classes)
def test_locked_themes_are_not_forms(self):
def test_locked_palettes_are_not_forms(self):
response = self.client.get(self.url)
parsed = lxml.html.fromstring(response.content)
locked = parsed.cssselect(".swatch.locked")
@@ -303,11 +303,11 @@ class SetThemeTest(TestCase):
for swatch in locked:
self.assertNotEqual(swatch.tag, "button")
def test_theme_picker_count_matches_context(self):
def test_palette_picker_count_matches_context(self):
response = self.client.get(self.url)
parsed = lxml.html.fromstring(response.content)
swatches = parsed.cssselect(".swatch")
self.assertEqual(len(swatches), len(response.context["themes"]))
self.assertEqual(len(swatches), len(response.context["palettes"]))
class ProfileViewTest(TestCase):
def setUp(self):
@@ -341,3 +341,36 @@ class ProfileViewTest(TestCase):
[username_input] = parsed.cssselect("#id_new_username")
self.assertEqual("discoman", username_input.get("value"))
class ToggleAppletsViewTest(TestCase):
def setUp(self):
self.user = User.objects.create(email="disco@test.io")
self.client.force_login(self.user)
self.username_applet, _ = Applet.objects.get_or_create(slug="username", defaults={"name": "Username"})
self.palette_applet, _ = Applet.objects.get_or_create(slug="palette", defaults={"name": "Palette"})
self.url = reverse("toggle_applets")
def test_unauthenticated_user_is_redirected(self):
self.client.logout()
response = self.client.post(self.url)
self.assertRedirects(
response, f"/?next={self.url}", fetch_redirect_response=False
)
def test_unchecked_applet_gets_user_applet_with_visible_false(self):
self.client.post(self.url, {"applets": ["username"]})
ua = UserApplet.objects.get(user=self.user, applet=self.palette_applet)
self.assertFalse(ua.visible)
def test_redirects_on_normal_post(self):
response = self.client.post(
self.url, {"applets": ["username", "palette"]}
)
self.assertRedirects(response, reverse("home"), fetch_redirect_response=False)
def test_returns_200_on_htmx_post(self):
response = self.client.post(
self.url,
{"applets": ["username", "palette"]},
HTTP_HX_REQUEST="true",
)
self.assertEqual(response.status_code, 200)

View File

@@ -5,7 +5,8 @@ urlpatterns = [
path('new_list', views.new_list, name='new_list'),
path('list/<uuid:list_id>/', views.view_list, name='view_list'),
path('list/<uuid:list_id>/share_list', views.share_list, name="share_list"),
path('set_theme', views.set_theme, name='set_theme'),
path('set_palette', views.set_palette, name='set_palette'),
path('set_profile', views.set_profile, name='set_profile'),
path('users/<uuid:user_id>/', views.my_lists, name='my_lists'),
path('toggle_applets', views.toggle_applets, name="toggle_applets"),
]

View File

@@ -1,18 +1,18 @@
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.http import HttpResponseForbidden
from django.http import HttpResponse, HttpResponseForbidden
from django.shortcuts import redirect, render
from .forms import ExistingListItemForm, ItemForm
from .models import Item, List
from apps.dashboard.forms import ExistingListItemForm, ItemForm
from apps.dashboard.models import Applet, Item, List, UserApplet
from apps.lyric.models import User
UNLOCKED_THEMES = frozenset(["theme-default"])
THEMES = [
{"name": "theme-default", "label": "Earthman", "locked": False},
{"name": "theme-nirvana", "label": "Nirvana", "locked": True},
{"name": "theme-sheol", "label": "Sheol", "locked": True},
UNLOCKED_PALETTES = frozenset(["palette-default"])
PALETTES = [
{"name": "palette-default", "label": "Earthman", "locked": False},
{"name": "palette-nirvana", "label": "Nirvana", "locked": True},
{"name": "palette-sheol", "label": "Sheol", "locked": True},
]
@@ -20,7 +20,7 @@ def home_page(request):
return render(
request, "apps/dashboard/home.html", {
"form": ItemForm(),
"themes": THEMES,
"palettes": PALETTES,
})
def new_list(request):
@@ -74,12 +74,12 @@ def share_list(request, list_id):
return redirect(our_list)
@login_required(login_url="/")
def set_theme(request):
def set_palette(request):
if request.method == "POST":
theme = request.POST.get("theme", "")
if theme in UNLOCKED_THEMES:
request.user.theme = theme
request.user.save(update_fields=["theme"])
palette = request.POST.get("palette", "")
if palette in UNLOCKED_PALETTES:
request.user.palette = palette
request.user.save(update_fields=["palette"])
return redirect("home")
@login_required(login_url="/")
@@ -89,3 +89,16 @@ def set_profile(request):
request.user.username = username
request.user.save(update_fields=["username"])
return redirect("/")
@login_required(login_url="/")
def toggle_applets(request):
checked = request.POST.getlist("applets")
for applet in Applet.objects.all():
UserApplet.objects.update_or_create(
user=request.user,
applet=applet,
defaults={"visible": applet.slug in checked},
)
if request.headers.get("HX-Request"):
return HttpResponse("")
return redirect("home")

View File

@@ -0,0 +1,22 @@
# Generated by Django 6.0 on 2026-03-05 19:01
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('lyric', '0003_user_theme'),
]
operations = [
migrations.RemoveField(
model_name='user',
name='theme',
),
migrations.AddField(
model_name='user',
name='palette',
field=models.CharField(default='palette-default', max_length=32),
),
]

View File

@@ -26,7 +26,7 @@ class User(AbstractBaseUser):
email = models.EmailField(unique=True)
username = models.CharField(max_length=35, unique=True, null=True, blank=True)
searchable = models.BooleanField(default=False)
theme = models.CharField(max_length=32, default="theme-default")
palette = models.CharField(max_length=32, default="palette-default")
is_staff = models.BooleanField(default=False)
is_superuser = models.BooleanField(default=False)

View File

@@ -51,7 +51,7 @@ class UserManagerTest(TestCase):
)
self.assertTrue(user.check_password("correct-password"))
class UserThemeTest(TestCase):
def test_theme_field_defaults_to_theme_default(self):
class UserPaletteTest(TestCase):
def test_palette_field_defaults_to_palette_default(self):
user = User.objects.create(email="a@b.cde")
self.assertEqual(user.theme, "theme-default")
self.assertEqual(user.palette, "palette-default")