diff options
-rw-r--r-- | static/css/style.css | 36 | ||||
-rw-r--r-- | static/img/unknown-icon-128.png | bin | 0 -> 2864 bytes | |||
-rw-r--r-- | static/img/unknown_icon.svg | 67 | ||||
-rw-r--r-- | takahe/urls.py | 1 | ||||
-rw-r--r-- | templates/base.html | 18 | ||||
-rw-r--r-- | templates/identity/select.html | 14 | ||||
-rw-r--r-- | templates/identity/view.html | 3 | ||||
-rw-r--r-- | users/views/identity.py | 24 |
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 Binary files differnew file mode 100644 index 0000000..0bfef8e --- /dev/null +++ b/static/img/unknown-icon-128.png 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: |