diff --git a/backend/static/css/components/chat-detail.css b/backend/static/css/components/chat-detail.css index 7a171241..6db575d4 100644 --- a/backend/static/css/components/chat-detail.css +++ b/backend/static/css/components/chat-detail.css @@ -1,221 +1,448 @@ /** * chat-detail.css - * Стили страницы детального чата: layout, scroll, bubbles, composer + * Чистая архитектура чата в Telegram/WhatsApp стиле */ -/* ───────────────────────────────────────────────────────────── - PAGE LAYOUT -────────────────────────────────────────────────────────────── */ -/* прибиваем низ страницы и добавляем воздух снизу */ -main.main { +/* ═══════════════════════════════════════════════════════════ + БАЗОВАЯ СТРУКТУРА +═══════════════════════════════════════════════════════════ */ + +/* КРИТИЧНО: Убираем все внешние скроллы */ +body:has(.chat-page) { + overflow: hidden !important; padding-bottom: 0 !important; } -/* ===== Контейнер страницы чата ===== */ +body:has(.chat-page) main { + overflow: hidden !important; + padding: 0 !important; + margin: 0 !important; +} + +/* Скрываем bottom navbar на странице чата */ +body:has(.chat-page) .bottom-navbar { + display: none; +} + +/* Страница чата на всю высоту экрана */ +.chat-page { + display: flex; + flex-direction: column; + height: calc(100vh - var(--navbar-h)); + width: 100%; + max-width: 100%; + overflow: hidden; + background: var(--bs-body-bg); + position: relative; +} + @supports(height: 100svh) { - .chat-root { + .chat-page { height: calc(100svh - var(--navbar-h)); } } -.chat-root > .row { - height: 100%; - min-height: 0; -} - -.chat-col { - display: flex; - flex-direction: column; - height: 93%; - min-height: 0; -} - -.section-header { - flex: 0 0 auto; -} - -/* ───────────────────────────────────────────────────────────── - CHAT BOX -────────────────────────────────────────────────────────────── */ -.card.chat-box { - position: relative; - flex: 1 1 auto; - min-height: 0; - display: grid; - grid-template-rows: 1fr auto; /* лента + композер */ - overflow: hidden; - margin-bottom: 0; - background: var(--bs-body-bg); -} - -/* ───────────────────────────────────────────────────────────── - CHAT SCROLL (с кастомным скроллбаром) -────────────────────────────────────────────────────────────── */ -.chat-scroll { - overflow-y: auto; - padding: var(--space-2); - min-height: 0; - scroll-behavior: smooth; - -webkit-overflow-scrolling: touch; - scrollbar-width: thin; - scrollbar-gutter: stable both-edges; - overscroll-behavior: contain; - scrollbar-color: color-mix(in srgb, var(--bs-body-color), transparent 70%) transparent; -} - -.chat-scroll:hover { - scrollbar-color: color-mix(in srgb, var(--bs-body-color), transparent 55%) transparent; -} - -.chat-scroll::-webkit-scrollbar { - width: 10px; - height: 10px; -} - -.chat-scroll::-webkit-scrollbar-track { - background: transparent; - margin-block: 0; -} - -.chat-scroll::-webkit-scrollbar-thumb { - background-color: color-mix(in srgb, var(--bs-body-color), transparent 75%); - border-radius: 999px; - border: 3px solid transparent; - background-clip: padding-box; -} - -.chat-scroll:hover::-webkit-scrollbar-thumb { - background-color: color-mix(in srgb, var(--bs-body-color), transparent 55%); -} - -[data-bs-theme="dark"] .chat-scroll { - scrollbar-color: color-mix(in srgb, var(--bs-body-color), transparent 78%) transparent; -} - -[data-bs-theme="dark"] .chat-scroll:hover { - scrollbar-color: color-mix(in srgb, var(--bs-body-color), transparent 60%) transparent; -} - -[data-bs-theme="dark"] .chat-scroll::-webkit-scrollbar-thumb { - background-color: color-mix(in srgb, var(--bs-body-color), transparent 78%); -} - -[data-bs-theme="dark"] .chat-scroll:hover::-webkit-scrollbar-thumb { - background-color: color-mix(in srgb, var(--bs-body-color), transparent 60%); -} - -@media (hover: none), (pointer: coarse) { - .chat-scroll { - scrollbar-width: none; +/* На десктопе добавляем container для контента */ +@media (min-width: 768px) { + .chat-page { + padding: 0 var(--space-3); } + + .chat-header, + .chat-container { + max-width: 1200px; + margin: 0 auto; + width: 100%; + } + + .chat-container { + border-radius: var(--bs-border-radius-lg); + border: 1px solid var(--bs-border-color); + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); + } +} - .chat-scroll::-webkit-scrollbar { +/* ═══════════════════════════════════════════════════════════ + ШАПКА ЧАТА +═══════════════════════════════════════════════════════════ */ + +.chat-header { + flex-shrink: 0; + background: var(--bs-body-bg); + padding: var(--space-2) var(--space-3); + z-index: 10; +} + +@media (min-width: 768px) { + .chat-header { + padding: var(--space-3) var(--space-4); + } +} + +@media (max-width: 767.98px) { + .chat-header { + padding: var(--space-1) var(--space-2); + border-bottom: 1px solid var(--bs-border-color); + } +} + +.chat-header-content { + display: flex; + align-items: center; + justify-content: space-between; + gap: var(--space-2); +} + +.chat-header-title { + display: flex; + align-items: center; + gap: var(--space-2); + flex: 1; + min-width: 0; +} + +.chat-header-title h1 { + font-size: 1.25rem; + font-weight: 700; + margin: 0; + color: var(--bs-body-color); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.chat-header-title i { + font-size: 1.5rem; + flex-shrink: 0; +} + +.chat-header-actions { + display: flex; + align-items: center; + gap: var(--space-1); + flex-shrink: 0; +} + +/* Аватар чата с бейджем */ +.chat-avatar-wrapper { + position: relative; + width: 40px; + height: 40px; + flex-shrink: 0; +} + +.chat-avatar { + width: 100%; + height: 100%; + border-radius: 10px; + object-fit: cover; + display: block; + border: 1px solid var(--bs-border-color); +} + +.chat-type-badge { + position: absolute; + bottom: -2px; + right: -2px; + width: 18px; + height: 18px; + border-radius: 50%; + background: var(--bs-primary); + color: white; + display: flex; + align-items: center; + justify-content: center; + font-size: 9px; + border: 2px solid var(--bs-body-bg); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); +} + +@media (max-width: 767.98px) { + .chat-header { + padding: var(--space-1) var(--space-2); + } + + .chat-header-title h1 { + font-size: 1.1rem; + } + + .chat-header-title i { + font-size: 1.25rem; + } + + .chat-header-actions .btn-sm span { display: none; } } -/* ───────────────────────────────────────────────────────────── - COMPOSER (Modern Messenger Style) -────────────────────────────────────────────────────────────── */ -.chat-composer { - position: sticky; - bottom: 0; - border-top: none; - box-shadow: inset 0 1px 0 var(--bs-border-color); - border-bottom-left-radius: inherit; - border-bottom-right-radius: inherit; - background-clip: padding-box; - background: color-mix(in srgb, var(--bs-body-bg) 94%, transparent); - padding: var(--space-1) var(--space-2) calc(var(--space-1) + env(safe-area-inset-bottom)) var(--space-2); - backdrop-filter: blur(8px); - z-index: 1; -} +/* ═══════════════════════════════════════════════════════════ + КОНТЕЙНЕР ЧАТА +═══════════════════════════════════════════════════════════ */ -.composer-wrap { +.chat-container { + flex: 1; display: flex; flex-direction: column; + min-height: 0; + overflow: hidden; + position: relative; +} + +/* ═══════════════════════════════════════════════════════════ + ОБЛАСТЬ СООБЩЕНИЙ +═══════════════════════════════════════════════════════════ */ + +.chat-messages-scroll { + flex: 1; + overflow-y: auto; + overflow-x: hidden; + padding: var(--space-3); + scroll-behavior: smooth; + -webkit-overflow-scrolling: touch; + scrollbar-width: thin; + scrollbar-color: color-mix(in srgb, var(--bs-body-color), transparent 70%) transparent; +} + +.chat-messages-scroll:hover { + scrollbar-color: color-mix(in srgb, var(--bs-body-color), transparent 55%) transparent; +} + +.chat-messages-scroll::-webkit-scrollbar { + width: 8px; +} + +.chat-messages-scroll::-webkit-scrollbar-track { + background: transparent; +} + +.chat-messages-scroll::-webkit-scrollbar-thumb { + background-color: color-mix(in srgb, var(--bs-body-color), transparent 75%); + border-radius: 4px; +} + +.chat-messages-scroll:hover::-webkit-scrollbar-thumb { + background-color: color-mix(in srgb, var(--bs-body-color), transparent 55%); +} + +@media (hover: none), (pointer: coarse) { + .chat-messages-scroll { + scrollbar-width: none; + } + .chat-messages-scroll::-webkit-scrollbar { + display: none; + } +} + +@media (max-width: 767.98px) { + .chat-messages-scroll { + padding: var(--space-2); + } +} + +/* ═══════════════════════════════════════════════════════════ + ИНДИКАТОР ПЕЧАТИ +═══════════════════════════════════════════════════════════ */ + +.chat-typing-indicator { + flex-shrink: 0; + padding: var(--space-1) var(--space-3); + font-size: 0.875rem; + color: var(--bs-secondary); + min-height: 32px; + display: flex; + align-items: center; gap: var(--space-1); } -.composer-input-group { +.chat-typing-indicator.d-none { + display: none !important; +} + +/* ═══════════════════════════════════════════════════════════ + КОМПОЗЕР (ФОРМА ВВОДА) +═══════════════════════════════════════════════════════════ */ + +.chat-composer-wrapper { + flex-shrink: 0; + padding: var(--space-3) var(--space-3); + padding-bottom: calc(var(--space-4) + env(safe-area-inset-bottom, 0px)); + background: var(--bs-body-bg); + border-top: 1px solid var(--bs-border-color); +} + +@media (min-width: 768px) { + .chat-composer-wrapper { + padding: var(--space-3) var(--space-4); + padding-bottom: var(--space-3); + border-radius: 0 0 var(--bs-border-radius-lg) var(--bs-border-radius-lg); + } +} + +@media (max-width: 767.98px) { + .chat-composer-wrapper { + padding: var(--space-2) var(--space-2); + padding-bottom: calc(var(--space-5) + env(safe-area-inset-bottom, 12px)); + } +} + +.message-field { display: flex; align-items: flex-end; gap: var(--space-1); - flex-wrap: nowrap; + width: 100%; } -.composer-textarea-wrapper { - flex: 1 1 auto; - position: relative; - min-width: 0; -} - -.composer-textarea { - resize: none; - max-height: 120px; - border-radius: 20px; - padding: 10px 16px; - line-height: 1.4; - border: 1px solid var(--bs-border-color); - background: var(--bs-body-bg); - transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; - overflow-y: hidden; /* Убираем полоску прокрутки */ - scrollbar-width: none; /* Firefox */ -} - -.composer-textarea::-webkit-scrollbar { - display: none; /* Chrome, Safari, Edge */ -} - -.composer-textarea:focus { - border-color: var(--bs-primary); - box-shadow: 0 0 0 0.2rem rgba(var(--bs-primary-rgb), 0.15); -} - -/* Стиль при перетаскивании файлов */ -.composer-textarea.drag-over { - border-color: var(--bs-success); - background: rgba(var(--bs-success-rgb), 0.05); - box-shadow: 0 0 0 0.2rem rgba(var(--bs-success-rgb), 0.15); -} - - -.btn-attachment { - padding: 8px 12px; - border-radius: 50%; - width: 40px; - height: 40px; +.composer-input-group { + flex: 1; display: flex; - align-items: center; - justify-content: center; - transition: all 0.2s ease; + align-items: flex-end; + gap: var(--space-1); + background: var(--bs-tertiary-bg); + border-radius: var(--bs-border-radius-lg); + padding: var(--space-1); + border: 1px solid color-mix(in srgb, var(--bs-border-color), transparent 50%); + transition: border-color 0.2s; } -.btn-attachment:hover { - background: var(--bs-secondary-bg); - transform: scale(1.05); +.composer-input-group:focus-within { + border-color: var(--bs-primary); +} + +.message-input { + flex: 1; + resize: none; + border: none; + background: transparent; + outline: none; + font-size: 1rem; + line-height: 1.5; + padding: var(--space-1); + max-height: 120px; + overflow-y: auto; + scrollbar-width: thin; + color: var(--bs-body-color); +} + +.message-input::placeholder { + color: var(--bs-secondary-color); +} + +.message-input::-webkit-scrollbar { + width: 4px; +} + +.message-input::-webkit-scrollbar-thumb { + background-color: color-mix(in srgb, var(--bs-body-color), transparent 70%); + border-radius: 2px; +} + +.composer-actions { + display: flex; + gap: 2px; + align-items: center; } .btn-send { - padding: 10px 16px; + background: var(--bs-primary); + color: white; + border: none; border-radius: 50%; - width: 40px; - height: 40px; + width: 36px; + height: 36px; display: flex; align-items: center; justify-content: center; - transition: all 0.2s ease; + transition: all 0.2s; + flex-shrink: 0; + padding: 0; } -.btn-send:hover { +.btn-send:hover:not(:disabled) { + background: color-mix(in srgb, var(--bs-primary), black 10%); transform: scale(1.05); + color: white; } -.btn-send i { - font-size: 1.1rem; +.btn-send:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +.btn-attachment, +.message-icon-btn { + width: 36px; + height: 36px; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.2s; + color: var(--bs-body-color); + background: transparent; + border: none; + padding: 0; +} + +.btn-attachment:hover, +.message-icon-btn:hover { + background: color-mix(in srgb, var(--bs-body-color), transparent 90%); +} + +/* ═══════════════════════════════════════════════════════════ + СООБЩЕНИЯ (BUBBLES) +═══════════════════════════════════════════════════════════ */ + +.bubble { + border-radius: 16px; + padding: var(--space-1) var(--space-2); + word-break: break-word; + white-space: pre-wrap; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.06); + max-width: 75%; +} + +@media (max-width: 767.98px) { + .bubble { + max-width: 85%; + } +} + +.bubble-me { + background: var(--bs-primary); + color: white; +} + +.bubble-other { + background: var(--bs-secondary-bg); + color: var(--bs-body-color); +} + +.mini-ava { + width: 32px; + height: 32px; + border-radius: 50%; + overflow: hidden; + display: flex; + align-items: center; + justify-content: center; + background: var(--bs-secondary-bg); + flex-shrink: 0; +} + +.mini-ava img { + width: 100%; + height: 100%; + object-fit: cover; +} + +/* ═══════════════════════════════════════════════════════════ + ВЛОЖЕНИЯ +═══════════════════════════════════════════════════════════ */ + +.message-attachments { + margin-top: var(--space-1); } -/* Превью вложений */ .attachment-preview { padding: var(--space-1) 0; max-height: 200px; @@ -224,300 +451,34 @@ main.main { .attachment-item { background: var(--bs-secondary-bg); - transition: background-color 0.15s ease; + border-radius: var(--bs-border-radius); + padding: var(--space-1); + transition: background-color 0.15s; } .attachment-item:hover { background: var(--bs-tertiary-bg); } -.attachment-item .btn-ghost { - opacity: 0.6; -} - -.attachment-item .btn-ghost:hover { - opacity: 1; -} - -/* Вложения в сообщениях */ -.message-attachments { - margin-top: 8px; -} - -.message-attachments .attachment-item { - background: transparent !important; - padding: 0; - margin-bottom: 8px; -} - -.message-attachments a { - background: rgba(0, 0, 0, 0.05); - border: none; - transition: all 0.2s ease; - color: inherit; -} - -.bubble-me .message-attachments a { - background: rgba(255, 255, 255, 0.15); - color: inherit; -} - -.message-attachments a:hover { - background: rgba(0, 0, 0, 0.08); -} - -.bubble-me .message-attachments a:hover { - background: rgba(255, 255, 255, 0.25); -} - -.message-attachments video, -.message-attachments img, -.message-attachments audio { - max-width: 100%; - width: auto; - border-radius: 8px; -} - - .chat-media { - display: inline-block; - width: auto !important; - max-width: min(640px, 85vw); - max-height: 520px; - height: auto !important; - border-radius: 12px; - background: rgba(0, 0, 0, 0.08); - content-visibility: auto; -} - -.message-attachments .attachment-item > a, -.message-attachments .attachment-item > video, -.message-attachments .attachment-item > img { - display: inline-flex; max-width: 100%; -} - -.chat-media--video { - background: #000; - object-fit: contain; -} - -.chat-media--image { - object-fit: cover; -} - -.message-pending .chat-media { - min-width: 90px; - min-height: 64px; -} - -.message-pending .bubble { - position: relative; - opacity: 0.8; -} - -.message-pending .message-status { - color: rgba(255, 255, 255, 0.8); -} - -.bubble-other .message-status { - color: rgba(0, 0, 0, 0.5); -} - -.message-attachments .text-secondary { - color: inherit; - opacity: 0.7; -} - -.bubble-me .message-attachments .text-secondary { - color: rgba(255, 255, 255, 0.9) !important; - opacity: 1; -} - -.bubble-me .message-attachments .fw-semibold { - color: rgba(255, 255, 255, 0.95) !important; -} - -.bubble-me .message-attachments i { - color: rgba(255, 255, 255, 0.9) !important; -} - -.history-loader { - display: flex; - align-items: center; - justify-content: center; - gap: 0.5rem; - padding: 0.75rem 0; - color: var(--bs-secondary-color); -} - -.history-loader .spinner-border { - width: 1rem; - height: 1rem; -} - -/* Выпадающее меню вложений */ -.dropdown-menu { - min-width: 200px; + max-height: 400px; border-radius: 12px; - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); - padding: 8px; } -.dropdown-item { - border-radius: 8px; - padding: 10px 12px; - transition: background-color 0.15s ease; -} +/* ═══════════════════════════════════════════════════════════ + РАЗДЕЛИТЕЛИ +═══════════════════════════════════════════════════════════ */ -.dropdown-item:hover { - background: var(--bs-secondary-bg); -} - -.dropdown-item i { - width: 24px; -} - -.composer-tools { - display: flex; - gap: var(--space-1); - align-items: center; -} - -/* ───────────────────────────────────────────────────────────── - SCROLL TO BOTTOM BUTTON -────────────────────────────────────────────────────────────── */ -.scroll-to-bottom { - position: absolute; - right: var(--space-2); - bottom: 4.25rem; - z-index: 2; - display: none; -} - -.scroll-to-bottom.show { - display: block; -} - -/* ───────────────────────────────────────────────────────────── - BUBBLES & AVATARS -────────────────────────────────────────────────────────────── */ -.bubble { - border-radius: 16px; - padding: var(--space-1) var(--space-2); - word-break: break-word; - white-space: pre-wrap; /* Сохраняет переносы строк и пробелы */ - box-shadow: 0 1px 2px rgba(0, 0, 0, .06); -} - -.bubble-me { - background: color-mix(in srgb, var(--bs-primary) 92%, #fff 0%); - color: #fff; -} - -.bubble-other { - background: var(--bs-secondary-bg); - color: var(--bs-body-color); -} - -/* Индикатор пересланного сообщения */ -.forwarded-indicator { - padding: 8px 12px; - border-left: 3px solid #6c757d; - background: rgba(108, 117, 125, 0.08); - border-radius: 6px; - margin-bottom: 8px; - word-break: break-word; - overflow-wrap: break-word; -} - -.forwarded-indicator i { - flex-shrink: 0; - font-size: 1rem; - opacity: 0.7; - margin-top: 2px; -} - -.forwarded-indicator > div { - flex: 1; - min-width: 0; -} - -.forwarded-indicator strong { - font-weight: 600; -} - -/* Стили для своих сообщений (голубые пилюли) */ -.bubble-me .forwarded-indicator { - background: rgba(0, 0, 0, 0.12); - border-left-color: rgba(255, 255, 255, 0.5); - color: rgba(255, 255, 255, 0.95) !important; -} - -.bubble-me .forwarded-indicator strong { - color: #ffffff !important; -} - -.bubble-me .forwarded-indicator i { - color: rgba(255, 255, 255, 0.9) !important; -} - -.bubble-me .forwarded-indicator .small { - color: rgba(255, 255, 255, 0.9) !important; -} - -/* Стили для чужих сообщений */ -.bubble-other .forwarded-indicator { - border-left-color: #0d6efd; - background: rgba(13, 110, 253, 0.08); -} - -[data-bs-theme="dark"] .forwarded-indicator { - background: rgba(255, 255, 255, 0.08); - border-left-color: #adb5bd; -} - -[data-bs-theme="dark"] .bubble-me .forwarded-indicator { - background: rgba(255, 255, 255, 0.12); - border-left-color: rgba(255, 255, 255, 0.6); -} - -[data-bs-theme="dark"] .bubble-other .forwarded-indicator { - background: rgba(13, 110, 253, 0.15); - border-left-color: #4dabf7; -} - -.mini-ava { - width: 32px; - height: 32px; - border-radius: 9999px; - overflow: hidden; - display: grid; - place-items: center; -} - -.mini-ava img { - width: 100%; - height: 100%; - object-fit: cover; - display: block; -} - -/* ───────────────────────────────────────────────────────────── - DIVIDERS (день / непрочитанные) -────────────────────────────────────────────────────────────── */ .day-divider, .unread-divider { display: flex; align-items: center; justify-content: center; gap: var(--space-1); - margin: var(--space-1) 0 var(--space-2); - font-size: .85rem; - line-height: 1; - font-weight: 700; - pointer-events: none; - user-select: none; + margin: var(--space-2) 0; + font-size: 0.85rem; + font-weight: 600; } .day-divider::before, @@ -525,22 +486,86 @@ main.main { .unread-divider::before, .unread-divider::after { content: ""; - flex: 1 1 auto; + flex: 1; height: 1px; - background: color-mix(in srgb, var(--bs-border-color), transparent 30%); -} - -.day-divider > span, -.unread-divider > span { - flex: 0 0 auto; - padding: 0 var(--space-1); - background: transparent; + background: var(--bs-border-color); } .day-divider { - color: var(--bs-secondary-color); + color: var(--bs-secondary); } .unread-divider { color: var(--bs-danger); } + +/* ═══════════════════════════════════════════════════════════ + КНОПКА "ВНИЗ" +═══════════════════════════════════════════════════════════ */ + +.scroll-to-bottom { + position: absolute; + right: var(--space-2); + bottom: calc(80px + var(--space-2)); + z-index: 2; + display: none; + width: 40px; + height: 40px; + border-radius: 50%; + background: var(--bs-primary); + color: white; + border: none; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); + transition: all 0.2s; +} + +@media (max-width: 767.98px) { + .scroll-to-bottom { + bottom: calc(var(--space-5) + 80px + env(safe-area-inset-bottom, 12px)); + } +} + +.scroll-to-bottom.show { + display: flex; + align-items: center; + justify-content: center; +} + +.scroll-to-bottom:hover { + transform: scale(1.1); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2); +} + +/* ═══════════════════════════════════════════════════════════ + ИНДИКАТОР ЗАГРУЗКИ +═══════════════════════════════════════════════════════════ */ + +.history-loader { + display: flex; + align-items: center; + justify-content: center; + gap: var(--space-1); + padding: var(--space-2) 0; + color: var(--bs-secondary); +} + +.history-loader .spinner-border { + width: 1rem; + height: 1rem; +} + +/* ═══════════════════════════════════════════════════════════ + ТЕМНАЯ ТЕМА +═══════════════════════════════════════════════════════════ */ + +[data-bs-theme="dark"] .chat-messages-scroll { + scrollbar-color: color-mix(in srgb, var(--bs-body-color), transparent 78%) transparent; +} + +[data-bs-theme="dark"] .chat-messages-scroll:hover { + scrollbar-color: color-mix(in srgb, var(--bs-body-color), transparent 60%) transparent; +} + +[data-bs-theme="dark"] .chat-type-badge { + border-color: var(--bs-body-bg); +}