This was refactored from an actual Mixin some time back. This PR moves
the function to the lib folder and keeps the same logic/interface
otherwise.
We'll remove the mixin file once all non-core repos change to use the
new file.
This is to try fix this error by sending the keys
on the element itself:
```
Failure/Error: page.send_keys([PLATFORM_KEY_MODIFIER, "v"])
Selenium::WebDriver::Error::StaleElementReferenceError:
stale element reference: stale element not found in the current frame
(Session info: chrome=135.0.7049.84); For documentation on this error, please visit: https://www.selenium.dev/documentation/webdriver/troubleshooting/errors#stale-element-reference-exception
```
When a flagged chat message has already been deleted, we offer an option
in the review queue to agree with the flag and keep the message deleted.
However, this option is currently broken due to a missing implementation
for the option.
Internal topic: t/152203.
discourse presence can be disabled, given it's a plugin the `composerPresenceManager` service couldn't be present anymore and would cause an error.
No test as it's testing imbrication of multiple plugins and hard to test reliably.
This introduces functionality to chat transcripts. If
you select _only_ a thread's OP and press the Copy button,
we will now copy the OP plus all of the thread's replies
into the transcript.
This is being done to make it easier to copy all the context
of a thread easily. The old way of selecting only some messages
inside a thread still works too.
This commit also unskips and refactors some transcript specs.
Fixing hashtag text glued to the icon, and updated some related properties to using better units.
---------
Co-authored-by: Martin Brennan <martin@discourse.org>
The system test relies on a MessageBus message being published and
received by the client. As we currently don't have a reliable way to
ensure that we run an assertion after the client has received all
MessageBus messages, we will just double the timeout for now.
This commit allows the ProseMirror rich editor to display chat
transcripts copied from chat using the "Copy" button.
The BBCode usually looks something like this:
```
[chat quote="hunter;29856;2025-03-20T07:13:04Z" channel="design gems 🎉" channelId="95"]
haha **ok** _cool_
[/chat]
```
But there are several variations that must be accounted for:
* Single message from single user
* Multiple messages from a single and multiple users
* Messages inside chat threads
The rich transcript extension has to ignore many of the chat transcript
markdown
tokens because they simply aren't necessary -- none of the ProseMirror
nodes need
to be editable. So, we basically recreate the same HTML that the chat
transcript markdown
rule does in the `toDOM()` function. Maybe in future we want to make the
markdown rule
do less and have this HTML creation in one place, but for now we need to
mirror in both files.
---------
Co-authored-by: Renato Atilio <renato@discourse.org>
We are not waiting for asynchronous operations to complete before
executing our assertions.
### Reviewer notes
Example test failure:
https://github.com/discourse/discourse/actions/runs/14100970402/job/39496976787
```
Failure/Error: measurement = Benchmark.measure { example.run }
expected `Chat::Channel.count` to have changed by 1, but was changed by 0
[Screenshot Image]: /__w/discourse/discourse/tmp/capybara/failures_r_spec_example_groups_chat_new_message_from_params_with_multiple_users_creates_a_dm_channel_when_none_exists_626.png
~~~~~~~ JS LOGS ~~~~~~~
~~~~~ END JS LOGS ~~~~~
./plugins/chat/spec/system/chat_new_message_spec.rb:57:in `block (3 levels) in <main>'
./spec/rails_helper.rb:619:in `block (3 levels) in <top (required)>'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/benchmark-0.4.0/lib/benchmark.rb:304:in `measure'
./spec/rails_helper.rb:619:in `block (2 levels) in <top (required)>'
./spec/rails_helper.rb:580:in `block (3 levels) in <top (required)>'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/timeout-0.4.3/lib/timeout.rb:185:in `block in timeout'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/timeout-0.4.3/lib/timeout.rb:192:in `timeout'
./spec/rails_helper.rb:570:in `block (2 levels) in <top (required)>'
./spec/rails_helper.rb:527:in `block (2 levels) in <top (required)>'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/webmock-3.25.1/lib/webmock/rspec.rb:39:in `block (2 levels) in <top (required)>'
```
A system test in `system/search_spec.rb` was failing with the following
error frequently on CI:
```
Failure/Error: expect(search_page).to have_heading_text("Search")
expected `#<PageObjects::Pages::Search:0x00007fb9fcd3f028>.has_heading_text?("Search")` to be truthy, got false
[Screenshot Image]: /__w/discourse/discourse/tmp/capybara/failures_r_spec_example_groups_search_when_using_full_page_search_on_mobile_works_and_clears_search_page_state_912.png
~~~~~~~ JS LOGS ~~~~~~~
(no logs)
~~~~~ END JS LOGS ~~~~~
./spec/system/search_spec.rb:42:in `block (3 levels) in <main>'
./spec/rails_helper.rb:619:in `block (3 levels) in <top (required)>'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/benchmark-0.4.0/lib/benchmark.rb:304:in `measure'
./spec/rails_helper.rb:619:in `block (2 levels) in <top (required)>'
./spec/rails_helper.rb:580:in `block (3 levels) in <top (required)>'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/timeout-0.4.3/lib/timeout.rb:185:in `block in timeout'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/timeout-0.4.3/lib/timeout.rb:192:in `timeout'
./spec/rails_helper.rb:570:in `block (2 levels) in <top (required)>'
./spec/rails_helper.rb:527:in `block (2 levels) in <top (required)>'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/webmock-3.25.1/lib/webmock/rspec.rb:39:in `block (2 levels) in <top (required)>'
```
The failure screenshot shows that the "user" is on the homepage even
though we have already clicked the search icon and ensured that the user
can see the search container. I suspect there is some sort of race
condition here since Capybara executes clicks in quick sucession where
we clicked on both the homepage logo and the search icon. It may be
possible that Ember redirected the user to the search page first
before the browser was able to finish navigating the user to the `/`
href.
### Reviewer notes
Test flaked in
https://github.com/discourse/discourse/actions/runs/14085443789/job/39448197089
with the following failure screenshot:

With header search we toggle the search icon when scrolling on topics,
which can feel visually jarring when the search icon is in the middle.
Switching the position of the chat icon in the header reduces the impact
of this.
Whenever you are about to reach the limit of users in a group DM, if you
send requests to add new users in parallel, they might all go through
ignoring the limit due to a race condition.
Internal - t/145895
We intend to replace this JQuery autocomplete implementation soon. But
before that, we'd like to drop the raw-hbs compilation/runtime system.
Therefore, this commit does a 1:1 conversion of the autocomplete
templates to vanilla JS. They're fairly small/simple, so they're still
fairly readable.
Currently, anonymous/shadow users go through the same permission checks
for chat as normal users do. This means that if a site has chat enabled
for all users, anonymous users also get access to chat. This may be
undesirable for some communities, so we're adding a new site setting
`allow_chat_in_anonymous_mode` to block access to chat for anonymous
users.
Internal topic: t/148088.
This change allows more flexibility when starting a 1-1 direct message
with another user. If there are no messages in the new DM channel then
we should still allow them to add additional users.
The test has been flaking in CI with the following message:
```
Failure/Error: measurement = Benchmark.measure { example.run }
expected: "draft"
got: ""
(compared using ==)
[Screenshot Image]: /__w/discourse/discourse/tmp/capybara/failures_r_spec_example_groups_chat_composer_draft_when_loading_a_channel_with_a_draft_loads_the_draft_458.png
~~~~~~~ JS LOGS ~~~~~~~
~~~~~ END JS LOGS ~~~~~
./plugins/chat/spec/system/chat_composer_draft_spec.rb:31:in `block (3 levels) in <main>'
```
When creating a DM to a group in chat, we show
a count of users for that group if the number of
users exceeds SiteSetting.chat_max_direct_message_users.
However, we were also counting bot users here, we should
only be counting human users, this led to confusion because
the chat group count was different from the one on
e.g. /g/:group_name
This was preventing favorites emojis to work correctly as it was using
topic context. No tests for now as it's a very specific behavior hard to
test with acceptance test and a system spec seems heavy for this.
This was causing some confusion and now that we have the chat preference
to send with enter or metakey+enter it's even more confusing.
If your setting is send on enter, you need to use shift+enter for a new
line, even in codeblocks. If your setting is meta+enter you need to use
enter in the codeblock for a new line, meta+enter will send as expected.
Prevents channel non-members from seeing the composer in threads. Since
they do not have the correct permissions to post within the channel, we
should show the join channel CTA in place of the chat composer.
We were using a zero width space added by CSS here, but have run into
occasional encoding issues for some reason? a couple people have
reported getting this instead of an empty space:

I can't repro the issue, but we can avoid it by removing this space in
CSS — setting the container height to `1em` along with `line-height:
normal` should make it consistent with the height of the text within.
In the blame it seems this static height on the container was added
after the pseudo hack, and achieves the same goal when I test it across
a Firefox/Chrome/Safari
This commit moves most of emoji logic into the discourse-emojis gem:
https://github.com/discourse/discourse-emojis/
Most notably:
- images are now symlinked from the gem
- the gem provides path to the json files
Search aliases have also been made asynchronous and memoized. When you
will search for an emoji we will now load the aliases and store the list
for future use.
---------
Co-authored-by: David Taylor <david@taylorhq.com>