The string introduced in
https://github.com/discourse/discourse/pull/31854 included a count but
was not pluralized. Even though the singular form may not be used in
English, proper pluralization is necessary for other languages. Some
languages have different plural forms depending on the number, so
explicitly defining pluralization ensures accurate translations.
Before this commit,
`PageObjects::Components::SelectKit#expanded_component` might not expand
the component if `is_collapsed?` returns true which can return true if
the method was called before the select-kit component appears on the
screen. When that happens, we end up not expanding the `select-kit`
because we think it is collapsed when in fact the select-kit component
is not rendered yet. This lead to system test failures with errors like:
```
Capybara::ElementNotFound:
Unable to find css "#add-synonyms.is-expanded"
```
This commit updates `PageObjects::Components::SelectKit#is_collapsed?`
to check that the select-kit component has rendered first before
checking if the component is not expanded.
### Reviewer notes
Instances of test flakiness due to this bug:
1.
https://github.com/discourse/discourse/actions/runs/13905226478/job/38906569777
2.
https://github.com/discourse/discourse/actions/runs/13848357836/job/38751122333
Follow up to e4401e587e98ac4020c2c4fd965e227146cf33d4.
In the previous change, when serialising a Reviewable post, we were appending the title to the post content whilst checking for watched word matches. This append action was acting upon the object itself, rather than just a copy of the string, causing the UI to display the title appended twice to the content.
Fortunately, since it was only happening in the serialiser, the incorrect data was never stored in the database, it was only happening when viewing the review queue.
When performing an action in the review queue, this change makes two improvements:
- The buttons on the reviewable item are disabled, so you can't accidentally multi-click.
- A toast is displayed when the action is complete, as a success indication.
Fixes the custom homepage crawler output to include links to the site's
top menu.

This also provides a way to override that content via a plugin. Example
usage in a `plugin.rb` file:
```
register_html_builder("server:custom-homepage-crawler-view") do |c|
"<div>override</div>"
end
```
This reduces some duplicate color assignment to tags and rolls them up
under a common css var
```css
:root {
--tag-text-color: var(--primary-high);
}
.extra-info-wrapper {
--tag-text-color: var(--header_primary-high);
}
```
This also makes the tag separator color consistent with the tag color,
by setting it to `--tag-text-color`
Ultimately we have 3 tag text color states (this appeared to be
consistent across all tag styles):
* Default: primary-high
* Header: header__primary-high
* Visited topic in the topic list: primary-medium
Previously we were showing a loading spinner, but the old user list
persisted so the spinner was often off the bottom of the screen. This
commit updates the `users` list to be a getter, so it's always perfectly
in-sync with the `_results` set. That means no users will be shown while
a new list is being loaded, so the spinner is more visible.
https://meta.discourse.org/t/load-spinner-missing-from-dynamic-pages/357525/4
This reverts commit 992bdf173ad8ad25764c0a89132bc35df4c81f12.
This change was causing issues in a [third party
plugin](https://meta.discourse.org/t/events-plugin):
https://meta.discourse.org/t/events-plugin/69776/869
```
Uncaught Error: Could not find module `discourse/mixins/singleton` imported from `discourse/plugins/discourse-events/discourse/models/provider`
at loader.js:247:1
at h (loader.js:258:1)
at u.findDeps (loader.js:168:1)
at h (loader.js:262:1)
at u.findDeps (loader.js:168:1)
at h (loader.js:262:1)
at requireModule (loader.js:24:1)
at y (app.js:170:18)
at b (app.js:193:19)
at app.js:156:29
at g.start (app.js:167:1)
at HTMLDocument.<anonymous> (start-app.js:5:7)
at discourse-boot.js:13:12
at discourse-boot.js:1:1
```
Currently we allow for 2 theme screenshots to be specified,
with a lightweight spec to allow both a light and dark
version of the screenshot. However, we were not storing this
screenshot name anywhere, so we would not be able to use it
for light/dark switching.
This commit fixes that issue, and also does some general refactoring
around theme screenshots, and adds more tests.
Test is flaking in CI with:
```
Failure/Error: expect(theme_translations_settings_editor.get_input_value).to have_content("Bonjour!")
expected to find text "Bonjour!" in "Hello there!"
[Screenshot Image]: /__w/discourse/discourse/tmp/capybara/failures_r_spec_example_groups_admin_customize_themes_when_editing_theme_translations_should_allow_admin_to_edit_and_save_the_theme_translations_from_other_languages_797.png
~~~~~~~ JS LOGS ~~~~~~~
(no logs)
~~~~~ END JS LOGS ~~~~~
./spec/system/admin_customize_themes_spec.rb:158:in `block (3 levels) in <main>'
```
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>'
```
This is a stripped-back version of the Search Banner
component https://meta.discourse.org/t/search-banner/122939,
which will be renamed to Advanced Search Banner,
see https://github.com/discourse/discourse-search-banner/pull/84.
This welcome banner interacts with the header search.
When `search_experience` is set to `search_field`, we only
show the header search after the welcome banner scrolls
out of view, and vice-versa.
Only new sites will get this feature turned on by default,
existing sites have a migration to disable it.
---------
Co-authored-by: Joffrey JAFFEUX <j.jaffeux@gmail.com>
Co-authored-by: Jordan Vidrine <jordan@jordanvidrine.com>
This is a follow up to 6820622467ab3613e824f0cb6219def2a575bc1d.
This commit addresses migrations that uses `remove_index` with the
`if_exists: true` option to drop an existing index before creating an
index using the `concurrently` option.
This commit also reruns two migration which may have caused indexes to
be left in an `invalid` state.
### Reviewers Note
Plugin tests are failing due to
https://github.com/discourse/discourse-translator/pull/251
Since d2409f2964e85a4cb134f119d1bdddce54156175, these calculations are
now incredibly cheap, and the caches introduced by `@computed` become
the bottleneck. On a site with 5k+ categories, removing these caches
results in a 4x render time improvement for their homepage (~2s ->
~0.5s).
Technically, this means these properties will no longer be reactive for
places that depend on them using computed property syntax. However, they
are still reactive for modern code with autotracking. Given that the set
of categories doesn't change after initial load, I think this is
acceptable. If we do run into any issues, then we can update the related
code to use autotracking.
Note: `@dependentKeyCompat` has similar perf overheads to `@computed`,
so that's why I haven't used that for backward-compat.
Now that we support multiple drafts, we can avoid the extra draft check
within composer when creating a new topic or reply. For posts, we
already autoload the existing draft into composer when the user tries to
create a new reply, so there is no longer a need for the abandon draft
dialog.
Drafts can still be deleted by closing the composer (using a different
dialog) or manually via the User Drafts page.
This change also correctly sets the draft key within composer actions
when switching from a post reply to a linked topic.