Files
Joffrey JAFFEUXandGitHub c2060e3a7e FIX: improves reliability of last visit line in chat (#30948)
This commit does several changes:

- it moves the ownership of the last message info to the channel instead
of storing it on the message, it avoids the need to iterate over every
messages
- makes an optimistic update of the last read message id
- adds a spec to confirm this behavior
2025-01-27 10:38:41 +01:00

139 lines
3.7 KiB
Plaintext

import Component from "@glimmer/component";
import { cached } from "@glimmer/tracking";
import { on } from "@ember/modifier";
import { action } from "@ember/object";
import { modifier } from "ember-modifier";
import concatClass from "discourse/helpers/concat-class";
import { i18n } from "discourse-i18n";
const IS_PINNED_CLASS = "is-pinned";
export default class ChatMessageSeparator extends Component {
track = modifier((element) => {
const intersectionObserver = new IntersectionObserver(
([entry]) => {
if (
entry.isIntersecting &&
entry.intersectionRatio < 1 &&
entry.boundingClientRect.y < entry.intersectionRect.y
) {
entry.target.classList.add(IS_PINNED_CLASS);
} else {
entry.target.classList.remove(IS_PINNED_CLASS);
}
},
{ threshold: [0, 1] }
);
intersectionObserver.observe(element);
return () => {
intersectionObserver?.disconnect();
};
});
@action
onDateClick() {
return this.args.fetchMessagesByDate?.(this.firstMessageOfTheDayAt);
}
@cached
get firstMessageOfTheDayAt() {
const message = this.args.message;
if (!message.previousMessage) {
return this.#startOfDay(message.createdAt);
}
if (
!this.#areDatesOnSameDay(
message.previousMessage.createdAt,
message.createdAt
)
) {
return this.#startOfDay(message.createdAt);
}
}
@cached
get formattedFirstMessageDate() {
if (this.firstMessageOfTheDayAt) {
return this.#calendarDate(this.firstMessageOfTheDayAt);
}
}
get isNewestMessage() {
return this.args.message.id === this.args.message.channel.newestMessage?.id;
}
#areDatesOnSameDay(a, b) {
return (
a.getFullYear() === b.getFullYear() &&
a.getMonth() === b.getMonth() &&
a.getDate() === b.getDate()
);
}
#startOfDay(date) {
return moment(date).startOf("day").format();
}
#calendarDate(date) {
return moment(date).calendar(moment(), {
sameDay: `[${i18n("chat.chat_message_separator.today")}]`,
lastDay: `[${i18n("chat.chat_message_separator.yesterday")}]`,
lastWeek: "LL",
sameElse: "LL",
});
}
<template>
{{#if this.formattedFirstMessageDate}}
<div
class={{concatClass
"chat-message-separator"
"chat-message-separator-date"
(if this.isNewestMessage "with-last-visit")
}}
role="button"
{{on "click" this.onDateClick passive=true}}
data-id={{@message.id}}
>
<div class="chat-message-separator__text-container" {{this.track}}>
<span class="chat-message-separator__text">
{{this.formattedFirstMessageDate}}
{{#if this.isNewestMessage}}
<span class="chat-message-separator__last-visit">
<span
class="chat-message-separator__last-visit-separator"
>-</span>
{{i18n "chat.last_visit"}}
</span>
{{/if}}
</span>
</div>
</div>
<div class="chat-message-separator__line-container">
<div class="chat-message-separator__line"></div>
</div>
{{else if this.isNewestMessage}}
<div
class="chat-message-separator chat-message-separator-new"
data-id={{@message.id}}
>
<div class="chat-message-separator__text-container">
<span class="chat-message-separator__text">
{{i18n "chat.last_visit"}}
</span>
</div>
<div class="chat-message-separator__line-container">
<div class="chat-message-separator__line"></div>
</div>
</div>
{{/if}}
</template>
}