Rework UI to have vertical menus

This commit is contained in:
Andrew Godwin 2022-11-17 17:43:00 -07:00
parent f5eafb0ca0
commit 2154e6f022
29 changed files with 344 additions and 246 deletions

View File

@ -40,7 +40,7 @@ class Home(FormView):
) )
.select_related("subject_post", "subject_post__author") .select_related("subject_post", "subject_post__author")
.prefetch_related("subject_post__attachments") .prefetch_related("subject_post__attachments")
.order_by("-created")[:100] .order_by("-created")[:50]
) )
context["interactions"] = PostInteraction.get_event_interactions( context["interactions"] = PostInteraction.get_event_interactions(
context["events"], self.request.identity context["events"], self.request.identity
@ -68,7 +68,7 @@ class Local(TemplateView):
Post.objects.filter(visibility=Post.Visibilities.public, author__local=True) Post.objects.filter(visibility=Post.Visibilities.public, author__local=True)
.select_related("author") .select_related("author")
.prefetch_related("attachments") .prefetch_related("attachments")
.order_by("-created")[:100] .order_by("-created")[:50]
) )
context["current_page"] = "local" context["current_page"] = "local"
return context return context
@ -85,7 +85,7 @@ class Federated(TemplateView):
Post.objects.filter(visibility=Post.Visibilities.public) Post.objects.filter(visibility=Post.Visibilities.public)
.select_related("author") .select_related("author")
.prefetch_related("attachments") .prefetch_related("attachments")
.order_by("-created")[:100] .order_by("-created")[:50]
) )
context["current_page"] = "federated" context["current_page"] = "federated"
return context return context

View File

@ -100,6 +100,7 @@ class Config(models.Model):
site_name: str = "takahē" site_name: str = "takahē"
highlight_color: str = "#449c8c" highlight_color: str = "#449c8c"
post_length: int = 500
identity_max_age: int = 24 * 60 * 60 identity_max_age: int = 24 * 60 * 60
class UserOptions(pydantic.BaseModel): class UserOptions(pydantic.BaseModel):

View File

@ -16,6 +16,7 @@ figure,
blockquote, blockquote,
dl, dl,
dd, dd,
fieldset,
menu { menu {
margin: 0; margin: 0;
padding: 0; padding: 0;
@ -166,6 +167,9 @@ header menu a.identity {
border-right: 0; border-right: 0;
text-align: right; text-align: right;
padding-right: 10px; padding-right: 10px;
background: var(--color-bg-menu);
border-radius: 0 5px 0 0;
width: 250px;
} }
header menu a i { header menu a i {
@ -188,26 +192,47 @@ header menu a small {
} }
nav { nav {
display: flex; padding: 10px 10px 20px 0;
height: 40px; }
background: var(--color-bg-menu);
nav h3 {
text-transform: uppercase;
font-weight: bold;
font-size: 90%;
padding: 15px 18px 7px 16px;
}
nav h3:first-child {
padding-top: 0;
} }
nav a { nav a {
display: block; display: block;
color: var(--color-text-dull); color: var(--color-text-dull);
text-transform: uppercase; padding: 7px 18px 7px 13px;
font-weight: bold; border-left: 3px solid transparent;
padding: 9px 18px 9px 18px;
} }
nav a.selected { nav a.selected {
color: var(--color-text-main); color: var(--color-text-main);
border-bottom: 3px solid var(--color-highlight); background: var(--color-bg-main);
border-radius: 0 5px 5px 0;
} }
nav a:hover { nav a:hover {
color: var(--color-text-main); color: var(--color-text-main);
border-left: 3px solid var(--color-highlight);
}
nav a.selected:hover {
border-left: 3px solid transparent;
}
nav a i {
width: 20px;
text-align: center;
margin-right: 4px;
display: inline-block;
} }
/* Left-right columns */ /* Left-right columns */
@ -225,6 +250,7 @@ nav a:hover {
.right-column { .right-column {
width: 250px; width: 250px;
background: var(--color-bg-menu); background: var(--color-bg-menu);
border-radius: 0 0 5px 0;
} }
.right-column h2 { .right-column h2 {
@ -237,22 +263,16 @@ nav a:hover {
/* Icon menus */ /* Icon menus */
.icon-menu { .icon-menu {}
display: flex;
flex-wrap: wrap;
padding: 30px 0 0 30px;
}
.icon-menu>a { .icon-menu>a {
margin: 0px 40px 40px 0; display: block;
margin: 0px 0 20px 0;
background: var(--color-bg-box); background: var(--color-bg-box);
box-shadow: 5px 5px 10px rgba(0, 0, 0, 0.1); box-shadow: 5px 5px 10px rgba(0, 0, 0, 0.1);
width: 370px;
height: 100px;
line-height: 100px;
color: inherit; color: inherit;
text-decoration: none; text-decoration: none;
padding: 0 20px; padding: 10px 20px;
border: 2px solid rgba(255, 255, 255, 0); border: 2px solid rgba(255, 255, 255, 0);
border-radius: 3px; border-radius: 3px;
} }
@ -291,8 +311,21 @@ nav a:hover {
/* Forms */ /* Forms */
form { fieldset {
padding: 20px 40px 20px 30px; border: 0;
background: var(--color-bg-box);
box-shadow: 5px 5px 10px rgba(0, 0, 0, 0.1);
margin: 25px 0 45px 0;
padding: 5px 15px;
}
fieldset legend {
position: relative;
top: -15px;
left: -15px;
font-weight: bold;
text-transform: uppercase;
color: var(--color-text-dull);
} }
.right-column form, .right-column form,
@ -316,10 +349,16 @@ form p {
} }
form .field { form .field {
margin: 25px 0 25px 0; margin: 0 0 25px 0;
background: var(--color-bg-box); display: flex;
box-shadow: 5px 5px 10px rgba(0, 0, 0, 0.1); }
padding: 10px 10px;
form .field:last-of-type {
margin-bottom: 10px;
}
form .field .label-input {
flex-grow: 1;
} }
.right-column form .field { .right-column form .field {
@ -334,6 +373,7 @@ form label {
text-transform: uppercase; text-transform: uppercase;
font-size: 100%; font-size: 100%;
font-weight: bold; font-weight: bold;
margin: 0 0 5px 0;
} }
form label small { form label small {
@ -349,7 +389,7 @@ form label small {
form .help { form .help {
color: var(--color-text-dull); color: var(--color-text-dull);
font-size: 90%; font-size: 90%;
margin: 2px 0 6px 0; margin: -5px 0 5px 0;
} }
form .errorlist { form .errorlist {
@ -422,9 +462,16 @@ form input[type=submit]:hover {
background: var(--color-button-main-hover); background: var(--color-button-main-hover);
} }
form img.preview {
max-height: 100%;
max-width: 100px;
margin: 0 0 0 20px;
align-self: center;
}
form .buttons { form .buttons {
text-align: right; text-align: right;
margin: 25px 0 15px 0; margin: -20px 0 15px 0;
} }
.right-column form .buttons { .right-column form .buttons {
@ -459,6 +506,12 @@ form .button.toggle {
background: var(--color-bg-main); background: var(--color-bg-main);
} }
form button.left,
form .button.left {
float: left;
margin: 0 5px 0 0;
}
form button.toggle.enabled, form button.toggle.enabled,
form .button.toggle.enabled { form .button.toggle.enabled {
background: var(--color-highlight); background: var(--color-highlight);
@ -661,3 +714,37 @@ h1.identity small {
font-size: 20px; font-size: 20px;
} }
} }
@media (max-width: 700px) {
header menu a.identity {
width: 50px;
padding: 10px 10px 0 0;
}
.right-column {
width: 50px;
}
.right-column nav {
padding-right: 0;
}
.right-column nav a {
font-size: 0;
padding: 10px 0 10px 10px;
}
.right-column nav a i {
font-size: 22px;
}
.right-column h3 {
visibility: hidden;
}
.right-column h2,
.right-column .compose {
display: none;
}
}

View File

@ -1,6 +1,27 @@
<nav> <nav>
<a href="/" {% if current_page == "home" %}class="selected"{% endif %}>Home</a> <a href="/" {% if current_page == "home" %}class="selected"{% endif %}>
<a href="/notifications/" {% if current_page == "notifications" %}class="selected"{% endif %}>Notifications</a> <i class="fa-solid fa-home"></i> Home
<a href="/local/" {% if current_page == "local" %}class="selected"{% endif %}>Local</a> </a>
<a href="/federated/" {% if current_page == "federated" %}class="selected"{% endif %}>Federated</a> <a href="/notifications/" {% if current_page == "notifications" %}class="selected"{% endif %}>
<i class="fa-solid fa-at"></i> Notifications
</a>
<a href="/local/" {% if current_page == "local" %}class="selected"{% endif %}>
<i class="fa-solid fa-city"></i> Local
</a>
<a href="/federated/" {% if current_page == "federated" %}class="selected"{% endif %}>
<i class="fa-solid fa-globe"></i> Federated
</a>
</nav> </nav>
{% if current_page == "home" %}
<h2>Compose</h2>
<form action="/compose/" method="POST" class="compose">
{% csrf_token %}
{{ form.text }}
{{ form.content_warning }}
<div class="buttons">
<span class="button toggle" _="on click toggle .enabled then toggle .hidden on #id_content_warning">CW</span>
<button>{% if config_identity.toot_mode %}Toot!{% else %}Post{% endif %}</button>
</div>
</form>
{% endif %}

View File

@ -3,15 +3,14 @@
{% block title %}Compose{% endblock %} {% block title %}Compose{% endblock %}
{% block content %} {% block content %}
<nav>
<a href="." class="selected">Compose</a>
</nav>
<form action="." method="POST"> <form action="." method="POST">
{% csrf_token %} {% csrf_token %}
{% for field in form %} <fieldset>
{% include "forms/_field.html" %} <legend>Content</legend>
{% endfor %} {% include "forms/_field.html" with field=form.text %}
{% include "forms/_field.html" with field=form.content_warning %}
{% include "forms/_field.html" with field=form.visibility %}
</fieldset>
<div class="buttons"> <div class="buttons">
<button>{% if config_identity.toot_mode %}Toot!{% else %}Post{% endif %}</button> <button>{% if config_identity.toot_mode %}Toot!{% else %}Post{% endif %}</button>
</div> </div>

View File

@ -3,19 +3,9 @@
{% block title %}Federated Timeline{% endblock %} {% block title %}Federated Timeline{% endblock %}
{% block content %} {% block content %}
{% include "activities/_home_menu.html" %} {% for post in posts %}
{% include "activities/_post.html" %}
<section class="columns"> {% empty %}
<div class="left-column"> No posts yet.
{% for post in posts %} {% endfor %}
{% include "activities/_post.html" %}
{% empty %}
No posts yet.
{% endfor %}
</div>
<div class="right-column">
<h2>?</h2>
</div>
</section>
{% endblock %} {% endblock %}

View File

@ -3,39 +3,18 @@
{% block title %}Home{% endblock %} {% block title %}Home{% endblock %}
{% block content %} {% block content %}
{% include "activities/_home_menu.html" %} {% for event in events %}
{% if event.type == "post" %}
<section class="columns"> {% include "activities/_post.html" with post=event.subject_post %}
{% elif event.type == "boost" %}
<div class="left-column"> <div class="boost-banner">
{% for event in events %} <a href="{{ event.subject_identity.urls.view }}">
{% if event.type == "post" %} {{ event.subject_identity.name_or_handle }}
{% include "activities/_post.html" with post=event.subject_post %} </a> boosted
{% elif event.type == "boost" %} </div>
<div class="boost-banner"> {% include "activities/_post.html" with post=event.subject_post %}
<a href="{{ event.subject_identity.urls.view }}"> {% endif %}
{{ event.subject_identity.name_or_handle }} {% empty %}
</a> boosted Nothing to show yet.
</div> {% endfor %}
{% include "activities/_post.html" with post=event.subject_post %}
{% endif %}
{% empty %}
Nothing to show yet.
{% endfor %}
</div>
<div class="right-column">
<h2>Compose</h2>
<form action="/compose/" method="POST" class="compose">
{% csrf_token %}
{{ form.text }}
{{ form.content_warning }}
<div class="buttons">
<span class="button toggle" _="on click toggle .enabled then toggle .hidden on #id_content_warning">CW</span>
<button>{% if config_identity.toot_mode %}Toot!{% else %}Post{% endif %}</button>
</div>
</form>
</div>
</section>
{% endblock %} {% endblock %}

View File

@ -3,19 +3,9 @@
{% block title %}Local Timeline{% endblock %} {% block title %}Local Timeline{% endblock %}
{% block content %} {% block content %}
{% include "activities/_home_menu.html" %} {% for post in posts %}
{% include "activities/_post.html" %}
<section class="columns"> {% empty %}
<div class="left-column"> No posts yet.
{% for post in posts %} {% endfor %}
{% include "activities/_post.html" %}
{% empty %}
No posts yet.
{% endfor %}
</div>
<div class="right-column">
<h2>?</h2>
</div>
</section>
{% endblock %} {% endblock %}

View File

@ -3,20 +3,9 @@
{% block title %}Notifications{% endblock %} {% block title %}Notifications{% endblock %}
{% block content %} {% block content %}
{% include "activities/_home_menu.html" %} {% for event in events %}
{% include "activities/_event.html" %}
<section class="columns"> {% empty %}
<div class="left-column"> No events yet.
{% for event in events %} {% endfor %}
{% include "activities/_event.html" %}
{% empty %}
No events yet.
{% endfor %}
</div>
<div class="right-column">
<h2>?</h2>
</div>
</section>
{% endblock %} {% endblock %}

View File

@ -1,6 +0,0 @@
<nav>
<a href="{% url "admin_basic" %}" {% if section == "basic" %}class="selected"{% endif %}>Basic</a>
<a href="{% url "admin_domains" %}" {% if section == "domains" %}class="selected"{% endif %}>Domains</a>
<a href="{% url "admin_users" %}" {% if section == "users" %}class="selected"{% endif %}>Users</a>
<a href="{% url "admin_identities" %}" {% if section == "identities" %}class="selected"{% endif %}>Identities</a>
</nav>

View File

@ -1,11 +1,8 @@
{% extends "base.html" %} {% extends "settings/base.html" %}
{% block title %}Add Domain - Admin{% endblock %} {% block title %}Add Domain - Admin{% endblock %}
{% block content %} {% block content %}
{% block menu %}
{% include "admin/_menu.html" %}
{% endblock %}
<form action="." method="POST"> <form action="." method="POST">
<h1>Add A Domain</h1> <h1>Add A Domain</h1>
<p> <p>
@ -28,12 +25,17 @@
service domain. service domain.
</p> </p>
{% csrf_token %} {% csrf_token %}
{% for field in form %} <fieldset>
{% include "forms/_field.html" %} <legend>Domain Details</legend>
{% endfor %} {% include "forms/_field.html" with field=form.domain %}
{% include "forms/_field.html" with field=form.service_domain %}
</fieldset>
<fieldset>
<legend>Access Control</legend>
{% include "forms/_field.html" with field=form.public %}
</fieldset>
<div class="buttons"> <div class="buttons">
<a href="{{ domain.urls.delete }}" class="button delete">Delete</a> <button>Create</button>
<button>Save</button>
</div> </div>
</form> </form>
{% endblock %} {% endblock %}

View File

@ -1,12 +1,8 @@
{% extends "base.html" %} {% extends "settings/base.html" %}
{% block title %}Delete {{ domain.domain }} - Admin{% endblock %} {% block title %}Delete {{ domain.domain }} - Admin{% endblock %}
{% block content %} {% block content %}
{% block menu %}
{% include "admin/_menu.html" %}
{% endblock %}
<form action="." method="POST"> <form action="." method="POST">
{% csrf_token %} {% csrf_token %}
@ -28,6 +24,5 @@
<button class="delete">Confirm Deletion</button> <button class="delete">Confirm Deletion</button>
</div> </div>
{% endif %} {% endif %}
</form>
{% endblock %} {% endblock %}

View File

@ -1,16 +1,19 @@
{% extends "base.html" %} {% extends "settings/base.html" %}
{% block title %}{{ domain.domain }} - Admin{% endblock %} {% block subtitle %}{{ domain.domain }}{% endblock %}
{% block content %} {% block content %}
{% block menu %}
{% include "admin/_menu.html" %}
{% endblock %}
<form action="." method="POST"> <form action="." method="POST">
{% csrf_token %} {% csrf_token %}
{% for field in form %} <fieldset>
{% include "forms/_field.html" %} <legend>Domain Details</legend>
{% endfor %} {% include "forms/_field.html" with field=form.domain %}
{% include "forms/_field.html" with field=form.service_domain %}
</fieldset>
<fieldset>
<legend>Access Control</legend>
{% include "forms/_field.html" with field=form.public %}
</fieldset>
<div class="buttons"> <div class="buttons">
<a href="{{ domain.urls.delete }}" class="button delete">Delete</a> <a href="{{ domain.urls.delete }}" class="button delete">Delete</a>
<button>Save</button> <button>Save</button>

View File

@ -1,11 +1,8 @@
{% extends "base.html" %} {% extends "settings/base.html" %}
{% block title %}{{ section.title }} - Admin{% endblock %} {% block subtitle %}Domains{% endblock %}
{% block content %} {% block content %}
{% block menu %}
{% include "admin/_menu.html" %}
{% endblock %}
<section class="icon-menu"> <section class="icon-menu">
{% for domain in domains %} {% for domain in domains %}
<a class="option" href="{{ domain.urls.edit }}"> <a class="option" href="{{ domain.urls.edit }}">

View File

@ -1,14 +1,9 @@
{% extends "base.html" %} {% extends "settings/base.html" %}
{% block title %}Identities - Admin{% endblock %} {% block subtitle %}Identities{% endblock %}
{% block content %} {% block content %}
{% block menu %} <p>
{% include "admin/_menu.html" %} Please use the <a href="/djadmin/users/identity/">Django Admin</a> for now.
{% endblock %} </p>
<form>
<p>
Please use the <a href="/djadmin/users/identity/">Django Admin</a> for now.
</p>
</form>
{% endblock %} {% endblock %}

View File

@ -1,18 +0,0 @@
{% extends "base.html" %}
{% block title %}{{ section.title }} - Admin{% endblock %}
{% block content %}
{% block menu %}
{% include "admin/_menu.html" %}
{% endblock %}
<form action="." method="POST">
{% csrf_token %}
{% for field in form %}
{% include "forms/_field.html" %}
{% endfor %}
<div class="buttons">
<button>Save</button>
</div>
</form>
{% endblock %}

View File

@ -1,14 +1,9 @@
{% extends "base.html" %} {% extends "settings/base.html" %}
{% block title %}Users - Admin{% endblock %} {% block subtitle %}Users{% endblock %}
{% block content %} {% block content %}
{% block menu %} <p>
{% include "admin/_menu.html" %} Please use the <a href="/djadmin/users/user/">Django Admin</a> for now.
{% endblock %} </p>
<form>
<p>
Please use the <a href="/djadmin/users/user/">Django Admin</a> for now.
</p>
</form>
{% endblock %} {% endblock %}

View File

@ -31,14 +31,12 @@
<a href="/compose/" title="Compose" {% if top_section == "compose" %}class="selected"{% endif %}> <a href="/compose/" title="Compose" {% if top_section == "compose" %}class="selected"{% endif %}>
<i class="fa-solid fa-feather"></i> Compose <i class="fa-solid fa-feather"></i> Compose
</a> </a>
<a href="#" title="Search" {% if top_section == "search" %}class="selected"{% endif %}>
<i class="fa-solid fa-search"></i> Search
</a>
<a href="{% url "settings" %}" title="Settings" {% if top_section == "settings" %}class="selected"{% endif %}> <a href="{% url "settings" %}" title="Settings" {% if top_section == "settings" %}class="selected"{% endif %}>
<i class="fa-solid fa-gear"></i> Settings <i class="fa-solid fa-gear"></i> Settings
</a> </a>
{% if request.user.admin %}
<a href="{% url "admin" %}" title="Admin" {% if top_section == "admin" %}class="selected"{% endif %}>
<i class="fa-solid fa-toolbox"></i> Admin
</a>
{% endif %}
<div class="gap"></div> <div class="gap"></div>
<a href="/identity/select/" class="identity"> <a href="/identity/select/" class="identity">
{% if not request.identity %} {% if not request.identity %}
@ -61,7 +59,18 @@
</menu> </menu>
</header> </header>
{% block content %} {% block full_content %}
<div class="columns">
<div class="left-column">
{% block content %}
{% endblock %}
</div>
<div class="right-column">
{% block right_content %}
{% include "activities/_home_menu.html" %}
{% endblock %}
</div>
</div>
{% endblock %} {% endblock %}
</main> </main>

View File

@ -1,13 +1,18 @@
<div class="field"> <div class="field">
<label for="{{ field.id_for_label }}"> <div class="label-input">
{{ field.label }} <label for="{{ field.id_for_label }}">
{% if field.field.required %}<small>(Required)</small>{% endif %} {{ field.label }}
</label> {% if field.field.required %}<small>(Required)</small>{% endif %}
{% if field.help_text %} </label>
<p class="help"> {% if field.help_text %}
{{ field.help_text|linebreaksbr }} <p class="help">
</p> {{ field.help_text|linebreaksbr }}
</p>
{% endif %}
{{ field.errors }}
{{ field }}
</div>
{% if preview %}
<img class="preview" src="{{ preview }}">
{% endif %} {% endif %}
{{ field.errors }}
{{ field }}
</div> </div>

View File

@ -0,0 +1,7 @@
{% extends "base.html" %}
{% block title %}{% block subtitle %}{% endblock %} - Settings{% endblock %}
{% block right_content %}
{% include "identity/_menu.html" %}
{% endblock %}

View File

@ -1,17 +1,19 @@
{% extends "base.html" %} {% extends "identity/base.html" %}
{% block title %}Create Identity{% endblock %} {% block title %}Create Identity{% endblock %}
{% block content %} {% block content %}
{% include "identity/_identity_menu.html" %}
<form action="." method="POST"> <form action="." method="POST">
<h1>Create New Identity</h1> <h1>Create New Identity</h1>
<p>You can have multiple identities - they are totally separate, and share <p>You can have multiple identities - they are totally separate, and share
nothing apart from your login details. Use them for alternates, projects, and more.</p> nothing apart from your login details. Use them for alternates, projects, and more.</p>
{% csrf_token %} {% csrf_token %}
{% for field in form %} <fieldset>
{% include "forms/_field.html" %} <legend>Identity Details</legend>
{% endfor %} {% include "forms/_field.html" with field=form.username %}
{% include "forms/_field.html" with field=form.domain %}
{% include "forms/_field.html" with field=form.name %}
</fieldset>
<div class="buttons"> <div class="buttons">
<button>Create</button> <button>Create</button>
</div> </div>

View File

@ -1,18 +1,12 @@
{% extends "base.html" %} {% extends "identity/base.html" %}
{% load static %}
{% block title %}Select Identity{% endblock %} {% block title %}Select Identity{% endblock %}
{% block content %} {% block content %}
{% include "identity/_identity_menu.html" %}
<section class="icon-menu"> <section class="icon-menu">
{% for identity in identities %} {% for identity in identities %}
<a class="option" href="{{ identity.urls.activate }}"> <a class="option" href="{{ identity.urls.activate }}">
{% if identity.icon_uri %} <img src="{{ identity.local_icon_url }}">
<img src="{{ identity.icon_uri }}">
{% else %}
<img src="{% static "img/unknown-icon-128.png" %}">
{% endif %}
<span class="handle"> <span class="handle">
{{ identity.name_or_handle }} {{ identity.name_or_handle }}
<small>@{{ identity.handle }}</small> <small>@{{ identity.handle }}</small>

View File

@ -1,5 +1,28 @@
<nav> <nav>
<a href="{% url "settings_profile" %}" {% if section == "profile" %}class="selected"{% endif %}>Profile</a> <h3>Identity</h3>
<a href="#" {% if section == "interface" %}class="selected"{% endif %}>Interface</a> <a href="{% url "settings_profile" %}" {% if section == "profile" %}class="selected"{% endif %}>
<a href="#" {% if section == "filtering" %}class="selected"{% endif %}>Filtering</a> <i class="fa-solid fa-user"></i> Profile
</a>
<a href="{% url "settings_interface" %}" {% if section == "interface" %}class="selected"{% endif %}>
<i class="fa-solid fa-display"></i> Interface
</a>
{% if request.user.admin %}
<h3>Account</h3>
<a href="#" {% if section == "login" %}class="selected"{% endif %}>
<i class="fa-solid fa-key"></i> Login &amp; Security
</a>
<h3>Administration</h3>
<a href="{% url "admin_basic" %}" {% if section == "basic" %}class="selected"{% endif %}>
<i class="fa-solid fa-book"></i> Basic
</a>
<a href="{% url "admin_domains" %}" {% if section == "domains" %}class="selected"{% endif %}>
<i class="fa-solid fa-globe"></i> Domains
</a>
<a href="{% url "admin_users" %}" {% if section == "users" %}class="selected"{% endif %}>
<i class="fa-solid fa-users"></i> Users
</a>
<a href="{% url "admin_identities" %}" {% if section == "identities" %}class="selected"{% endif %}>
<i class="fa-solid fa-id-card"></i> Identities
</a>
{% endif %}
</nav> </nav>

View File

@ -0,0 +1,7 @@
{% extends "base.html" %}
{% block title %}{% block subtitle %}{% endblock %} - Settings{% endblock %}
{% block right_content %}
{% include "settings/_menu.html" %}
{% endblock %}

View File

@ -1,18 +1,22 @@
{% extends "base.html" %} {% extends "settings/base.html" %}
{% block title %}Profile - Settings{% endblock %} {% block subtitle %}Profile{% endblock %}
{% block content %} {% block content %}
{% block menu %}
{% include "settings/_menu.html" %}
{% endblock %}
<form action="." method="POST" enctype="multipart/form-data" > <form action="." method="POST" enctype="multipart/form-data" >
{% csrf_token %} {% csrf_token %}
{% for field in form %} <fieldset>
{% include "forms/_field.html" %} <legend>Details</legend>
{% endfor %} {% include "forms/_field.html" with field=form.name %}
{% include "forms/_field.html" with field=form.summary %}
</fieldset>
<fieldset>
<legend>Images</legend>
{% include "forms/_field.html" with field=form.icon preview=request.identity.icon.url %}
{% include "forms/_field.html" with field=form.image preview=request.identity.image.url %}
</fieldset>
<div class="buttons"> <div class="buttons">
<a href="{{ request.identity.urls.view }}" class="button secondary">View Profile</a> <a href="{{ request.identity.urls.view }}" class="button secondary left">View Profile</a>
<button>Save</button> <button>Save</button>
</div> </div>
</form> </form>

View File

@ -1,15 +1,17 @@
{% extends "base.html" %} {% extends "settings/base.html" %}
{% block title %}{{ section.title }} - Settings{% endblock %} {% block subtitle %}{{ section.title }}{% endblock %}
{% block content %} {% block content %}
{% block menu %}
{% include "settings/_menu.html" %}
{% endblock %}
<form action="." method="POST"> <form action="." method="POST">
{% csrf_token %} {% csrf_token %}
{% for field in form %} {% for title, fields in fieldsets.items %}
{% include "forms/_field.html" %} <fieldset>
<legend>{{ title }}</legend>
{% for field in fields %}
{% include "forms/_field.html" %}
{% endfor %}
</fieldset>
{% endfor %} {% endfor %}
<div class="buttons"> <div class="buttons">
<button>Save</button> <button>Save</button>

View File

@ -24,7 +24,6 @@ class AdminSettingsPage(SettingsPage):
at the bottom of the page. Don't add this to a URL directly - subclass! at the bottom of the page. Don't add this to a URL directly - subclass!
""" """
template_name = "admin/settings.html"
options_class = Config.SystemOptions options_class = Config.SystemOptions
def load_config(self): def load_config(self):
@ -47,6 +46,15 @@ class BasicPage(AdminSettingsPage):
"title": "Highlight Color", "title": "Highlight Color",
"help_text": "Used for logo background and other highlights", "help_text": "Used for logo background and other highlights",
}, },
"post_length": {
"title": "Maximum Post Length",
"help_text": "The maximum number of characters allowed per post",
},
}
layout = {
"Branding": ["site_name", "highlight_color"],
"Posts": ["post_length"],
} }

View File

@ -1,5 +1,5 @@
from functools import partial from functools import partial
from typing import ClassVar, Dict from typing import ClassVar, Dict, List
from django import forms from django import forms
from django.shortcuts import redirect from django.shortcuts import redirect
@ -27,6 +27,7 @@ class SettingsPage(FormView):
template_name = "settings/settings.html" template_name = "settings/settings.html"
section: ClassVar[str] section: ClassVar[str]
options: Dict[str, Dict[str, str]] options: Dict[str, Dict[str, str]]
layout: Dict[str, List[str]]
def get_form_class(self): def get_form_class(self):
# Create the fields dict from the config object # Create the fields dict from the config object
@ -42,6 +43,8 @@ class SettingsPage(FormView):
) )
elif config_field.type_ is str: elif config_field.type_ is str:
form_field = forms.CharField form_field = forms.CharField
elif config_field.type_ is int:
form_field = forms.IntegerField
else: else:
raise ValueError(f"Cannot render settings type {config_field.type_}") raise ValueError(f"Cannot render settings type {config_field.type_}")
fields[key] = form_field( fields[key] = form_field(
@ -68,6 +71,10 @@ class SettingsPage(FormView):
def get_context_data(self): def get_context_data(self):
context = super().get_context_data() context = super().get_context_data()
context["section"] = self.section context["section"] = self.section
# Gather fields into fieldsets
context["fieldsets"] = {}
for title, fields in self.layout.items():
context["fieldsets"][title] = [context["form"][field] for field in fields]
return context return context
def form_valid(self, form): def form_valid(self, form):
@ -87,10 +94,12 @@ class InterfacePage(SettingsPage):
options = { options = {
"toot_mode": { "toot_mode": {
"title": "I Will Toot As I Please", "title": "I Will Toot As I Please",
"help_text": "If enabled, changes all 'Post' buttons to 'Toot!'", "help_text": "Changes all 'Post' buttons to 'Toot!'",
} }
} }
layout = {"Posting": ["toot_mode"]}
@method_decorator(identity_required, name="dispatch") @method_decorator(identity_required, name="dispatch")
class ProfilePage(FormView): class ProfilePage(FormView):
@ -102,9 +111,18 @@ class ProfilePage(FormView):
class form_class(forms.Form): class form_class(forms.Form):
name = forms.CharField(max_length=500) name = forms.CharField(max_length=500)
summary = forms.CharField(widget=forms.Textarea, required=False) summary = forms.CharField(
icon = forms.ImageField(required=False) widget=forms.Textarea,
image = forms.ImageField(required=False) required=False,
help_text="Describe you and your interests",
label="Bio",
)
icon = forms.ImageField(
required=False, help_text="Shown next to all your posts and activities"
)
image = forms.ImageField(
required=False, help_text="Shown at the top of your profile"
)
def get_initial(self): def get_initial(self):
return { return {