mirror of
https://github.com/NodeBB/NodeBB.git
synced 2026-02-25 08:01:15 +01:00
move btn-ghost to core
remove "btn-outline", it can be replaced with "btn-ghost border" move chats templates and css from harmony to core, persona nd peace will use same templates
This commit is contained in:
@@ -51,18 +51,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.btn-outline {
|
||||
@include btn-ghost-base();
|
||||
border-color: $border-color;
|
||||
}
|
||||
|
||||
.btn-outline-sm {
|
||||
@include btn-ghost-base();
|
||||
border-color: $border-color;
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.25rem;
|
||||
}
|
||||
|
||||
@include color-mode(dark) {
|
||||
.btn-light {
|
||||
@extend .btn-dark;
|
||||
@@ -70,7 +58,7 @@
|
||||
.text-bg-light {
|
||||
@extend .text-bg-dark;
|
||||
}
|
||||
.btn-ghost, .btn-ghost-sm, .btn-outline, .btn-outline-sm {
|
||||
.btn-ghost, .btn-ghost-sm {
|
||||
color: $btn-ghost-color-dark;
|
||||
&:hover, &.active {
|
||||
background-color: $btn-ghost-hover-color-dark;
|
||||
|
||||
51
public/scss/btn-ghost.scss
Normal file
51
public/scss/btn-ghost.scss
Normal file
@@ -0,0 +1,51 @@
|
||||
$btn-ghost-hover-color: mix($light, $dark, 90%);
|
||||
$btn-ghost-active-color: lighten($btn-ghost-hover-color, 5%);
|
||||
$btn-ghost-hover-color-dark: mix($dark, $light, 90%);
|
||||
$btn-ghost-active-color-dark: lighten($btn-ghost-hover-color-dark, 5%);
|
||||
|
||||
:root {
|
||||
--btn-ghost-hover-color: #{$btn-ghost-hover-color};
|
||||
--btn-ghost-active-color: #{$btn-ghost-active-color};
|
||||
}
|
||||
[data-bs-theme="dark"] {
|
||||
--btn-ghost-hover-color: #{$btn-ghost-hover-color-dark};
|
||||
--btn-ghost-active-color: #{$btn-ghost-active-color-dark};
|
||||
}
|
||||
|
||||
@mixin btn-ghost-base {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: ($spacer * 0.5);
|
||||
border-radius: $border-radius-sm;
|
||||
border-width: 1px;
|
||||
border-color: transparent;
|
||||
background-color: transparent;
|
||||
box-shadow: none;
|
||||
padding: ($spacer * 0.25) ($spacer * 0.5);
|
||||
text-align: left;
|
||||
--bs-text-opacity: 1;
|
||||
color: inherit !important;
|
||||
cursor: pointer;
|
||||
&:hover, &.active {
|
||||
background-color: var(--btn-ghost-hover-color);
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-ghost {
|
||||
@include btn-ghost-base();
|
||||
line-height: 1.5rem;
|
||||
> i {
|
||||
line-height: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-ghost-sm {
|
||||
@include btn-ghost-base();
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.25rem;
|
||||
> i {
|
||||
line-height: 1.25rem;
|
||||
}
|
||||
}
|
||||
127
public/scss/chats.scss
Normal file
127
public/scss/chats.scss
Normal file
@@ -0,0 +1,127 @@
|
||||
// chats need a bit of css
|
||||
|
||||
.stacked-avatars {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
span:first-child {
|
||||
top: 0;
|
||||
left: 8px;
|
||||
}
|
||||
span:last-child {
|
||||
left: 0;
|
||||
top: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
body.page-user-chats {
|
||||
#content {
|
||||
max-width: 100%;
|
||||
margin-bottom: 0!important;
|
||||
}
|
||||
overflow: hidden;
|
||||
[data-widget-area="footer"] {
|
||||
display: none;
|
||||
}
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
[component="chat/recent"] {
|
||||
.active .chat-room-btn {
|
||||
background-color: var(--btn-ghost-hover-color);
|
||||
}
|
||||
}
|
||||
|
||||
[component="chat/nav-wrapper"] {
|
||||
width: 300px;
|
||||
[component="chat/public/room"].unread {
|
||||
font-weight: $font-weight-bold;
|
||||
}
|
||||
}
|
||||
|
||||
[component="chat/user/list"] [data-uid] {
|
||||
[component="chat/user/list/username"] {
|
||||
color: $text-muted;
|
||||
}
|
||||
&.online {
|
||||
[component="chat/user/list/username"] {
|
||||
color: initial;
|
||||
font-weight: $font-weight-semibold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.expanded-chat {
|
||||
.chat-content {
|
||||
.message-body {
|
||||
@include fix-lists;
|
||||
}
|
||||
|
||||
.chat-message {
|
||||
.message-body-wrapper {
|
||||
.controls {
|
||||
opacity: 0;
|
||||
transition: $transition-fade;
|
||||
&:has([aria-expanded="true"]) { opacity: 1; }
|
||||
[data-action="restore"], [data-action="unpin"] { display: none; }
|
||||
}
|
||||
&:hover {
|
||||
.controls { opacity: 1; }
|
||||
}
|
||||
}
|
||||
&.deleted {
|
||||
.message-body { opacity: 0.3; }
|
||||
.message-body-wrapper .controls {
|
||||
[data-action] { display: none; }
|
||||
[data-action="restore"] { display: block; }
|
||||
}
|
||||
}
|
||||
&.pinned {
|
||||
.message-body-wrapper .controls {
|
||||
[data-action="pin"] { display: none; }
|
||||
[data-action="unpin"] { display: block;}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Mobile handling of chat page */
|
||||
@include media-breakpoint-down(lg) {
|
||||
.page-user-chats.chat-loaded {
|
||||
padding-bottom: 4.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
@include media-breakpoint-down(md) {
|
||||
.page-user-chats.chat-loaded {
|
||||
padding-bottom: initial;
|
||||
}
|
||||
[component="chat/nav-wrapper"] {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
||||
.page-user-chats.chat-loaded .bottombar {
|
||||
display: none!important;
|
||||
}
|
||||
|
||||
[component="chat/nav-wrapper"][data-loaded="1"] {
|
||||
display: none!important;
|
||||
}
|
||||
|
||||
[component="chat/nav-wrapper"][data-loaded="0"] + [component="chat/main-wrapper"] {
|
||||
display: none!important;
|
||||
}
|
||||
}
|
||||
|
||||
.chat-modal {
|
||||
left: auto;
|
||||
top: auto;
|
||||
bottom: 0px;
|
||||
right: 2rem;
|
||||
width: auto!important;
|
||||
height: auto!important;
|
||||
[component="chat/user/list/btn"], [component="chat/pinned/messages/btn"] {
|
||||
display: none!important;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
// core scss files shared by all themes
|
||||
@import "flags";
|
||||
@import "chats";
|
||||
@import "global";
|
||||
@import "modals";
|
||||
@import "btn-ghost";
|
||||
@import "modules/picture-switcher";
|
||||
@import "modules/bottom-sheet";
|
||||
@import "modules/icon-picker";
|
||||
|
||||
37
src/views/chat.tpl
Normal file
37
src/views/chat.tpl
Normal file
@@ -0,0 +1,37 @@
|
||||
<div id="chat-modal" class="chat-modal d-flex flex-nowrap modal hide overflow-visible" tabindex="-1" role="dialog" aria-labelledby="Chat" aria-hidden="true" data-center="false">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content" component="chat/message/window">
|
||||
<div class="modal-header d-flex gap-4 justify-content-between">
|
||||
<div class="fs-6 flex-grow-1 fw-semibold tracking-tight text-truncate text-nowrap" component="chat/room/name" data-icon="{icon}">{{{ if ./roomName }}}<i class="fa {icon} text-muted"></i> {roomName}{{{ else }}}{./chatWithMessage}{{{ end}}}</div>
|
||||
<div class="d-flex gap-1 align-items-center">
|
||||
<button type="button" class="btn-ghost-sm d-none d-md-flex" data-action="maximize" title="[[modules:chat.maximize]]" data-bs-toggle="tooltip" data-bs-placement="bottom">
|
||||
<i class="fa fa-fw fa-expand text-muted"></i>
|
||||
</button>
|
||||
|
||||
<!-- IMPORT partials/chats/options.tpl -->
|
||||
|
||||
<button id="chat-close-btn" type="button" class="btn-close btn-ghost-sm" aria-label="Close"></button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- IMPORT partials/chats/scroll-up-alert.tpl -->
|
||||
<div class="modal-body d-flex flex-column" style="height: 500px;">
|
||||
<div class="d-flex flex-grow-1 gap-1 overflow-auto" style="min-width: 0px;">
|
||||
<div component="chat/messages" class="expanded-chat d-flex flex-column flex-grow-1" data-roomid="{roomId}" style="min-width: 0px;">
|
||||
|
||||
<ul component="chat/message/content" class="chat-content p-0 m-0 list-unstyled overflow-auto flex-grow-1">
|
||||
<!-- IMPORT partials/chats/messages.tpl -->
|
||||
</ul>
|
||||
<ul component="chat/message/search/results" class="chat-content p-0 m-0 list-unstyled overflow-auto flex-grow-1 hidden">
|
||||
<div component="chat/message/search/no-results" class="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">[[search:no-matches]]</div>
|
||||
</div>
|
||||
</ul>
|
||||
<!-- IMPORT partials/chats/composer.tpl -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="imagedrop"><div>[[topic:composer.drag-and-drop-images]]</div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
54
src/views/chats.tpl
Normal file
54
src/views/chats.tpl
Normal file
@@ -0,0 +1,54 @@
|
||||
<div class="chats-full d-flex gap-1 h-100 mt-3 mt-md-0 py-md-3">
|
||||
<div component="chat/nav-wrapper" class="flex-shrink-0 d-flex flex-column h-100 gap-1" data-loaded="{{{ if roomId }}}1{{{ else }}}0{{{ end }}}">
|
||||
|
||||
<div>
|
||||
<button component="chat/create" class="btn btn-primary btn-sm w-100">[[modules:chat.create-room]]</button>
|
||||
</div>
|
||||
|
||||
{{{ if publicRooms.length }}}
|
||||
<hr class="my-1">
|
||||
|
||||
<div class="d-flex flex-column gap-1">
|
||||
<div class="d-flex gap-1 align-items-center justify-content-between justify-content-lg-start">
|
||||
<button class="btn-ghost-sm p-1 order-1 order-lg-0" data-bs-toggle="collapse" data-bs-target="#public-rooms"
|
||||
onclick="$(this).find('i').toggleClass('fa-rotate-180');"><i class="fa fa-fw fa-chevron-up" style="transition: 0.25s ease;"></i></button>
|
||||
<label class="text-sm text-muted lh-1">[[modules:chat.public-rooms, {publicRooms.length}]]</label>
|
||||
</div>
|
||||
<div id="public-rooms" component="chat/public" class="collapse show">
|
||||
<div class="d-flex gap-1 flex-column">
|
||||
{{{ each publicRooms }}}
|
||||
<div component="chat/public/room" class="btn-ghost-sm ff-sans justify-content-between hover-parent {{{ if ./unread}}}unread{{{ end }}}" data-roomid="{./roomId}">
|
||||
<div class="d-flex gap-1 align-items-center"><i class="fa {./icon} text-muted"></i> {./roomName} <div component="chat/public/room/unread/count" data-count="{./unreadCount}" class="badge border bg-light text-primary {{{ if !./unreadCount }}}hidden{{{ end }}}">{./unreadCountText}</div></div>
|
||||
<div>
|
||||
<div component="chat/public/room/sort/handle" class="text-muted {{{ if isAdmin }}}hover-d-block{{{ else }}}d-none{{{ end }}}" style="cursor:grab;"><i class="fa fa-bars"></i></div>
|
||||
</div>
|
||||
</div>
|
||||
{{{ end }}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{{ end }}}
|
||||
|
||||
<hr class="my-1">
|
||||
|
||||
<div class="d-flex flex-column gap-1 overflow-auto">
|
||||
{{{ if publicRooms.length }}}
|
||||
<div class="d-flex gap-1 align-items-center justify-content-between justify-content-lg-start">
|
||||
<button class="btn-ghost-sm p-1 order-1 order-lg-0" data-bs-toggle="collapse" data-bs-target="#private-rooms"
|
||||
onclick="$(this).find('i').toggleClass('fa-rotate-180')"><i class="fa fa-fw fa-chevron-up" style="transition: 0.25s ease;"></i></button>
|
||||
<label class="text-sm text-muted lh-1">[[modules:chat.private-rooms, {privateRoomCount}]]</label>
|
||||
</div>
|
||||
{{{ end }}}
|
||||
|
||||
<div id="private-rooms" component="chat/recent" class="chats-list overflow-auto mb-0 pe-1 pb-5 pb-lg-0 collapse show" data-nextstart="{nextStart}">
|
||||
{{{each rooms}}}
|
||||
<!-- IMPORT partials/chats/recent_room.tpl -->
|
||||
{{{end}}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div component="chat/main-wrapper" class="flex-grow-1 ms-md-2 ps-md-2 border-1 border-start-md h-100" style="min-width: 0;" data-roomid="{roomId}">
|
||||
<!-- IMPORT partials/chats/message-window.tpl -->
|
||||
</div>
|
||||
<div class="imagedrop"><div>[[topic:composer.drag-and-drop-images]]</div></div>
|
||||
</div>
|
||||
1
src/views/partials/chats/add-reaction.tpl
Normal file
1
src/views/partials/chats/add-reaction.tpl
Normal file
@@ -0,0 +1 @@
|
||||
<!-- This partial intentionally left blank; overwritten by nodebb-plugin-reactions -->
|
||||
27
src/views/partials/chats/composer.tpl
Normal file
27
src/views/partials/chats/composer.tpl
Normal file
@@ -0,0 +1,27 @@
|
||||
<div component="chat/composer" class="d-flex flex-column border-top pt-2 align-items-start">
|
||||
<div component="chat/composer/replying-to" data-tomid="" class="text-sm px-2 mb-1 d-flex gap-2 align-items-center hidden">
|
||||
<div component="chat/composer/replying-to-text"></div> <button component="chat/composer/replying-to-cancel" class="btn-ghost-sm px-2 py-1"><i class="fa fa-times"></i></button>
|
||||
</div>
|
||||
<div class="w-100 flex-grow-1 flex-nowrap position-relative d-flex rounded-2 border border-secondary p-1 align-items-end">
|
||||
<button component="chat/upload/button" class="btn-ghost-sm px-2" type="button" title="[[global:upload]]" data-bs-toggle="tooltip"><i class="fa fa-fw fa-upload"></i></button>
|
||||
<div class="flex-grow-1 align-self-center">
|
||||
<textarea component="chat/input" placeholder="[[modules:chat.placeholder.mobile]]" class="bg-transparent text-body form-control chat-input mousetrap rounded-0 border-0 shadow-none px-1 py-0" style="min-height: 1.5rem;height:0;max-height:30vh;resize:none;"></textarea>
|
||||
</div>
|
||||
<div class="d-flex gap-1">
|
||||
{{{ each composerActions }}}
|
||||
<button data-action="{./action}" class="btn-ghost-sm px-2 {./class}" type="button" title="{./title}" data-bs-toggle="tooltip"><i class="fa {./icon}"></i></button>
|
||||
{{{ end }}}
|
||||
<button class="btn-ghost-sm px-2" type="button" data-action="send" title="[[modules:chat.send]]" data-bs-toggle="tooltip"><i class="fa fa-fw fa-paper-plane text-primary"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between align-items-center text-xs w-100 px-2 mt-1">
|
||||
<div component="chat/composer/typing" class="">
|
||||
<div component="chat/composer/typing/users" class="hidden"></div>
|
||||
<div component="chat/composer/typing/text" class="hidden"></div>
|
||||
</div>
|
||||
<div component="chat/message/remaining" class="text-xs text-muted">{maximumChatMessageLength}</div>
|
||||
</div>
|
||||
<form class="hidden" component="chat/upload" method="post" enctype="multipart/form-data">
|
||||
<input type="file" name="files[]" multiple class="hidden"/>
|
||||
</form>
|
||||
</div>
|
||||
10
src/views/partials/chats/dropdown.tpl
Normal file
10
src/views/partials/chats/dropdown.tpl
Normal file
@@ -0,0 +1,10 @@
|
||||
{{{ if !rooms.length }}}
|
||||
<li class="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">[[modules:chat.no-active]]</div>
|
||||
</li>
|
||||
{{{ end }}}
|
||||
|
||||
{{{ each rooms }}}
|
||||
<!-- IMPORT partials/chats/recent_room.tpl -->
|
||||
{{{ end }}}
|
||||
43
src/views/partials/chats/message-window.tpl
Normal file
43
src/views/partials/chats/message-window.tpl
Normal file
@@ -0,0 +1,43 @@
|
||||
<div component="chat/message/window" class="d-flex flex-column h-100">
|
||||
{{{ if widgets.header.length }}}
|
||||
<div data-widget-area="header">
|
||||
{{{each widgets.header}}}
|
||||
{{widgets.header.html}}
|
||||
{{{end}}}
|
||||
</div>
|
||||
{{{ end }}}
|
||||
{{{ if !roomId }}}
|
||||
<div class="d-flex flex-column align-items-center gap-3 my-auto">
|
||||
<i class="fa-solid fa-wind fs-2 text-muted"></i>
|
||||
<span class="text-muted text-sm">[[modules:chat.no-active]]</span>
|
||||
</div>
|
||||
{{{ else }}}
|
||||
<div component="chat/header" class="d-flex align-items-center px-md-3 gap-3">
|
||||
<a href="#" data-action="close" role="button" class="flex-shrink-0 d-flex d-md-none btn btn-ghost border align-text-top"><i class="fa fa-chevron-left"></i></a>
|
||||
<h5 component="chat/header/title" class="members flex-grow-1 fw-semibold tracking-tight mb-0 text-truncate text-nowrap">
|
||||
{{{ if ./roomName }}}<i class="fa {icon} text-muted"></i> {roomName}{{{ else }}}{./chatWithMessage}{{{ end}}}
|
||||
</h5>
|
||||
|
||||
<!-- IMPORT partials/chats/options.tpl -->
|
||||
</div>
|
||||
<!-- IMPORT partials/chats/scroll-up-alert.tpl -->
|
||||
<hr class="my-1"/>
|
||||
<div class="d-flex flex-grow-1 gap-1 overflow-auto" style="min-width: 0px;">
|
||||
<div component="chat/messages" class="expanded-chat d-flex flex-column flex-grow-1" data-roomid="{roomId}" style="min-width: 0px;">
|
||||
<ul component="chat/message/content" class="chat-content p-0 m-0 list-unstyled overflow-auto flex-grow-1">
|
||||
<!-- IMPORT partials/chats/messages.tpl -->
|
||||
</ul>
|
||||
<ul component="chat/message/search/results" class="chat-content p-0 m-0 list-unstyled overflow-auto flex-grow-1 hidden">
|
||||
<div component="chat/message/search/no-results" class="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">[[search:no-matches]]</div>
|
||||
</div>
|
||||
</ul>
|
||||
<!-- IMPORT partials/chats/composer.tpl -->
|
||||
</div>
|
||||
|
||||
<!-- IMPORT partials/chats/user-list.tpl -->
|
||||
<!-- IMPORT partials/chats/pinned-messages.tpl -->
|
||||
</div>
|
||||
{{{ end }}}
|
||||
</div>
|
||||
73
src/views/partials/chats/message.tpl
Normal file
73
src/views/partials/chats/message.tpl
Normal file
@@ -0,0 +1,73 @@
|
||||
<li component="chat/message" class="chat-message mx-2 pe-2 {{{ if messages.deleted }}} deleted{{{ end }}} {{{ if messages.pinned}}} pinned{{{ end }}} {{{ if messages.newSet }}}border-top pt-3{{{ end }}}" data-mid="{messages.messageId}" data-uid="{messages.fromuid}" data-index="{messages.index}" data-self="{messages.self}" data-break="{messages.newSet}" data-timestamp="{messages.timestamp}" data-username="{messages.fromUser.username}">
|
||||
|
||||
{{{ if messages.parent }}}
|
||||
<!-- IMPORT partials/chats/parent.tpl -->
|
||||
{{{ end }}}
|
||||
|
||||
<div class="message-header lh-1 d-flex align-items-center gap-2 text-sm {{{ if !messages.newSet }}}hidden{{{ end }}} pb-2">
|
||||
<a href="{config.relative_path}/user/{messages.fromUser.userslug}" class="text-decoration-none">{buildAvatar(messages.fromUser, "18px", true, "not-responsive")}</a>
|
||||
<span class="chat-user fw-semibold"><a href="{config.relative_path}/user/{messages.fromUser.userslug}">{messages.fromUser.displayname}</a></span>
|
||||
{{{ if messages.fromUser.banned }}}
|
||||
<span class="badge bg-danger">[[user:banned]]</span>
|
||||
{{{ end }}}
|
||||
{{{ if messages.fromUser.deleted }}}
|
||||
<span class="badge bg-danger">[[user:deleted]]</span>
|
||||
{{{ end }}}
|
||||
<span class="chat-timestamp text-muted timeago" title="{messages.timestampISO}"></span>
|
||||
|
||||
<div component="chat/message/edited" class="text-muted ms-auto {{{ if !messages.edited }}}hidden{{{ end }}}" title="[[global:edited-timestamp, {isoTimeToLocaleString(messages.editedISO, config.userLang)}]]"><i class="fa fa-edit"></i></span></div>
|
||||
</div>
|
||||
<div class="message-body-wrapper">
|
||||
<div component="chat/message/body" class="message-body ps-0 py-0 overflow-auto text-break">
|
||||
{messages.content}
|
||||
</div>
|
||||
<!-- IMPORT partials/chats/reactions.tpl -->
|
||||
<div component="chat/message/controls" class="position-relative">
|
||||
<div class="btn-group border shadow-sm controls position-absolute bg-body end-0" style="bottom:1rem;">
|
||||
<!-- IMPORT partials/chats/add-reaction.tpl -->
|
||||
<button class="btn btn-sm btn-link" data-action="reply" title="[[topic:reply]]"><i class="fa fa-reply"></i></button>
|
||||
|
||||
<div class="btn-group d-inline-block">
|
||||
<button class="btn btn-sm btn-link dropdown-toggle" data-bs-toggle="dropdown"><i class="fa fa-ellipsis" type="button"></i></button>
|
||||
<ul class="dropdown-menu dropdown-menu-end p-1 text-sm list-unstyled">
|
||||
{{{ if (isAdminOrGlobalMod || (!config.disableChatMessageEditing && messages.self)) }}}
|
||||
<li>
|
||||
<a href="#" class="dropdown-item rounded-1" data-action="edit"><span class="d-inline-flex align-items-center gap-2"><i class="fa fa-fw fa-pencil text-muted"></i> [[topic:edit]]</span></a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="dropdown-item rounded-1" data-action="delete"><span class="d-inline-flex align-items-center gap-2"><i class="fa fa-fw fa-trash text-muted"></i> [[topic:delete]]</span></a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="dropdown-item rounded-1" data-action="restore"><span class="d-inline-flex align-items-center gap-2"><i class="fa fa-fw fa-repeat text-muted"></i> [[topic:restore]]</span></a>
|
||||
</li>
|
||||
{{{ end }}}
|
||||
|
||||
{{{ if (isAdminOrGlobalMod || isOwner )}}}
|
||||
<li>
|
||||
<a href="#" class="dropdown-item rounded-1" data-action="pin"><span class="d-inline-flex align-items-center gap-2"><i class="fa fa-fw fa-thumbtack text-muted"></i> [[modules:chat.pin-message]]</span></a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="dropdown-item rounded-1" data-action="unpin"><span class="d-inline-flex align-items-center gap-2"><i class="fa fa-fw fa-thumbtack fa-rotate-90 text-muted"></i> [[modules:chat.unpin-message]]</span></a>
|
||||
</li>
|
||||
<li class="dropdown-divider"></li>
|
||||
{{{ end }}}
|
||||
|
||||
{{{ if isAdminOrGlobalMod }}}
|
||||
<li>
|
||||
<a href="#" class="dropdown-item rounded-1 chat-ip-button"><span class="d-inline-flex align-items-center gap-2"><i class="fa fa-fw fa-info-circle text-muted"></i> [[modules:chat.show-ip]]</span></a>
|
||||
</li>
|
||||
{{{ end }}}
|
||||
|
||||
<li>
|
||||
<a href="#" class="dropdown-item rounded-1" data-action="copy-text" data-mid="{messages.mid}"><span class="d-inline-flex align-items-center gap-2"><i class="fa fa-fw fa-copy text-muted"></i> [[modules:chat.copy-text]]</span></a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="#" class="dropdown-item rounded-1" data-action="copy-link" data-mid="{messages.mid}"><span class="d-inline-flex align-items-center gap-2"><i class="fa fa-fw fa-link text-muted"></i> [[modules:chat.copy-link]]</span></a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
7
src/views/partials/chats/messages.tpl
Normal file
7
src/views/partials/chats/messages.tpl
Normal file
@@ -0,0 +1,7 @@
|
||||
{{{each messages}}}
|
||||
{{{ if !./system }}}
|
||||
<!-- IMPORT partials/chats/message.tpl -->
|
||||
{{{ else }}}
|
||||
<!-- IMPORT partials/chats/system-message.tpl -->
|
||||
{{{ end }}}
|
||||
{{{end}}}
|
||||
100
src/views/partials/chats/options.tpl
Normal file
100
src/views/partials/chats/options.tpl
Normal file
@@ -0,0 +1,100 @@
|
||||
<div class="d-flex gap-1 align-items-stretch">
|
||||
<!-- search -->
|
||||
<button class="btn-ghost-sm" component="chat/room/search/toggle" data-manual-tooltip="1" title="[[global:header.search]]">
|
||||
<i class="fa fa-search text-muted"></i>
|
||||
</button>
|
||||
<div component="chat/room/search/container" class="position-relative hidden align-self-center">
|
||||
<input component="chat/room/search" class="form-control form-control-sm" placeholder="[[search:type-to-search]]" style="width: 150px;">
|
||||
<a component="chat/room/search/clear" href="#" class="hidden px-2 py-1 position-absolute top-50 end-0 translate-middle-y">
|
||||
<i class="fa fa-times text-muted opacity-75"></i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- notification dropdown -->
|
||||
<div class="dropdown d-flex" data-manual-tooltip="1" title="[[modules:chat.notification-settings]]" component="chat/notification/setting">
|
||||
<button class="btn-ghost-sm position-relative" data-bs-toggle="dropdown">
|
||||
<i class="fa fa-bell text-muted"></i>
|
||||
<span class="position-absolute top-0 end-0 text-xs text-muted opacity-50" style="font-size: 10px!important; padding: 1px; line-height: 10px;">
|
||||
<i component="chat/notification/setting/icon" class="fa {notificationOptionsIcon}"></i>
|
||||
</span>
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-end p-1 text-sm">
|
||||
{{{ each notificationOptions }}}
|
||||
<li>
|
||||
<a class="dropdown-item rounded-1 d-flex align-items-center gap-2" href="#" data-value="{./value}" data-icon="{./icon}">
|
||||
<div class="d-flex flex-column gap-1">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<div class="flex-grow-1">{./label}</div>
|
||||
<i class="flex-shrink-0 fa fa-fw fa-check {{{ if !./selected }}}hidden{{{ end }}}"></i>
|
||||
</div>
|
||||
{{{ if @first}}}
|
||||
<div component="chat/notification/setting/sub-label" class="text-sm text-muted">{./subLabel}</div>
|
||||
{{{ end }}}
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
{{{ if @first }}}
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
{{{ end }}}
|
||||
{{{ end }}}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- pinned messages -->
|
||||
<button component="chat/pinned/messages/btn" class="btn-ghost-sm d-none d-lg-flex flex-nowrap" title="[[modules:chat.pinned-messages]]" data-bs-toggle="tooltip" data-bs-placement="bottom">
|
||||
<i class="fa fa-thumb-tack text-muted"></i>
|
||||
</button>
|
||||
|
||||
<!-- manage/options dropdown -->
|
||||
<div class="dropdown d-flex" data-manual-tooltip="1" title="[[modules:chat.options]]">
|
||||
<button class="btn-ghost-sm" data-bs-toggle="dropdown" component="chat/controlsToggle">
|
||||
<i class="fa fa-gear text-muted"></i>
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-end p-1 text-sm" component="chat/controls">
|
||||
<li>
|
||||
<a class="dropdown-item rounded-1 d-flex align-items-center gap-2" href="#" data-action="manage">
|
||||
<i class="fa fa-fw text-muted fa-cog"></i> [[modules:chat.manage-room]]
|
||||
</a>
|
||||
</li>
|
||||
{{{ if isOwner }}}
|
||||
<li>
|
||||
<a class="dropdown-item rounded-1 d-flex align-items-center gap-2" href="#" data-action="rename">
|
||||
<i class="fa fa-fw text-muted fa-edit"></i> [[modules:chat.rename-room]]
|
||||
</a>
|
||||
</li>
|
||||
{{{ end }}}
|
||||
<li>
|
||||
<a class="dropdown-item rounded-1 d-flex align-items-center gap-2" href="#" data-action="leave">
|
||||
<i class="fa fa-fw text-muted fa-sign-out"></i> [[modules:chat.leave-room]]
|
||||
</a>
|
||||
</li>
|
||||
{{{ if (public && isAdmin) }}}
|
||||
<li>
|
||||
<a class="dropdown-item rounded-1 d-flex align-items-center gap-2" href="#" data-action="delete">
|
||||
<i class="fa fa-fw text-danger fa-trash"></i> [[modules:chat.delete-room]]
|
||||
</a>
|
||||
</li>
|
||||
{{{ end }}}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- users toggle -->
|
||||
{{{ if users.length }}}
|
||||
<div component="chat/user/list/btn" class="btn-ghost-sm d-none d-lg-flex flex-nowrap gap-3" title="[[modules:chat.view-users-list]]" data-bs-toggle="tooltip" data-bs-placement="bottom">
|
||||
<div class="d-flex text-nowrap">
|
||||
{{{ if ./users.0 }}}
|
||||
<span style="width: 18px; z-index: 3;" class="text-decoration-none" href="{config.relative_path}/user/{./users.0.userslug}">{buildAvatar(./users.0, "24px", true)}</span>
|
||||
{{{ end }}}
|
||||
{{{ if ./users.1 }}}
|
||||
<span style="width: 18px; z-index: 2;" class="text-decoration-none" href="{config.relative_path}/user/{./users.1.userslug}">{buildAvatar(./users.1, "24px", true)}</span>
|
||||
{{{ end }}}
|
||||
{{{ if ./users.2 }}}
|
||||
<span style="width: 18px; z-index: 1;" class="text-decoration-none" href="{config.relative_path}/user/{./users.2.userslug}">{buildAvatar(./users.2, "24px", true)}</span>
|
||||
{{{ end }}}
|
||||
</div>
|
||||
{./userCount}
|
||||
</div>
|
||||
{{{ end }}}
|
||||
</div>
|
||||
13
src/views/partials/chats/parent.tpl
Normal file
13
src/views/partials/chats/parent.tpl
Normal file
@@ -0,0 +1,13 @@
|
||||
<div class="d-flex ms-4 mb-2 align-items-center">
|
||||
<div component="chat/message/parent" data-parent-mid="{messages.parent.mid}" data-uid="{messages.parent.fromuid}" class="btn-ghost-sm ff-secondary align-items-start flex-row w-100">
|
||||
<div class="d-flex gap-2 text-sm text-nowrap">
|
||||
<div><i class="fa fa-sm fa-reply opacity-50"></i></div>
|
||||
<div class="d-flex flex-nowrap gap-1 align-items-center">
|
||||
<a href="{config.relative_path}/user/{messages.parent.user.userslug}" class="text-decoration-none lh-1">{buildAvatar(messages.parent.user, "14px", true, "not-responsive align-middle")}</a>
|
||||
<a class="chat-user fw-semibold" href="{config.relative_path}/user/{messages.parent.user.userslug}">{messages.parent.user.displayname}</a>
|
||||
</div>
|
||||
<span class="chat-timestamp text-muted timeago text-nowrap hidden" title="{messages.parent.timestampISO}"></span>
|
||||
</div>
|
||||
<div component="chat/message/parent/content" class="text-muted line-clamp-1 w-100">{messages.parent.content}</div>
|
||||
</div>
|
||||
</div>
|
||||
32
src/views/partials/chats/pinned-messages-list.tpl
Normal file
32
src/views/partials/chats/pinned-messages-list.tpl
Normal file
@@ -0,0 +1,32 @@
|
||||
{{{ each messages }}}
|
||||
<li component="chat/message" class="chat-message mx-2 pe-2 {{{ if messages.deleted }}} deleted{{{ end }}} {{{ if messages.pinned}}} pinned{{{ end }}} border-top pt-3" data-mid="{messages.messageId}" data-uid="{messages.fromuid}" data-self="{messages.self}" data-timestamp="{messages.timestamp}" data-username="{messages.fromUser.username}" data-index="{./index}">
|
||||
|
||||
{{{ if messages.parent }}}
|
||||
<!-- IMPORT partials/chats/parent.tpl -->
|
||||
{{{ end }}}
|
||||
|
||||
<div class="message-header lh-1 d-flex align-items-center gap-2 text-sm pb-2">
|
||||
<a href="{config.relative_path}/user/{messages.fromUser.userslug}" class="text-decoration-none">{buildAvatar(messages.fromUser, "18px", true, "not-responsive")}</a>
|
||||
<span class="chat-user fw-semibold"><a href="{config.relative_path}/user/{messages.fromUser.userslug}">{messages.fromUser.displayname}</a></span>
|
||||
|
||||
<span class="chat-timestamp text-muted timeago" title="{messages.timestampISO}"></span>
|
||||
<div component="chat/message/edited" class="text-muted ms-auto {{{ if !messages.edited }}}hidden{{{ end }}}" title="[[global:edited-timestamp, {isoTimeToLocaleString(messages.editedISO, config.userLang)}]]"><i class="fa fa-edit"></i></span></div>
|
||||
</div>
|
||||
<div class="message-body-wrapper">
|
||||
<div component="chat/message/body" class="message-body ps-0 py-0 overflow-auto text-break">
|
||||
{messages.content}
|
||||
</div>
|
||||
<div component="chat/message/controls" class="position-relative">
|
||||
<div class="btn-group border shadow-sm controls position-absolute bg-body end-0" style="bottom:1rem;">
|
||||
{{{ if (isAdminOrGlobalMod || (!config.disableChatMessageEditing && messages.self)) }}}
|
||||
<button class="btn btn-sm btn-link" data-action="edit" title="[[topic:edit]]"><i class="fa fa-pencil"></i></button>
|
||||
{{{ end }}}
|
||||
{{{ if (isAdminOrGlobalMod || isOwner )}}}
|
||||
<button class="btn btn-sm btn-link" data-action="pin" title="[[modules:chat.pin-message]]"><i class="fa fa-thumbtack"></i></button>
|
||||
<button class="btn btn-sm btn-link" data-action="unpin" title="[[modules:chat.unpin-message]]"><i class="fa fa-thumbtack fa-rotate-90"></i></button>
|
||||
{{{ end }}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
{{{ end }}}
|
||||
12
src/views/partials/chats/pinned-messages.tpl
Normal file
12
src/views/partials/chats/pinned-messages.tpl
Normal file
@@ -0,0 +1,12 @@
|
||||
<div component="chat/messages/pinned/container" class="d-flex flex-column expanded-chat border-start hidden" style="min-width:340px; width: 340px;">
|
||||
<h3 class="fs-6 p-1 mb-0 text-center text-secondary">[[modules:chat.pinned-messages]]</h3>
|
||||
|
||||
<div component="chat/messages/pinned/empty" class="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">[[modules:chat.no-pinned-messages]]</div>
|
||||
</div>
|
||||
|
||||
<ul component="chat/messages/pinned" class="chat-content list-unstyled d-flex flex-column gap-1 p-1 overflow-auto">
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
1
src/views/partials/chats/reactions.tpl
Normal file
1
src/views/partials/chats/reactions.tpl
Normal file
@@ -0,0 +1 @@
|
||||
<!-- This partial intentionally left blank; overwritten by nodebb-plugin-reactions -->
|
||||
55
src/views/partials/chats/recent_room.tpl
Normal file
55
src/views/partials/chats/recent_room.tpl
Normal file
@@ -0,0 +1,55 @@
|
||||
{{{ if (loadingMore && @first)}}}
|
||||
<hr class="my-1" />
|
||||
{{{ end }}}
|
||||
<div component="chat/recent/room" data-roomid="{./roomId}" data-full="1" class="rounded-1 {{{ if ./unread }}}unread{{{ end }}}">
|
||||
<div class="d-flex gap-1 justify-content-between">
|
||||
<div class="chat-room-btn position-relative d-flex flex-grow-1 gap-2 justify-content-start align-items-start btn-ghost-sm ff-sans">
|
||||
<div class="main-avatar">
|
||||
{{{ if ./users.length }}}
|
||||
{{{ if ./groupChat}}}
|
||||
<div class="position-relative stacked-avatars">
|
||||
<span class="text-decoration-none position-absolute" href="{config.relative_path}/user/{./users.1.userslug}">{buildAvatar(./users.1, "24px", true)}</span>
|
||||
<span class="text-decoration-none position-absolute" href="{config.relative_path}/user/{./users.0.userslug}" >{buildAvatar(./users.0, "24px", true)}</span>
|
||||
</div>
|
||||
{{{ else }}}
|
||||
<span href="{config.relative_path}/user/{./users.0.userslug}" class="text-decoration-none">{buildAvatar(./users.0, "32px", true)}</span>
|
||||
{{{ end }}}
|
||||
{{{ else }}}
|
||||
<span class="avatar avatar-rounded text-bg-warning" component="avatar/icon" style="--avatar-size: 32px;">?</span>
|
||||
{{{ end }}}
|
||||
</div>
|
||||
|
||||
<div class="d-flex flex-grow-1 flex-column w-100">
|
||||
<div component="chat/room/title" class="room-name fw-semibold text-xs text-break">
|
||||
{{{ if ./roomName}}}
|
||||
{./roomName}
|
||||
{{{ else }}}
|
||||
{{{ if !./lastUser.uid }}}
|
||||
[[modules:chat.no-users-in-room]]
|
||||
{{{ else }}}
|
||||
{./usernames}
|
||||
{{{ end }}}
|
||||
{{{ end }}}
|
||||
</div>
|
||||
|
||||
{{{ if ./teaser }}}
|
||||
<div class="teaser-content text-sm line-clamp-3 text-break">
|
||||
<span href="#" class="text-decoration-none">{buildAvatar(./teaser.user, "14px", true)}</span>
|
||||
<strong class="text-xs fw-semibold teaser-username">{./teaser.user.username}:</strong>
|
||||
{./teaser.content}
|
||||
</div>
|
||||
<div class="teaser-timestamp text-muted text-xs">{{{ if ./teaser.timeagoLong }}}{./teaser.timeagoLong}{{{ else }}}<span class="timeago" title="{./teaser.timestampISO}"></span>{{{ end }}}</div>
|
||||
{{{ end }}}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<button class="mark-read btn-ghost-sm" style="width: 1.5rem; height: 1.5rem;">
|
||||
<i class="unread fa fa-2xs fa-circle text-primary {{{ if !./unread }}}hidden{{{ end }}}"></i>
|
||||
<i class="read fa fa-2xs fa-circle-o text-secondary {{{ if ./unread }}}hidden{{{ end }}}"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{{ if !@last }}}
|
||||
<hr class="my-1" />
|
||||
{{{ end }}}
|
||||
3
src/views/partials/chats/scroll-up-alert.tpl
Normal file
3
src/views/partials/chats/scroll-up-alert.tpl
Normal file
@@ -0,0 +1,3 @@
|
||||
<div class="position-relative">
|
||||
<div component="chat/messages/scroll-up-alert" class="py-1 mt-1 position-absolute start-50 top-50 translate-middle text-sm scroll-up-alert alert alert-info d-none d-md-block text-nowrap hidden" role="button" style="z-index: 500;"><i class="fa fa-fw fa-arrow-down"></i> [[modules:chat.scroll-up-alert]]</div>
|
||||
</div>
|
||||
7
src/views/partials/chats/system-message.tpl
Normal file
7
src/views/partials/chats/system-message.tpl
Normal file
@@ -0,0 +1,7 @@
|
||||
<li component="chat/system-message" class="system-message text-muted small py-2 gap-3 d-flex align-items-center justify-content-center" data-mid="{messages.messageId}" data-uid="{messages.fromuid}" data-index="{messages.index}" data-self="{messages.self}" data-break="0" data-timestamp="{messages.timestamp}">
|
||||
<hr class="d-inline-block my-1" style="width: 10%;"/>
|
||||
<div>
|
||||
[[modules:chat.system.{messages.content}, {messages.fromUser.username}, {messages.timestampISO}]]
|
||||
</div>
|
||||
<hr class="d-inline-block my-1" style="width: 10%;"/>
|
||||
</li>
|
||||
11
src/views/partials/chats/user-list.tpl
Normal file
11
src/views/partials/chats/user-list.tpl
Normal file
@@ -0,0 +1,11 @@
|
||||
<div component="chat/user/list" class="border-start hidden d-flex flex-column gap-1 p-1 overflow-auto" style="min-width:240px; width: 240px;">
|
||||
{{{ each users }}}
|
||||
<a data-index="{./index}" data-uid="{./uid}" class="btn-ghost-sm ff-secondary d-flex justify-content-start align-items-center gap-2 {{{ if ./online }}}online{{{ end}}}" href="{config.relative_path}/uid/{./uid}">
|
||||
<div>{buildAvatar(users, "24px", true)}</div>
|
||||
<div class="d-flex gap-1 flex-grow-1 text-nowrap text-truncate">
|
||||
<span component="chat/user/list/username" class="text-truncate">{./username}</span>
|
||||
{{{ if ./isOwner }}}<span><i class="fa fa-star text-warning" data-bs-toggle="tooltip" title="[[modules:chat.owner]]"></i></span>{{{ end }}}
|
||||
</div>
|
||||
</a>
|
||||
{{{ end }}}
|
||||
</div>
|
||||
1
src/views/partials/chats/user.tpl
Normal file
1
src/views/partials/chats/user.tpl
Normal file
@@ -0,0 +1 @@
|
||||
<a href="{config.relative_path}/user/{rooms.users.userslug}" class="text-decoration-none">{buildAvatar(rooms.users, "32px", true)}</a>
|
||||
Reference in New Issue
Block a user