[FIX] mail: discuss jump to present scrolls reliably

Before this commit, when looking at old messages of a message,
clicking on "Jump to present" was frequently not scrolling to
most recent message.

This happens because loading messages is not immediate, and thread
UI has some heuristics to adjust scrolls that were working against
scrolling to present when the RPC to load messages around present
is not immediate.

This commit fixes the issue by handling the actual scroll to present
in the same workflow as all other scroll adjustments. Also the logic
for adjusting scrolls requires immediate scrolling, hence this commit
has to remove smooth scrolling to comply with current scroll
adjustment techniques.

closes odoo/odoo#149915

Signed-off-by: Sébastien Theys (seb) <seb@odoo.com>
This commit is contained in:
Alexandre Kühn
2024-01-18 23:10:28 +00:00
parent 64c995384d
commit 9f24bb72ec
2 changed files with 133 additions and 24 deletions
+3 -6
View File
@@ -116,7 +116,7 @@ export class Thread extends Component {
if (this.props.jumpPresent !== this.lastJumpPresent) {
this.messageHighlight?.clearHighlight();
if (this.props.thread.loadNewer) {
this.jumpToPresent("instant");
this.jumpToPresent();
} else {
if (this.props.order === "desc") {
this.scrollableRef.el.scrollTop = 0;
@@ -368,14 +368,11 @@ export class Thread extends Component {
this.threadService.fetchMoreMessages(this.props.thread);
}
async jumpToPresent(behavior) {
async jumpToPresent() {
this.messageHighlight?.clearHighlight();
await this.threadService.loadAround(this.props.thread);
this.props.thread.loadNewer = false;
this.present.el?.scrollIntoView({
behavior: behavior ?? (this.props.order === "asc" ? "smooth" : "instant"), // FIXME somehow smooth not working in desc mode
block: "center",
});
this.props.thread.scrollTop = this.props.order === "desc" ? 0 : "bottom";
this.state.showJumpPresent = false;
}
@@ -5,18 +5,12 @@ import { startServer } from "@bus/../tests/helpers/mock_python_environment";
import { PRESENT_THRESHOLD } from "@mail/core/common/thread";
import { start } from "@mail/../tests/helpers/test_utils";
import { patchWithCleanup } from "@web/../tests/helpers/utils";
import { click, contains, scroll } from "@web/../tests/utils";
import { click, contains, insertText, scroll } from "@web/../tests/utils";
import { SIZES, patchUiSize } from "../helpers/patch_ui_size";
QUnit.module("jump to present");
QUnit.test("Basic jump to present when scrolling to outdated messages", async (assert) => {
// make scroll behavior instantaneous.
patchWithCleanup(Element.prototype, {
scrollIntoView() {
return super.scrollIntoView(true);
},
});
const pyEnv = await startServer();
const channelId = pyEnv["discuss.channel"].create({ name: "General" });
for (let i = 0; i < 20; i++) {
@@ -28,7 +22,7 @@ QUnit.test("Basic jump to present when scrolling to outdated messages", async (a
});
}
const { openDiscuss } = await start();
openDiscuss(channelId);
await openDiscuss(channelId);
await contains(".o-mail-Message", { count: 20 });
await contains(".o-mail-Thread");
assert.ok(
@@ -46,13 +40,41 @@ QUnit.test("Basic jump to present when scrolling to outdated messages", async (a
await contains(".o-mail-Thread", { scroll: "bottom" });
});
QUnit.test("Jump to old reply should prompt jump to presence", async () => {
// make scroll behavior instantaneous.
patchWithCleanup(Element.prototype, {
scrollIntoView() {
return super.scrollIntoView(true);
},
});
QUnit.test(
"Basic jump to present when scrolling to outdated messages (chatter, DESC)",
async (assert) => {
patchUiSize({ size: SIZES.XXL });
const pyEnv = await startServer();
const partnerId = pyEnv["res.partner"].create({ name: "Demo User" });
for (let i = 0; i < 20; i++) {
pyEnv["mail.message"].create({
body: "Non Empty Body ".repeat(100),
message_type: "comment",
model: "res.partner",
res_id: partnerId,
});
}
const { openFormView } = await start();
await openFormView("res.partner", partnerId);
await contains(".o-mail-Message", { count: 20 });
await contains(".o-mail-Thread");
assert.ok(
document.querySelector(".o-mail-Thread").scrollHeight > PRESENT_THRESHOLD,
"should have enough scroll height to trigger jump to present"
);
await contains(".o-mail-Chatter", { scroll: 0 });
await scroll(".o-mail-Chatter", "bottom");
await contains(".o-mail-Chatter", { text: "You're viewing older messagesJump to Present" });
await click(".o-mail-Thread-jumpPresent");
await contains(".o-mail-Thread", {
count: 0,
text: "You're viewing older messagesJump to Present",
});
await contains(".o-mail-Chatter", { scroll: 0 });
}
);
QUnit.test("Jump to old reply should prompt jump to present", async () => {
const pyEnv = await startServer();
const channelId = pyEnv["discuss.channel"].create({ name: "General" });
const oldestMessageId = pyEnv["mail.message"].create({
@@ -82,14 +104,104 @@ QUnit.test("Jump to old reply should prompt jump to presence", async () => {
parent_id: oldestMessageId,
});
const { openDiscuss } = await start();
openDiscuss(channelId);
await openDiscuss(channelId);
await contains(".o-mail-Message", { count: 30 });
await click(".o-mail-MessageInReply .cursor-pointer");
await contains(".o-mail-Message", { count: 16 });
await contains(":nth-child(1 of .o-mail-Message)", { text: "Hello world!" });
await contains(".o-mail-Thread", { text: "You're viewing older messagesJump to Present" });
await contains(".o-mail-Thread-jumpPresent");
await click(".o-mail-Thread-jumpPresent");
await contains(".o-mail-Thread-jumpPresent", { count: 0 });
await contains(".o-mail-Message", { count: 30 });
await contains(".o-mail-Thread", { scroll: "bottom" });
});
QUnit.test("Jump to old reply should prompt jump to present (RPC small delay)", async () => {
// same test as before but with a small RPC delay
const pyEnv = await startServer();
const channelId = pyEnv["discuss.channel"].create({ name: "General" });
const oldestMessageId = pyEnv["mail.message"].create({
body: "<p>Hello world!</p>",
model: "discuss.channel",
res_id: channelId,
});
for (let i = 0; i < 100; i++) {
pyEnv["mail.message"].create({
body: "<p>Non Empty Body</p>".repeat(100),
message_type: "comment",
model: "discuss.channel",
/**
* The first message following the oldest message should have it as its parent message
* so that the oldest message is inserted through the parent field during "load around"
* to have the coverage of this part of the code (in particular having parent message
* body being inserted with markup).
*/
parent_id: i === 0 ? oldestMessageId : undefined,
res_id: channelId,
});
}
pyEnv["mail.message"].create({
body: "Most Recent!",
model: "discuss.channel",
res_id: channelId,
parent_id: oldestMessageId,
});
const { openDiscuss } = await start({
async mockRPC(route, args) {
if (route === "/discuss/channel/messages") {
await new Promise(setTimeout); // small delay
}
},
});
await openDiscuss(channelId);
await contains(".o-mail-Message", { count: 30 });
await click(".o-mail-MessageInReply .cursor-pointer");
await contains(".o-mail-Thread-jumpPresent");
await click(".o-mail-Thread-jumpPresent");
await contains(".o-mail-Thread-jumpPresent", { count: 0 });
await contains(".o-mail-Thread", { scroll: "bottom" });
});
QUnit.test("Post message when seeing old message should jump to present", async () => {
const pyEnv = await startServer();
const channelId = pyEnv["discuss.channel"].create({ name: "General" });
const oldestMessageId = pyEnv["mail.message"].create({
body: "<p>Hello world!</p>",
model: "discuss.channel",
res_id: channelId,
});
for (let i = 0; i < 100; i++) {
pyEnv["mail.message"].create({
body: "<p>Non Empty Body</p>".repeat(100),
message_type: "comment",
model: "discuss.channel",
/**
* The first message following the oldest message should have it as its parent message
* so that the oldest message is inserted through the parent field during "load around"
* to have the coverage of this part of the code (in particular having parent message
* body being inserted with markup).
*/
parent_id: i === 0 ? oldestMessageId : undefined,
res_id: channelId,
});
}
pyEnv["mail.message"].create({
body: "Most Recent!",
model: "discuss.channel",
res_id: channelId,
parent_id: oldestMessageId,
});
const { openDiscuss } = await start();
await openDiscuss(channelId);
await contains(".o-mail-Message", { count: 30 });
await click(".o-mail-MessageInReply .cursor-pointer");
await contains(".o-mail-Thread-jumpPresent");
await insertText(".o-mail-Composer-input", "Newly posted");
await click(".o-mail-Composer button:enabled", { text: "Send" });
await contains(".o-mail-Thread-jumpPresent", { count: 0 });
await contains(".o-mail-Thread", { scroll: "bottom" });
await contains(".o-mail-Message-content", {
text: "Newly posted",
after: [".o-mail-Message-content", { text: "Most Recent!" }], // should load around present
});
});