mirror of
https://github.com/discourse/discourse.git
synced 2024-12-03 05:13:41 +08:00
bf886662df
This pull request is a full overhaul of the chat-composer and contains various improvements to the thread panel. They have been grouped in the same PR as lots of improvements/fixes to the thread panel needed an improved composer. This is meant as a first step. ### New features included in this PR - A resizable side panel - A clear dropzone area for uploads - A simplified design for image uploads, this is only a first step towards more redesign of this area in the future ### Notable fixes in this PR - Correct placeholder in thread panel - Allows to edit the last message of a thread with arrow up - Correctly focus composer when replying to a message - The reply indicator is added instantly in the channel when starting a thread - Prevents a large variety of bug where the composer could bug and prevent sending message or would clear your input while it has content ### Technical notes To achieve this PR, three important changes have been made: - `<ChatComposer>` has been fully rewritten and is now a glimmer component - The chat composer now takes a `ChatMessage` as input which can directly be used in other operations, it simplifies a lot of logic as we are always working a with a `ChatMessage` - `TextareaInteractor` has been created to wrap the existing `TextareaTextManipulation` mixin, it will make future migrations easier and allow us to have a less polluted `<ChatComposer>` Note ".chat-live-pane" has been renamed ".chat-channel" Design for upload dropzone is from @chapoi
146 lines
4.0 KiB
JavaScript
146 lines
4.0 KiB
JavaScript
import {
|
|
acceptance,
|
|
exists,
|
|
publishToMessageBus,
|
|
query,
|
|
} from "discourse/tests/helpers/qunit-helpers";
|
|
import { click, fillIn, settled, visit } from "@ember/test-helpers";
|
|
import { skip } from "qunit";
|
|
import {
|
|
baseChatPretenders,
|
|
chatChannelPretender,
|
|
} from "../helpers/chat-pretenders";
|
|
|
|
const GROUP_NAME = "group1";
|
|
|
|
acceptance("Discourse Chat - Composer", function (needs) {
|
|
needs.user({ has_chat_enabled: true });
|
|
needs.settings({ chat_enabled: true, enable_rich_text_paste: true });
|
|
needs.pretender((server, helper) => {
|
|
baseChatPretenders(server, helper);
|
|
chatChannelPretender(server, helper);
|
|
server.get("/chat/:id/messages.json", () =>
|
|
helper.response({ chat_messages: [], meta: {} })
|
|
);
|
|
server.get("/chat/emojis.json", () =>
|
|
helper.response({ favorites: [{ name: "grinning" }] })
|
|
);
|
|
server.post("/chat/drafts", () => {
|
|
return helper.response([]);
|
|
});
|
|
|
|
server.get("/chat/api/mentions/groups.json", () => {
|
|
return helper.response({
|
|
unreachable: [GROUP_NAME],
|
|
over_members_limit: [],
|
|
invalid: [],
|
|
});
|
|
});
|
|
});
|
|
|
|
needs.hooks.beforeEach(function () {
|
|
Object.defineProperty(this, "chatService", {
|
|
get: () => this.container.lookup("service:chat"),
|
|
});
|
|
});
|
|
|
|
skip("when pasting html in composer", async function (assert) {
|
|
await visit("/chat/c/another-category/11");
|
|
|
|
const clipboardEvent = new Event("paste", { bubbles: true });
|
|
clipboardEvent.clipboardData = {
|
|
types: ["text/html"],
|
|
getData: (type) => {
|
|
if (type === "text/html") {
|
|
return "<a href>Foo</a>";
|
|
}
|
|
},
|
|
};
|
|
|
|
document
|
|
.querySelector(".chat-composer__input")
|
|
.dispatchEvent(clipboardEvent);
|
|
|
|
await settled();
|
|
|
|
assert.equal(document.querySelector(".chat-composer__input").value, "Foo");
|
|
});
|
|
});
|
|
|
|
let sendAttempt = 0;
|
|
acceptance("Discourse Chat - Composer - unreliable network", function (needs) {
|
|
needs.user({ id: 1, has_chat_enabled: true });
|
|
needs.settings({ chat_enabled: true });
|
|
needs.pretender((server, helper) => {
|
|
chatChannelPretender(server, helper);
|
|
server.get("/chat/:id/messages.json", () =>
|
|
helper.response({ chat_messages: [], meta: {} })
|
|
);
|
|
server.post("/chat/drafts", () => helper.response(500, {}));
|
|
server.post("/chat/:id.json", () => {
|
|
sendAttempt += 1;
|
|
return sendAttempt === 1
|
|
? helper.response(500, {})
|
|
: helper.response({ success: true });
|
|
});
|
|
});
|
|
|
|
needs.hooks.beforeEach(function () {
|
|
Object.defineProperty(this, "chatService", {
|
|
get: () => this.container.lookup("service:chat"),
|
|
});
|
|
});
|
|
|
|
needs.hooks.afterEach(function () {
|
|
sendAttempt = 0;
|
|
});
|
|
|
|
skip("Sending a message with unreliable network", async function (assert) {
|
|
await visit("/chat/c/-/11");
|
|
await fillIn(".chat-composer__input", "network-error-message");
|
|
await click(".send-btn");
|
|
|
|
assert.ok(
|
|
exists(".chat-message-container[data-id='1'] .retry-staged-message-btn"),
|
|
"it adds a retry button"
|
|
);
|
|
|
|
await fillIn(".chat-composer__input", "network-error-message");
|
|
await click(".send-btn");
|
|
await publishToMessageBus(`/chat/11`, {
|
|
type: "sent",
|
|
staged_id: 1,
|
|
chat_message: {
|
|
cooked: "network-error-message",
|
|
id: 175,
|
|
user: { id: 1 },
|
|
},
|
|
});
|
|
|
|
assert.notOk(
|
|
exists(".chat-message-container[data-id='1'] .retry-staged-message-btn"),
|
|
"it removes the staged message"
|
|
);
|
|
assert.ok(
|
|
exists(".chat-message-container[data-id='175']"),
|
|
"it sends the message"
|
|
);
|
|
assert.strictEqual(
|
|
query(".chat-composer__input").value,
|
|
"",
|
|
"it clears the input"
|
|
);
|
|
});
|
|
|
|
skip("Draft with unreliable network", async function (assert) {
|
|
await visit("/chat/c/-/11");
|
|
this.chatService.set("isNetworkUnreliable", true);
|
|
await settled();
|
|
|
|
assert.ok(
|
|
exists(".chat-composer__unreliable-network"),
|
|
"it displays a network error icon"
|
|
);
|
|
});
|
|
});
|