mirror of
https://github.com/discourse/discourse.git
synced 2025-01-18 16:52:45 +08:00
UI: chat composer step 2 (#21641)
- few improved alignments - displays emoji picker button inline on desktop - keeps composer focused when focusing dropdown button - align buttons to bottom when increasing height of textarea - max-height of textarea is now linked to the height of the screen Co-authored-by: chapoi <charlie@discourse.org>
This commit is contained in:
parent
5cce829901
commit
bdfd80bfe0
|
@ -9,6 +9,7 @@
|
|||
"chat-composer-dropdown__trigger-btn"
|
||||
(if @hasActivePanel "has-active-panel")
|
||||
}}
|
||||
...attributes
|
||||
/>
|
||||
|
||||
{{#if this.isExpanded}}
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
{{#each this.buttons as |button|}}
|
||||
<DButton
|
||||
@icon={{button.icon}}
|
||||
@class={{concat "chat-composer-inline-button " button.id}}
|
||||
@action={{action button.action}}
|
||||
/>
|
||||
{{/each}}
|
|
@ -1,5 +0,0 @@
|
|||
import Component from "@ember/component";
|
||||
|
||||
export default class ChatComposerInlineButtons extends Component {
|
||||
tagName = "";
|
||||
}
|
|
@ -39,6 +39,8 @@
|
|||
this.context
|
||||
}}
|
||||
@onCloseActivePanel={{this.chatEmojiPickerManager.close}}
|
||||
{{on "focus" (fn this.computeIsFocused true)}}
|
||||
{{on "blur" (fn this.computeIsFocused false)}}
|
||||
/>
|
||||
|
||||
<div
|
||||
|
@ -66,20 +68,36 @@
|
|||
/>
|
||||
</div>
|
||||
|
||||
{{#if this.inlineButtons.length}}
|
||||
{{#each this.inlineButtons as |button|}}
|
||||
<Chat::Composer::Button
|
||||
@icon={{button.icon}}
|
||||
class={{button.id}}
|
||||
disabled={{button.disabled}}
|
||||
tabindex={{if button.disabled -1 0}}
|
||||
{{on
|
||||
"click"
|
||||
(fn this.handleInlineButonAction button.action)
|
||||
bubbles=false
|
||||
}}
|
||||
{{on "focus" (fn this.computeIsFocused true)}}
|
||||
{{on "blur" (fn this.computeIsFocused false)}}
|
||||
/>
|
||||
{{/each}}
|
||||
|
||||
<Chat::Composer::Separator />
|
||||
{{/if}}
|
||||
|
||||
<Chat::Composer::Button
|
||||
{{on "click" this.onSend}}
|
||||
@icon="paper-plane"
|
||||
class="chat-composer__send-btn"
|
||||
title={{i18n "chat.composer.send"}}
|
||||
disabled={{or this.disabled (not this.sendEnabled)}}
|
||||
tabindex={{if this.sendEnabled 0 -1}}
|
||||
{{on "click" this.onSend}}
|
||||
{{on "focus" (fn this.computeIsFocused true)}}
|
||||
{{on "blur" (fn this.computeIsFocused false)}}
|
||||
/>
|
||||
|
||||
{{#unless this.disabled}}
|
||||
<ChatComposerInlineButtons @buttons={{this.inlineButtons}} />
|
||||
{{/unless}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -122,6 +122,13 @@ export default class ChatComposer extends Component {
|
|||
cancel(this._persistHandler);
|
||||
}
|
||||
|
||||
@action
|
||||
handleInlineButonAction(buttonAction, event) {
|
||||
event.stopPropagation();
|
||||
|
||||
buttonAction();
|
||||
}
|
||||
|
||||
get currentMessage() {
|
||||
return this.composer.message;
|
||||
}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
<div class="chat-composer-separator"></div>
|
|
@ -0,0 +1,3 @@
|
|||
import Component from "@glimmer/component";
|
||||
|
||||
export default class ChatComposerSeparator extends Component {}
|
|
@ -18,6 +18,7 @@ export default {
|
|||
|
||||
initialize(container) {
|
||||
this.chatService = container.lookup("service:chat");
|
||||
this.site = container.lookup("service:site");
|
||||
this.siteSettings = container.lookup("service:site-settings");
|
||||
this.appEvents = container.lookup("service:app-events");
|
||||
this.appEvents.on("discourse:focus-changed", this, "_handleFocusChanged");
|
||||
|
@ -58,8 +59,8 @@ export default {
|
|||
label: "chat.emoji",
|
||||
id: "emoji",
|
||||
class: "chat-emoji-btn",
|
||||
icon: "discourse-emojis",
|
||||
position: "dropdown",
|
||||
icon: "far-smile",
|
||||
position: this.site.desktopView ? "inline" : "dropdown",
|
||||
context: "channel",
|
||||
action() {
|
||||
const chatEmojiPickerManager = container.lookup(
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
box-sizing: border-box;
|
||||
width: 50px;
|
||||
border: 0;
|
||||
height: 100%;
|
||||
height: 50px;
|
||||
background: none;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
.chat-composer-inline-button {
|
||||
border-radius: 6px;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
|
||||
& + .chat-composer-inline-button {
|
||||
margin-left: 0.25rem;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
.chat-composer-separator {
|
||||
width: 1px;
|
||||
margin: 10px 0.25rem;
|
||||
box-sizing: border-box;
|
||||
background: var(--primary-low-mid);
|
||||
height: 30px;
|
||||
display: flex;
|
||||
}
|
|
@ -4,7 +4,7 @@
|
|||
flex-direction: column;
|
||||
z-index: 3;
|
||||
background-color: var(--primary-very-low);
|
||||
padding: 12px 10px env(safe-area-inset-bottom) 10px;
|
||||
padding: 0.5rem 0 env(safe-area-inset-bottom) 0;
|
||||
|
||||
.keyboard-visible & {
|
||||
padding-bottom: 0;
|
||||
|
@ -22,7 +22,7 @@
|
|||
|
||||
.chat-composer-button,
|
||||
.chat-composer-separator {
|
||||
align-self: stretch;
|
||||
align-self: flex-end;
|
||||
}
|
||||
|
||||
&__outer-container {
|
||||
|
@ -30,6 +30,19 @@
|
|||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
padding-inline: 1rem;
|
||||
}
|
||||
|
||||
&__inline-button {
|
||||
.d-icon {
|
||||
color: var(--primary-medium);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.d-icon {
|
||||
color: var(--primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__inner-container {
|
||||
|
@ -85,6 +98,7 @@
|
|||
|
||||
.d-icon {
|
||||
background: none !important;
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
.chat-composer.is-send-enabled & {
|
||||
|
@ -122,10 +136,6 @@
|
|||
background: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.d-icon {
|
||||
color: var(--primary);
|
||||
}
|
||||
}
|
||||
|
||||
&__input-container {
|
||||
|
@ -136,6 +146,11 @@
|
|||
align-self: stretch;
|
||||
}
|
||||
|
||||
--100dvh: 100vh;
|
||||
@supports (height: 100dvh) {
|
||||
--100dvh: 100dvh;
|
||||
}
|
||||
|
||||
&__input {
|
||||
overflow-x: hidden;
|
||||
width: 100%;
|
||||
|
@ -143,13 +158,17 @@
|
|||
outline: none;
|
||||
border: 0;
|
||||
resize: none;
|
||||
max-height: 125px;
|
||||
max-height: calc(
|
||||
(
|
||||
var(--100dvh) - var(--header-offset, 0px) -
|
||||
var(--chat-header-offset, 0px)
|
||||
) / 100 * 25
|
||||
);
|
||||
background: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
margin: 5px 0;
|
||||
text-overflow: ellipsis;
|
||||
cursor: inherit;
|
||||
|
||||
@include chat-scrollbar();
|
||||
|
||||
&[disabled] {
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
@import "chat-channel-settings-saved-indicator";
|
||||
@import "chat-channel-title";
|
||||
@import "chat-composer-dropdown";
|
||||
@import "chat-composer-inline-button";
|
||||
@import "chat-composer-upload";
|
||||
@import "chat-composer-uploads";
|
||||
@import "chat-composer";
|
||||
|
@ -52,3 +51,4 @@
|
|||
@import "chat-thread-list-item";
|
||||
@import "chat-threads-list";
|
||||
@import "chat-thread-original-message";
|
||||
@import "chat-composer-separator";
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
}
|
||||
|
||||
.chat-message:not(.user-info-hidden) {
|
||||
padding: 0.65em 1em 0.15em;
|
||||
padding: 0.65rem 1rem 0.15rem;
|
||||
}
|
||||
|
||||
.chat-message-text {
|
||||
|
@ -52,7 +52,7 @@
|
|||
}
|
||||
|
||||
.chat-message.user-info-hidden {
|
||||
padding: 0.15em 1em;
|
||||
padding: 0.15rem 1rem;
|
||||
|
||||
.chat-time {
|
||||
color: var(--secondary-medium);
|
||||
|
|
|
@ -192,8 +192,7 @@ RSpec.describe "Chat composer", type: :system, js: true do
|
|||
SiteSetting.emoji_deny_list = "monkey|peach"
|
||||
|
||||
chat.visit_channel(channel_1)
|
||||
channel.open_action_menu
|
||||
channel.click_action_button("emoji")
|
||||
channel.composer.open_emoji_picker
|
||||
|
||||
expect(page).to have_no_selector("[data-emoji='monkey']")
|
||||
expect(page).to have_no_selector("[data-emoji='peach']")
|
||||
|
|
|
@ -158,6 +158,7 @@ RSpec.describe "Message notifications - mobile", type: :system, js: true, mobile
|
|||
expect(page).to have_css(".chat-header-icon .chat-channel-unread-indicator", text: "1")
|
||||
expect(page).to have_css(
|
||||
".chat-channel-row[data-chat-channel-id=\"#{dm_channel_1.id}\"] .chat-channel-unread-indicator",
|
||||
wait: 25,
|
||||
)
|
||||
|
||||
using_session(:user_1) do |session|
|
||||
|
@ -165,7 +166,11 @@ RSpec.describe "Message notifications - mobile", type: :system, js: true, mobile
|
|||
session.quit
|
||||
end
|
||||
|
||||
expect(page).to have_css(".chat-header-icon .chat-channel-unread-indicator", text: "2")
|
||||
expect(page).to have_css(
|
||||
".chat-header-icon .chat-channel-unread-indicator",
|
||||
text: "2",
|
||||
wait: 25,
|
||||
)
|
||||
end
|
||||
|
||||
it "reorders channels" do
|
||||
|
|
|
@ -1,172 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe "Navigating to message", type: :system, js: true do
|
||||
fab!(:current_user) { Fabricate(:user) }
|
||||
fab!(:channel_1) { Fabricate(:category_channel) }
|
||||
fab!(:first_message) { Fabricate(:chat_message, chat_channel: channel_1) }
|
||||
|
||||
let(:chat_page) { PageObjects::Pages::Chat.new }
|
||||
let(:chat_drawer_page) { PageObjects::Pages::ChatDrawer.new }
|
||||
let(:link) { "My favorite message" }
|
||||
|
||||
before do
|
||||
chat_system_bootstrap
|
||||
channel_1.add(current_user)
|
||||
75.times { Fabricate(:chat_message, chat_channel: channel_1) }
|
||||
sign_in(current_user)
|
||||
end
|
||||
|
||||
context "when in full page mode" do
|
||||
before { chat_page.prefers_full_page }
|
||||
|
||||
context "when clicking a link containing a message id" do
|
||||
fab!(:topic_1) { Fabricate(:topic) }
|
||||
|
||||
before do
|
||||
Fabricate(
|
||||
:post,
|
||||
topic: topic_1,
|
||||
raw: "<a href=\"/chat/c/-/#{channel_1.id}/#{first_message.id}\">#{link}</a>",
|
||||
)
|
||||
end
|
||||
|
||||
it "highlights the correct message" do
|
||||
visit("/t/-/#{topic_1.id}")
|
||||
click_link(link)
|
||||
|
||||
expect(page).to have_css(
|
||||
".chat-message-container.highlighted[data-id='#{first_message.id}']",
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context "when clicking a link to a message from the current channel" do
|
||||
before do
|
||||
Fabricate(
|
||||
:chat_message,
|
||||
chat_channel: channel_1,
|
||||
message: "[#{link}](/chat/c/-/#{channel_1.id}/#{first_message.id})",
|
||||
)
|
||||
end
|
||||
|
||||
it "highlights the correct message" do
|
||||
chat_page.visit_channel(channel_1)
|
||||
click_link(link)
|
||||
|
||||
expect(page).to have_css(
|
||||
".chat-message-container.highlighted[data-id='#{first_message.id}']",
|
||||
)
|
||||
end
|
||||
|
||||
it "highlights the correct message after using the bottom arrow" do
|
||||
chat_page.visit_channel(channel_1)
|
||||
|
||||
click_link(link)
|
||||
|
||||
expect(page).to have_css(
|
||||
".chat-message-container.highlighted[data-id='#{first_message.id}']",
|
||||
)
|
||||
|
||||
click_button(class: "chat-scroll-to-bottom")
|
||||
|
||||
expect(page).to have_content(link)
|
||||
|
||||
click_link(link)
|
||||
|
||||
expect(page).to have_css(
|
||||
".chat-message-container.highlighted[data-id='#{first_message.id}']",
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context "when clicking a link to a message from another channel" do
|
||||
fab!(:channel_2) { Fabricate(:category_channel) }
|
||||
|
||||
before do
|
||||
Fabricate(
|
||||
:chat_message,
|
||||
chat_channel: channel_2,
|
||||
message: "[#{link}](/chat/c/-/#{channel_1.id}/#{first_message.id})",
|
||||
)
|
||||
channel_2.add(current_user)
|
||||
end
|
||||
|
||||
it "highlights the correct message" do
|
||||
chat_page.visit_channel(channel_2)
|
||||
click_link(link)
|
||||
|
||||
expect(page).to have_css(
|
||||
".chat-message-container.highlighted[data-id='#{first_message.id}']",
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context "when navigating directly to a message link" do
|
||||
it "highglights the correct message" do
|
||||
visit("/chat/c/-/#{channel_1.id}/#{first_message.id}")
|
||||
|
||||
expect(page).to have_css(
|
||||
".chat-message-container.highlighted[data-id='#{first_message.id}']",
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when in drawer" do
|
||||
context "when clicking a link containing a message id" do
|
||||
fab!(:topic_1) { Fabricate(:topic) }
|
||||
|
||||
before do
|
||||
Fabricate(
|
||||
:post,
|
||||
topic: topic_1,
|
||||
raw: "<a href=\"/chat/c/-/#{channel_1.id}/#{first_message.id}\">#{link}</a>",
|
||||
)
|
||||
end
|
||||
|
||||
it "highlights correct message" do
|
||||
visit("/t/-/#{topic_1.id}")
|
||||
click_link(link)
|
||||
|
||||
expect(page).to have_css(
|
||||
".chat-drawer.is-expanded .chat-message-container.highlighted[data-id='#{first_message.id}']",
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context "when clicking a link to a message from the current channel" do
|
||||
before do
|
||||
Fabricate(
|
||||
:chat_message,
|
||||
chat_channel: channel_1,
|
||||
message: "[#{link}](/chat/c/-/#{channel_1.id}/#{first_message.id})",
|
||||
)
|
||||
end
|
||||
|
||||
it "highlights the correct message" do
|
||||
visit("/")
|
||||
chat_page.open_from_header
|
||||
chat_drawer_page.open_channel(channel_1)
|
||||
click_link(link)
|
||||
|
||||
expect(page).to have_css(
|
||||
".chat-message-container.highlighted[data-id='#{first_message.id}']",
|
||||
)
|
||||
end
|
||||
|
||||
it "highlights the correct message after using the bottom arrow" do
|
||||
visit("/")
|
||||
chat_page.open_from_header
|
||||
chat_drawer_page.open_channel(channel_1)
|
||||
|
||||
click_link(link)
|
||||
click_button(class: "chat-scroll-to-bottom")
|
||||
click_link(link)
|
||||
|
||||
expect(page).to have_css(
|
||||
".chat-message-container.highlighted[data-id='#{first_message.id}']",
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -3,6 +3,10 @@
|
|||
module PageObjects
|
||||
module Pages
|
||||
class ChatChannel < PageObjects::Pages::Base
|
||||
def composer
|
||||
@composer ||= PageObjects::Components::Chat::Composer.new(".chat-channel")
|
||||
end
|
||||
|
||||
def replying_to?(message)
|
||||
find(".chat-channel .chat-reply", text: message.message)
|
||||
end
|
||||
|
|
|
@ -27,6 +27,10 @@ module PageObjects
|
|||
def edit_last_message_shortcut
|
||||
input.send_keys(%i[arrow_up])
|
||||
end
|
||||
|
||||
def open_emoji_picker
|
||||
find(context).find(SELECTOR).find(".chat-composer-button__btn.emoji").click
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
|
||||
import { exists } from "discourse/tests/helpers/qunit-helpers";
|
||||
import hbs from "htmlbars-inline-precompile";
|
||||
import { module, test } from "qunit";
|
||||
import { render } from "@ember/test-helpers";
|
||||
|
||||
module(
|
||||
"Discourse Chat | Component | chat-composer-inline-buttons",
|
||||
function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
test("buttons", async function (assert) {
|
||||
this.set("buttons", [{ id: "foo", icon: "times", action: () => {} }]);
|
||||
|
||||
await render(
|
||||
hbs`<ChatComposerInlineButtons @buttons={{this.buttons}} />`
|
||||
);
|
||||
|
||||
assert.true(exists(".chat-composer-inline-button.foo"));
|
||||
assert.true(exists(".chat-composer-inline-button.foo .d-icon-times"));
|
||||
});
|
||||
}
|
||||
);
|
Loading…
Reference in New Issue
Block a user