dkforest

A forum and chat platform (onion)
git clone https://git.dasho.dev/n0tr1v/dkforest.git
Log | Files | Refs | LICENSE

messages.qtpl (24576B)


      1 {% import "dkforest/pkg/database" %}
      2 {% import "dkforest/pkg/managers" %}
      3 {% import humanize "github.com/dustin/go-humanize" %}
      4 
      5 {%- func GenerateStyle(AuthUser *database.User, Data ChatMessagesData) -%}
      6 <style>
      7    /* http://meyerweb.com/eric/tools/css/reset/
      8        v2.0 | 20110126
      9        License: none (public domain)
     10     */
     11     html, body, div, span, applet, object, iframe,
     12     h1, h2, h3, h4, h5, h6, p, blockquote, pre,
     13     a, abbr, acronym, address, big, cite, code,
     14     del, dfn, em, img, ins, kbd, q, s, samp,
     15     small, strike, strong, sub, sup, tt, var,
     16     b, u, i, center,
     17     dl, dt, dd, ol, ul, li,
     18     fieldset, form, label, legend,
     19     table, caption, tbody, tfoot, thead, tr, th, td,
     20     article, aside, canvas, details, embed,
     21     figure, figcaption, footer, header, hgroup,
     22     menu, nav, output, ruby, section, summary,
     23     time, mark, audio, video {
     24         margin: 0;
     25         padding: 0;
     26         border: 0;
     27         font-size: 100%;
     28         font: inherit;
     29         vertical-align: baseline;
     30     }
     31     /* HTML5 display-role reset for older browsers */
     32     article, aside, details, figcaption, figure,
     33     footer, header, hgroup, menu, nav, section {
     34         display: block;
     35     }
     36     body {
     37         line-height: 1;
     38     }
     39     ol, ul {
     40         list-style: none;
     41     }
     42     blockquote, q {
     43         quotes: none;
     44     }
     45     blockquote:before, blockquote:after,
     46     q:before, q:after {
     47         content: '';
     48         content: none;
     49     }
     50     table {
     51         border-collapse: collapse;
     52         border-spacing: 0;
     53     }
     54     /* --- end --- */
     55 
     56     i { font-style: italic; }
     57 
     58     /* Remove button padding in FF */
     59     button::-moz-focus-inner {
     60         border:0;
     61         padding:0;
     62     }
     63 
     64     body { font-family: Lato,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol"; }
     65     a       { color: #00bc8c; text-decoration: none; }
     66     a:hover { color: #007053; text-decoration: underline; }
     67     .unread_room       { color: #2392da; text-decoration: none; }
     68     .unread_room:hover { color: #004970; text-decoration: underline; }
     69     .emoji {
     70         font-family: "Noto Color Emoji", "Apple Color Emoji", "Segoe UI Emoji", Times, Symbola, Aegyptus, Code2000, Code2001, Code2002, Musica, serif, LastResort;
     71         font-size: 17px;
     72     }
     73     .mod-btn {
     74         width: 16px; height: 16px;
     75         margin: 0; padding: 0;
     76         border: 1px solid gray;
     77         display: inline;
     78         text-align: center;
     79         vertical-align: middle;
     80         user-select: none;
     81         background-color: #444;
     82         color: #ea2a2a;
     83         -webkit-box-shadow: 1px 1px 1px rgba(0,0,0,0.25);
     84         -moz-box-shadow: 1px 1px 1px rgba(0,0,0,0.25);
     85         -webkit-border-radius: 3px;
     86         -moz-border-radius: 3px;
     87     }
     88     .mod-btn:hover {
     89         background-color: #222;
     90     }
     91     .delete_msg_btn {
     92         font-size: 15px;
     93         line-height: 1;
     94     }
     95     @keyframes hide_btn {
     96         100% { visibility: hidden;  }
     97     }
     98     @keyframes orange_btn {
     99         99% { color: ea2a2a; } 100% { color: orange;  }
    100     }
    101     .delete_msg_btn::after { content: "×"; }
    102     .hb_btn {
    103         font-size: 10px;
    104         line-height: 1.4;
    105     }
    106     .hb_btn::after { content: "hb"; }
    107     .k_btn {
    108         font-size: 10px;
    109         line-height: 1.4;
    110     }
    111     .k_btn::after { content: "k"; }
    112     .connection-closed {
    113         color: #bf2718;
    114         display: block;
    115         position: fixed;
    116         text-align: center;
    117         top: 0;
    118         left: calc(50% - 200px);
    119         width: 400px;
    120         z-index: 2;
    121         background-color: #500000;
    122         border: 2px solid #ff0000;
    123     }
    124     #manualrefresh {
    125         {% if !Data.ForceManualRefresh %}
    126             top: -200%;
    127             animation: timeout_messages {%dl Data.ManualRefreshTimeout %}s forwards;
    128         {% endif %}
    129         color: #bf2718;
    130         display: block;
    131         position: fixed;
    132         text-align: center;
    133         left: calc(50% - 200px);
    134         width: 400px;
    135         z-index: 2;
    136         background-color: #500000;
    137         border: 2px solid #ff0000;
    138     }
    139     @keyframes timeout_messages {
    140         0%   { top: -200%; }
    141         99% { top: -200%; }
    142         100% { top: 0; }
    143     }
    144     .date { color: #999; font-family: 'Courier New', Courier, monospace; font-size: 14px; }
    145     .date-link:hover { color: #999; text-decoration: underline; }
    146     .sysmsg { color: #fff; font-family: 'Courier New', Courier, monospace; font-size: 14px; }
    147     small { font-size: 80%; font-weight: 400; }
    148     .msg { padding: 3px 0 3px 8px; border-bottom: 1px solid #444; color: #888; position: relative; }
    149     .msg p:first-of-type { display: inline; }
    150     strong { font-weight: bold; }
    151     em { font-style: italic; }
    152     pre { border: 1px solid #2b442b; padding: 2px; margin: 2px 0; max-height: {%dl AuthUser.CodeBlockHeight %}px; overflow: auto; background-color: rgba(39,40,34,0.6) !important;
    153         font-family: SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace; }
    154     .fullscreen { position: absolute; margin-top: -19px; right: 3px; }
    155     code {  border: 1px solid #2b442b; color: #f92672; padding: 0 2px; margin: 0px 0; background-color: rgba(39,40,34,0.6) !important; }
    156     .censored { background-color: black; color: black; padding: 0 3px; }
    157     .censored:hover { background-color: black; color: white; }
    158     .censored > a { color: black; }
    159     .censored > a:hover { color: #007053; text-decoration: underline; }
    160     ul { list-style-type: disc; margin: 0 0 0 15px; }
    161     * {
    162         -webkit-font-smoothing: antialiased;
    163         -moz-osx-font-smoothing: grayscale;
    164     }
    165     .afk-indicator { color: #ff9a27; }
    166     .spacer16 { display: inline-block; width: 16px; }
    167     .spacer36 { display: inline-block; width: 36px; }
    168     .spacer56 { display: inline-block; width: 56px; }
    169     .d-inline { display: inline; }
    170     .o-wrap { overflow-wrap: break-word; }
    171     .f-def-clr { color: #888; }
    172     .f-orange-clr { color: orange; }
    173     .f-white-clr, .f-white-clr:hover { color: white; }
    174     .line-through { text-decoration: line-through; }
    175     .hb-row { background-color: rgba(0, 0, 0, 0.7); opacity: {%f AuthUser.GetHellbanOpacityF64() %}; }
    176     .own-highlight { background-color: rgba(255,241,176,0.05); }
    177     .read-marker { border-style: outset; border-top: {%dl AuthUser.ChatReadMarkerSize %}px solid {%s AuthUser.ChatReadMarkerColor %}; }
    178     #msgs { {% if !Data.HideRightColumn %}width: calc(100% - 185px); float: left; {% endif %}line-height: 1.2; }
    179     #no-msg { padding-left: 10px; color: #ddd; }
    180     #rgt-pane-w { width: 150px; height: 100%; position: fixed; right: 15px; overflow-y: auto; }
    181     #rgt-pane { line-height: 1.15; }
    182     .rgt-title { font-weight: bolder; color: #ced4da; }
    183     .mb-20px { margin-bottom: 20px; }
    184     .mb-30px { margin-bottom: 30px; }
    185     .mt-5px { margin-top: 5px; }
    186     .mt-10px { margin-top: 10px; }
    187     .notif, .notif:hover { color: #e74c3c; }
    188     #msg-err { background-color: #8f2d2d; color: #ffffff; padding: 1px 5px 2px 5px; display: block; text-decoration: none; }
    189     #i {
    190         background-color: #006400;
    191         width: 18px;
    192         height: 18px;
    193         position: fixed;
    194         top: 1px;
    195         right: {% if !Data.HideRightColumn %}35px{% else %}8px{% endif %};
    196         border-radius: 9px;
    197         animation: i1 30s forwards;
    198     }
    199     @keyframes i1 { 0% { background-color: #006400; } 32% { background-color: #006400; } 33% { background-color: #626400; } 99% { background-color: #626400; } 100% { background-color: #8b0000; } }
    200     @keyframes i2 { 0% { background-color: #006400; } 32% { background-color: #006400; } 33% { background-color: #626400; } 99% { background-color: #626400; } 100% { background-color: #8b0000; } }
    201     @keyframes horizontal-shaking1 {
    202         0% { transform: translateX(0) }
    203         25% { transform: translateX(9px) }
    204         50% { transform: translateX(-9px) }
    205         75% { transform: translateX(9px) }
    206         100% { transform: translateX(0) }
    207     }
    208     @keyframes horizontal-shaking2 {
    209         0% { transform: translateX(0) }
    210         25% { transform: translateX(9px) }
    211         50% { transform: translateX(-9px) }
    212         75% { transform: translateX(9px) }
    213         100% { transform: translateX(0) }
    214     }
    215 </style>
    216 {%- endfunc -%}
    217 
    218 {% func Messages(VERSION, CSRF, NullUsername string, AuthUser *database.User, Data ChatMessagesData) -%}
    219 <html lang="en">
    220     <head>
    221         <title></title>
    222         {%- if !Data.ChatMenuData.PreventRefresh && !Data.ForceManualRefresh -%}<meta http-equiv="refresh" content="{%dl AuthUser.RefreshRate %}">{%- endif -%}
    223         {%- if AuthUser.CollectMetadata -%}<link rel="stylesheet" type="text/css" href="/public/css/meta.css?v={%s VERSION %}" />{%- endif -%}
    224         {%-= GenerateStyle(AuthUser, Data) -%}
    225         <style>#btn_delete_useless:active { background-image: url('/api/v1/chat/messages/delete/useless'); }</style>
    226     </head>
    227     <body>
    228 
    229         {%- if Data.ForceManualRefresh -%}
    230             <div id="manualrefresh">
    231                 <h4>Manual refresh required</h4>
    232                 <form method="get" action="/chat/{%s Data.ChatMenuData.RoomName %}" target="_top">
    233                     <input type="submit" value="Reload" />
    234                 </form>
    235             </div>
    236         {%- elseif !Data.ChatMenuData.PreventRefresh -%}
    237             <div id="manualrefresh">
    238                 <h4>Manual refresh required</h4>
    239                 <form method="get">
    240                     <input type="submit" value="Reload" />
    241                 </form>
    242             </div>
    243         {%- endif -%}
    244 
    245         {%- if Data.Error != "" -%}
    246             <a href="/api/v1/chat/messages/{%s Data.ChatMenuData.RoomName %}" id="msg-err">× {%s Data.Error %}</a>
    247         {%- endif -%}
    248 
    249         {%- if Data.PmSound || Data.TaggedSound -%}
    250             <audio src="/public/mp3/sound5.mp3" autoplay></audio>
    251         {%- elseif Data.NewMessageSound -%}
    252             <audio src="/public/mp3/sound6.mp3" autoplay></audio>
    253         {%- endif -%}
    254 
    255         <div id="msgs">
    256             {%-= RenderMessages(AuthUser, Data, CSRF, NullUsername, nil, false) -%}
    257         </div>
    258 
    259         {% if !AuthUser.HideRightColumn %}
    260             <div id="rgt-pane-w">
    261                 {%-= RenderRightColumn(AuthUser, Data.ChatMenuData) -%}
    262             </div>
    263         {%- endif -%}
    264         {%- if AuthUser.CollectMetadata -%}
    265             <div class="div_1"></div>
    266             <div class="div_2"></div>
    267             <div class="div_f0">a</div>
    268         {%- endif -%}
    269     </body>
    270 </html>
    271 {% endfunc %}
    272 
    273 
    274 {%- func RenderRightColumn(AuthUser *database.User, Data ChatMenuData) -%}
    275 <div id="rgt-pane">
    276     <div class="mb-20px">
    277         <div class="rgt-title">Inbox (<a href="/settings/inbox" target="_top"{% if Data.InboxCount > 0 %}class="notif"{% endif %}>{%dl Data.InboxCount %}</a>)</div>
    278     </div>
    279     <div class="mb-20px">
    280         <div class="rgt-title">Rooms:</div>
    281         {%- for _, e := range Data.OfficialRooms -%}
    282             {%- if e.Name == "club" -%}
    283                 {%- if AuthUser.IsClubMember -%}
    284                     <div><a href="/chat/club" target="_top"{% if e.IsUnread %} class="unread_room"{% endif %}>#club</a></div>
    285                 {%- endif -%}
    286             {%- elseif e.Name == "moderators" -%}
    287                 {%- if AuthUser.IsModerator() -%}
    288                     <div><a href="/chat/moderators" target="_top"{% if e.IsUnread %} class="unread_room"{% endif %}>#moderators</a></div>
    289                 {%- endif -%}
    290             {%- else -%}
    291                 <div><a href="/chat/{%s e.Name %}" target="_top"{% if e.IsUnread %} class="unread_room"{% endif %}>#{%s e.Name %}</a></div>
    292             {%- endif -%}
    293         {%- endfor -%}
    294         {%- if len(Data.SubscribedRooms) > 0 -%}
    295             <div class="mt-10px"></div>
    296             {%- for _, e := range Data.SubscribedRooms -%}
    297                 <div><a href="/chat/{%s e.Name %}" target="_top"{% if e.IsUnread %} class="unread_room"{% endif %}>#{%s e.Name %}</a></div>
    298             {%- endfor -%}
    299         {%- endif -%}
    300         <div class="mt-5px"><a href="/rooms" target="_top">[...]</a></div>
    301     </div>
    302     <div class="mb-20px">
    303         <div class="rgt-title">In this room:</div>
    304         {%- for _, e := range Data.Members -%}
    305             {%= Member(AuthUser, Data, e) %}
    306         {%- endfor -%}
    307     </div>
    308     {%- if Data.VisibleMemberInChat || AuthUser.DisplayHellbanned -%}
    309         <div class="mb-30px">
    310             {%- if len(Data.MembersInChat) > 0 -%}
    311                 <div class="rgt-title">In other rooms:</div>
    312                 {%- for _, e := range Data.MembersInChat -%}
    313                     {%= Member(AuthUser, Data, e) %}
    314                 {%- endfor -%}
    315             {%- endif -%}
    316         </div>
    317     {%- endif -%}
    318 </div>
    319 {%- endfunc -%}
    320 
    321 {%- func RenderMessages(AuthUser database.IUserRenderMessage, Data ChatMessagesData, CSRF, NullUsername string, readMarkerRev *int, isEdit bool) -%}
    322     {%- code
    323         baseTopBarURL := "/api/v1/chat/top-bar/" + Data.ChatMenuData.RoomName
    324         readMarkerRendered := false
    325         isFirstMsg := true
    326     -%}
    327     <style>#btn_delete_useless:active { background-image: url('/api/v1/chat/messages/delete/useless'); }</style>
    328     {%- for idx, e := range Data.Messages -%}
    329         {%-= RenderMessage(idx, e, AuthUser, Data, baseTopBarURL, &readMarkerRendered, &isFirstMsg, CSRF, NullUsername, readMarkerRev, isEdit) -%}
    330     {% endfor %}
    331     {% if len(Data.Messages) == 0 %}
    332         <div id="no-msg"><em>No message yet</em></div>
    333     {% endif %}
    334 {%- endfunc -%}
    335 
    336 {%- func RenderMessage(idx int, e database.ChatMessage, AuthUser database.IUserRenderMessage,
    337     Data ChatMessagesData, baseTopBarURL string, readMarkerRendered, isFirstMsg *bool, CSRF, NullUsername string, readMarkerRev *int, isEdit bool) -%}
    338     {%- if e.UserCanSee(AuthUser) -%}
    339         {%- if AuthUser.GetChatReadMarkerEnabled() && e.CreatedAt.Before(Data.ReadMarker.ReadAt) && !*readMarkerRendered  && !isEdit%}
    340             <div class="read-marker read-marker-0"{% if idx == 0 %} style="display:none;"{% endif %}></div>
    341             {%- code *readMarkerRendered = true -%}
    342         {%- elseif AuthUser.GetChatReadMarkerEnabled() && e.CreatedAt.After(Data.ReadMarker.ReadAt) && !*readMarkerRendered && isEdit && readMarkerRev != nil -%}
    343             <style>.read-marker-{%d (*readMarkerRev) %}{display:none !important;}</style>
    344             {%- code *readMarkerRev++ -%}
    345             <div class="read-marker read-marker-{%d *readMarkerRev %}"></div>
    346             {%- code *readMarkerRendered = true -%}
    347         {% endif %}
    348         <div id="msgid-{%s e.UUID %}" class="msgidc-{%s e.UUID %}-{%dl e.Rev %} msg
    349             {%- if (e.User.IsHellbanned || e.IsHellbanned) && AuthUser.GetDisplayHellbanned() %} hb-row
    350             {%- elseif AuthUser.GetID() == e.User.ID && AuthUser.GetHighlightOwnMessages() %} own-highlight{%- endif -%}
    351         ">
    352             {%- if e.UserCanDelete(AuthUser) -%}
    353                 {%- if AuthUser.GetDisplayDeleteButton() -%}
    354                     {%- if e.TooOldToDelete() -%}
    355                         <button id="btn_delete_{%s e.UUID %}" class="mod-btn delete_msg_btn f-orange-clr" title="delete"></button>
    356                     {%- else -%}
    357                         <button id="btn_delete_{%s e.UUID %}" class="mod-btn delete_msg_btn" title="delete"
    358                             style="
    359                             {%- if string(e.User.Username) != NullUsername -%}
    360                                 {%- if (AuthUser.IsModerator() && e.UserID != AuthUser.GetID()) || AuthUser.GetIsAdmin() || e.IsRoomOwner(AuthUser.GetID()) -%}
    361                                     animation: {%dl e.DeleteSecondsRemaining() %}s 1s forwards orange_btn;
    362                                 {%- else -%}
    363                                     animation: {%dl e.DeleteSecondsRemaining() %}s 1s forwards hide_btn;
    364                                 {%- endif -%}
    365                             {%- endif -%}
    366                             "
    367                             ></button>
    368                     {%- endif -%}
    369                     <style>#btn_delete_{%s e.UUID %}:active { background-image: url('/api/v1/chat/messages/delete/{%s e.UUID %}'); }</style>
    370                 {%- endif -%}
    371                 {%- if AuthUser.IsModerator() -%}
    372                     {%- if !e.OwnMessage(AuthUser.GetID()) -%}
    373                         {%- if AuthUser.GetDisplayHellbanButton() -%}
    374                             {%- if string(e.User.Username) == NullUsername -%}
    375                                 <div class="spacer16"></div>
    376                             {%- else -%}
    377                                 {%- if e.User.IsHellbanned -%}
    378                                     <form method="post" action="/api/v1/users/{%s e.UserID.String() %}/unhellban" class="d-inline">
    379                                         <input type="hidden" name="csrf" value="{%s CSRF %}" />
    380                                         <button class="mod-btn hb_btn f-orange-clr line-through" title="unhellban"></button>
    381                                     </form>
    382                                 {%- else -%}
    383                                     <form method="post" action="/api/v1/users/{%s e.UserID.String() %}/hellban" class="d-inline">
    384                                         <input type="hidden" name="csrf" value="{%s CSRF %}" />
    385                                         <button class="mod-btn hb_btn f-orange-clr" title="hellban"></button>
    386                                     </form>
    387                                 {%- endif -%}
    388                             {%- endif -%}
    389                         {%- endif -%}
    390                         {%- if AuthUser.GetDisplayKickButton() -%}
    391                             <form method="post" action="/api/v1/users/{%s e.UserID.String() %}/kick" class="d-inline">
    392                                 <input type="hidden" name="csrf" value="{%s CSRF %}" />
    393                                 <button class="mod-btn k_btn f-orange-clr" title="kick"></button>
    394                             </form>
    395                         {%- endif -%}
    396                     {%- else -%}
    397                         {%- if AuthUser.GetDisplayKickButton() && AuthUser.GetDisplayHellbanButton() -%}
    398                             <div class="spacer36"></div>
    399                         {%- elseif AuthUser.GetDisplayKickButton() || AuthUser.GetDisplayHellbanButton() -%}
    400                             <div class="spacer16"></div>
    401                         {%- endif -%}
    402                     {%- endif -%}
    403                 {%- endif -%}
    404             {%- else -%}
    405                 {%- if Data.NbButtons == 3 -%}
    406                     <div class="spacer56"></div>
    407                 {%- elseif Data.NbButtons == 2 -%}
    408                     <div class="spacer36"></div>
    409                 {%- elseif Data.NbButtons == 1 -%}
    410                     <div class="spacer16"></div>
    411                 {%- endif -%}
    412             {%- endif -%}
    413             {%- if !Data.HideTimestamps -%}
    414                 <a href="{%s baseTopBarURL %}?quote={%s e.UUID %}{%= unesc(Data.TopBarQueryParams) %}" target="iframe1" class="date date-link" title="{%s e.CreatedAt.Format("01-02 15:04:05") %}{% if *isFirstMsg %} ({%s humanize.Time(e.CreatedAt) %}){% endif %}">{%s e.CreatedAt.Format(AuthUser.GetDateFormat()) %}</a>
    415                 {% code *isFirstMsg = false %}
    416             {%- endif -%}
    417             <a href="/u/{%s string(e.GetProfile(AuthUser.GetID())) %}" target="_blank" class="f-def-clr">-</a>
    418             {%- if e.System -%}
    419                 <span class="sysmsg">{%= unesc(e.Message) %}</span>
    420             {%- else -%}
    421                 {%- if e.Moderators -%}<span class="sysmsg">[<a href="{%s baseTopBarURL %}?m=1{%= unesc(Data.TopBarQueryParams) %}" target="iframe1" class="sysmsg f-white-clr">M</a>]</span>&nbsp;{%- endif -%}
    422                 {%- if e.GroupID != nil -%}<span class="sysmsg">[<a href="{%s baseTopBarURL %}?g={%s e.Group.Name %}{%= unesc(Data.TopBarQueryParams) %}" target="iframe1" class="sysmsg f-white-clr">{%s e.Group.Name %}</a>]</span>&nbsp;{%- endif -%}
    423                 {%- if e.ToUserID != nil -%}
    424                     [{%-= fromUsername(AuthUser, Data, e, baseTopBarURL) -%}
    425     426                     {%- if e.ToUser.ID == AuthUser.GetID() -%}<span {%= unesc(e.ToUser.GenerateChatStyle()) %}>{%s string(e.ToUser.Username) %}</span>{%- else -%}<a {%= unesc(e.ToUser.GenerateChatStyle()) %} href="{%s baseTopBarURL %}?pm={%s string(e.ToUser.Username) %}{%s Data.TopBarQueryParams %}" target="iframe1">{%s string(e.ToUser.Username) %}</a>{%- endif -%}] -
    427                 {%- else -%}
    428                     {%-= fromUsername(AuthUser, Data, e, baseTopBarURL) -%}
    429                     {%- if !e.IsMe() -%}
    430                         {%- if AuthUser.CanSeeHB() && (e.IsHellbanned || e.User.IsHellbanned) && (e.ToUserID == nil) -%}
    431                             <a href="{%s baseTopBarURL %}?hbm=1{%= unesc(Data.TopBarQueryParams) %}" target="iframe1" class="f-def-clr">-</a>
    432                         {%- else -%}
    433                             -
    434                         {%- endif -%}
    435                     {%- endif -%}
    436                 {%- endif -%}
    437                 <span {%= unesc(e.User.GenerateChatStyle()) %}><span class="o-wrap">{%s= unesc(e.MsgToDisplay(AuthUser)) %}</span></span>
    438             {%- endif -%}
    439         </div>
    440     {%- endif -%}
    441 {%- endfunc -%}
    442 
    443 
    444 {% func Member(AuthUser *database.User, Data ChatMenuData, El managers.UserInfo) %}
    445     {%- code baseTopBarURL := "/api/v1/chat/top-bar/" + Data.RoomName -%}
    446     {%- if El.Username == AuthUser.GetUsername() -%}
    447         <div>
    448             <span style="color: {%s El.Color %};">{%s string(El.Username) %}</span>
    449             {%- if El.AfkIndicatorEnabled -%}
    450                 <small class="afk-indicator">afk</small>
    451             {%- endif -%}
    452         </div>
    453     {%- elseif len(El.Username) > 0 -%}
    454         {%- if !El.IsHellbanned || AuthUser.GetID() == El.UserID || (El.IsHellbanned && AuthUser.GetIsHellbanned()) || AuthUser.GetDisplayHellbanned() -%}
    455             <div>
    456                 {%- code tagType := "tag" -%}
    457                 {%- if AuthUser.CanSeeHB() && El.IsHellbanned -%}
    458                     {%- code tagType = "htag" -%}
    459                 {%- endif -%}
    460                 {%- stripspace -%}
    461                     <a href="{%s baseTopBarURL %}?{%s tagType %}={%s string(El.Username) %}{%= unesc(Data.TopBarQueryParams) %}"
    462                        target="iframe1"
    463                        {% if El.IsHellbanned && AuthUser.GetDisplayHellbanned() %}class="hb-row"{% endif %}
    464                        style="color: {%s El.Color %};">{%s string(El.Username[0]) %}</a>
    465                     <a href="{%s baseTopBarURL %}?pm={%s string(El.Username) %}{%= unesc(Data.TopBarQueryParams) %}"
    466                        target="iframe1"
    467                        {% if El.IsHellbanned && AuthUser.GetDisplayHellbanned() %}class="hb-row"{% endif %}
    468                        style="color: {%s El.Color %};">{%s string(El.Username[1 : len(El.Username)-1]) %}</a>
    469                     <a href="/u/{%s string(El.Username) %}"
    470                        rel="noopener noreferrer" target="_blank"
    471                        {% if El.IsHellbanned && AuthUser.GetDisplayHellbanned() %}class="hb-row"{% endif %}
    472                        style="color: {%s El.Color %};">{%s string(El.Username[len(El.Username)-1]) %}</a>
    473                 {%- endstripspace -%}
    474                 {%- if El.AfkIndicatorEnabled -%}
    475                     <small class="afk-indicator">afk</small>
    476                 {%- endif -%}
    477             </div>
    478         {%- endif -%}
    479     {%- endif -%}
    480 {% endfunc %}
    481 
    482 
    483 {%- func fromUsername(AuthUser database.IUserRenderMessage, Data ChatMessagesData, El database.ChatMessage, baseTopBarURL string) -%}
    484     {%- code tagType := "tag" -%}
    485     {%- if El.ToUserID != nil -%}
    486         {%- code tagType = "pm" -%}
    487     {%- elseif AuthUser.CanSeeHB() && (El.IsHellbanned || El.User.IsHellbanned) -%}
    488         {%- code tagType = "htag" -%}
    489     {%- elseif AuthUser.IsModerator() && El.Moderators -%}
    490         {%- code tagType = "mtag" -%}
    491     {%- endif -%}
    492     {%- if El.User.ID == AuthUser.GetID() -%}
    493         {%- if El.CanBeEdited() -%}<a {%= unesc(El.User.GenerateChatStyle()) %} href="{%s baseTopBarURL %}?e={%s El.CreatedAt.Format("15:04:05") %}{%= unesc(Data.TopBarQueryParams) %}" target="iframe1">{%s string(El.User.Username) %}</a>{%- else -%}<span {%= unesc(El.User.GenerateChatStyle()) %}>{%s string(El.User.Username) %}</span>{%- endif -%}
    494     {%- else -%}<a {%= unesc(El.User.GenerateChatStyle()) %} href="{%s baseTopBarURL %}?{%s tagType %}={%s string(El.User.Username) %}{%= unesc(Data.TopBarQueryParams) %}" target="iframe1">{%s string(El.User.Username) %}</a>{%- endif -%}
    495 {%- endfunc -%}