diff options
| author | Bobby <[email protected]> | 2024-08-25 23:27:40 -0400 |
|---|---|---|
| committer | Bobby <[email protected]> | 2024-08-25 23:27:40 -0400 |
| commit | ced2c54b4e1d2aa1757c1a58a0b624a36bda5c21 (patch) | |
| tree | 2f1e5047c13cb15df6311e42d57c830d11d77364 | |
| parent | a99b3870b362bda483fc1009895447c58cabfff4 (diff) | |
| download | yugen-ced2c54b4e1d2aa1757c1a58a0b624a36bda5c21.tar.xz yugen-ced2c54b4e1d2aa1757c1a58a0b624a36bda5c21.zip | |
Ability to save preferences
| -rw-r--r-- | authentication/urls.py | 1 | ||||
| -rw-r--r-- | authentication/views.py | 12 | ||||
| -rw-r--r-- | static/css/main.css | 151 | ||||
| -rw-r--r-- | templates/partials/base.html | 2 | ||||
| -rw-r--r-- | templates/partials/navbar.html | 2 | ||||
| -rw-r--r-- | templates/user_profile/user_profile.html | 134 | ||||
| -rw-r--r-- | user_profile/urls.py | 1 | ||||
| -rw-r--r-- | user_profile/views.py | 50 |
8 files changed, 337 insertions, 16 deletions
diff --git a/authentication/urls.py b/authentication/urls.py index f238dfa..f9ce23c 100644 --- a/authentication/urls.py +++ b/authentication/urls.py @@ -6,4 +6,5 @@ app_name = "auth" urlpatterns = [ path("callback", views.callback, name="callback"), path("logout", views.logout_user, name="logout"), + path("unauthorized", views.unauthorized, name="unauthorized"), ] diff --git a/authentication/views.py b/authentication/views.py index a2b407b..781aa99 100644 --- a/authentication/views.py +++ b/authentication/views.py @@ -5,7 +5,9 @@ from django.shortcuts import redirect, render def callback(request): - # Coming from Discord OAuth2 + if request.user.is_authenticated: + return redirect("home:index") + code = request.GET.get("code") if not code: return render(request, "messages/unauthorized.html", {"error": "You can't access the site if you keep cancelling the login!", "redirect_uri": get_redirect_uri()}) @@ -18,7 +20,7 @@ def callback(request): user = authenticate_user(exchange_response=response) if not user: - return render(request, "messages/unauthorized.html", {"redirect_uri": get_redirect_uri()}) + return redirect("auth:unauthorized") next_url = request.session.pop("next", None) # login the user and redirect to the referrer @@ -28,3 +30,9 @@ def callback(request): def logout_user(request): logout(request) return HttpResponseRedirect(request.META.get("HTTP_REFERER")) + +def unauthorized(request): + if request.user.is_authenticated: + return redirect("home:index") + + return render(request, "messages/unauthorized.html", {"redirect_uri": get_redirect_uri(), "error": "You are not part of our elite cult!"}) diff --git a/static/css/main.css b/static/css/main.css index 5b62e7d..309af7f 100644 --- a/static/css/main.css +++ b/static/css/main.css @@ -554,6 +554,40 @@ video { --tw-contain-style: ; } +.container { + width: 100%; +} + +@media (min-width: 640px) { + .container { + max-width: 640px; + } +} + +@media (min-width: 768px) { + .container { + max-width: 768px; + } +} + +@media (min-width: 1024px) { + .container { + max-width: 1024px; + } +} + +@media (min-width: 1280px) { + .container { + max-width: 1280px; + } +} + +@media (min-width: 1536px) { + .container { + max-width: 1536px; + } +} + .pointer-events-none { pointer-events: none; } @@ -562,6 +596,10 @@ video { position: static; } +.fixed { + position: fixed; +} + .absolute { position: absolute; } @@ -570,6 +608,10 @@ video { position: relative; } +.sticky { + position: sticky; +} + .inset-0 { inset: 0px; } @@ -603,10 +645,30 @@ video { right: 2.5rem; } +.top-0 { + top: 0px; +} + +.right-4 { + right: 1rem; +} + +.top-4 { + top: 1rem; +} + +.bottom-4 { + bottom: 1rem; +} + .z-10 { z-index: 10; } +.z-50 { + z-index: 50; +} + .mx-auto { margin-left: auto; margin-right: auto; @@ -657,6 +719,18 @@ video { margin-top: 2.5rem; } +.-ml-1 { + margin-left: -0.25rem; +} + +.mr-3 { + margin-right: 0.75rem; +} + +.ml-2 { + margin-left: 0.5rem; +} + .block { display: block; } @@ -724,6 +798,10 @@ video { height: 100%; } +.h-5 { + height: 1.25rem; +} + .max-h-24 { max-height: 6rem; } @@ -777,6 +855,14 @@ video { width: max-content; } +.w-5 { + width: 1.25rem; +} + +.w-6 { + width: 1.5rem; +} + .max-w-7xl { max-width: 80rem; } @@ -786,6 +872,10 @@ video { max-width: max-content; } +.max-w-xs { + max-width: 20rem; +} + .-translate-x-1\/2 { --tw-translate-x: -50%; transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); @@ -800,10 +890,24 @@ video { transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); } +@keyframes spin { + to { + transform: rotate(360deg); + } +} + +.animate-spin { + animation: spin 1s linear infinite; +} + .cursor-pointer { cursor: pointer; } +.cursor-not-allowed { + cursor: not-allowed; +} + .select-none { -webkit-user-select: none; -moz-user-select: none; @@ -834,6 +938,10 @@ video { align-items: center; } +.justify-start { + justify-content: flex-start; +} + .justify-end { justify-content: flex-end; } @@ -872,6 +980,12 @@ video { margin-left: calc(2.5rem * calc(1 - var(--tw-space-x-reverse))); } +.space-y-2 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(0.5rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(0.5rem * var(--tw-space-y-reverse)); +} + .overflow-auto { overflow: auto; } @@ -892,6 +1006,10 @@ video { border-radius: 0.75rem; } +.rounded-md { + border-radius: 0.375rem; +} + .border { border-width: 1px; } @@ -1245,6 +1363,24 @@ video { color: rgb(249 115 22 / var(--tw-text-opacity)); } +.text-green-700 { + --tw-text-opacity: 1; + color: rgb(21 128 61 / var(--tw-text-opacity)); +} + +.text-red-700 { + --tw-text-opacity: 1; + color: rgb(185 28 28 / var(--tw-text-opacity)); +} + +.opacity-25 { + opacity: 0.25; +} + +.opacity-75 { + opacity: 0.75; +} + .shadow-lg { --tw-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); --tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color); @@ -1268,6 +1404,12 @@ video { transition-duration: 150ms; } +.transition-opacity { + transition-property: opacity; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; +} + .duration-300 { transition-duration: 300ms; } @@ -1276,6 +1418,10 @@ video { transition-duration: 100ms; } +.duration-500 { + transition-duration: 500ms; +} + .ease-in-out { transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); } @@ -1335,6 +1481,11 @@ main { background-color: rgb(147 51 234 / var(--tw-bg-opacity)); } +.hover\:bg-purple-700:hover { + --tw-bg-opacity: 1; + background-color: rgb(126 34 206 / var(--tw-bg-opacity)); +} + .hover\:bg-opacity-20:hover { --tw-bg-opacity: 0.2; } diff --git a/templates/partials/base.html b/templates/partials/base.html index 6977c0b..f7fa72c 100644 --- a/templates/partials/base.html +++ b/templates/partials/base.html @@ -14,7 +14,7 @@ <title>Yugen</title> </head> <body class="bg-black text-white"> - <header>{% include "partials/navbar.html" %}</header> + {% include "partials/navbar.html" %} <main class="mx-auto">{% block content %} {% endblock %}</main> </body> {% block scripts %} {% endblock %} diff --git a/templates/partials/navbar.html b/templates/partials/navbar.html index e7ece9e..009c75a 100644 --- a/templates/partials/navbar.html +++ b/templates/partials/navbar.html @@ -1,5 +1,5 @@ {% load static %} -<nav class="bg-neutral-950 px-4 py-3 border-b border-neutral-800"> +<nav class="bg-neutral-950 px-4 py-3 border-b border-neutral-800 sticky top-0 z-50 shadow-lg"> <!-- Mobile UI: Icons (Top) Search (Botton)--> <div class="xl:hidden flex flex-col gap-4"> <!-- Icons (Top) with Hidden Logo --> diff --git a/templates/user_profile/user_profile.html b/templates/user_profile/user_profile.html index 3a40d4d..41c61f5 100644 --- a/templates/user_profile/user_profile.html +++ b/templates/user_profile/user_profile.html @@ -5,7 +5,13 @@ <div class="absolute inset-0" style="background: linear-gradient(45deg, rgb(8, 8, 8) 15%, transparent 60%), linear-gradient(0deg, rgb(8, 8, 8) 0%, transparent 60%);"></div> <div class="flex flex-col items-center gap-2 absolute bottom-0 left-10"> <img src="https://cdn.discordapp.com/avatars/{{ request.user.discord_id }}/{{ request.user.discord_avatar }}?size=256" alt="avatar" class="rounded-full w-24 h-24"> - <h1 class="text-2xl font-bold">{{ user.discord_global_name }}</h1> + <h1 class="text-2xl font-bold"> + {% if request.user.preferences.display_guild_name_instead_of_username and request.user.discord_guild_name %} + {{ request.user.discord_guild_name }} + {% else %} + {{ request.user.discord_global_name }} + {% endif %} + </h1> </div> <a href="{% url "auth:logout" %}" class="bg-white bg-opacity-10 text-white text-sm font-bold py-3 px-6 rounded-full flex items-center gap-2 absolute bottom-10 right-10"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="size-6"> @@ -16,29 +22,29 @@ </div> <section class="w-full flex flex-col max-w-7xl mx-auto my-4 p-2"> <section class="inline-flex w-11/12 lg:w-max flex-row rounded-full bg-white bg-opacity-10 mx-auto"> - <a class="flex flex-row items-center focus:outline-none gap-2 text-sm font-bold py-2 px-4 rounded-full {% if request.GET.category == "preferences" or not request.GET.category %}bg-purple-600{% endif %}" href="{% url "user_profile:user_profile" %}?category=preferences"> + <a class="flex flex-row items-center focus:outline-none gap-2 text-sm font-bold py-2 px-4 rounded-full {% if req_category == "preferences" %}bg-purple-600{% endif %}" href="{% url "user_profile:user_profile" %}?category=preferences"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="size-4"> <path d="M18.75 12.75h1.5a.75.75 0 0 0 0-1.5h-1.5a.75.75 0 0 0 0 1.5ZM12 6a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 12 6ZM12 18a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 12 18ZM3.75 6.75h1.5a.75.75 0 1 0 0-1.5h-1.5a.75.75 0 0 0 0 1.5ZM5.25 18.75h-1.5a.75.75 0 0 1 0-1.5h1.5a.75.75 0 0 1 0 1.5ZM3 12a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 3 12ZM9 3.75a2.25 2.25 0 1 0 0 4.5 2.25 2.25 0 0 0 0-4.5ZM12.75 12a2.25 2.25 0 1 1 4.5 0 2.25 2.25 0 0 1-4.5 0ZM9 15.75a2.25 2.25 0 1 0 0 4.5 2.25 2.25 0 0 0 0-4.5Z" /> </svg> <span>Preferences</span> </a> - <a class="flex flex-row items-center focus:outline-none gap-2 text-sm font-bold py-2 px-4 rounded-full {% if request.GET.category == "anime_list" %}bg-purple-600{% endif %}" href="{% url "user_profile:user_profile" %}?category=anime_list"> + <a class="flex flex-row items-center focus:outline-none gap-2 text-sm font-bold py-2 px-4 rounded-full {% if req_category == "anime_list" %}bg-purple-600{% endif %}" href="{% url "user_profile:user_profile" %}?category=anime_list"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="size-4"> <path d="M19.5 6h-15v9h15V6Z" /> <path fill-rule="evenodd" d="M3.375 3C2.339 3 1.5 3.84 1.5 4.875v11.25C1.5 17.16 2.34 18 3.375 18H9.75v1.5H6A.75.75 0 0 0 6 21h12a.75.75 0 0 0 0-1.5h-3.75V18h6.375c1.035 0 1.875-.84 1.875-1.875V4.875C22.5 3.839 21.66 3 20.625 3H3.375Zm0 13.5h17.25a.375.375 0 0 0 .375-.375V4.875a.375.375 0 0 0-.375-.375H3.375A.375.375 0 0 0 3 4.875v11.25c0 .207.168.375.375.375Z" clip-rule="evenodd" /> </svg> <span>Anime List</span> </a> - <a class="flex flex-row items-center focus:outline-none gap-2 text-sm font-bold py-2 px-4 rounded-full {% if request.GET.category == "user_directory" %}bg-purple-600{% endif %}" href="{% url "user_profile:user_profile" %}?category=user_directory"> + <a class="flex flex-row items-center focus:outline-none gap-2 text-sm font-bold py-2 px-4 rounded-full {% if req_category == "user_directory" %}bg-purple-600{% endif %}" href="{% url "user_profile:user_profile" %}?category=user_directory"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="size-4"> <path d="M18.375 2.25c-1.035 0-1.875.84-1.875 1.875v15.75c0 1.035.84 1.875 1.875 1.875h.75c1.035 0 1.875-.84 1.875-1.875V4.125c0-1.036-.84-1.875-1.875-1.875h-.75ZM9.75 8.625c0-1.036.84-1.875 1.875-1.875h.75c1.036 0 1.875.84 1.875 1.875v11.25c0 1.035-.84 1.875-1.875 1.875h-.75a1.875 1.875 0 0 1-1.875-1.875V8.625ZM3 13.125c0-1.036.84-1.875 1.875-1.875h.75c1.036 0 1.875.84 1.875 1.875v6.75c0 1.035-.84 1.875-1.875 1.875h-.75A1.875 1.875 0 0 1 3 19.875v-6.75Z" /> </svg> <span>User Directory</span> </a> </section> - {% if request.GET.category == "anime_list" %} + {% if req_category == "anime_list" %} {% comment %} TODO: anime list {% endcomment %} - {% elif request.GET.category == "user_directory" %} + {% elif req_category == "user_directory" %} {% comment %} TODO: user directory {% endcomment %} {% else %} <h2 class="text-2xl font-bold text-center mt-8 flex gap-2 items-center"> @@ -195,7 +201,7 @@ <div class="relative"> <label class="flex items-center cursor-pointer"> <input type="checkbox" class="hidden peer" id="show_history_on_home_checkbox" {% if user.preferences.show_history_on_home %}checked{% endif %}> - <div class="w-10 h-6 bg-neutral-700 rounded-full p-1 flex items-center peer-checked:bg-purple-600 transition-colors duration-300"> + <div class="w-10 h-6 bg-neutral-700 rounded-full p-1 flex items-center peer-checked:bg-purple-600 transition-colors duration-100"> <div class="toggle-dot w-4 h-4 bg-white rounded-full shadow-md transform duration-100 ease-in-out"></div> </div> </label> @@ -209,7 +215,7 @@ <div class="relative"> <label class="flex items-center cursor-pointer"> <input type="checkbox" class="hidden peer" id="auto_skip_intro_checkbox" {% if user.preferences.auto_skip_intro %}checked{% endif %}> - <div class="w-10 h-6 bg-neutral-700 rounded-full p-1 flex items-center peer-checked:bg-purple-600 transition-colors duration-300"> + <div class="w-10 h-6 bg-neutral-700 rounded-full p-1 flex items-center peer-checked:bg-purple-600 transition-colors duration-100"> <div class="toggle-dot w-4 h-4 bg-white rounded-full shadow-md transform duration-100 ease-in-out"></div> </div> </label> @@ -223,7 +229,7 @@ <div class="relative"> <label class="flex items-center cursor-pointer"> <input type="checkbox" class="hidden peer" id="auto_play_video_checkbox" {% if user.preferences.auto_play_video %}checked{% endif %}> - <div class="w-10 h-6 bg-neutral-700 rounded-full p-1 flex items-center peer-checked:bg-purple-600 transition-colors duration-300"> + <div class="w-10 h-6 bg-neutral-700 rounded-full p-1 flex items-center peer-checked:bg-purple-600 transition-colors duration-100"> <div class="toggle-dot w-4 h-4 bg-white rounded-full shadow-md transform duration-100 ease-in-out"></div> </div> </label> @@ -237,7 +243,7 @@ <div class="relative"> <label class="flex items-center cursor-pointer"> <input type="checkbox" class="hidden peer" id="auto_next_episode_checkbox" {% if user.preferences.auto_next_episode %}checked{% endif %}> - <div class="w-10 h-6 bg-neutral-700 rounded-full p-1 flex items-center peer-checked:bg-purple-600 transition-colors duration-300"> + <div class="w-10 h-6 bg-neutral-700 rounded-full p-1 flex items-center peer-checked:bg-purple-600 transition-colors duration-100"> <div class="toggle-dot w-4 h-4 bg-white rounded-full shadow-md transform duration-100 ease-in-out"></div> </div> </label> @@ -255,12 +261,23 @@ <div class="relative"> <label class="flex items-center cursor-pointer"> <input type="checkbox" class="hidden peer" id="display_guild_name_instead_of_username_checkbox" {% if user.preferences.display_guild_name_instead_of_username %}checked{% endif %}> - <div class="w-10 h-6 bg-neutral-700 rounded-full p-1 flex items-center peer-checked:bg-purple-600 transition-colors duration-300"> + <div class="w-10 h-6 bg-neutral-700 rounded-full p-1 flex items-center peer-checked:bg-purple-600 transition-colors duration-100"> <div class="toggle-dot w-4 h-4 bg-white rounded-full shadow-md transform duration-100 ease-in-out"></div> </div> </label> </div> </div> + <!-- Save Button --> + <div class="w-full py-4 flex flex-row justify-start items-center"> + <button id="saveButton" class="bg-purple-600 hover:bg-purple-700 text-white font-bold py-2 px-4 rounded-full focus:outline-none flex items-center transition-colors duration-100 ease-in-out" onclick="handleSave()"> + <svg id="spinner" class="hidden animate-spin mr-2 h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> + <path d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z" class="opacity-25 fill-current"/> + <path d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z" class="fill-current"/> + </svg> + <span id="buttonText">Save Preferences</span> + </button> + </div> + <div id="toastContainer" class="fixed bottom-4 left-1/2 transform -translate-x-1/2 z-50 flex flex-col space-y-2"></div> {% endif %} </section> {% endblock content %} @@ -293,5 +310,100 @@ }); }); </script> +<script> + function showToast(message, isSuccess) { + const toast = document.createElement('div'); + toast.className = `flex items-center p-4 rounded-md shadow-lg transition-opacity duration-500 ease-in-out animate__animated ${ + isSuccess ? 'bg-green-100 text-green-700 animate__fadeInUp' : 'bg-red-100 text-red-700 animate__fadeInUp' + }`; + + const checkSVG = `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4"><path stroke-linecap="round" stroke-linejoin="round" d="m4.5 12.75 6 6 9-13.5" /></svg>` + + const errorSVG = `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6"> <path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126ZM12 15.75h.007v.008H12v-.008Z" /></svg>` + + toast.innerHTML = ` + <div class="flex items-center"> + ${isSuccess ? checkSVG : errorSVG} + <span class="ml-2">${message}</span> + </div> + `; + + // Append the toast to the container + toastContainer.appendChild(toast); + + // Remove the toast after 3 seconds + setTimeout(() => { + toast.classList.add('animate__fadeOutDown'); + setTimeout(() => { + toastContainer.removeChild(toast); + }, 500); + }, 3000); + } + + function handleSave() { + const button = document.getElementById('saveButton'); + const spinner = document.getElementById('spinner'); + const buttonText = document.getElementById('buttonText'); + + // Show the spinner and disable the button + spinner.classList.remove('hidden'); + buttonText.textContent = 'Saving...'; + button.classList.add('bg-white', 'bg-opacity-20', 'cursor-not-allowed'); + button.classList.remove('bg-purple-600', 'hover:bg-purple-700'); + button.disabled = true; + + // Gather preferences + const cardLayout = document.getElementById('card_layout').textContent.trim().toLowerCase(); + const titleLanguage = document.getElementById('title_language').textContent.trim().toLowerCase(); + const characterNameLanguage = document.getElementById('character_name_language').textContent.trim().toLowerCase(); + const defaultLanguage = document.getElementById('default_language').textContent.trim().toLowerCase(); + const defaultProvider = document.getElementById('default_provider').textContent.trim().toLowerCase(); + const defaultWatchPage = document.getElementById('default_watch_page').textContent.trim().toLowerCase(); + const showHistoryOnHome = document.getElementById('show_history_on_home_checkbox').checked; + const autoSkipIntro = document.getElementById('auto_skip_intro_checkbox').checked; + const autoPlayVideo = document.getElementById('auto_play_video_checkbox').checked; + const autoNextEpisode = document.getElementById('auto_next_episode_checkbox').checked; + const displayGuildNameInsteadOfUsername = document.getElementById('display_guild_name_instead_of_username_checkbox').checked; + + const body = { + cardLayout, + titleLanguage, + characterNameLanguage, + defaultLanguage, + defaultProvider, + defaultWatchPage, + showHistoryOnHome, + autoSkipIntro, + autoPlayVideo, + autoNextEpisode, + displayGuildNameInsteadOfUsername + }; + + // Send a POST request to save the preferences + fetch('{% url "profile:save_user_preferences" %}', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-CSRFToken': '{{ csrf_token }}' + }, + body: JSON.stringify(body) + }).then(response => { + if (response.ok) { + showToast('Preferences saved successfully!', true); + } else { + showToast('An error occurred while saving preferences.', false); + } + }).catch(error => { + showToast('An error occurred while saving preferences.', false); + }).finally(() => { + // Hide the spinner and enable the button + spinner.classList.add('hidden'); + buttonText.textContent = 'Save Preferences'; + button.classList.remove('bg-white', 'bg-opacity-20', 'cursor-not-allowed'); + button.classList.add('bg-purple-600', 'hover:bg-purple-700'); + button.disabled = false; + }); + } + </script> {% endblock scripts %} diff --git a/user_profile/urls.py b/user_profile/urls.py index e2f6d47..6afb7ef 100644 --- a/user_profile/urls.py +++ b/user_profile/urls.py @@ -5,4 +5,5 @@ from . import views app_name = "user_profile" urlpatterns = [ path("", views.user_profile, name="user_profile"), + path("save_user_preferences", views.save_user_preferences, name="save_user_preferences"), ]
\ No newline at end of file diff --git a/user_profile/views.py b/user_profile/views.py index 25d7bd2..ef3c8e3 100644 --- a/user_profile/views.py +++ b/user_profile/views.py @@ -1,8 +1,56 @@ +import json from django.shortcuts import render +from django.http import JsonResponse +from user_profile.models import UserPreferences def user_profile(request): + category = request.GET.get("category", "preferences") + + supported_categories = ["preferences", "anime_list", "user_directory"] + if category not in supported_categories: + category = "preferences" + + print(category) + + context = { + "req_category": category + } + + return render(request, "user_profile/user_profile.html", context) + + +def save_user_preferences(request): + if request.method != "POST": + return JsonResponse({"error": "Invalid request method"}, status=400) + user = request.user + + data = json.loads(request.body) + card_layout = data.get("cardLayout") + title_language = data.get("titleLanguage") + character_name_language = data.get("characterNameLanguage") + default_language = data.get("defaultLanguage") + default_provider = data.get("defaultProvider") + default_watch_page = data.get("defaultWatchPage") + show_history_on_home = data.get("showHistoryOnHome") + auto_skip_intro = data.get("autoSkipIntro") + auto_play_video = data.get("autoPlayVideo") + auto_next_episode = data.get("autoNextEpisode") + display_guild_name_instead_of_username = data.get("displayGuildNameInsteadOfUsername") + user_preferences, created = UserPreferences.objects.get_or_create(user=user) + user_preferences.card_layout = card_layout + user_preferences.title_language = title_language + user_preferences.character_name_language = character_name_language + user_preferences.default_language = default_language + user_preferences.default_provider = default_provider + user_preferences.default_watch_page = default_watch_page + user_preferences.show_history_on_home = show_history_on_home + user_preferences.auto_skip_intro = auto_skip_intro + user_preferences.auto_play_video = auto_play_video + user_preferences.auto_next_episode = auto_next_episode + user_preferences.display_guild_name_instead_of_username = display_guild_name_instead_of_username + user_preferences.save() - return render(request, "user_profile/user_profile.html")
\ No newline at end of file + return JsonResponse({"success": "User preferences saved"}, status=200) |
