[CARDS][Note] Note factory template macro created, allows Notes to be represented with completely different macros/blocks, possible to extend types through additional events. Compact Notes have a max height, content can be scrolled by [CSS] Avatars, and Embed attachments now have a max-block-size which acts independently of image orientation
This commit is contained in:
parent
a71c16d654
commit
f1caabd296
|
@ -1,5 +1,5 @@
|
|||
{% extends 'stdgrid.html.twig' %}
|
||||
{% import '/cards/macros/note.html.twig' as noteView %}
|
||||
{% import '/cards/macros/note/factory.html.twig' as NoteFactory %}
|
||||
|
||||
{% block title %}{% if page_title is defined %}{{ page_title | trans }}{% endif %}{% endblock %}
|
||||
|
||||
|
@ -39,9 +39,11 @@
|
|||
{% for conversation in notes %}
|
||||
{% block current_note %}
|
||||
{% if conversation is instanceof('array') %}
|
||||
{{ noteView.note_vanilla(conversation['note'], conversation['replies'], {'depth': 0}) }}
|
||||
{% else %}
|
||||
{{ noteView.note_vanilla(conversation) }}
|
||||
{% set args = { 'type': 'vanilla_full', 'note': conversation['note'], 'replies': conversation['replies'] | default, 'extra': { 'depth': 0 } } %}
|
||||
{{ NoteFactory.constructor(args) }}
|
||||
{# {% else %}
|
||||
{% set args = { 'type': 'vanilla_full', 'note': conversation, 'extra': { 'depth': 0 } } %}
|
||||
{{ NoteFactory.constructor(args) }}#}
|
||||
{% endif %}
|
||||
<hr class="hr-replies-end" role="separator" aria-label="{{ 'Marks the end of previous conversation\'s initial note' | trans }}">
|
||||
{% endblock current_note %}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
{% import '/cards/macros/note.html.twig' as noteView %}
|
||||
{% import '/cards/macros/note/factory.html.twig' as NoteFactory %}
|
||||
|
||||
<section class="frame-section frame-section-padding">
|
||||
<div class="section-title">
|
||||
|
@ -8,6 +8,7 @@
|
|||
</div>
|
||||
|
||||
{% for note in related_notes %}
|
||||
{{ noteView.note_compact(note) }}
|
||||
{% set args = { 'type': 'vanilla_compact', 'note': note } %}
|
||||
{{ NoteFactory.constructor(args) }}
|
||||
{% endfor %}
|
||||
</section>
|
|
@ -73,15 +73,10 @@
|
|||
}
|
||||
|
||||
.embed img {
|
||||
height: auto;
|
||||
margin-right: var(--s);
|
||||
width: 25%;
|
||||
}
|
||||
|
||||
.embed img {
|
||||
max-height: min-content;
|
||||
max-width: min-content;
|
||||
padding: unset;
|
||||
margin-right: var(--s);
|
||||
max-block-size: 128px;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.feed-header>h1,.feed-header>h2,.feed-header>h3,.feed-header>h4,.feed-header>h5,.feed-header>h6 {
|
||||
|
@ -238,7 +233,6 @@
|
|||
border-radius: 0 var(--s) 0 0;
|
||||
display: flex;
|
||||
line-height: initial;
|
||||
padding: 4px var(--s) 0 0;
|
||||
}
|
||||
|
||||
.note-info {
|
||||
|
@ -338,11 +332,6 @@
|
|||
opacity: .5;
|
||||
}
|
||||
|
||||
.hr-replies-end {
|
||||
margin-top: var(--xl);
|
||||
margin-bottom: var(--xl);
|
||||
}
|
||||
|
||||
.hr-replies-end:last-of-type {
|
||||
display: none;
|
||||
}
|
||||
|
@ -350,19 +339,11 @@
|
|||
.note-sidebar {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: var(--s);
|
||||
padding: var(--s) 4px 4px var(--s);
|
||||
}
|
||||
|
||||
.note-sidebar>* {
|
||||
border-radius: 2px;
|
||||
height: auto;
|
||||
max-height: 3rem;
|
||||
max-width: 3rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.note-sidebar img {
|
||||
background: unset !important;
|
||||
.note-text {
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.note-text a {
|
||||
|
@ -389,35 +370,44 @@
|
|||
.note-wrapper {
|
||||
height: inherit;
|
||||
flex: 1;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.posting-extra .h-entry {
|
||||
padding: var(--s);
|
||||
.posting-extra [id|="note-compact"] {
|
||||
border: unset;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.posting-extra .note-sidebar {
|
||||
all: unset;
|
||||
[id|="note-compact"] .note-wrapper {
|
||||
padding: var(--s);
|
||||
}
|
||||
|
||||
.posting-extra .note-sidebar .avatar {
|
||||
[id|="note-compact"] .note-sidebar {
|
||||
all: unset;
|
||||
max-width: 1em;
|
||||
max-height: 1em;
|
||||
min-width: 1em;
|
||||
display: inline-flex;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
[id|="note-compact"] .note-sidebar img,
|
||||
.note-sidebar img {
|
||||
background: unset !important;
|
||||
}
|
||||
|
||||
[id|="note-compact"] hr {
|
||||
margin-bottom: 4px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
[id|="note-compact"] {
|
||||
display: block;
|
||||
width: 100%;
|
||||
max-height: 16vh;
|
||||
word-break: break-all;
|
||||
overflow: auto;
|
||||
touch-action: manipulation;
|
||||
}
|
||||
|
||||
@media only screen and (max-width:1280px) {
|
||||
.note-sidebar {
|
||||
max-width: min-content !important;
|
||||
}
|
||||
|
||||
.note-sidebar .avatar {
|
||||
min-width: var(--xl) !important;
|
||||
}
|
||||
|
||||
.note-replies .note-replies {
|
||||
margin-left: .33em;
|
||||
}
|
||||
|
|
|
@ -96,8 +96,8 @@ body {
|
|||
}
|
||||
|
||||
p {
|
||||
margin-top: 4px;
|
||||
margin-bottom: 4px;
|
||||
margin-top: 0;
|
||||
margin-bottom: var(--s);
|
||||
}
|
||||
|
||||
h1,h2,h3,h4,h5 {
|
||||
|
|
|
@ -18,16 +18,6 @@
|
|||
padding: 2px 6px;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
border-radius: 2px;
|
||||
height: auto;
|
||||
margin-right: 5px;
|
||||
max-height: 4rem;
|
||||
max-width: 4rem;
|
||||
min-width: var(--xxl);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.button-container {
|
||||
background-color: var(--foreground);
|
||||
border: none !important;
|
||||
|
@ -227,7 +217,17 @@
|
|||
}
|
||||
|
||||
.profile-info .avatar {
|
||||
flex: .5;
|
||||
border-radius: 2px;
|
||||
height: auto;
|
||||
|
||||
max-block-size: 3em;
|
||||
width: auto;
|
||||
margin-right: var(--s);
|
||||
}
|
||||
|
||||
.avatar {
|
||||
max-block-size: 2em;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.profile-info section {
|
||||
|
@ -281,10 +281,6 @@
|
|||
mask-image: url(../icons/remove-actor.svg);
|
||||
}
|
||||
|
||||
section {
|
||||
margin-bottom: var(--s);
|
||||
}
|
||||
|
||||
.section-details-subtitle .section-details-subtitle:not(:last-of-type) {
|
||||
margin-bottom: var(--s);
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
{% endblock note_actions %}
|
||||
|
||||
{% block note_replies %}
|
||||
{% import '/cards/macros/note.html.twig' as noteView %}
|
||||
{% import '/cards/macros/note/factory.html.twig' as NoteFactory %}
|
||||
|
||||
{% if replies is defined and replies is not empty %}
|
||||
<section class="note-replies" title="{{ 'Replies to ' | trans }}{{ nickname }}{{ '\'s note' | trans }}">
|
||||
|
@ -37,7 +37,8 @@
|
|||
{% set extra = extra|merge(parent) %}
|
||||
{% endif %}
|
||||
<span class="note-replies-indicator" role="presentation"></span>
|
||||
{{ noteView.note_vanilla(conversation['note'], conversation['replies'], extra) }}
|
||||
{% set args = { 'type': 'vanilla_full', 'note': conversation['note'], 'replies': conversation['replies'] | default, 'extra': extra } %}
|
||||
{{ NoteFactory.constructor(args) }}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</section>
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
{% from 'cards/macros/note.html.twig' import note_compact %}
|
||||
{{ note_compact(note) }}
|
||||
{% from 'cards/macros/note/types.html.twig' import vanilla_compact %}
|
||||
{{ vanilla_compact({'note': note}) }}
|
|
@ -1,76 +0,0 @@
|
|||
{% macro note_vanilla(note, replies, extra) %}
|
||||
{% set actor = note.getActor() %}
|
||||
{% set nickname = actor.getNickname() %}
|
||||
{% set fullname = actor.getFullname() %}
|
||||
{% set actor_uri = actor.getUri() %}
|
||||
{% set actor_url = actor.getUrl() %}
|
||||
{% set mention = mention(actor) %}
|
||||
{% set note_language = note.getNoteLanguageShortDisplay() %}
|
||||
|
||||
<article
|
||||
tabindex="0"
|
||||
title="{{'A note by actor' | trans}} {{ nickname }}"
|
||||
id="{{ 'note-anchor-' ~ note.getId() }}"
|
||||
class="h-entry hentry note"
|
||||
lang="{{ note.getLanguageLocale() }}">
|
||||
{{ block('note_sidebar', 'cards/blocks/note.html.twig') }}
|
||||
|
||||
<div class="note-wrapper">
|
||||
{{ block('note_info', 'cards/blocks/note.html.twig') }}
|
||||
|
||||
<section class="e-content entry-content note-content" title="{{ 'Note\'s main content' | trans }}">
|
||||
{% if extra.parent is defined %}
|
||||
<em
|
||||
tabindex="0"
|
||||
class="note-replies-parent"
|
||||
title="{{'This is a reply with depth: ' | trans}} {{ extra.depth }}"
|
||||
aria-flowto="note-anchor-">
|
||||
{{'in reply to' | trans}} {{ extra.parent | raw }}
|
||||
</em>
|
||||
{% endif %}
|
||||
{{ block('note_text', 'cards/blocks/note.html.twig') }}
|
||||
{{ block('note_attachments', 'cards/blocks/note.html.twig') }}
|
||||
{{ block('note_links', 'cards/blocks/note.html.twig') }}
|
||||
</section>
|
||||
</div>
|
||||
{{ block('note_complementary', 'cards/blocks/note.html.twig') }}
|
||||
</article>
|
||||
|
||||
{% if replies is defined %}
|
||||
{{ block('note_replies', 'cards/blocks/note.html.twig') }}
|
||||
{% endif %}
|
||||
{% endmacro note_vanilla %}
|
||||
|
||||
{% macro note_compact(note) %}
|
||||
{% set actor = note.getActor() %}
|
||||
{% set nickname = actor.getNickname() %}
|
||||
{% set fullname = actor.getFullname() %}
|
||||
{% set actor_uri = actor.getUri() %}
|
||||
{% set actor_url = actor.getUrl() %}
|
||||
{% set mention = mention(actor) %}
|
||||
|
||||
<article
|
||||
tabindex="0"
|
||||
title="{{'A note by actor' | trans}} {{ nickname }}"
|
||||
id="{{ 'note-compact-anchor-' ~ note.getId() }}"
|
||||
class="h-entry hentry note"
|
||||
lang="{{ note.getLanguageLocale() }}">
|
||||
<div class="note-wrapper">
|
||||
<header class="note-info">
|
||||
{{ block('note_sidebar', 'cards/blocks/note.html.twig') }}
|
||||
{{ block('note_author', 'cards/blocks/note.html.twig') }}
|
||||
</header>
|
||||
|
||||
<section class="e-content entry-content note-content" title="{{ 'Note\'s main content' | trans }}">
|
||||
<small class="note-conversation-info">
|
||||
<a href="{{ note.getConversationUrl() }}"
|
||||
class="note-conversation-url">{{ 'in conversation' | trans }}</a>
|
||||
<a href="{{ note.getUrl() }}"
|
||||
class="note-url">{{ note.getModified() | ago }}</a>
|
||||
</small>
|
||||
<hr>
|
||||
{{ block('note_text', 'cards/blocks/note.html.twig') }}
|
||||
</section>
|
||||
</div>
|
||||
</article>
|
||||
{% endmacro note_compact %}
|
40
templates/cards/macros/note/factory.html.twig
Normal file
40
templates/cards/macros/note/factory.html.twig
Normal file
|
@ -0,0 +1,40 @@
|
|||
{#
|
||||
Note factory constructor
|
||||
|
||||
There are various Note types:
|
||||
- Vanilla (common type);
|
||||
+ Full;
|
||||
+ Compact;
|
||||
- Page;
|
||||
+ Full;
|
||||
+ Compact;
|
||||
|
||||
This constructor accepts an array of args, this array may or not contain some elements, depending on the Note
|
||||
type in question.
|
||||
However, there's 1 GUARANTEE:
|
||||
- First element identifies note type via a string;
|
||||
This string will be compared, when there's a match the contructor delegates its respective macro.
|
||||
|
||||
The array of args passed should have the following structure for the default types:
|
||||
- Vanilla (common type);
|
||||
+ Full: { 'type': { 'vanilla_full' }, 'note': note, ?'replies': { note, ?replies }, ?'extra': { 'foo': bar };
|
||||
+ Compact: { 'type': { 'vanilla_compact' }, 'note': note, ?'extra': { 'foo': bar } };
|
||||
- Page;
|
||||
+ Full { 'type': { 'page_full' }, 'note': note, ?'replies': { note, ?replies }, ?'extra': { 'foo': bar };
|
||||
+ Compact { 'type': { 'page_compact' }, 'note': note, ?'extra': { 'foo': bar } };
|
||||
#}
|
||||
{% macro constructor(args) %}
|
||||
{% import '/cards/macros/note/types.html.twig' as NoteTypes %}
|
||||
|
||||
{% if args.type is same as 'vanilla_full' %}
|
||||
{{ NoteTypes.vanilla_full(args) }}
|
||||
{% elseif args.type is same as 'vanilla_compact' %}
|
||||
{{ NoteTypes.vanilla_compact(args) }}
|
||||
{% elseif args.type is same as 'page_full' %}
|
||||
{{ NoteTypes.page_full(args) }}
|
||||
{% elseif args.type is same as 'page_compact' %}
|
||||
{{ NoteTypes.page_compact(args) }}
|
||||
{% else %}
|
||||
{{ NoteTypes.vanilla_full(args) }}
|
||||
{% endif %}
|
||||
{% endmacro constructor %}
|
175
templates/cards/macros/note/types.html.twig
Normal file
175
templates/cards/macros/note/types.html.twig
Normal file
|
@ -0,0 +1,175 @@
|
|||
|
||||
{# args: { 'type': { 'vanilla_full' }, 'note': note, ?'replies': { note, ?replies }, ?'extra': { 'foo': bar } #}
|
||||
{% macro vanilla_full(args) %}
|
||||
{% set note = args.note %}
|
||||
{% if args.replies is defined %}{% set replies = args.replies %}{% else %}{% set replies = null %}{% endif %}
|
||||
{% if args.extra is defined %}{% set extra = args.extra %}{% else %}{% set extra = null %}{% endif %}
|
||||
|
||||
{% set actor = note.getActor() %}
|
||||
{% set nickname = actor.getNickname() %}
|
||||
{% set fullname = actor.getFullname() %}
|
||||
{% set actor_uri = actor.getUri() %}
|
||||
{% set actor_url = actor.getUrl() %}
|
||||
{% set mention = mention(actor) %}
|
||||
{% set note_language = note.getNoteLanguageShortDisplay() %}
|
||||
|
||||
<article
|
||||
tabindex="0"
|
||||
title="{{'A note by actor' | trans}} {{ nickname }}"
|
||||
id="{{ 'note-anchor-' ~ note.getId() }}"
|
||||
class="h-entry hentry note"
|
||||
lang="{{ note.getLanguageLocale() }}">
|
||||
{{ block('note_sidebar', 'cards/blocks/note.html.twig') }}
|
||||
|
||||
<div class="note-wrapper">
|
||||
{{ block('note_info', 'cards/blocks/note.html.twig') }}
|
||||
|
||||
<section class="e-content entry-content note-content" title="{{ 'Note\'s main content' | trans }}">
|
||||
{% if extra.parent is defined %}
|
||||
<em
|
||||
tabindex="0"
|
||||
class="note-replies-parent"
|
||||
title="{{'This is a reply with depth: ' | trans}} {{ extra.depth }}"
|
||||
aria-flowto="note-anchor-">
|
||||
{{'in reply to' | trans}} {{ extra.parent | raw }}
|
||||
</em>
|
||||
{% endif %}
|
||||
{{ block('note_text', 'cards/blocks/note.html.twig') }}
|
||||
{{ block('note_attachments', 'cards/blocks/note.html.twig') }}
|
||||
{{ block('note_links', 'cards/blocks/note.html.twig') }}
|
||||
</section>
|
||||
</div>
|
||||
{{ block('note_complementary', 'cards/blocks/note.html.twig') }}
|
||||
</article>
|
||||
|
||||
{% if replies is defined %}
|
||||
{{ block('note_replies', 'cards/blocks/note.html.twig') }}
|
||||
{% endif %}
|
||||
{% endmacro vanilla_full %}
|
||||
|
||||
|
||||
{# args: { 'type': { 'vanilla_compact' }, 'note': note, ?'extra': { 'foo': bar } } #}
|
||||
{% macro vanilla_compact(args) %}
|
||||
{% set note = args.note %}
|
||||
{% if args.extra is defined %}{% set extra = args.extra %}{% else %}{% set extra = null %}{% endif %}
|
||||
|
||||
{% set actor = note.getActor() %}
|
||||
{% set nickname = actor.getNickname() %}
|
||||
{% set fullname = actor.getFullname() %}
|
||||
{% set actor_uri = actor.getUri() %}
|
||||
{% set actor_url = actor.getUrl() %}
|
||||
{% set mention = mention(actor) %}
|
||||
|
||||
<article
|
||||
tabindex="0"
|
||||
title="{{'A note by actor' | trans}} {{ nickname }}"
|
||||
id="{{ 'note-compact-anchor-' ~ note.getId() }}"
|
||||
class="h-entry hentry note"
|
||||
lang="{{ note.getLanguageLocale() }}">
|
||||
<div class="note-wrapper">
|
||||
<header class="note-info">
|
||||
{{ block('note_sidebar', 'cards/blocks/note.html.twig') }}
|
||||
{{ block('note_author', 'cards/blocks/note.html.twig') }}
|
||||
</header>
|
||||
|
||||
<section class="e-content entry-content note-content-compact" title="{{ 'Note\'s main content' | trans }}">
|
||||
<small class="note-conversation-info">
|
||||
<a href="{{ note.getConversationUrl() }}"
|
||||
class="note-conversation-url">{{ 'in conversation' | trans }}</a>
|
||||
<a href="{{ note.getUrl() }}"
|
||||
class="note-url">{{ note.getModified() | ago }}</a>
|
||||
</small>
|
||||
<hr>
|
||||
{{ block('note_text', 'cards/blocks/note.html.twig') }}
|
||||
</section>
|
||||
</div>
|
||||
</article>
|
||||
{% endmacro vanilla_compact %}
|
||||
|
||||
|
||||
{# args: { 'type': { 'page_full' }, 'note': note, ?'replies': { note, ?replies }, ?'extra': { 'foo': bar } #}
|
||||
{% macro page_full(args) %}
|
||||
{% set note = args.note %}
|
||||
{% if args.replies is defined %}{% set replies = args.replies %}{% else %}{% set replies = null %}{% endif %}
|
||||
{% if args.extra is defined %}{% set extra = args.extra %}{% else %}{% set extra = null %}{% endif %}
|
||||
|
||||
{% set actor = note.getActor() %}
|
||||
{% set nickname = actor.getNickname() %}
|
||||
{% set fullname = actor.getFullname() %}
|
||||
{% set actor_uri = actor.getUri() %}
|
||||
{% set actor_url = actor.getUrl() %}
|
||||
{% set mention = mention(actor) %}
|
||||
{% set note_language = note.getNoteLanguageShortDisplay() %}
|
||||
|
||||
<article
|
||||
tabindex="0"
|
||||
title="{{'A note by actor' | trans}} {{ nickname }}"
|
||||
id="{{ 'note-anchor-' ~ note.getId() }}"
|
||||
class="h-entry hentry note"
|
||||
lang="{{ note.getLanguageLocale() }}">
|
||||
{{ block('note_sidebar', 'cards/blocks/note.html.twig') }}
|
||||
|
||||
<div class="note-wrapper">
|
||||
{{ block('note_info', 'cards/blocks/note.html.twig') }}
|
||||
|
||||
<section class="e-content entry-content note-content" title="{{ 'Note\'s main content' | trans }}">
|
||||
{% if extra.parent is defined %}
|
||||
<em
|
||||
tabindex="0"
|
||||
class="note-replies-parent"
|
||||
title="{{'This is a reply with depth: ' | trans}} {{ extra.depth }}"
|
||||
aria-flowto="note-anchor-">
|
||||
{{'in reply to' | trans}} {{ extra.parent | raw }}
|
||||
</em>
|
||||
{% endif %}
|
||||
{{ block('note_text', 'cards/blocks/note.html.twig') }}
|
||||
{{ block('note_attachments', 'cards/blocks/note.html.twig') }}
|
||||
{{ block('note_links', 'cards/blocks/note.html.twig') }}
|
||||
</section>
|
||||
</div>
|
||||
{{ block('note_complementary', 'cards/blocks/note.html.twig') }}
|
||||
</article>
|
||||
|
||||
{% if replies is defined %}
|
||||
{{ block('note_replies', 'cards/blocks/note.html.twig') }}
|
||||
{% endif %}
|
||||
{% endmacro page_full %}
|
||||
|
||||
|
||||
{# args: { 'type': { 'page_compact' }, 'note': note, ?'extra': { 'foo': bar } } #}
|
||||
{% macro page_compact(args) %}
|
||||
{% set note = args.note %}
|
||||
{% if args.extra is defined %}{% set extra = args.extra %}{% else %}{% set extra = null %}{% endif %}
|
||||
|
||||
{% set actor = note.getActor() %}
|
||||
{% set nickname = actor.getNickname() %}
|
||||
{% set fullname = actor.getFullname() %}
|
||||
{% set actor_uri = actor.getUri() %}
|
||||
{% set actor_url = actor.getUrl() %}
|
||||
{% set mention = mention(actor) %}
|
||||
|
||||
<article
|
||||
tabindex="0"
|
||||
title="{{'A note by actor' | trans}} {{ nickname }}"
|
||||
id="{{ 'note-compact-anchor-' ~ note.getId() }}"
|
||||
class="h-entry hentry note"
|
||||
lang="{{ note.getLanguageLocale() }}">
|
||||
<div class="note-wrapper">
|
||||
<header class="note-info">
|
||||
{{ block('note_sidebar', 'cards/blocks/note.html.twig') }}
|
||||
{{ block('note_author', 'cards/blocks/note.html.twig') }}
|
||||
</header>
|
||||
|
||||
<section class="e-content entry-content note-content" title="{{ 'Note\'s main content' | trans }}">
|
||||
<small class="note-conversation-info">
|
||||
<a href="{{ note.getConversationUrl() }}"
|
||||
class="note-conversation-url">{{ 'in conversation' | trans }}</a>
|
||||
<a href="{{ note.getUrl() }}"
|
||||
class="note-url">{{ note.getModified() | ago }}</a>
|
||||
</small>
|
||||
<hr>
|
||||
{{ block('note_text', 'cards/blocks/note.html.twig') }}
|
||||
</section>
|
||||
</div>
|
||||
</article>
|
||||
{% endmacro page_compact %}
|
Loading…
Reference in New Issue
Block a user