FIX: Make long thread titles readable (#25456)
When reaching the top of a thread, the full thread title will be displayed if it was too long to fit. It works in mobile, drawer mode, and fullscreen. --------- Co-authored-by: Joffrey JAFFEUX <j.jaffeux@gmail.com>
This commit is contained in:
parent
283fe48243
commit
ab326d10d8
|
@ -150,6 +150,7 @@ export default class ChatThread extends Component {
|
|||
this.isScrolling = false;
|
||||
this.resetIdle();
|
||||
this.atBottom = state.atBottom;
|
||||
this.args.setFullTitle?.(state.atTop);
|
||||
|
||||
if (state.atBottom) {
|
||||
this.fetchMoreMessages({ direction: FUTURE });
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { tracked } from "@glimmer/tracking";
|
||||
import { array } from "@ember/helper";
|
||||
import { action } from "@ember/object";
|
||||
import didInsert from "@ember/render-modifiers/modifiers/did-insert";
|
||||
|
@ -15,6 +16,12 @@ export default class ChatDrawerRoutesChannelThread extends Component {
|
|||
@service chatChannelsManager;
|
||||
@service chatHistory;
|
||||
|
||||
@tracked showThreadFullTitle = false;
|
||||
|
||||
get showfullTitle() {
|
||||
return this.chatStateManager.isDrawerExpanded && this.showThreadFullTitle;
|
||||
}
|
||||
|
||||
get backButton() {
|
||||
const link = {
|
||||
models: this.chat.activeChannel?.routeModels,
|
||||
|
@ -65,8 +72,17 @@ export default class ChatDrawerRoutesChannelThread extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
@action
|
||||
setFullTitle(value) {
|
||||
this.showThreadFullTitle = value;
|
||||
}
|
||||
|
||||
<template>
|
||||
<Navbar @onClick={{this.chat.toggleDrawer}} as |navbar|>
|
||||
<Navbar
|
||||
@onClick={{this.chat.toggleDrawer}}
|
||||
@showFullTitle={{this.showfullTitle}}
|
||||
as |navbar|
|
||||
>
|
||||
<navbar.BackButton
|
||||
@title={{this.backButton.title}}
|
||||
@route={{this.backButton.route}}
|
||||
|
@ -92,6 +108,7 @@ export default class ChatDrawerRoutesChannelThread extends Component {
|
|||
<ChatThread
|
||||
@thread={{thread}}
|
||||
@targetMessageId={{@params.messageId}}
|
||||
@setFullTitle={{this.setFullTitle}}
|
||||
/>
|
||||
{{/if}}
|
||||
{{/each}}
|
||||
|
|
|
@ -17,10 +17,7 @@ export default class ChatDrawerRoutesThreads extends Component {
|
|||
<navbar.Title
|
||||
@title={{i18n "chat.my_threads.title"}}
|
||||
@icon="discourse-threads"
|
||||
as |title|
|
||||
>
|
||||
<title.SubTitle @title={{this.chat.activeChannel.title}} />
|
||||
</navbar.Title>
|
||||
/>
|
||||
<navbar.Actions as |action|>
|
||||
<action.ThreadsListButton />
|
||||
<action.ToggleDrawerButton />
|
||||
|
|
|
@ -1,29 +1,51 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { hash } from "@ember/helper";
|
||||
import { on } from "@ember/modifier";
|
||||
import { action } from "@ember/object";
|
||||
import concatClass from "discourse/helpers/concat-class";
|
||||
import noop from "discourse/helpers/noop";
|
||||
import ChatOnResize from "../../../modifiers/chat/on-resize";
|
||||
import Actions from "./actions";
|
||||
import BackButton from "./back-button";
|
||||
import ChannelTitle from "./channel-title";
|
||||
import Title from "./title";
|
||||
|
||||
const ChatNavbar = <template>
|
||||
{{! template-lint-disable no-invalid-interactive }}
|
||||
<div
|
||||
class={{concatClass "c-navbar-container" (if @onClick "-clickable")}}
|
||||
{{on "click" (if @onClick @onClick (noop))}}
|
||||
>
|
||||
<nav class="c-navbar">
|
||||
{{yield
|
||||
(hash
|
||||
BackButton=BackButton
|
||||
ChannelTitle=ChannelTitle
|
||||
Title=Title
|
||||
Actions=Actions
|
||||
)
|
||||
}}
|
||||
</nav>
|
||||
</div>
|
||||
</template>;
|
||||
export default class ChatNavbar extends Component {
|
||||
@action
|
||||
handleResize(entries) {
|
||||
for (let entry of entries) {
|
||||
const height = entry.target.clientHeight;
|
||||
|
||||
export default ChatNavbar;
|
||||
requestAnimationFrame(() => {
|
||||
document.documentElement.style.setProperty(
|
||||
"--chat-header-expanded-offset",
|
||||
`${height}px`
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
<template>
|
||||
{{! template-lint-disable no-invalid-interactive }}
|
||||
<div
|
||||
class={{concatClass
|
||||
"c-navbar-container"
|
||||
(if @onClick "-clickable")
|
||||
(if @showFullTitle "-full-title")
|
||||
}}
|
||||
{{on "click" (if @onClick @onClick (noop))}}
|
||||
{{ChatOnResize this.handleResize}}
|
||||
>
|
||||
<nav class="c-navbar">
|
||||
{{yield
|
||||
(hash
|
||||
BackButton=BackButton
|
||||
ChannelTitle=ChannelTitle
|
||||
Title=Title
|
||||
Actions=Actions
|
||||
)
|
||||
}}
|
||||
</nav>
|
||||
</div>
|
||||
</template>
|
||||
}
|
||||
|
|
|
@ -1,16 +1,22 @@
|
|||
import { hash } from "@ember/helper";
|
||||
import concatClass from "discourse/helpers/concat-class";
|
||||
import icon from "discourse-common/helpers/d-icon";
|
||||
import SubTitle from "./sub-title";
|
||||
|
||||
const ChatNavbarTitle = <template>
|
||||
<div title={{@title}} class="c-navbar__title">
|
||||
<div
|
||||
title={{@title}}
|
||||
class={{concatClass "c-navbar__title" (if @showFullTitle "full-title")}}
|
||||
>
|
||||
{{#if (has-block)}}
|
||||
{{if @icon (icon @icon)}}
|
||||
{{@title}}
|
||||
<span class="c-navbar__title-text">{{if @icon (icon @icon)}}
|
||||
{{@title}}</span>
|
||||
{{yield (hash SubTitle=SubTitle)}}
|
||||
{{else}}
|
||||
{{if @icon (icon @icon)}}
|
||||
{{@title}}
|
||||
<span class="c-navbar__title-text">{{if
|
||||
@icon
|
||||
(icon @icon)
|
||||
}}{{@title}}</span>
|
||||
{{/if}}
|
||||
</div>
|
||||
</template>;
|
||||
|
|
|
@ -1,19 +1,33 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { tracked } from "@glimmer/tracking";
|
||||
import { array } from "@ember/helper";
|
||||
import { action } from "@ember/object";
|
||||
import ThreadHeader from "discourse/plugins/chat/discourse/components/chat/thread/header";
|
||||
import Thread from "discourse/plugins/chat/discourse/components/chat-thread";
|
||||
|
||||
const ChatRoutesChannelThread = <template>
|
||||
<div class="c-routes-channel-thread">
|
||||
{{#each (array @thread) as |thread|}}
|
||||
<ThreadHeader @thread={{thread}} />
|
||||
export default class ChatRoutesChannelThread extends Component {
|
||||
@tracked showfullTitle = false;
|
||||
|
||||
<Thread
|
||||
@thread={{thread}}
|
||||
@targetMessageId={{@targetMessageId}}
|
||||
@includeHeader={{true}}
|
||||
/>
|
||||
{{/each}}
|
||||
</div>
|
||||
</template>;
|
||||
@action
|
||||
setFullTitle(value) {
|
||||
this.showfullTitle = value;
|
||||
}
|
||||
|
||||
export default ChatRoutesChannelThread;
|
||||
<template>
|
||||
<div class="c-routes-channel-thread">
|
||||
{{#each (array @thread) as |thread|}}
|
||||
<ThreadHeader
|
||||
@thread={{thread}}
|
||||
@showFullTitle={{this.showfullTitle}}
|
||||
/>
|
||||
|
||||
<Thread
|
||||
@thread={{thread}}
|
||||
@targetMessageId={{@targetMessageId}}
|
||||
@includeHeader={{true}}
|
||||
@setFullTitle={{this.setFullTitle}}
|
||||
/>
|
||||
{{/each}}
|
||||
</div>
|
||||
</template>
|
||||
}
|
||||
|
|
|
@ -53,7 +53,7 @@ export default class ChatThreadHeader extends Component {
|
|||
}
|
||||
|
||||
<template>
|
||||
<Navbar as |navbar|>
|
||||
<Navbar @showFullTitle={{@showFullTitle}} as |navbar|>
|
||||
{{#if @thread}}
|
||||
<navbar.BackButton
|
||||
@route={{this.backLink.route}}
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
--full-page-border-radius: 12px;
|
||||
--full-page-sidebar-width: 275px;
|
||||
--channel-list-avatar-size: 30px;
|
||||
--chat-header-offset: 46px;
|
||||
--chat-header-offset: 45px;
|
||||
--chat-header-expanded-offset: 0px;
|
||||
}
|
||||
|
||||
// Very specific hack to ensure the contextual menu (copy/paste/...) is
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
overflow: hidden;
|
||||
grid-area: main;
|
||||
min-width: 250px;
|
||||
@include chat-height;
|
||||
@include chat-height(var(--chat-header-offset, 0px));
|
||||
|
||||
.chat-messages-scroll {
|
||||
flex-grow: 1;
|
||||
|
|
|
@ -1,16 +1,15 @@
|
|||
@mixin chat-height($inset: 0px) {
|
||||
// desktop and mobile
|
||||
// 46px is the height of the navbar
|
||||
// -1px is for the bottom border of the chat navbar
|
||||
height: calc(
|
||||
var(--chat-vh, 1vh) * 100 - var(--header-offset, 0px) -
|
||||
var(--composer-height, 0px) - var(--chat-header-offset, 0px)
|
||||
var(--composer-height, 0px) - 1px - $inset
|
||||
);
|
||||
|
||||
// mobile with keyboard opened
|
||||
.keyboard-visible & {
|
||||
height: calc(
|
||||
var(--chat-vh, 1vh) * 100 - var(--header-offset, 0px) -
|
||||
var(--chat-header-offset, 0px)
|
||||
var(--chat-vh, 1vh) * 100 - var(--header-offset, 0px) - 1px - $inset
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -18,7 +17,7 @@
|
|||
.footer-nav-ipad & {
|
||||
height: calc(
|
||||
var(--chat-vh, 1vh) * 100 - var(--header-offset, 0px) -
|
||||
var(--chat-header-offset, 0px) - var(--composer-height, 0px)
|
||||
var(--composer-height, 0px) - 1px - $inset
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,59 +1,94 @@
|
|||
.c-navbar {
|
||||
.c-navbar-container {
|
||||
position: relative;
|
||||
border-bottom: 1px solid var(--primary-low);
|
||||
background: var(--secondary);
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
gap: 0.25rem;
|
||||
z-index: z("composer", "content") - 1;
|
||||
padding: 0 1rem;
|
||||
|
||||
&-container {
|
||||
border-bottom: 1px solid var(--primary-low);
|
||||
background: var(--secondary);
|
||||
height: var(--chat-header-offset);
|
||||
min-height: var(--chat-header-offset);
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
z-index: z("composer", "content") - 1;
|
||||
padding-inline: 1rem;
|
||||
&.-clickable {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&.-clickable {
|
||||
cursor: pointer;
|
||||
&.-full-title {
|
||||
.c-navbar__title {
|
||||
min-height: var(--chat-header-offset);
|
||||
}
|
||||
|
||||
.c-navbar__title,
|
||||
.c-navbar__title-text {
|
||||
height: auto;
|
||||
overflow: visible;
|
||||
white-space: normal;
|
||||
text-overflow: unset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.c-navbar {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
width: 100%;
|
||||
gap: 0.25rem;
|
||||
position: relative;
|
||||
|
||||
.single-select-header {
|
||||
padding: 0.3675rem 0.584rem;
|
||||
}
|
||||
}
|
||||
|
||||
.c-navbar__channel-title {
|
||||
.c-navbar__back-button {
|
||||
height: var(--chat-header-offset);
|
||||
}
|
||||
|
||||
.c-navbar__channel-title {
|
||||
@include ellipsis();
|
||||
font-weight: 700;
|
||||
height: var(--chat-header-offset);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.c-navbar__title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@include ellipsis();
|
||||
height: var(--chat-header-offset);
|
||||
|
||||
&-text {
|
||||
@include ellipsis();
|
||||
font-weight: 700;
|
||||
}
|
||||
max-width: 100%;
|
||||
vertical-align: middle;
|
||||
padding-block: 5px;
|
||||
|
||||
.c-navbar__title {
|
||||
@include ellipsis();
|
||||
font-weight: 700;
|
||||
|
||||
.chat-drawer & {
|
||||
padding-left: 1rem;
|
||||
> .d-icon {
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.c-navbar__back-button ~ .c-navbar__title {
|
||||
padding-left: 0;
|
||||
.chat-drawer & {
|
||||
padding-left: 1rem;
|
||||
}
|
||||
|
||||
.c-navbar__sub-title {
|
||||
line-height: var(--line-height-small);
|
||||
font-size: var(--font-down-1-rem);
|
||||
font-weight: normal;
|
||||
.d-icon {
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
.c-navbar__threads-list-button {
|
||||
gap: 0.25rem;
|
||||
.c-navbar__sub-title {
|
||||
line-height: var(--line-height-small);
|
||||
font-size: var(--font-down-1-rem);
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
&.has-unreads {
|
||||
.d-icon-discourse-threads {
|
||||
color: var(--tertiary-med-or-tertiary);
|
||||
}
|
||||
.c-navbar__threads-list-button {
|
||||
gap: 0.25rem;
|
||||
|
||||
&.has-unreads {
|
||||
.d-icon-discourse-threads {
|
||||
color: var(--tertiary-med-or-tertiary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -63,4 +98,12 @@
|
|||
margin-left: auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
> .btn {
|
||||
height: var(--chat-header-offset);
|
||||
}
|
||||
}
|
||||
|
||||
.c-navbar__back-button ~ .c-navbar__title {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
@include chat-height;
|
||||
@include chat-height(var(--chat-header-expanded-offset, 0px));
|
||||
|
||||
&__body {
|
||||
overflow-y: scroll;
|
||||
|
@ -12,5 +12,6 @@
|
|||
overscroll-behavior: contain;
|
||||
display: flex;
|
||||
flex-direction: column-reverse;
|
||||
transition: padding-top 0.2s ease-in-out;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,9 @@
|
|||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
@include chat-height(env(safe-area-inset-bottom));
|
||||
@include chat-height(
|
||||
calc(var(--chat-header-offset) + env(safe-area-inset-bottom))
|
||||
);
|
||||
|
||||
&__items {
|
||||
overflow-y: scroll;
|
||||
|
|
|
@ -10,6 +10,19 @@
|
|||
&__back-button {
|
||||
align-self: stretch;
|
||||
}
|
||||
&.-full-title {
|
||||
.c-navbar {
|
||||
&__actions,
|
||||
&__back-button {
|
||||
height: calc(var(--chat-header-offset) - 2px) !important;
|
||||
}
|
||||
&__title {
|
||||
padding-block: calc(1rem + 2px);
|
||||
}
|
||||
}
|
||||
left: 0.25rem;
|
||||
width: calc(100% - 0.5rem);
|
||||
}
|
||||
|
||||
.c-navbar__title {
|
||||
.c-routes-direct-messages &,
|
||||
|
|
Loading…
Reference in New Issue