~fabrixxm/activist

abc4d7e8e6da432a12758b1c3fe68e5b473f2a07 — fabrixxm a month ago aebe5f5
ui: Add activity summary on top of objects

add 'in reply to' and first activity (or 'why we see this')
M activist/db/models.py => activist/db/models.py +14 -1
@@ 1,7 1,7 @@
import json
from typing import Optional, Union
from dataclasses import dataclass
from datetime import datetime, timezone
from datetime import datetime, timezone, timedelta

from ..config import settings
from .orm import Base, DbType, fk, QueryExecutor


@@ 144,6 144,19 @@ class Object(Base):
    def __repr__(self):
        return f"<{self.type} {self.id}>"

    def get_first_activity(self):
        """Return, hopefully, the activity that had us import this object

        It must be an activity, from a contact, not after 'received' value
        """

        return self.activities().where(
            "received < ? ", self.received + timedelta(seconds=1),
            "actor != ?", self.attributedTo_id,
            actor = List.list_by_id('following').as_list('object'),
        ).get()


class List(Base):
    id:str


M activist/static/icons.css => activist/static/icons.css +39 -32
@@ 35,57 35,64 @@
}


.i-link::before {
    --si-icon-path: url("data:image/svg+xml;utf8,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2224%22 height=%2224%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22currentColor%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E %3Cpath d=%22M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71%22 /%3E %3Cpath d=%22M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71%22 /%3E%3C/svg%3E") no-repeat;
.i-tv::before {
    --si-icon-path: url("data:image/svg+xml;utf8,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2224%22 height=%2224%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22currentColor%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E %3Crect width=%2220%22 height=%2215%22 x=%222%22 y=%227%22 rx=%222%22 ry=%222%22 /%3E %3Cpolyline points=%2217 2 12 7 7 2%22 /%3E%3C/svg%3E") no-repeat;
    -webkit-mask: var(--si-icon-path) no-repeat;
    mask: var(--si-icon-path) no-repeat;
    mask-size: 100% 100%;
    -webkit-mask: var(--si-icon-path) no-repeat;
    -webkit-mask-size: 100% 100%;}
.i-reply::before {
    --si-icon-path: url("data:image/svg+xml;utf8,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2224%22 height=%2224%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22currentColor%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E %3Cpolyline points=%229 17 4 12 9 7%22 /%3E %3Cpath d=%22M20 18v-2a4 4 0 0 0-4-4H4%22 /%3E%3C/svg%3E") no-repeat;
.i-message-circle-reply::before {
    --si-icon-path: url("data:image/svg+xml;utf8,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2224%22 height=%2224%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22currentColor%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E %3Cpath d=%22M7.9 20A9 9 0 1 0 4 16.1L2 22Z%22 /%3E %3Cpath d=%22m10 15-3-3 3-3%22 /%3E %3Cpath d=%22M7 12h7a2 2 0 0 1 2 2v1%22 /%3E%3C/svg%3E") no-repeat;
    -webkit-mask: var(--si-icon-path) no-repeat;
    mask: var(--si-icon-path) no-repeat;
    mask-size: 100% 100%;
    -webkit-mask: var(--si-icon-path) no-repeat;
    -webkit-mask-size: 100% 100%;}
.i-rotate-ccw-square::before {
    --si-icon-path: url("data:image/svg+xml;utf8,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2224%22 height=%2224%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22currentColor%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E %3Cpath d=%22M20 9V7a2 2 0 0 0-2-2h-6%22 /%3E %3Cpath d=%22m15 2-3 3 3 3%22 /%3E %3Cpath d=%22M20 13v5a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V7a2 2 0 0 1 2-2h2%22 /%3E%3C/svg%3E") no-repeat;
.i-users::before {
    --si-icon-path: url("data:image/svg+xml;utf8,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2224%22 height=%2224%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22currentColor%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E %3Cpath d=%22M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2%22 /%3E %3Ccircle cx=%229%22 cy=%227%22 r=%224%22 /%3E %3Cpath d=%22M22 21v-2a4 4 0 0 0-3-3.87%22 /%3E %3Cpath d=%22M16 3.13a4 4 0 0 1 0 7.75%22 /%3E%3C/svg%3E") no-repeat;
    -webkit-mask: var(--si-icon-path) no-repeat;
    mask: var(--si-icon-path) no-repeat;
    mask-size: 100% 100%;
    -webkit-mask: var(--si-icon-path) no-repeat;
    -webkit-mask-size: 100% 100%;}
.i-log-out::before {
    --si-icon-path: url("data:image/svg+xml;utf8,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2224%22 height=%2224%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22currentColor%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E %3Cpath d=%22M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4%22 /%3E %3Cpolyline points=%2216 17 21 12 16 7%22 /%3E %3Cline x1=%2221%22 x2=%229%22 y1=%2212%22 y2=%2212%22 /%3E%3C/svg%3E") no-repeat;
.i-reply::before {
    --si-icon-path: url("data:image/svg+xml;utf8,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2224%22 height=%2224%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22currentColor%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E %3Cpolyline points=%229 17 4 12 9 7%22 /%3E %3Cpath d=%22M20 18v-2a4 4 0 0 0-4-4H4%22 /%3E%3C/svg%3E") no-repeat;
    -webkit-mask: var(--si-icon-path) no-repeat;
    mask: var(--si-icon-path) no-repeat;
    mask-size: 100% 100%;
    -webkit-mask: var(--si-icon-path) no-repeat;
    -webkit-mask-size: 100% 100%;}
.i-image::before {
    --si-icon-path: url("data:image/svg+xml;utf8,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2224%22 height=%2224%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22currentColor%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E %3Crect width=%2218%22 height=%2218%22 x=%223%22 y=%223%22 rx=%222%22 ry=%222%22 /%3E %3Ccircle cx=%229%22 cy=%229%22 r=%222%22 /%3E %3Cpath d=%22m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21%22 /%3E%3C/svg%3E") no-repeat;
.i-message-circle::before {
    --si-icon-path: url("data:image/svg+xml;utf8,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2224%22 height=%2224%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22currentColor%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E %3Cpath d=%22M7.9 20A9 9 0 1 0 4 16.1L2 22Z%22 /%3E%3C/svg%3E") no-repeat;
    -webkit-mask: var(--si-icon-path) no-repeat;
    mask: var(--si-icon-path) no-repeat;
    mask-size: 100% 100%;
    -webkit-mask: var(--si-icon-path) no-repeat;
    -webkit-mask-size: 100% 100%;}
.i-trash::before {
    --si-icon-path: url("data:image/svg+xml;utf8,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2224%22 height=%2224%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22currentColor%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E %3Cpath d=%22M3 6h18%22 /%3E %3Cpath d=%22M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6%22 /%3E %3Cpath d=%22M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2%22 /%3E%3C/svg%3E") no-repeat;
.i-ellipsis-vertical::before {
    --si-icon-path: url("data:image/svg+xml;utf8,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2224%22 height=%2224%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22currentColor%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E %3Ccircle cx=%2212%22 cy=%2212%22 r=%221%22 /%3E %3Ccircle cx=%2212%22 cy=%225%22 r=%221%22 /%3E %3Ccircle cx=%2212%22 cy=%2219%22 r=%221%22 /%3E%3C/svg%3E") no-repeat;
    -webkit-mask: var(--si-icon-path) no-repeat;
    mask: var(--si-icon-path) no-repeat;
    mask-size: 100% 100%;
    -webkit-mask: var(--si-icon-path) no-repeat;
    -webkit-mask-size: 100% 100%;}
.i-message-circle::before {
    --si-icon-path: url("data:image/svg+xml;utf8,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2224%22 height=%2224%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22currentColor%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E %3Cpath d=%22M7.9 20A9 9 0 1 0 4 16.1L2 22Z%22 /%3E%3C/svg%3E") no-repeat;
.i-key-round::before {
    --si-icon-path: url("data:image/svg+xml;utf8,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2224%22 height=%2224%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22currentColor%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E %3Cpath d=%22M2 18v3c0 .6.4 1 1 1h4v-3h3v-3h2l1.4-1.4a6.5 6.5 0 1 0-4-4Z%22 /%3E %3Ccircle cx=%2216.5%22 cy=%227.5%22 r=%22.5%22 fill=%22currentColor%22 /%3E%3C/svg%3E") no-repeat;
    -webkit-mask: var(--si-icon-path) no-repeat;
    mask: var(--si-icon-path) no-repeat;
    mask-size: 100% 100%;
    -webkit-mask: var(--si-icon-path) no-repeat;
    -webkit-mask-size: 100% 100%;}
.i-plus::before {
    --si-icon-path: url("data:image/svg+xml;utf8,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2224%22 height=%2224%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22currentColor%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E %3Cpath d=%22M5 12h14%22 /%3E %3Cpath d=%22M12 5v14%22 /%3E%3C/svg%3E") no-repeat;
.i-link::before {
    --si-icon-path: url("data:image/svg+xml;utf8,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2224%22 height=%2224%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22currentColor%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E %3Cpath d=%22M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71%22 /%3E %3Cpath d=%22M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71%22 /%3E%3C/svg%3E") no-repeat;
    -webkit-mask: var(--si-icon-path) no-repeat;
    mask: var(--si-icon-path) no-repeat;
    mask-size: 100% 100%;
    -webkit-mask: var(--si-icon-path) no-repeat;
    -webkit-mask-size: 100% 100%;}
.i-star-off::before {
    --si-icon-path: url("data:image/svg+xml;utf8,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2224%22 height=%2224%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22currentColor%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E %3Cpath d=%22M8.34 8.34 2 9.27l5 4.87L5.82 21 12 17.77 18.18 21l-.59-3.43%22 /%3E %3Cpath d=%22M18.42 12.76 22 9.27l-6.91-1L12 2l-1.44 2.91%22 /%3E %3Cline x1=%222%22 x2=%2222%22 y1=%222%22 y2=%2222%22 /%3E%3C/svg%3E") no-repeat;
    -webkit-mask: var(--si-icon-path) no-repeat;
    mask: var(--si-icon-path) no-repeat;
    mask-size: 100% 100%;


@@ 98,57 105,57 @@
    mask-size: 100% 100%;
    -webkit-mask: var(--si-icon-path) no-repeat;
    -webkit-mask-size: 100% 100%;}
.i-key-round::before {
    --si-icon-path: url("data:image/svg+xml;utf8,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2224%22 height=%2224%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22currentColor%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E %3Cpath d=%22M2 18v3c0 .6.4 1 1 1h4v-3h3v-3h2l1.4-1.4a6.5 6.5 0 1 0-4-4Z%22 /%3E %3Ccircle cx=%2216.5%22 cy=%227.5%22 r=%22.5%22 fill=%22currentColor%22 /%3E%3C/svg%3E") no-repeat;
.i-star::before {
    --si-icon-path: url("data:image/svg+xml;utf8,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2224%22 height=%2224%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22currentColor%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E %3Cpolygon points=%2212 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2%22 /%3E%3C/svg%3E") no-repeat;
    -webkit-mask: var(--si-icon-path) no-repeat;
    mask: var(--si-icon-path) no-repeat;
    mask-size: 100% 100%;
    -webkit-mask: var(--si-icon-path) no-repeat;
    -webkit-mask-size: 100% 100%;}
.i-tv::before {
    --si-icon-path: url("data:image/svg+xml;utf8,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2224%22 height=%2224%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22currentColor%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E %3Crect width=%2220%22 height=%2215%22 x=%222%22 y=%227%22 rx=%222%22 ry=%222%22 /%3E %3Cpolyline points=%2217 2 12 7 7 2%22 /%3E%3C/svg%3E") no-repeat;
.i-search::before {
    --si-icon-path: url("data:image/svg+xml;utf8,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2224%22 height=%2224%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22currentColor%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E %3Ccircle cx=%2211%22 cy=%2211%22 r=%228%22 /%3E %3Cpath d=%22m21 21-4.3-4.3%22 /%3E%3C/svg%3E") no-repeat;
    -webkit-mask: var(--si-icon-path) no-repeat;
    mask: var(--si-icon-path) no-repeat;
    mask-size: 100% 100%;
    -webkit-mask: var(--si-icon-path) no-repeat;
    -webkit-mask-size: 100% 100%;}
.i-messages-square::before {
    --si-icon-path: url("data:image/svg+xml;utf8,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2224%22 height=%2224%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22currentColor%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E %3Cpath d=%22M14 9a2 2 0 0 1-2 2H6l-4 4V4c0-1.1.9-2 2-2h8a2 2 0 0 1 2 2z%22 /%3E %3Cpath d=%22M18 9h2a2 2 0 0 1 2 2v11l-4-4h-6a2 2 0 0 1-2-2v-1%22 /%3E%3C/svg%3E") no-repeat;
.i-log-out::before {
    --si-icon-path: url("data:image/svg+xml;utf8,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2224%22 height=%2224%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22currentColor%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E %3Cpath d=%22M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4%22 /%3E %3Cpolyline points=%2216 17 21 12 16 7%22 /%3E %3Cline x1=%2221%22 x2=%229%22 y1=%2212%22 y2=%2212%22 /%3E%3C/svg%3E") no-repeat;
    -webkit-mask: var(--si-icon-path) no-repeat;
    mask: var(--si-icon-path) no-repeat;
    mask-size: 100% 100%;
    -webkit-mask: var(--si-icon-path) no-repeat;
    -webkit-mask-size: 100% 100%;}
.i-ellipsis-vertical::before {
    --si-icon-path: url("data:image/svg+xml;utf8,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2224%22 height=%2224%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22currentColor%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E %3Ccircle cx=%2212%22 cy=%2212%22 r=%221%22 /%3E %3Ccircle cx=%2212%22 cy=%225%22 r=%221%22 /%3E %3Ccircle cx=%2212%22 cy=%2219%22 r=%221%22 /%3E%3C/svg%3E") no-repeat;
.i-rotate-ccw-square::before {
    --si-icon-path: url("data:image/svg+xml;utf8,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2224%22 height=%2224%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22currentColor%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E %3Cpath d=%22M20 9V7a2 2 0 0 0-2-2h-6%22 /%3E %3Cpath d=%22m15 2-3 3 3 3%22 /%3E %3Cpath d=%22M20 13v5a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V7a2 2 0 0 1 2-2h2%22 /%3E%3C/svg%3E") no-repeat;
    -webkit-mask: var(--si-icon-path) no-repeat;
    mask: var(--si-icon-path) no-repeat;
    mask-size: 100% 100%;
    -webkit-mask: var(--si-icon-path) no-repeat;
    -webkit-mask-size: 100% 100%;}
.i-search::before {
    --si-icon-path: url("data:image/svg+xml;utf8,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2224%22 height=%2224%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22currentColor%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E %3Ccircle cx=%2211%22 cy=%2211%22 r=%228%22 /%3E %3Cpath d=%22m21 21-4.3-4.3%22 /%3E%3C/svg%3E") no-repeat;
.i-image::before {
    --si-icon-path: url("data:image/svg+xml;utf8,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2224%22 height=%2224%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22currentColor%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E %3Crect width=%2218%22 height=%2218%22 x=%223%22 y=%223%22 rx=%222%22 ry=%222%22 /%3E %3Ccircle cx=%229%22 cy=%229%22 r=%222%22 /%3E %3Cpath d=%22m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21%22 /%3E%3C/svg%3E") no-repeat;
    -webkit-mask: var(--si-icon-path) no-repeat;
    mask: var(--si-icon-path) no-repeat;
    mask-size: 100% 100%;
    -webkit-mask: var(--si-icon-path) no-repeat;
    -webkit-mask-size: 100% 100%;}
.i-message-circle-x::before {
    --si-icon-path: url("data:image/svg+xml;utf8,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2224%22 height=%2224%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22currentColor%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E %3Cpath d=%22M7.9 20A9 9 0 1 0 4 16.1L2 22Z%22 /%3E %3Cpath d=%22m15 9-6 6%22 /%3E %3Cpath d=%22m9 9 6 6%22 /%3E%3C/svg%3E") no-repeat;
.i-plus::before {
    --si-icon-path: url("data:image/svg+xml;utf8,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2224%22 height=%2224%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22currentColor%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E %3Cpath d=%22M5 12h14%22 /%3E %3Cpath d=%22M12 5v14%22 /%3E%3C/svg%3E") no-repeat;
    -webkit-mask: var(--si-icon-path) no-repeat;
    mask: var(--si-icon-path) no-repeat;
    mask-size: 100% 100%;
    -webkit-mask: var(--si-icon-path) no-repeat;
    -webkit-mask-size: 100% 100%;}
.i-users::before {
    --si-icon-path: url("data:image/svg+xml;utf8,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2224%22 height=%2224%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22currentColor%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E %3Cpath d=%22M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2%22 /%3E %3Ccircle cx=%229%22 cy=%227%22 r=%224%22 /%3E %3Cpath d=%22M22 21v-2a4 4 0 0 0-3-3.87%22 /%3E %3Cpath d=%22M16 3.13a4 4 0 0 1 0 7.75%22 /%3E%3C/svg%3E") no-repeat;
.i-trash::before {
    --si-icon-path: url("data:image/svg+xml;utf8,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2224%22 height=%2224%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22currentColor%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E %3Cpath d=%22M3 6h18%22 /%3E %3Cpath d=%22M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6%22 /%3E %3Cpath d=%22M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2%22 /%3E%3C/svg%3E") no-repeat;
    -webkit-mask: var(--si-icon-path) no-repeat;
    mask: var(--si-icon-path) no-repeat;
    mask-size: 100% 100%;
    -webkit-mask: var(--si-icon-path) no-repeat;
    -webkit-mask-size: 100% 100%;}
.i-star::before {
    --si-icon-path: url("data:image/svg+xml;utf8,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2224%22 height=%2224%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22currentColor%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E %3Cpolygon points=%2212 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2%22 /%3E%3C/svg%3E") no-repeat;
.i-messages-square::before {
    --si-icon-path: url("data:image/svg+xml;utf8,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2224%22 height=%2224%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22currentColor%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E %3Cpath d=%22M14 9a2 2 0 0 1-2 2H6l-4 4V4c0-1.1.9-2 2-2h8a2 2 0 0 1 2 2z%22 /%3E %3Cpath d=%22M18 9h2a2 2 0 0 1 2 2v11l-4-4h-6a2 2 0 0 1-2-2v-1%22 /%3E%3C/svg%3E") no-repeat;
    -webkit-mask: var(--si-icon-path) no-repeat;
    mask: var(--si-icon-path) no-repeat;
    mask-size: 100% 100%;

M activist/static/style.css => activist/static/style.css +5 -0
@@ 184,6 184,10 @@ footer.navbar.mobile>.navbar-section>.btn:not(:first-child) {
	gap: 0.5rem;
}

.object-activities-summary {
    padding: 0.2rem 0.8rem 0;
}

/** index **/
.index .tab-block {
    margin-top: 2rem;


@@ 315,6 319,7 @@ body.article article>header>h1::after {
.card>.card-image {
    background-color: #444;
    padding-top: 0;
    margin-top: 0.8rem;
}

.card>.card-image>img {

M activist/templates/f/object.tpl.j2 => activist/templates/f/object.tpl.j2 +57 -49
@@ 1,58 1,66 @@
{#
	obj: AS object to show
	active: [bool] is active
	level: object level
	replies: show relplies
    obj: AS object to show
    replies: show relplies
#}
{% set level = level if level else 1 %}
{% set replies = true if replies else false %}


{% if level > 0 %}
{% set _border = "border-left-width:{{level * 2}}px" %}
{% endif %}

{% set actor = obj.attributedTo %}
<div class="card" style="{{ _border }}">
	{% include "f/header.tpl.j2" %}
	{% if obj.data.name %}
	<div class="card-header">
		<div class="card-title h5">
			{% if obj.type == "Page" %}<i class="icon i-link"></i>{% endif %} 
			<a href="{{ obj|firstof('url', 'id') }}">{{ obj.data.name }}</a>
		</div>
	</div>
	{% endif %}
	<div class="card-body">
		{{ obj.data.content }}

		{% set attachments = obj.data.attachment %}
		{% for a in attachments  %}
			{# TODO: check type and mediaType #}
			<img src="{{ a.url }}" class="img-responsive">
		{% endfor %}
	</div>

	{% if obj.data.url and obj.data.mediaType and obj.data.mediaType.startswith("image/") %}
	<div class="card-image">
		<img src="{{ obj.data.url }}" class="img-responsive" title="{{ obj.data.name }}">
	</div>
	{% endif %}

	<div class="card-footer">
		{% include 'f/actions.tpl.j2' %}
	</div>

	{% set qreplies = obj.replies() %}
	{% if replies and qreplies.count() > 0 %}
	<div class="card-body">
	{% set active = false %}
	{% set level = level + 1%}
	{% for obj in qreplies.fetch()  %}
		{% include "f/object-small.tpl.j2" %}
	{% endfor %}
	</div>
	{% endif %}
<div class="card">
    <div class="object-activities-summary text-tiny text-gray">
    {% if obj.inReplyTo_id %}
        <i class="icon i-message-circle-reply"></i> 
        in reply to <a href="{{ url_for('thread', uri=obj.inReplyTo_id|urlsafe )}}">{{ obj.inReplyTo.attributedTo.data|firstof('name', 'preferredUsername') }}</a><br>
    {% endif %}
    {% set firstactivity = obj.get_first_activity() %}
    {% if firstactivity %}
        <i class="{{ class(
            'icon', {
            'i-star': firstactivity.type == 'Like',
            'i-star-off': firstactivity.type == 'Dislike',
            'i-rotate-ccw-square': firstactivity.type == 'Announce',
        }) }}"></i>
        {{ firstactivity.type|pastaction(Announce = 'reshared') }} by <a href="{{ firstactivity.actor_id }}">{{ firstactivity.actor.data|firstof('name', 'preferredUsername') }}</a>
    {% endif %}

    </div>
    {% include "f/header.tpl.j2" %}
    {% if obj.data.name %}
    <div class="card-header">
        <div class="card-title h5">
            {% if obj.type == "Page" %}<i class="icon i-link"></i>{% endif %} 
            <a href="{{ obj|firstof('url', 'id') }}">{{ obj.data.name }}</a>
        </div>
    </div>
    {% endif %}
    <div class="card-body">
        {{ obj.data.content }}

        {% set attachments = obj.data.attachment %}
        {% for a in attachments  %}
            {# TODO: check type and mediaType #}
            <img src="{{ a.url }}" class="img-responsive">
        {% endfor %}
    </div>

    {% if obj.data.url and obj.data.mediaType and obj.data.mediaType.startswith("image/") %}
    <div class="card-image">
        <img src="{{ obj.data.url }}" class="img-responsive" title="{{ obj.data.name }}">
    </div>
    {% endif %}

    <div class="card-footer">
        {% include 'f/actions.tpl.j2' %}
    </div>

    {% set qreplies = obj.replies() %}
    {% if replies and qreplies.count() > 0 %}
    <div class="card-body">
    {% for obj in qreplies.fetch()  %}
        {% include "f/object-small.tpl.j2" %}
    {% endfor %}
    </div>
    {% endif %}

</div>