summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--static/css/style.css36
-rw-r--r--static/img/unknown-icon-128.pngbin0 -> 2864 bytes
-rw-r--r--static/img/unknown_icon.svg67
-rw-r--r--takahe/urls.py1
-rw-r--r--templates/base.html18
-rw-r--r--templates/identity/select.html14
-rw-r--r--templates/identity/view.html3
-rw-r--r--users/views/identity.py24
8 files changed, 157 insertions, 6 deletions
diff --git a/static/css/style.css b/static/css/style.css
index 93672c0..69eaa44 100644
--- a/static/css/style.css
+++ b/static/css/style.css
@@ -97,6 +97,8 @@ header {
}
header h1 {
+ font-family: "Raleway";
+ font-weight: normal;
background: var(--color-fg2);
padding: 10px 7px 7px 7px;
font-size: 130%;
@@ -104,6 +106,12 @@ header h1 {
color: var(--color-fg1);
}
+header h1 img {
+ display: inline;
+ vertical-align: top;
+ margin: 0 3px 0 0;
+}
+
header a {
color: inherit;
text-decoration: none;
@@ -117,8 +125,9 @@ header menu {
}
header menu li {
- padding: 20px 10px 7px 10px;
+ padding: 10px 10px 7px 10px;
color: #eee;
+ line-height: 32px;
}
main {
@@ -150,10 +159,28 @@ main {
.modal .option {
display: block;
- padding: 20px 30px;
+ padding: 15px 20px;
color: var(--color-text-main);
text-decoration: none;
border-left: 3px solid transparent;
+ line-height: 32px;
+}
+
+.modal .option img {
+ display: inline;
+ vertical-align: middle;
+ margin: 0 10px 3px 0;
+}
+
+.modal .option i {
+ display: inline-block;
+ vertical-align: middle;
+ width: 32px;
+ height: 32px;
+ text-align: center;
+ line-height: 32px;
+ font-size: 150%;
+ margin: 0 10px 3px 0;
}
.modal a.option:hover {
@@ -165,6 +192,11 @@ main {
color: var(--color-text-dull);
}
+.modal .option small {
+ margin: 0 0 0 5px;
+ color: var(--color-text-dull);
+}
+
.modal form {
padding: 10px 10px 1px 10px;
}
diff --git a/static/img/unknown-icon-128.png b/static/img/unknown-icon-128.png
new file mode 100644
index 0000000..0bfef8e
--- /dev/null
+++ b/static/img/unknown-icon-128.png
Binary files differ
diff --git a/static/img/unknown_icon.svg b/static/img/unknown_icon.svg
new file mode 100644
index 0000000..0801de8
--- /dev/null
+++ b/static/img/unknown_icon.svg
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ width="1024"
+ height="1024"
+ viewBox="0 0 270.93333 270.93333"
+ version="1.1"
+ id="svg5"
+ xml:space="preserve"
+ inkscape:version="1.2.1 (9c6d41e410, 2022-07-14)"
+ sodipodi:docname="unknown_icon.svg"
+ inkscape:export-filename="unknown-icon-128.png"
+ inkscape:export-xdpi="12.000001"
+ inkscape:export-ydpi="12.000001"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
+ id="namedview7"
+ pagecolor="#ffffff"
+ bordercolor="#000000"
+ borderopacity="0.25"
+ inkscape:showpageshadow="2"
+ inkscape:pageopacity="0.0"
+ inkscape:pagecheckerboard="0"
+ inkscape:deskcolor="#d1d1d1"
+ inkscape:document-units="mm"
+ showgrid="false"
+ inkscape:zoom="0.5946522"
+ inkscape:cx="794.58211"
+ inkscape:cy="476.74927"
+ inkscape:current-layer="layer1"><inkscape:grid
+ type="xygrid"
+ id="grid111" /></sodipodi:namedview><defs
+ id="defs2" /><g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"><rect
+ style="fill:#bdcdd4;stroke-width:4.23333"
+ id="rect971"
+ width="270.93332"
+ height="270.93332"
+ x="0"
+ y="0" /><path
+ style="opacity:1;fill:#9eabb0;fill-opacity:1;stroke-width:0.264583"
+ d="m 0,69.41032 c 0,0 15.97341,-15.642206 48.0533,-21.973129 34.750862,-6.858036 65.71908,-10e-7 65.71908,-10e-7 l 29.94258,29.982012 -1.77975,90.322408 c 0,0 20.77918,34.00148 8.1347,35.37354 -26.69628,2.89682 -33.27837,33.1808 -28.04801,50.49948 5.23035,17.31867 4.34048,17.3187 4.34048,17.3187 L 0,270.9333 Z"
+ id="path18329"
+ sodipodi:nodetypes="cscccssccc" /><path
+ style="fill:#9eabb0;fill-opacity:1;stroke-width:0.264583"
+ d="m 113.77238,47.437191 c 0,0 14.85651,51.896699 13.28342,78.010049 -1.5731,26.11334 -5.0339,27.68645 -5.0339,27.68645 l 73.1442,-51.48951 C 179.27554,82.720765 145.61903,53.85498 113.77238,47.437191 Z"
+ id="path12139"
+ sodipodi:nodetypes="csccc" /><path
+ style="opacity:1;fill:#9eabb0;fill-opacity:1;stroke-width:0.264583"
+ d="m 122.0219,153.13369 73.1442,-51.48951 c 0,0 17.14886,15.4811 33.97498,41.63762 16.02811,24.91601 28.61719,57.75025 28.61719,57.75025 0,0 -36.26795,-3.41078 -67.93143,-3.01587 -46.29774,0.57743 -66.45321,7.99579 -66.45321,7.99579 -9.60125,-22.07547 -9.60125,-30.80281 -1.35173,-52.87828 z"
+ id="path13694"
+ sodipodi:nodetypes="ccscscc" /><text
+ xml:space="preserve"
+ style="font-weight:200;font-size:154.312px;font-family:Raleway;-inkscape-font-specification:'Raleway Ultra-Light';opacity:0.608833;fill:#ffffff;stroke-width:33.0667"
+ x="98.045929"
+ y="187.69951"
+ id="text1027"><tspan
+ sodipodi:role="line"
+ id="tspan1025"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:Raleway;-inkscape-font-specification:'Raleway Bold';fill:#ffffff;stroke-width:33.0667"
+ x="98.045929"
+ y="187.69951">?</tspan></text></g></svg>
diff --git a/takahe/urls.py b/takahe/urls.py
index c43e4fa..d6e4d8f 100644
--- a/takahe/urls.py
+++ b/takahe/urls.py
@@ -14,6 +14,7 @@ urlpatterns = [
path("@<handle>/actor/", identity.Actor.as_view()),
path("@<handle>/actor/inbox/", identity.Inbox.as_view()),
# Identity selection
+ path("@<handle>/activate/", identity.ActivateIdentity.as_view()),
path("identity/select/", identity.SelectIdentity.as_view()),
path("identity/create/", identity.CreateIdentity.as_view()),
# Well-known endpoints
diff --git a/templates/base.html b/templates/base.html
index 4dc62ca..af2887f 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -13,11 +13,25 @@
<body class="{% block body_class %}{% endblock %}">
<header>
- <h1><a href="/">{{ config.site_name }}</a></h1>
+ <h1>
+ <a href="/">
+ <img src="{% static "img/icon-128.png" %}" width="32">
+ {{ config.site_name }}
+ </a>
+ </h1>
<menu>
<li>
+ <a href="#"><i class="fa-solid fa-gear"></i></a>
+ </li>
+ <li>
{% if user.is_authenticated %}
- Logout
+ <a href="/identity/select/">
+ {% if user.icon_uri %}
+ <img src="{{ user.icon_uri }}" width="32">
+ {% else %}
+ <img src="{% static "img/unknown-icon-128.png" %}" width="32">
+ {% endif %}
+ </a>
{% else %}
<a href="/auth/login/">Login</a>
{% endif %}
diff --git a/templates/identity/select.html b/templates/identity/select.html
index f5be401..dae1ca1 100644
--- a/templates/identity/select.html
+++ b/templates/identity/select.html
@@ -1,4 +1,5 @@
{% extends "base.html" %}
+{% load static %}
{% block title %}Select Identity{% endblock %}
@@ -6,12 +7,23 @@
<section class="modal identities">
<h1>Select Identity</h1>
{% for identity in identities %}
- <a class="option" href="{{ identity.urls.activate }}">{{ identity }}</a>
+ <a class="option" href="{{ identity.urls.activate }}">
+ {% if identity.icon_uri %}
+ <img src="{{ identity.icon_uri }}" width="32">
+ {% else %}
+ <img src="{% static "img/unknown-icon-128.png" %}" width="32">
+ {% endif %}
+ {{ identity }}
+ <small>@{{ identity.short_handle }}</small>
+ </a>
{% empty %}
<p class="option empty">You have no identities.</p>
{% endfor %}
<a href="/identity/create/" class="option new">
<i class="fa-solid fa-plus"></i> Create a new identity
</a>
+ <a href="/auth/logout/" class="option new">
+ <i class="fa-solid fa-right-from-bracket"></i> Logout
+ </a>
</section>
{% endblock %}
diff --git a/templates/identity/view.html b/templates/identity/view.html
index 68bb7a3..deb7ae5 100644
--- a/templates/identity/view.html
+++ b/templates/identity/view.html
@@ -1,4 +1,5 @@
{% extends "base.html" %}
+{% load static %}
{% block title %}{{ identity }}{% endblock %}
@@ -6,6 +7,8 @@
<h1 class="identity">
{% if identity.icon_uri %}
<img src="{{identity.icon_uri}}" class="icon">
+ {% else %}
+ <img src="{% static "img/unknown-icon-128.png" %}" class="icon">
{% endif %}
{{ identity }} <small>{{ identity.handle }}</small>
</h1>
diff --git a/users/views/identity.py b/users/views/identity.py
index 9f2a7f9..fff521b 100644
--- a/users/views/identity.py
+++ b/users/views/identity.py
@@ -1,3 +1,5 @@
+import string
+
from django import forms
from django.conf import settings
from django.contrib.auth.decorators import login_required
@@ -37,6 +39,20 @@ class SelectIdentity(TemplateView):
@method_decorator(login_required, name="dispatch")
+class ActivateIdentity(View):
+ def get(self, request, handle):
+ identity = by_handle_or_404(request, handle)
+ if not identity.users.filter(pk=request.user.pk).exists():
+ raise Http404()
+ request.session["identity_id"] = identity.id
+ # Get next URL, not allowing offsite links
+ next = request.GET.get("next") or "/"
+ if ":" in next:
+ next = "/"
+ return redirect("/")
+
+
+@method_decorator(login_required, name="dispatch")
class CreateIdentity(FormView):
template_name = "identity/create.html"
@@ -50,10 +66,16 @@ class CreateIdentity(FormView):
def clean_handle(self):
# Remove any leading @
value = self.cleaned_data["handle"].lstrip("@")
+ # Validate it's all ascii characters
+ for character in value:
+ if character not in string.ascii_letters + string.digits + "_-":
+ raise forms.ValidationError(
+ "Only the letters a-z, numbers 0-9, dashes and underscores are allowed."
+ )
# Don't allow custom domains here quite yet
if "@" in value:
raise forms.ValidationError(
- "You are not allowed an @ sign in your handle"
+ "You are not allowed an @ sign in your handle."
)
# Ensure there is a domain on the end
if "@" not in value: