real initial commit

This commit is contained in:
☙◦ The Tablet ❀ GamerGirlandCo ◦❧ 2025-04-12 19:23:06 -04:00
parent c1b132e8af
commit c0102d8395
Signed by: tablet
GPG Key ID: 924A5F6AF051E87C
20 changed files with 1083 additions and 27 deletions

View File

@ -1,5 +1,5 @@
{
"name": "nodebb-theme-quickstart",
"name": "nodebb-theme-rockfic",
"version": "0.1.0",
"description": "Enter a description here",
"main": "lib/theme.js",

View File

@ -1,26 +1,3 @@
{
"id": "nodebb-theme-quickstart",
"hooks": [
{ "hook": "static:app.load", "method": "init" },
{ "hook": "filter:admin.header.build", "method": "addAdminNavigation" },
{ "hook": "filter:widgets.getAreas", "method": "defineWidgetAreas" },
{ "hook": "filter:config.get", "method": "getThemeConfig" },
{ "hook": "filter:settings.get", "method": "getAdminSettings"},
{ "hook": "filter:user.saveSettings", "method": "saveUserSettings" },
{ "hook": "filter:user.profileMenu", "method": "addProfileItem" },
{ "hook": "filter:middleware.renderHeader", "method": "filterMiddlewareRenderHeader" }
],
"scripts": [
"public/client.js",
"../nodebb-theme-harmony/public/harmony.js"
],
"templates": "templates",
"modules": {
"../admin/plugins/theme-quickstart.js": "../nodebb-theme-harmony/public/admin.js",
"../client/account/theme.js": "../nodebb-theme-harmony/public/settings.js"
},
"staticDirs": {
"inter": "node_modules/@fontsource/inter/files",
"poppins": "node_modules/@fontsource/poppins/files"
}
}
"id": "nodebb-theme-rockfic"
}

292
templates/account/info.tpl Normal file
View File

@ -0,0 +1,292 @@
<!-- IMPORT partials/account/header.tpl -->
<!-- IF sessions.length -->
<div class="row mb-3">
<div class="col-12 col-md-12">
<h4>[[global:sessions]]</h4>
<ul class="list-group" component="user/sessions">
{{{each sessions}}}
<li class="list-group-item" data-uuid="{../uuid}">
<div class="float-end">
<!-- IF isSelfOrAdminOrGlobalModerator -->
<!-- IF !../current -->
<button class="btn btn-sm btn-outline-secondary" type="button" data-action="revokeSession">Revoke Session</button>
<!-- ENDIF !../current -->
<!-- ENDIF isSelfOrAdminOrGlobalModerator -->
{function.userAgentIcons}
<i class="fa fa-circle text-<!-- IF ../current -->success<!-- ELSE -->muted<!-- ENDIF ../current -->"></i>
</div>
{../browser} {../version} on {../platform}<br />
<small class="timeago text-muted" title="{../datetimeISO}"></small>
<ul>
<li><strong>[[global:ip-address]]</strong>: {../ip}</li>
</ul>
</li>
{{{end}}}
</ul>
</div>
</div>
<!-- ENDIF sessions.length -->
<div class="row">
<div class="col-sm-6">
<div class="card mb-3">
<h5 class="card-header">
[[global:recentips]]
</h5>
<div class="card-body">
<ul>
{{{each ips}}}
<li>{@value}</li>
{{{end}}}
</ul>
</div>
</div>
<div class="card mb-3">
<h5 class="card-header">
[[user:info.username-history]]
</h5>
<div class="card-body">
<ul class="list-unstyled mb-0">
{{{ each usernames }}}
<li class="d-flex justify-content-between mb-1">
<span class="text-sm">{./value}</span>
<div>
{{{ if ./byUid }}}
<a class="lh-1" href="{{{ if ./byUser.remoteId }}}{config.relative_path}/user/{./byUser.remoteId}{{{ else }}}#{{{ end }}}">
{buildAvatar(./byUser, "18px", true)}</a>
{{{ end }}}
<span class="timeago text-sm lh-1" title="{./timestampISO}"></span>
</div>
</li>
{{{ end }}}
</ul>
</div>
</div>
<div class="card mb-3">
<h5 class="card-header">
[[user:info.email-history]]
</h5>
<div class="card-body">
<ul class="list-unstyled mb-0">
{{{ each emails }}}
<li class="d-flex justify-content-between mb-1">
<span class="text-sm">{./value}</span>
<div>
{{{ if ./byUid }}}
<a class="lh-1" href="{{{ if ./byUser.remoteId }}}{config.relative_path}/user/{./byUser.remoteId}{{{ else }}}#{{{ end }}}">
{buildAvatar(./byUser, "18px", true)}</a>
{{{ end }}}
<span class="timeago text-sm lh-1" title="{./timestampISO}"></span>
</div>
</li>
{{{ end }}}
</ul>
</div>
</div>
<!-- IF isAdminOrGlobalModerator -->
<div class="card">
<h5 class="card-header">
[[user:info.moderation-note]]
</h5>
<div class="card-body">
<textarea component="account/moderation-note" class="form-control"></textarea>
<br/>
<button class="btn btn-sm float-end btn-success" component="account/save-moderation-note">[[user:info.moderation-note.add]]</button>
<br/>
<div component="account/moderation-note/list">
{{{ each moderationNotes }}}
<hr/>
<div data-id="{./id}">
<div class="mb-1">
<a href="{{{ if ./user.remoteId }}}{config.relative_path}/user/{./user.remoteId}{{{ else }}}#{{{ end }}}">{buildAvatar(./user, "24px", true)}</a>
<a href="{{{ if ./user.remoteId }}}{config.relative_path}/user/{./user.remoteId}{{{ else }}}#{{{ end }}}" class="fw-bold" itemprop="author" data-username="{./user.username}" data-uid="{./user.uid}">{./user.username}</a>
<span class="timeago" title="{./timestampISO}"></span>
</div>
<div component="account/moderation-note/content-area" class="d-flex flex-column">
<div class="content">
{./note}
</div>
<button component="account/moderation-note/edit" class="btn btn-sm btn-link align-self-end">[[topic:edit]]</button>
</div>
<div component="account/moderation-note/edit-area" class="d-flex flex-column gap-2">
<textarea class="form-control w-100 overflow-hidden">{./rawNote}</textarea>
<div class="align-self-end">
<button component="account/moderation-note/cancel-edit" class="btn btn-sm btn-link text-danger align-self-end">[[global:cancel]]</button>
<button component="account/moderation-note/save-edit" class="btn btn-sm btn-primary align-self-end">[[global:save]]</button>
</div>
</div>
</div>
{{{ end }}}
</div>
<!-- IMPORT partials/paginator.tpl -->
</div>
</div>
<!-- ENDIF isAdminOrGlobalModerator -->
</div>
<div class="col-sm-6">
<div class="card mb-3">
<h5 class="card-header">
[[user:info.latest-flags]]
</h5>
<div class="card-body">
<!-- IF history.flags.length -->
<ul class="recent-flags list-unstyled">
{{{ each history.flags }}}
<li class="mb-4 border-bottom">
<div class="mb-1 d-flex align-items-center justify-content-between">
<div>
{{{ if (./type == "user")}}}
<span class="badge text-bg-info">[[user:info.profile]]</span>
{{{ else }}}
<span class="badge text-bg-info">[[user:info.post]]</span>
{{{ end }}}
<span class="timestamp timeago" title="{./timestampISO}"></span>
</div>
<a href="{config.relative_path}/flags/{./flagId}" class="badge badge border border-gray-300 text-body">[[user:info.view-flag]]</a>
</div>
{{{ if (./type == "post") }}}
<p class="mb-1">
{{{ if history.flags.targetPurged }}}
<div>[[flags:target-purged]]</div>
{{{ else }}}
<a class="title" href="{config.relative_path}/post/{./pid}">{./title}</a>
{{{ end }}}
</p>
{{{ end }}}
<div class="d-flex gap-2 align-items-center mb-3">
<span class="text-sm">[[user:info.reported-by]]</span>
<div class="d-flex text-nowrap">
{{{ each ./reports }}}
<a style="width: 18px; z-index: 3;" class="text-decoration-none" href="{config.relative_path}/user/{./reporter.remoteId}">{buildAvatar(./reporter, "24px", true)}</a>
{{{ end }}}
</div>
</div>
</li>
{{{ end }}}
</ul>
<!-- ELSE -->
<div class="alert alert-success">[[user:info.no-flags]]</div>
<!-- ENDIF history.flags.length -->
</div>
</div>
<div class="card mb-3">
<h5 class="card-header">
[[user:info.ban-history]]
<!-- IF !banned -->
<!-- IF !isSelf -->
<button class="btn btn-sm float-end btn-danger" component="account/ban">[[user:ban-account]]</button>
<!-- ENDIF !isSelf -->
<!-- ELSE -->
<!-- IF !isSelf -->
<button class="btn btn-sm float-end btn-success" component="account/unban">[[user:unban-account]]</button>
<!-- ENDIF !isSelf -->
<!-- ENDIF !banned -->
</h5>
<div class="card-body">
<!-- IF history.bans.length -->
<ul class="ban-history list-unstyled">
{{{ each history.bans }}}
<li class="mb-4 border-bottom">
<div class="mb-1 d-flex align-items-center justify-content-between">
<div>
<a href="{config.relative_path}/user/{./user.remoteId}">{buildAvatar(./user, "24px", true)}</a>
<strong>
<a href="{{{ if ./user.remoteId }}}{config.relative_path}/user/{./user.remoteId}{{{ else }}}#{{{ end }}}" itemprop="author" data-username="{./user.username}" data-uid="{./user.uid}">{./user.username}</a>
</strong>
<span class="timestamp timeago" title="{./timestampISO}"></span>
</div>
{{{ if (./type != "unban") }}}
<span class="badge text-bg-danger">[[user:banned]]</span>
{{{ else }}}
<span class="badge text-bg-success">[[user:unbanned]]</span>
{{{ end }}}
</div>
<p class="mb-1">
<span class="reason">[[user:info.banned-reason-label]]: <strong>{./reason}</strong></span>
</p>
<p class="">
{{{ if ./until }}}
<span class="expiry">[[user:info.banned-until, {isoTimeToLocaleString(./untilISO, config.userLang)}]]</span>
{{{ else }}}
{{{ if (./type != "unban") }}}
<span class="expiry">[[user:info.banned-permanently]]</span>
{{{ end }}}
{{{ end }}}
</p>
</li>
{{{ end }}}
</ul>
<!-- ELSE -->
<div class="alert alert-success">[[user:info.no-ban-history]]</div>
<!-- ENDIF history.bans.length -->
</div>
</div>
<div class="card mb-3">
<h5 class="card-header">
[[user:info.mute-history]]
{{{ if !muted }}}
{{{ if !isSelf }}}
<button class="btn btn-sm float-end btn-danger" component="account/mute">[[user:mute-account]]</button>
{{{ end }}}
{{{ else }}}
{{{ if !isSelf }}}
<button class="btn btn-sm float-end btn-success" component="account/unmute">[[user:unmute-account]]</button>
{{{ end }}}
{{{ end }}}
</h5>
<div class="card-body">
{{{ if history.mutes.length }}}
<ul class="ban-history list-unstyled">
{{{ each history.mutes }}}
<li class="mb-4 border-bottom">
<div class="mb-1 d-flex align-items-center justify-content-between">
<div>
<a href="{config.relative_path}/user/{./user.remoteId}">{buildAvatar(./user, "24px", true)}</a>
<strong>
<a href="{{{ if ./user.remoteId }}}{config.relative_path}/user/{./user.remoteId}{{{ else }}}#{{{ end }}}" itemprop="author" data-username="{./user.username}" data-uid="{./user.uid}">{./user.username}</a>
</strong>
<span class="timestamp timeago" title="{./timestampISO}"></span>
</div>
{{{ if (./type != "unmute") }}}
<span class="badge text-bg-danger">[[user:muted]]</span>
{{{ else }}}
<span class="badge text-bg-success">[[user:unmuted]]</span>
{{{ end }}}
</div>
<p class="mb-1">
<span class="reason">[[user:info.banned-reason-label]]: <strong>{./reason}</strong></span>
</p>
<p class="">
{{{ if ./until }}}
<span class="expiry">[[user:info.muted-until, {isoTimeToLocaleString(./untilISO, config.userLang)}]]</span>
{{{ end }}}
</p>
</li>
{{{ end }}}
</ul>
{{{ else }}}
<div class="alert alert-success">[[user:info.no-mute-history]]</div>
{{{ end }}}
</div>
</div>
</div>
</div>
<!-- IMPORT partials/account/footer.tpl -->

View File

@ -3,7 +3,7 @@
{{{ if @first }}}
<div component="category/posts" class="ps-2 text-xs d-flex flex-column h-100 gap-1">
<div class="text-nowrap text-truncate">
<a class="text-decoration-none avatar-tooltip" title="{./user.displayname}" href="{config.relative_path}/user/{./user.userslug}">{buildAvatar(posts.user, "18px", true)}</a>
<a class="text-decoration-none avatar-tooltip" title="{./user.username}" href="{config.relative_path}/user/{./user.remoteId}">{buildAvatar(posts.user, "18px", true)}</a>
<a class="permalink text-muted timeago text-xs" href="{config.relative_path}/topic/{./topic.slug}{{{ if ./index }}}/{./index}{{{ end }}}" title="{./timestampISO}" aria-label="[[global:lastpost]]"></a>
</div>
<div class="post-content text-xs text-break line-clamp-sm-2 lh-sm position-relative flex-fill">

View File

@ -0,0 +1,41 @@
{{{ if config.loggedIn }}}
<ul class="nav nav-pills">
<li class="nav-item">
<a class="nav-link text-decoration-none" href="#" data-bs-target="#notifications" data-bs-toggle="tab"><span class="counter unread-count" component="notifications/icon" data-content="{unreadCount.notification}"></span> <i class="fa fa-fw fa-bell"></i></a>
</li>
{{{ if !config.disableChat }}}
<li class="nav-item">
<a class="nav-link text-decoration-none" href="#" data-bs-target="#chats" data-bs-toggle="tab"><i class="counter unread-count" component="chat/icon" data-content="{unreadCount.chat}"></i> <i class="fa fa-fw fa-comment"></i></a>
</li>
{{{ end }}}
<li class="nav-item">
<a class="nav-link active text-decoration-none" href="#" data-bs-target="#profile" data-bs-toggle="tab">
{buildAvatar(user, "24px", true, "user-icon")}
</a>
</li>
</ul>
<div class="tab-content">
<div class="tab-pane fade show active" id="profile">
<section class="menu-section" data-section="profile">
<ul class="menu-section-list dropdown-menu show text-bg-dark w-100 border-0" component="header/usercontrol"></ul>
</section>
</div>
<div class="tab-pane fade" id="notifications">
<section class="menu-section text-bg-dark px-1" data-section="notifications">
<ul class="menu-section-list notification-list-mobile list-unstyled" component="notifications/list"></ul>
<div class="menu-section-list text-center p-3"><a href="{relative_path}/notifications">[[notifications:see-all]]</a></div>
</section>
</div>
{{{ if !config.disableChat }}}
<div class="tab-pane fade" id="chats">
<section class="menu-section text-bg-dark px-1" data-section="chats">
<ul class="menu-section-list chat-list list-unstyled" component="chat/list">
</ul>
<div class="menu-section-list text-center p-3"><a class="navigation-link" href="{relative_path}/user/{user.remoteId}/chats">[[modules:chat.see-all]]</a></div>
</section>
</div>
{{{ end }}}
</div>
{{{ end }}}

View File

@ -0,0 +1,33 @@
<label class="text-xs text-muted">[[groups:invited.search]]</label>
<div class="input-group mb-2">
<input class="form-control" type="text" component="groups/members/invite"/>
<span class="input-group-text search-button"><i class="fa fa-search"></i></span>
</div>
<div class="mb-2">
<label class="text-xs text-muted">[[groups:bulk-invite-instructions]]</label>
<textarea class="form-control" component="groups/members/bulk-invite"></textarea>
</div>
<div class="mb-2 clearfix">
<button type="button" class="btn btn-primary btn-sm float-end" component="groups/members/bulk-invite-button">[[groups:bulk-invite]]</button>
</div>
<div style="max-height: 500px; overflow: auto;">
<div component="groups/invited/alert" class="alert alert-info {{{ if group.invited.length }}}hidden{{{ end }}}">[[groups:invited.none]]</div>
<table component="groups/invited" class="table table-hover">
<tbody>
{{{ each group.invited }}}
<tr data-uid="{group.invited.uid}" class="align-middle">
<td class="member-name p-2 d-flex align-items-center justify-content-between">
<div class="d-flex align-items-center gap-2">
<a class="text-decoration-none" href="{config.relative_path}/user/{group.invited.remoteId}">{buildAvatar(group.invited, "24px", true)}</a>
<a href="{config.relative_path}/user/{group.invited.remoteId}">{group.invited.username}</a>
</div>
<button class="btn btn-outline-secondary btn-sm text-nowrap" data-action="rescindInvite">[[groups:invited.uninvite]]</button>
</td>
</tr>
{{{ end }}}
</tbody>
</table>
</div>

View File

@ -0,0 +1,21 @@
{{{each groups}}}
<div class="col-lg-4 col-md-6 col-sm-12 mb-3" component="groups/summary" data-slug="{groups.slug}">
<div class="card h-100">
<a href="{config.relative_path}/groups/{groups.slug}" class="card-header list-cover" style="{{{ if groups.cover:thumb:url }}}background-image: url({./cover:thumb:url});background-size: cover; min-height: 125px; background-position: {./cover:position}{{{ end }}}">
<h5 class="card-title d-inline-block mw-100 px-2 py-1 text-truncate text-capitalize fw-bold rounded-1" style="color: white;background-color: rgba(0,0,0,0.5);">{groups.displayName} <small>{formattedNumber(groups.memberCount)}</small></h5>
</a>
<div class="card-body">
<p class="text-muted">
{./description}
</p>
<ul class="members list-unstyled d-flex align-items-center gap-2 flex-wrap">
{{{each groups.members}}}
<li>
<a href="{config.relative_path}/user/{groups.members.remoteId}">{buildAvatar(groups.members, "24px", true)}</a>
</li>
{{{end}}}
</ul>
</div>
</div>
</div>
{{{end}}}

View File

@ -0,0 +1,43 @@
<div class="d-flex mb-3">
<!-- IF group.isOwner -->
<div class="flex-shrink-0">
<button component="groups/members/add" type="button" class="btn btn-primary me-3" title="[[groups:details.add-member]]"><i class="fa fa-user-plus"></i></button>
</div>
<!-- ENDIF group.isOwner -->
<div class="flex-grow-1">
<div class="input-group">
<input class="form-control" type="text" component="groups/members/search" placeholder="[[global:search]]"/>
<span class="input-group-text search-button"><i class="fa fa-search"></i></span>
</div>
</div>
</div>
<div component="groups/members" data-nextstart="{group.membersNextStart}" style="max-height: 500px; overflow: auto;">
<table class="table table-striped table-hover">
<tbody>
{{{each group.members}}}
<tr data-uid="{group.members.uid}" data-isowner="{{{ if group.members.isOwner }}}1{{{ else }}}0{{{ end }}}">
<td class="p-2">
<a href="{config.relative_path}/user/{group.members.remoteId}">{buildAvatar(group.members, "24px", true)}</a>
</td>
<td class="member-name w-100 p-2">
<a class="align-text-top" href="{config.relative_path}/user/{group.members.remoteId}">{group.members.username}</a>
<i component="groups/owner/icon" title="[[groups:owner]]" class="user-owner-icon fa fa-star align-text-top text-warning <!-- IF !group.members.isOwner -->invisible<!-- ENDIF !group.members.isOwner -->"></i>
<!-- IF group.isOwner -->
<div class="owner-controls btn-group float-end">
<a class="btn btn-sm" href="#" data-ajaxify="false" data-action="toggleOwnership" title="[[groups:details.grant]]">
<i class="fa fa-star"></i>
</a>
<a class="btn btn-sm" href="#" data-ajaxify="false" data-action="kick" title="[[groups:details.kick]]">
<i class="fa fa-ban"></i>
</a>
</div>
<!-- ENDIF group.isOwner -->
</td>
</tr>
{{{end}}}
</tbody>
</table>
</div>

View File

@ -0,0 +1,28 @@
{{{ if group.pending.length }}}
<div class="d-flex justify-content-end gap-2 mb-3">
<button class="btn btn-danger btn-sm" data-action="rejectAll">[[groups:pending.reject-all]]</button>
<button class="btn btn-success btn-sm" data-action="acceptAll">[[groups:pending.accept-all]]</button>
</div>
{{{ end }}}
<div style="max-height: 500px;overflow: auto;">
<div component="groups/pending/alert" class="alert alert-info {{{ if group.pending.length }}}hidden{{{ end }}}">[[groups:pending.none]]</div>
<table component="groups/pending" class="table table-hover">
<tbody>
{{{ each group.pending }}}
<tr data-uid="{group.pending.uid}" class="align-middle">
<td class="member-name p-2 d-flex align-items-center justify-content-between">
<div class="d-flex gap-2">
<a class="text-decoration-none" href="{config.relative_path}/user/{group.pending.remoteId}">{buildAvatar(group.pending, "24px", true)}</a>
<a href="{config.relative_path}/user/{group.pending.remoteId}">{group.pending.username}</a>
</div>
<div class="d-flex gap-2">
<button class="btn btn-danger btn-sm" data-action="reject">[[groups:pending.reject]]</a></li>
<button class="btn btn-success btn-sm" data-action="accept">[[groups:pending.accept]]</a></li>
</div>
</td>
</tr>
{{{ end }}}
</tbody>
</table>
</div>

View File

@ -0,0 +1,35 @@
<a class="nav-link" data-bs-toggle="dropdown" href="{relative_path}/user/{user.remoteId}/chats" data-ajaxify="false" id="chat_dropdown" component="chat/dropdown" role="button" aria-haspopup="true" aria-expanded="false">
<i component="chat/icon" class="fa {{{ if unreadCount.chat}}}fa-comment{{{ else }}}fa-comment-o{{{ end }}} fa-fw unread-count" data-content="{unreadCount.chat}"></i> <span class="d-inline d-sm-none">[[global:header.chats]]</span>
</a>
<ul class="dropdown-menu dropdown-menu-end p-1" aria-labelledby="chat_dropdown" role="menu">
<li>
<ul component="chat/list" class="list-unstyled chat-list chats-list ghost-scrollbar pe-1">
<div class="rounded-1">
<div class="d-flex gap-1 justify-content-between">
<div class="dropdown-item p-2 d-flex gap-2 placeholder-wave">
<div class="main-avatar">
<div class="placeholder" style="width: 32px; height: 32px;"></div>
</div>
<div class="d-flex flex-grow-1 flex-column w-100">
<div class="text-xs"><div class="placeholder col-3"></div></div>
<div class="text-sm"><div class="placeholder col-11"></div></div>
<div class="text-xs"><div class="placeholder col-4"></div></div>
</div>
</div>
<div>
<button class="mark-read btn btn-ghost btn-sm d-flex align-items-center justify-content-center flex-grow-0 flex-shrink-0 p-1" style="width: 1.5rem; height: 1.5rem;">
<i class="unread fa fa-2xs fa-circle text-primary"></i>
</button>
</div>
</div>
</div>
</ul>
</li>
<li class="dropdown-divider"></li>
<li>
<div class="d-flex justify-content-center gap-1 flex-wrap">
<a class="btn btn-light btn-sm mark-all-read flex-fill text-nowrap" href="#" component="chats/mark-all-read"><i class="fa fa-check-double"></i> [[modules:chat.mark-all-read]]</a>
<a class="btn btn-primary btn-sm flex-fill text-nowrap" href="{relative_path}/user/{user.remoteId}/chats"><i class="fa fa-comments"></i> [[modules:chat.see-all]]</a>
</div>
</li>
</ul>

View File

@ -0,0 +1,102 @@
<li id="user_label" class="nav-item dropdown px-3" title="[[global:header.profile]]">
<a href="#" for="user-control-list-check" data-bs-toggle="dropdown" id="user_dropdown" role="button" component="header/avatar" aria-haspopup="true" aria-expanded="false">
{buildAvatar(user, "32px", true)}
<span id="user-header-name" class="d-block d-sm-none">{user.username}</span>
</a>
<input type="checkbox" class="hidden" id="user-control-list-check" aria-hidden="true">
<ul id="user-control-list" component="header/usercontrol" class="overscroll-behavior-contain user-dropdown dropdown-menu dropdown-menu-end shadow p-1 text-sm ff-base" role="menu">
<li>
<a class="dropdown-item rounded-1 d-flex align-items-center gap-2" component="header/profilelink" href="{relative_path}/user/{user.remoteId}" role="menuitem" aria-label="[[user:profile]]">
<span component="user/status" class="flex-shrink-0 border border-white border-2 rounded-circle status {user.status}"><span class="visually-hidden">[[global:{user.status}]]</span></span>
<span class="fw-semibold" component="header/username">{user.username}</span>
</a>
</li>
<li role="presentation" class="dropdown-divider"></li>
<li><h6 class="dropdown-header text-xs">[[global:status]]</h6></li>
<li>
<a href="#" class="dropdown-item rounded-1 user-status d-flex align-items-center gap-2 {{{ if user.online }}}selected{{{ end }}}" data-status="online" role="menuitem">
<span component="user/status" class="flex-shrink-0 border border-white border-2 rounded-circle status online"></span>
<span class="flex-grow-1">[[global:online]]</span>
</a>
</li>
<li>
<a href="#" class="dropdown-item rounded-1 user-status d-flex align-items-center gap-2 {{{ if user.away }}}selected{{{ end }}}" data-status="away" role="menuitem">
<span component="user/status" class="flex-shrink-0 border border-white border-2 rounded-circle status away"></span>
<span class="flex-grow-1">[[global:away]]</span>
</a>
</li>
<li>
<a href="#" class="dropdown-item rounded-1 user-status d-flex align-items-center gap-2 {{{ if user.dnd }}}selected{{{ end }}}" data-status="dnd" role="menuitem">
<span component="user/status" class="flex-shrink-0 border border-white border-2 rounded-circle status dnd"></span>
<span class="flex-grow-1">[[global:dnd]]</span>
</a>
</li>
<li>
<a href="#" class="dropdown-item rounded-1 user-status d-flex align-items-center gap-2 {{{ if user.offline }}}selected{{{ end }}}" data-status="offline" role="menuitem">
<span component="user/status" class="flex-shrink-0 border border-white border-2 rounded-circle status offline"></span>
<span class="flex-grow-1">[[global:invisible]]</span>
</a>
</li>
<li role="presentation" class="dropdown-divider"></li>
<li>
<a class="dropdown-item" href="{relative_path}/user/{user.remoteId}/bookmarks" role="menuitem">
<i class="fa fa-fw fa-bookmark"></i> <span>[[user:bookmarks]]</span>
</a>
</li>
<li>
<a class="dropdown-item" component="header/profilelink/edit" href="{relative_path}/user/{user.remoteId}/edit" role="menuitem">
<i class="fa fa-fw fa-edit"></i> <span>[[user:edit-profile]]</span>
</a>
</li>
<li>
<a class="dropdown-item" component="header/profilelink/settings" href="{relative_path}/user/{user.remoteId}/settings" role="menuitem">
<i class="fa fa-fw fa-gear"></i> <span>[[user:settings]]</span>
</a>
</li>
{{{ if showModMenu }}}
<li role="presentation" class="dropdown-divider"></li>
<li><h6 class="dropdown-header">[[pages:moderator-tools]]</h6></li>
<li>
<a class="dropdown-item" href="{relative_path}/flags" role="menuitem">
<i class="fa fa-fw fa-flag"></i> <span>[[pages:flagged-content]]</span>
</a>
</li>
<li>
<a class="dropdown-item" href="{relative_path}/post-queue" role="menuitem">
<i class="fa fa-fw fa-list-alt"></i> <span>[[pages:post-queue]]</span>
</a>
</li>
{{{ if registrationQueueEnabled }}}
<li>
<a class="dropdown-item" href="{relative_path}/registration-queue" role="menuitem">
<i class="fa fa-fw fa-list-alt"></i> <span>[[pages:registration-queue]]</span>
</a>
</li>
{{{ end }}}
<li>
<a class="dropdown-item" href="{relative_path}/ip-blacklist" role="menuitem">
<i class="fa fa-fw fa-ban"></i> <span>[[pages:ip-blacklist]]</span>
</a>
</li>
{{{ else }}}
{{{ if postQueueEnabled }}}
<li>
<a class="dropdown-item" href="{relative_path}/post-queue" role="menuitem">
<i class="fa fa-fw fa-list-alt"></i> <span>[[pages:post-queue]]</span>
</a>
</li>
{{{ end }}}
{{{ end }}}
<li role="presentation" class="dropdown-divider"></li>
<li component="user/logout">
<form method="post" action="{relative_path}/logout">
<input type="hidden" name="_csrf" value="{config.csrf_token}">
<input type="hidden" name="noscript" value="true">
<button type="submit" class="dropdown-item" role="menuitem">
<i class="fa fa-fw fa-sign-out"></i><span> [[global:logout]]</span>
</button>
</form>
</li>
</ul>
</li>

View File

@ -0,0 +1,44 @@
{{{ if !notifications.length }}}
<div class="no-notifs text-center p-4 d-flex flex-column">
<div class="p-4"><i class="fa-solid fa-wind fs-2 text-muted"></i></div>
<div class="text-xs fw-semibold text-muted">[[notifications:no-notifs]]</div>
</div>
{{{ end }}}
{{{ each notifications }}}
<div class="{./readClass}" data-nid="{./nid}" data-path="{./path}" {{{ if ./pid }}}data-pid="{./pid}"{{{ end }}}{{{ if ./tid }}}data-tid="{./tid}"{{{ end }}}>
<div class="d-flex gap-1 justify-content-between">
<div class="btn btn-ghost btn-sm d-flex gap-2 flex-grow-1 align-items-start text-start">
<a class="flex-grow-0 flex-shrink-0" href="{{{ if ./user.remoteId}}}{config.relative_path}/user/{./user.remoteId}{{{ else }}}#{{{ end }}}">
{{{ if (./image && ./from) }}}
<img class="avatar avatar-rounded" style="--avatar-size: 32px;" src="{./image}" />
{{{ else }}}
{{{ if ./icon }}}
<div class="avatar avatar-rounded" style="--avatar-size: 32px;"><i class="text-secondary fa {./icon}"></i></div>
{{{ else }}}
<div class="avatar avatar-rounded" style="--avatar-size: 32px; background-color: {./user.icon:bgColor};">{./user.icon:text}</div>
{{{ end }}}
{{{ end }}}
</a>
<div class="d-flex flex-grow-1 flex-column align-items-start position-relative">
<a href="{./path}" class="text-decoration-none d-inline-block text-reset text-break text-sm ff-sans stretched-link" component="notifications/item/link">
{./bodyShort}
</a>
<div class="text-xs text-muted">{{{ if ./timeagoLong }}}{./timeagoLong}{{{ else }}}<span class="timeago" title="{./datetimeISO}"></span>{{{ end }}}</div>
</div>
</div>
<div>
{{{ if ./nid }}}
<button class="mark-read btn btn-ghost btn-sm flex-grow-0 flex-shrink-0 p-1" style="width: 1.5rem; height: 1.5rem;">
<i class="unread fa fa-2xs fa-circle text-primary {{{ if ./read }}}hidden{{{ end }}}" aria-label="[[unread:mark-as-read]]"></i>
<i class="read fa fa-2xs fa-circle-o text-secondary {{{ if !./read }}}hidden{{{ end }}}" aria-label="[[unread:mark-as-unread]]"></i>
</button>
{{{ end }}}
</div>
</div>
</div>
{{{ if !@last }}}
<hr class="my-1" />
{{{ end }}}
{{{ end }}}

View File

@ -0,0 +1,34 @@
<li component="post" class="posts-list-item row {{{ if ./deleted }}} deleted{{{ else }}}{{{ if ./topic.deleted }}} deleted{{{ end }}}{{{ end }}}{{{ if ./topic.scheduled }}} scheduled{{{ end }}}" data-pid="{./pid}" data-uid="{./uid}">
<div class="col-lg-11 col-sm-10 col-9 post-body pb-3">
<a class="topic-title text-reset" href="{config.relative_path}/post/{encodeURIComponent(../pid)}">
{{{ if !./isMainPost }}}RE: {{{ end }}}{./topic.title}
</a>
<div component="post/content" class="content mb-3">
{../content}
</div>
<div class="mb-3">
<a class="topic-category text-xs fw-bold text-uppercase text-secondary mb-3" href="{config.relative_path}/category/{../category.slug}">[[global:posted-in, {../category.name}]]</a>
{{{ if ../isMainPost }}}
{{{ if ../topic.tags.length }}}
<span class="tag-list">
{{{ each ../topic.tags }}}
<a href="{config.relative_path}/tags/{topic.tags.valueEncoded}"><span class="tag tag-item tag-class-{topic.tags.class}">{topic.tags.valueEscaped}</span></a>
{{{ end }}}
</span>
{{{ end }}}
{{{ end }}}
</div>
<div class="post-info">
<a href="{config.relative_path}/user/{./user.remoteId}">{buildAvatar(./user, "28px", true, "user-img not-responsive")}</a>
<div class="post-author text-secondary text-uppercase">
<a class="text-reset" href="{config.relative_path}/user/{./user.remoteId}">{./user.displayname}</a><br />
<span class="timeago" title="{./timestampISO}"></span>
</div>
</div>
</div>
</li>

View File

@ -0,0 +1,54 @@
<div id="results" class="search-results col-md-12" data-search-query="{search_query}">
{{{ if matchCount }}}
<div class="alert alert-info">[[search:results-matching, {matchCount}, {search_query}, {time}]] </div>
{{{ else }}}
{{{ if search_query }}}
<div class="alert alert-warning">[[search:no-matches]]</div>
{{{ end }}}
{{{ end }}}
{{{each posts}}}
<div class="topic-row card clearfix mb-3">
<div class="card-body">
<div class="mb-2">
<a href="{config.relative_path}/user/{./user.remoteId}">{buildAvatar(./user, "24px", true)}</a>
<a class="topic-title fw-semibold fs-5" href="{config.relative_path}/post/{encodeURIComponent(posts.pid)}">{./topic.title}</a>
</div>
{{{ if showAsPosts }}}
<div component="post/content" class="content">
{./content}
</div>
{{{ end }}}
<small class="post-info">
<a href="{config.relative_path}/category/{./category.slug}">
<div class="category-item d-inline-block">
{buildCategoryIcon(./category, "24px", "rounded-circle")}
{./category.name}
</div>
</a> &bull;
<span class="timeago" title="{./timestampISO}"></span>
</small>
</div>
</div>
{{{end}}}
{{{ if users.length }}}
<!-- IMPORT partials/users_list.tpl -->
{{{ end }}}
{{{ if tags.length }}}
<!-- IMPORT partials/tags_list.tpl -->
{{{ end }}}
{{{ if categories.length }}}
<ul class="categories">
{{{each categories}}}
<!-- IMPORT partials/categories/item.tpl -->
{{{end}}}
</ul>
{{{ end }}}
<!-- IMPORT partials/paginator.tpl -->
</div>

View File

@ -0,0 +1,12 @@
<div class="clearfix">
<div class="icon float-start">
<a href="<!-- IF post.user.remoteId -->{config.relative_path}/user/{post.user.remoteId}<!-- ELSE -->#<!-- ENDIF post.user.remoteId -->">
{buildAvatar(post.user, "24px", true, "", "user/picture")} {post.user.username}
</a>
</div>
<small class="float-end">
<span class="timeago" title="{post.timestampISO}"></span>
</small>
</div>
<div>{post.content}</div>

View File

@ -0,0 +1 @@
<small data-editor="{editor.remoteId}" component="post/editor" class="hidden">[[global:last-edited-by, {editor.username}]] <span class="timeago" title="{isoTimeToLocaleString(editedISO, config.userLang)}"></span></small>

View File

@ -0,0 +1,140 @@
{{{ if (!./index && widgets.mainpost-header.length) }}}
<div data-widget-area="mainpost-header">
{{{ each widgets.mainpost-header }}}
{widgets.mainpost-header.html}
{{{ end }}}
</div>
{{{ end }}}
<div class="clearfix post-header">
<div class="icon float-start">
<a href="<!-- IF posts.user.remoteId -->{config.relative_path}/user/{posts.user.remoteId}<!-- ELSE -->#<!-- ENDIF posts.user.remoteId -->">
{buildAvatar(posts.user, "48px", true, "", "user/picture")}
{{{ if ./user.isLocal }}}
<span component="user/status" class="position-absolute top-100 start-100 border border-white border-2 rounded-circle status {posts.user.status}"><span class="visually-hidden">[[global:{posts.user.status}]]</span></span>
{{{ else }}}
<span component="user/locality" class="position-absolute top-100 start-100 lh-1 border border-white border-2 rounded-circle small" title="[[global:remote-user]]">
<span class="visually-hidden">[[global:remote-user]]</span>
<i class="fa fa-globe"></i>
</span>
{{{ end }}}
</a>
</div>
<small class="d-flex">
<div class="d-flex align-items-center gap-1 flex-wrap w-100">
<strong class="text-nowrap" itemprop="author" itemscope itemtype="https://schema.org/Person">
<meta itemprop="name" content="{./user.username}">
{{{ if ./user.remoteId }}}<meta itemprop="url" content="{config.relative_path}/user/{./user.remoteId}">{{{ end }}}
<a href="<!-- IF posts.user.remoteId -->{config.relative_path}/user/{posts.user.remoteId}<!-- ELSE -->#<!-- ENDIF posts.user.remoteId -->" data-username="{posts.user.username}" data-uid="{posts.user.uid}">{posts.user.displayname}</a>
</strong>
{{{ each posts.user.selectedGroups }}}
{{{ if posts.user.selectedGroups.slug }}}
<!-- IMPORT partials/groups/badge.tpl -->
{{{ end }}}
{{{ end }}}
<!-- IF posts.user.banned -->
<span class="badge bg-danger">[[user:banned]]</span>
<!-- ENDIF posts.user.banned -->
<span class="visible-xs-inline-block visible-sm-inline-block visible-md-inline-block visible-lg-inline-block">
{{{ if posts.toPid }}}
<a component="post/parent" class="btn btn-sm btn-ghost py-0 px-1 text-xs hidden-xs" data-topid="{posts.toPid}" href="{config.relative_path}/post/{posts.toPid}"><i class="fa fa-reply"></i> @{{{ if posts.parent.user.remoteId }}}{posts.parent.user.username}{{{ else }}}[[global:guest]]{{{ end }}}</a>
{{{ end }}}
<span>
<!-- IF posts.user.custom_profile_info.length -->
&#124;
{{{each posts.user.custom_profile_info}}}
{posts.user.custom_profile_info.content}
{{{end}}}
<!-- ENDIF posts.user.custom_profile_info.length -->
</span>
</span>
<div class="d-flex align-items-center gap-1 flex-grow-1 justify-content-end">
<span>
<i component="post/edit-indicator" class="fa fa-pencil-square<!-- IF privileges.posts:history --> pointer<!-- END --> edit-icon <!-- IF !posts.editor.username -->hidden<!-- ENDIF !posts.editor.username -->"></i>
<span data-editor="{posts.editor.remoteId}" component="post/editor" class="hidden">[[global:last-edited-by, {posts.editor.username}]] <span class="timeago" title="{isoTimeToLocaleString(posts.editedISO, config.userLang)}"></span></span>
<span class="visible-xs-inline-block visible-sm-inline-block visible-md-inline-block visible-lg-inline-block">
<a class="permalink text-muted" href="{config.relative_path}/post/{posts.pid}"><span class="timeago" title="{posts.timestampISO}"></span></a>
</span>
</span>
<span class="bookmarked"><i class="fa fa-bookmark-o"></i></span>
</div>
</div>
</small>
</div>
<br />
<div class="content" component="post/content" itemprop="text">
{posts.content}
</div>
<div class="post-footer">
{{{ if posts.user.signature }}}
<div component="post/signature" data-uid="{posts.user.uid}" class="post-signature">{posts.user.signature}</div>
{{{ end }}}
<div class="clearfix">
{{{ if !hideReplies }}}
<a component="post/reply-count" data-target-component="post/replies/container" href="#" class="threaded-replies user-select-none float-start text-muted {{{ if (!./replies || shouldHideReplyContainer(@value)) }}}hidden{{{ end }}}">
<span component="post/reply-count/avatars" class="avatars d-inline-flex gap-1 align-items-top hidden-xs {{{ if posts.replies.hasMore }}}hasMore{{{ end }}}">
{{{each posts.replies.users}}}
<span>{buildAvatar(posts.replies.users, "16px", true, "")}</span>
{{{end}}}
{{{ if posts.replies.hasMore}}}
<span><i class="fa fa-ellipsis"></i></span>
{{{ end }}}
</span>
<span class="replies-count small" component="post/reply-count/text" data-replies="{posts.replies.count}">{posts.replies.text}</span>
<span class="replies-last hidden-xs small">[[topic:last-reply-time]] <span class="timeago" title="{posts.replies.timestampISO}"></span></span>
<i class="fa fa-fw fa-chevron-down" component="post/replies/open"></i>
</a>
{{{ end }}}
<small class="d-flex justify-content-end align-items-center gap-1" component="post/actions">
<!-- IMPORT partials/topic/reactions.tpl -->
<span class="post-tools">
<a component="post/reply" href="#" class="btn btn-sm btn-link user-select-none <!-- IF !privileges.topics:reply -->hidden<!-- ENDIF !privileges.topics:reply -->">[[topic:reply]]</a>
<a component="post/quote" href="#" class="btn btn-sm btn-link user-select-none <!-- IF !privileges.topics:reply -->hidden<!-- ENDIF !privileges.topics:reply -->">[[topic:quote]]</a>
</span>
{{{ if ./announces }}}
<a component="post/announce-count" href="#" class="btn-ghost-sm" title="[[topic:announcers]]"><i class="fa fa-share-alt text-primary"></i> {./announces}</a>
{{{ end }}}
<!-- IF !reputation:disabled -->
<span class="votes">
<a component="post/upvote" href="#" class="btn btn-sm btn-link <!-- IF posts.upvoted -->upvoted<!-- ENDIF posts.upvoted -->">
<i class="fa fa-chevron-up"></i>
</a>
<span class="btn btn-sm btn-link" component="post/vote-count" data-votes="{posts.votes}">{posts.votes}</span>
<!-- IF !downvote:disabled -->
<a component="post/downvote" href="#" class="btn btn-sm btn-link <!-- IF posts.downvoted -->downvoted<!-- ENDIF posts.downvoted -->">
<i class="fa fa-chevron-down"></i>
</a>
<!-- ENDIF !downvote:disabled -->
</span>
<!-- ENDIF !reputation:disabled -->
<!-- IMPORT partials/topic/post-menu.tpl -->
</small>
</div>
<div component="post/replies/container"></div>
</div>
{{{ if (!./index && widgets.mainpost-footer.length) }}}
<div data-widget-area="mainpost-footer">
{{{ each widgets.mainpost-footer }}}
{widgets.mainpost-footer.html}
{{{ end }}}
</div>
{{{ end }}}

View File

@ -0,0 +1,27 @@
{{{ if privileges.topics:reply }}}
<div component="topic/quickreply/container" class="quick-reply d-flex gap-3 mb-4">
<div class="icon hidden-xs">
<a class="d-inline-block position-relative" href="{{{ if loggedInUser.remoteId }}}{config.relative_path}/user/{loggedInUser.remoteId}{{{ else }}}#{{{ end }}}">
{buildAvatar(loggedInUser, "48px", true, "", "user/picture")}
</a>
</div>
<form class="flex-grow-1 d-flex flex-column gap-2" method="post" action="{config.relative_path}/compose">
<input type="hidden" name="tid" value="{tid}" />
<input type="hidden" name="_csrf" value="{config.csrf_token}" />
<div class="quickreply-message position-relative">
<textarea rows="4" name="content" component="topic/quickreply/text" class="form-control mousetrap" placeholder="[[modules:composer.textarea.placeholder]]"></textarea>
<div class="imagedrop"><div>[[topic:composer.drag-and-drop-images]]</div></div>
</div>
<div>
<div class="d-flex justify-content-end gap-2">
<button type="button" component="topic/quickreply/upload/button" class="btn btn-ghost btn-sm border"><i class="fa fa-upload"></i></button>
<button type="button" component="topic/quickreply/expand" class="btn btn-ghost btn-sm border" title="[[topic:open-composer]]"><i class="fa fa-expand"></i></button>
<button type="submit" component="topic/quickreply/button" class="btn btn-sm btn-primary">[[topic:post-quick-reply]]</button>
</div>
</div>
</form>
<form class="d-none" component="topic/quickreply/upload" method="post" enctype="multipart/form-data">
<input type="file" name="files[]" multiple class="hidden"/>
</form>
</div>
{{{ end }}}

View File

@ -0,0 +1,131 @@
<ul component="category" class="topics-list list-unstyled" itemscope itemtype="http://www.schema.org/ItemList" data-nextstart="{nextStart}" data-set="{set}">
{{{ each topics }}}
<li component="category/topic" class="category-item hover-parent py-2 mb-2 d-flex flex-column flex-lg-row align-items-start {function.generateTopicClass}" <!-- IMPORT partials/data/category.tpl -->>
<link itemprop="url" content="{config.relative_path}/topic/{./slug}" />
<meta itemprop="name" content="{function.stripTags, ./title}" />
<meta itemprop="itemListOrder" content="descending" />
<meta itemprop="position" content="{increment(./index, "1")}" />
<a id="{./index}" data-index="{./index}" component="topic/anchor"></a>
<div class="d-flex p-0 col-12 col-lg-7 gap-2 gap-lg-3 pe-1 align-items-start {{{ if config.theme.mobileTopicTeasers }}}mb-2 mb-lg-0{{{ end }}}">
<div class="flex-shrink-0 position-relative">
<a class="d-inline-block text-decoration-none avatar-tooltip" title="{./user.displayname}" href="{{{ if ./user.remoteId }}}{config.relative_path}/user/{./user.remoteId}{{{ else }}}#{{{ end }}}">
{buildAvatar(./user, "40px", true)}
</a>
{{{ if showSelect }}}
<div class="checkbox position-absolute top-100 start-50 translate-middle-x m-0 d-none d-lg-flex" style="max-width:max-content">
<i component="topic/select" class="fa text-muted pointer fa-square-o p-1 hover-visible"></i>
</div>
{{{ end }}}
</div>
<div class="flex-grow-1 d-flex flex-wrap gap-1 position-relative">
<h3 component="topic/header" class="title text-break fs-5 fw-semibold m-0 tracking-tight w-100 {{{ if showSelect }}}me-4 me-lg-0{{{ end }}}">
<a class="text-reset" href="{{{ if topics.noAnchor }}}#{{{ else }}}{config.relative_path}/topic/{./slug}{{{ if ./bookmark }}}/{./bookmark}{{{ end }}}{{{ end }}}">{./title}</a>
</h3>
<div component="topic/labels" class="d-flex flex-wrap gap-1 w-100 align-items-center">
<span component="topic/watched" class="badge border border-gray-300 text-body {{{ if !./followed }}}hidden{{{ end }}}">
<i class="fa fa-bell-o"></i>
<span>[[topic:watching]]</span>
</span>
<span component="topic/ignored" class="badge border border-gray-300 text-body {{{ if !./ignored }}}hidden{{{ end }}}">
<i class="fa fa-eye-slash"></i>
<span>[[topic:ignoring]]</span>
</span>
<span component="topic/scheduled" class="badge border border-gray-300 text-body {{{ if !./scheduled }}}hidden{{{ end }}}">
<i class="fa fa-clock-o"></i>
<span>[[topic:scheduled]]</span>
</span>
<span component="topic/pinned" class="badge border border-gray-300 text-body {{{ if (./scheduled || !./pinned) }}}hidden{{{ end }}}">
<i class="fa fa-thumb-tack"></i>
<span>{{{ if !./pinExpiry }}}[[topic:pinned]]{{{ else }}}[[topic:pinned-with-expiry, {isoTimeToLocaleString(./pinExpiryISO, config.userLang)}]]{{{ end }}}</span>
</span>
<span component="topic/locked" class="badge border border-gray-300 text-body {{{ if !./locked }}}hidden{{{ end }}}">
<i class="fa fa-lock"></i>
<span>[[topic:locked]]</span>
</span>
<span component="topic/moved" class="badge border border-gray-300 text-body {{{ if !./oldCid }}}hidden{{{ end }}}">
<i class="fa fa-arrow-circle-right"></i>
<span>[[topic:moved]]</span>
</span>
{{{each ./icons}}}<span class="lh-1">{@value}</span>{{{end}}}
{{{ if !template.category }}}
{function.buildCategoryLabel, ./category, "a", "border"}
{{{ end }}}
<span data-tid="{./tid}" component="topic/tags" class="lh-1 tag-list d-flex flex-wrap gap-1 {{{ if !./tags.length }}}hidden{{{ end }}}">
{{{ each ./tags }}}
<a href="{config.relative_path}/tags/{./valueEncoded}"><span class="badge border border-gray-300 fw-normal tag tag-class-{./class}" data-tag="{./value}">{./valueEscaped}</span></a>
{{{ end }}}
</span>
<div class="d-flex gap-1 d-block d-lg-none w-100">
<span class="badge text-body border stats text-xs text-muted">
<i class="fa-regular fa-fw fa-message"></i>
<span component="topic/post-count" class="fw-normal">{humanReadableNumber(./postcount, 0)}</span>
</span>
<a href="{config.relative_path}/topic/{./slug}{{{ if (./teaser.timestampISO && !config.theme.mobileTopicTeasers) }}}/{./teaser.index}{{{ end }}}" class="border badge bg-transparent text-muted fw-normal timeago" title="{{{ if (./teaser.timestampISO && !config.theme.mobileTopicTeasers) }}}{./teaser.timestampISO}{{{ else }}}{./timestampISO}{{{ end }}}"></a>
</div>
<a href="{config.relative_path}/topic/{./slug}" class="d-none d-lg-block badge bg-transparent text-muted fw-normal timeago" title="{./timestampISO}"></a>
</div>
{{{ if showSelect }}}
<div class="checkbox position-absolute top-0 end-0 m-0 d-flex d-lg-none" style="max-width:max-content">
<i component="topic/select" class="fa fa-square-o text-muted pointer p-1"></i>
</div>
{{{ end }}}
</div>
{{{ if ./thumbs.length }}}
<a class="topic-thumbs position-relative text-decoration-none flex-shrink-0 d-none d-xl-block" href="{config.relative_path}/topic/{./slug}{{{ if ./bookmark }}}/{./bookmark}{{{ end }}}" aria-label="[[topic:thumb-image]]">
<img class="topic-thumb rounded-1 bg-light" style="width:auto;max-width: 5.33rem;height: 3.33rem;object-fit: contain;" src="{./thumbs.0.url}" alt=""/>
<span data-numthumbs="{./thumbs.length}" class="px-1 position-absolute bottom-0 end-0 badge rounded-0 border fw-semibold text-bg-light" style="z-index: 1; border-top-left-radius: 0.25rem!important; border-bottom-right-radius: 0.25rem!important;">{./thumbs.length}</span>
</a>
{{{ end }}}
</div>
<div class="d-flex p-0 col-lg-5 col-12 align-content-stretch">
<div class="meta stats d-none d-lg-grid col-6 gap-1 pe-2 text-muted" style="grid-template-columns: 1fr 1fr 1fr;">
{{{ if !reputation:disabled }}}
<div class="stats-votes overflow-hidden d-flex flex-column align-items-center">
<span class="fs-4" title="{./votes}">{humanReadableNumber(./votes, 0)}</span>
<span class="d-none d-xl-flex text-uppercase text-xs">[[global:votes]]</span>
<i class="d-xl-none fa fa-fw text-xs text-muted opacity-75 fa-chevron-up"></i>
</div>
{{{ end }}}
<div class="stats-postcount overflow-hidden d-flex flex-column align-items-center">
<span class="fs-4" title="{./postcount}">{humanReadableNumber(./postcount, 0)}</span>
<span class="d-none d-xl-flex text-uppercase text-xs">[[global:posts]]</span>
<i class="d-xl-none fa-regular fa-fw text-xs text-muted opacity-75 fa-message"></i>
</div>
<div class="stats-viewcount overflow-hidden d-flex flex-column align-items-center">
<span class="fs-4" title="{./viewcount}">{humanReadableNumber(./viewcount, 0)}</span>
<span class="d-none d-xl-flex text-uppercase text-xs">[[global:views]]</span>
<i class="d-xl-none fa fa-fw text-xs text-muted opacity-75 fa-eye"></i>
</div>
</div>
<div component="topic/teaser" class="meta teaser col-lg-6 col-12 {{{ if !config.theme.mobileTopicTeasers }}}d-none d-lg-block{{{ end }}}">
<div class="lastpost border-start border-4 lh-sm h-100 d-flex flex-column gap-1" style="border-color: {./category.bgColor}!important;">
{{{ if ./unreplied }}}
<div class="ps-2 text-xs">
[[category:no-replies]]
</div>
{{{ else }}}
{{{ if ./teaser.pid }}}
<div class="ps-2">
<a href="{{{ if ./teaser.user.remoteId }}}{config.relative_path}/user/{./teaser.user.remoteId}{{{ else }}}#{{{ end }}}" class="text-decoration-none avatar-tooltip" title="{./teaser.user.displayname}">{buildAvatar(./teaser.user, "18px", true)}</a>
<a class="permalink text-muted timeago text-xs" href="{config.relative_path}/topic/{./slug}/{./teaser.index}" title="{./teaser.timestampISO}" aria-label="[[global:lastpost]]"></a>
</div>
<div class="post-content text-xs ps-2 line-clamp-sm-2 lh-sm text-break position-relative flex-fill">
<a class="stretched-link" tabindex="-1" href="{config.relative_path}/topic/{./slug}/{./teaser.index}" aria-label="[[global:lastpost]]"></a>
{./teaser.content}
</div>
{{{ end }}}
{{{ end }}}
</div>
</div>
</div>
</li>
{{{end}}}
</ul>

View File

@ -0,0 +1,41 @@
<li class="users-box registered-user text-center pb-3" data-uid="{users.uid}" style="width: 102px;">
<a href="{config.relative_path}/user/{users.remoteId}">{buildAvatar(users, "64px", true)}</a>
<div class="user-info">
<div class="text-nowrap text-truncate">
<a href="{config.relative_path}/user/{users.remoteId}">{users.username}</a>
</div>
<!-- IF section_online -->
<div class="lastonline">
<span class="timeago" title="{users.lastonlineISO}"></span>
</div>
<!-- ENDIF section_online -->
<!-- IF section_joindate -->
<div class="joindate">
<span class="timeago" title="{users.joindateISO}"></span>
</div>
<!-- ENDIF section_joindate -->
<!-- IF section_sort-reputation -->
<div class="reputation">
<i class="fa fa-star"></i>
<span>{formattedNumber(users.reputation)}</span>
</div>
<!-- ENDIF section_sort-reputation -->
<!-- IF section_sort-posts -->
<div class="post-count">
<i class="fa fa-pencil"></i>
<span>{formattedNumber(users.postcount)}</span>
</div>
<!-- ENDIF section_sort-posts -->
<!-- IF section_flagged -->
<div class="flag-count">
<i class="fa fa-flag"></i>
<span><a href="{config.relative_path}/flags?targetUid={users.uid}">{users.flags}</a></span>
</div>
<!-- ENDIF section_flagged -->
</div>
</li>