In the past we were only intercepting 429 and 404; it's probably better to surface any error.
There are already tests for the 404 and 429, I consider them enough for now.
This commit ensures we have correct icon and title on mobile for the chat header icon.
It also fixes a bug where the site setting was not correctly used when the user has not yet set the user option.
Both cases are now correctly tested.
This commit was incorrectly removed of https://github.com/discourse/discourse/pull/23078 and would set the state only on entering (or exiting) chat route. The tests were already present in the previous PR.
Our code assumed the content_range interval was inclusive, but they are open-ended due to Postgres' [discrete range types](https://www.postgresql.org/docs/current/rangetypes.html#RANGETYPES-DISCRETE), meaning [1,2] will be represented as [1,3).
It also fixes some flaky tests due to test data not being correctly setup and the registry not being resetted after each test.
Each time a message is created through a webhook, we create we webhook_event associated to this webhook.
When destroying a webhook, we were not destroying the webhook_events which was causing orphans records and more importantly errors in the app expecting to find and associated webhook.
This commit moves the calendar date and time picker shown in
the local dates modal into a core component that can be reused
in other places. Also add system specs to make sure there isn't
any breakages with this feature, and a section to the styleguide.
Not sure when we added this but it is no longer necessary,
hashtags are cooked appropriately when sending chat messages
and the mention transform was not used anywhere.
The specs were relying a lot on mock and stubs. I suspect that under certain circumstances it didn't play well with fabricators and we ended up with the stub of another spec causing this kind of error:
```
1) Chat::AutoJoinChannelBatch.call when arguments are valid when channel is found when more than one membership is created publishes an event
Failure/Error: subject(:result) { described_class.call(params) }
Mocha::ExpectationError:
unexpected invocation: Chat::Publisher.publish_new_channel(#<Chat::CategoryChannel:0x39b840>, #<User::ActiveRecord_Relation:0x39b868>)
unsatisfied expectations:
- expected exactly once, invoked never: Chat::Publisher.publish_new_channel(#<Chat::CategoryChannel:0x39b890>, [#<User:0x39b8b8>, #<User:0x39b8e0>])
satisfied expectations:
- allowed any number of times, invoked once: Chat::Action::CreateMembershipsForAutoJoin.call(has_entries({:channel => #<Chat::CategoryChannel:0x39b890>, :contract => instance_of(Chat::AutoJoinChannelBatch::Contract)}))
- allowed any number of times, invoked never: Chat::ChannelMembershipManager.new(#<Chat::CategoryChannel:0x39b890>)
- allowed any number of times, invoked never: #<Mock:0x39b930>.recalculate_user_count(any_parameters)
# ./plugins/chat/app/services/chat/auto_join_channel_batch.rb:65:in `publish_new_channel'
# ./plugins/chat/app/services/service/base.rb:118:in `instance_exec'
# ./plugins/chat/app/services/service/base.rb:118:in `call'
# ./plugins/chat/app/services/service/base.rb:368:in `block in run!'
# ./plugins/chat/app/services/service/base.rb:368:in `each'
# ./plugins/chat/app/services/service/base.rb:368:in `run!'
# ./plugins/chat/app/services/service/base.rb:361:in `run'
# ./plugins/chat/app/services/service/base.rb:229:in `call'
# ./plugins/chat/spec/services/chat/auto_join_channel_batch_spec.rb:50:in `block (3 levels) in <main>'
# ./plugins/chat/spec/services/chat/auto_join_channel_batch_spec.rb:110:in `block (6 levels) in <main>'
# ./spec/rails_helper.rb:412:in `block (2 levels) in <top (required)>'
```
The spec is now simplified and shouldn't have this issue anymore.
Prior to this fix we would output an image with no width/height which would then bypass a large part of `CookedProcessorMixin` and have no aspect ratio. As a result, an image with no size would cause layout shift.
It also removes a fix for oneboxes in chat messages due to this case.
I suspect it was moreover possibly related to a flaky spec:
```
1) Chat::AutoJoinChannelBatch.call when arguments are valid when channel is found when more than one membership is created publishes an event
Failure/Error: subject(:result) { described_class.call(params) }
Mocha::ExpectationError:
unexpected invocation: Chat::Publisher.publish_new_channel(#<Chat::CategoryChannel:0x401f28>, #<User::ActiveRecord_Relation:0x401f50>)
unsatisfied expectations:
- expected exactly once, invoked never: Chat::Publisher.publish_new_channel(#<Chat::CategoryChannel:0x401f78>, [#<User:0x401fa0>, #<User:0x401fc8>])
satisfied expectations:
- allowed any number of times, invoked once: Chat::Action::CreateMembershipsForAutoJoin.call(has_entries({:channel => #<Chat::CategoryChannel:0x401f78>, :contract => instance_of(Chat::AutoJoinChannelBatch::Contract)}))
- allowed any number of times, invoked never: Chat::ChannelMembershipManager.new(#<Chat::CategoryChannel:0x401f78>)
- allowed any number of times, invoked never: #<Mock:0x402018>.recalculate_user_count(any_parameters)
# ./plugins/chat/app/services/chat/auto_join_channel_batch.rb:65:in `publish_new_channel'
# ./plugins/chat/app/services/service/base.rb:118:in `instance_exec'
# ./plugins/chat/app/services/service/base.rb:118:in `call'
# ./plugins/chat/app/services/service/base.rb:368:in `block in run!'
# ./plugins/chat/app/services/service/base.rb:368:in `each'
# ./plugins/chat/app/services/service/base.rb:368:in `run!'
# ./plugins/chat/app/services/service/base.rb:361:in `run'
# ./plugins/chat/app/services/service/base.rb:229:in `call'
# ./plugins/chat/spec/services/chat/auto_join_channel_batch_spec.rb:50:in `block (3 levels) in <main>'
# ./plugins/chat/spec/services/chat/auto_join_channel_batch_spec.rb:110:in `block (6 levels) in <main>'
# ./spec/rails_helper.rb:393:in `block (2 levels) in <top (required)>'
```
If a selenium finder takes the full wait duration to resolve, that means it has been written inefficiently. Most likely a matcher has been negated incorrectly.
This commit introduces a patch which will raise an error in this situation so that we can catch the issues while developing specs.
This commit also fixes chat's visit_thread helper. It was spinning on `has_css?(".chat-skeleton")` for the full selenium wait duration, and then returns false. That's because the thread is often already fully loaded before `has_css?` is even called. It's now updated to only look for the final expected state.
This commit removes any logic in the app and in specs around
enable_experimental_hashtag_autocomplete and deletes some
old category hashtag code that is no longer necessary.
It also adds a `slug_ref` category instance method, which
will generate a reference like `parent:child` for a category,
with an optional depth, which hashtags use. Also refactors
PostRevisor which was using CategoryHashtagDataSource directly
which is a no-no.
Deletes the old hashtag markdown rule as well.
Prior to this fix `context.membership&.update!(last_viewed_at: Time.zone.now)` would generate an update statement from a GET request which is not permitted by default when in readonly mode.
The usual fix in this case is to check for readonly or rescue an error, however, this common pattern of updating "last seen" or similar can be better handled in a `Schedule::Defer` block, which won't raise the `ActiveRecord::ReadOnlyError` when in readonly and will also prevent the controller to wait for this operation.
This commit makes some visual tweaks to the admin panel plugin list, and introduces functional 'toggle switches' for admins to enable/disable plugins more easily.
Co-authored-by: Jordan Vidrine <jordan@jordanvidrine.com>
This was forgotten during the work in 22991bba44
This revealed two differences we were depending on: the merged `actions` hash (re-implemented on the service), and a couple of calls to `composer.send` (now removed)
1. recent css regression related to modal upgrade
2. autofocus and on-enter regressions
3. array related linting issue (reliance on Ember's firstObject/lastObject)
We were attempting to fetch from last read but this is actually complicated to get right when you have a lot unread, as we might still have more to load after this but the last unread id is still the same and would make the user end up in a loop.
What is the problem here?
In multiple controllers, we are accepting a `limit` params but do not
impose any upper bound on the values being accepted. Without an upper
bound, we may be allowing arbituary users from generating DB queries
which may end up exhausing the resources on the server.
What is the fix here?
A new `fetch_limit_from_params` helper method is introduced in
`ApplicationController` that can be used by controller actions to safely
get the limit from the params as a default limit and maximum limit has
to be set. When an invalid limit params is encountered, the server will
respond with the 400 response code.
We did some testing and saw that making one query per month is
cheaper than querying all chat messages at ones. Note that even
though the export job will be performing one query per month,
the exported messages will be streamed into a single CSV file, so
nothing changes from the user's point of view.
This is extracted from #22390.
This patch introduces a scope to avoid duplication and a new method,
`Chat::Channel.find_by_id_or_slug` to allow finding a channel either by
its id or by its slug (or its category slug).
`Jobs::AutoJoinChannelBatch` was holding a lot of logic which should be in a service. Moreover, this refactoring is the opportunity to address a bug which could cause a duplicate key error.
From now when trying to insert a new membership it won't fail if a membership is already present.
Example error:
```
Job exception: ERROR: duplicate key value violates unique constraint "user_chat_channel_unique_memberships"
DETAIL: Key (user_id, chat_channel_id)=(1, 2) already exists.
Backtrace
rack-mini-profiler-3.1.0/lib/patches/db/pg.rb:110:in `exec'
rack-mini-profiler-3.1.0/lib/patches/db/pg.rb:110:in `async_exec'
(eval):29:in `async_exec'
mini_sql-1.4.0/lib/mini_sql/postgres/connection.rb:209:in `run'
mini_sql-1.4.0/lib/mini_sql/active_record_postgres/connection.rb:38:in `block in run'
mini_sql-1.4.0/lib/mini_sql/active_record_postgres/connection.rb:34:in `block in with_lock'
activesupport-7.0.5.1/lib/active_support/concurrency/load_interlock_aware_monitor.rb:25:in `handle_interrupt'
activesupport-7.0.5.1/lib/active_support/concurrency/load_interlock_aware_monitor.rb:25:in `block in synchronize'
activesupport-7.0.5.1/lib/active_support/concurrency/load_interlock_aware_monitor.rb:21:in `handle_interrupt'
activesupport-7.0.5.1/lib/active_support/concurrency/load_interlock_aware_monitor.rb:21:in `synchronize'
mini_sql-1.4.0/lib/mini_sql/active_record_postgres/connection.rb:34:in `with_lock'
mini_sql-1.4.0/lib/mini_sql/active_record_postgres/connection.rb:38:in `run'
mini_sql-1.4.0/lib/mini_sql/postgres/connection.rb:64:in `query_single'
/var/www/discourse/plugins/chat/app/jobs/regular/chat/auto_join_channel_batch.rb:38:in `execute'
```
Note this commit is also using main branch of `shoulda-matchers` as the gem has not been released yet.
Co-authored-by: Loïc Guitaut <5648+Flink@users.noreply.github.com>
Prior to this commit we were loading a large number of thread messages without any pagination. This commit attempts to fix this and also improves the following points:
- code sharing between channels and threads:
Attempts to reuse/share the code use in channels for threads. To make it possible part of this code has been extracted in dedicated helpers or has been improved to reduce the duplication needed.
Examples of extracted helpers:
- `stackingContextFix`: the ios hack for rendering bug when momentum scrolling is interrupted
- `scrollListToMessage`, `scrollListToTop`, `scrollListToBottom`: a series of helper to correctly scroll to a specific position in the list of messages
- better general performance of listing messages:
One of the main changes which has been made is to remove the computation of visible message during scroll, it will only happen when needed (update last read for example). This constant recomputation of `message.visible` on intersection observer event while scrolling was consuming a lot of CPU time.
Followup to f5e8e73.
This switches the placeholder label to the existing string "optional
tags" and only shows it if there are no items picked.
Co-authored-by: Jarek Radosz <jradosz@gmail.com>
I was only able to get one failure out of 100 tries, this failure didn't get me more info. My best guess ATM is that sometimes, the first session was still loading while receiving the reaction and created some unexpected situation.
The commit attempts to start the "check" session before the session making the reaction hoping that will be enough to prevent this case, if this is the issue.
Sharing a link in chat will create a onebox embed with a source that includes a site icon and title.
This change prevents loading the site icon into lightbox.
This is extracted from #22390.
This patch simplifies a little how we handle uploads in chat, relying on
ActiveRecord mechanisms instead of calling custom methods.
This also makes `Chat::Message#validate_message` a “real” AR validation,
meaning it will run automatically when `#valid?` is called.
The gjs/gts formats are a new pattern for authoring Ember components. This commit introduces support for these patterns to our build pipeline for core/plugins, and converts a handful of components to use the new format. It also introduces relevant updates to our linting config, and to our sample vscode configuration.
Co-authored-by: Godfrey Chan <godfreykfc@gmail.com>
Co-authored-by: Krystan HuffMenne <kmenne+github@gmail.com>
This only moves code around and doesn't change any behavior. This does two things:
1. Extracts the `channel.joined_by?` methods
2. Uses term "members" instead of "participants" for chat members
This is extracted from #22390.
This patch adds a new `optional` option to the `model` step. This
means if an optional model returns something blank (`nil` or an empty
collection) then the service won’t fail and will execute the next step.
However if a model is properly returned, the step will try to check if
it is valid or not (if it responds to `#invalid?`). If the model isn’t
valid, then the step will fail (so no change here).
Turns out making a html4 fragment and then operating on parts of it using html5 fragments is a bad idea. ;)
This seems to fix the issue with occasionally missing GH icons in oneboxes.
Someone who cannot chat is also not able to join chat channels,
so we may not check all the time user.can_chat? && user.can_join_chat_channel?
and just call user.can_join_chat_channel? instead.
Followup to 07c3782e51
The above incorrectly removed the channel unread count in
the mobile/drawer channel list when the user has mentions
(meaning the unreads are urgent). This commit adds it
back and refactors system specs a little.
Define new concept of panels in sidebar. Panels are wrappers around sidebar sections. In the future, it allows creating full focus mode by switching between panels.
A new API method called addSidebarPanel was added. Default main panel is already registered and by default all API sections are mounted to main.
Why this change?
The test being changed in question has been flaky on our CI. However, we
are unable to view the screenshot of why it failed because
ActionDispatch will only take a screenshot of the default session upon
failure. At the same time, taking screenshot of all sessions
automatically upon failure is not possible via the official Capybara or
Rails APIs at the moment. Therefore, we're changing this system test to
avoid using two custom session and instead have the main assertion use
the default session such that any failures will provide us with a
screenshot.
This commit fixes two issues with the thread list:
1. All threads were being shown regardless of whether the user had
a membership in the thread. This was happening because the list
and the channel share the same thread store, so if the channel
had OMs with threads we would load them and they showed in the list.
2. Threads created by the user from a staged thread would double up.
This is because the _cache in the channel threadsManager would use
the staged thread ID even after we'd replaced the object's ID with
the actual thread from the DB. The answer to this is to remove and
re-add the thread to the local cache with the actual ID.
* DEV: Fix and re-enable chat flakys
The early return in JS was added to prevent an error
from channel being null, and it's better to use known
users for the message fabrications in the specs.
* DEV: Use travel_to in drawer spec for thread tracking
Sometimes in the system test the datetime that is last
viewed for the channel for the user and the datetime for
the last message created_at is only microseconds of difference,
and we do not provide that level of fidelity in the MessageBus
serializer, so unreadThreadsCountSinceLastViewed is not
accurate.
Better to just utilize travel_to and move forward 1 minute in
time before sending the new message to easily differentiate.
When we have subscriptions for new messages in a channel,
we also have special handling for messages in a thread. For
cases like DM channels where threads are made in the background
but not used in the UI, this is causing JS errors because we
are trying to fetch the thread but it returns 404.
We only want to do things with messages in threads if the
channel actually has threading enabled.
We need a nice way to only return some hashtag data
sources based on various site settings. This commit
adds an enabled? method that every hashtag data source
must implement. If this returns false the data source
will not be used at all for hashtag lookups or search.
On tablets like iPad where we allow channel and thread to be on the same screen, it was not possible to resize the panels due to code being thought for mouse events. This commit should now correctly allow for this.
The "resizer" has also been made larger to simplify touching.
No test as it's hard to test on iPad and dragging events are also complex.
On iOS we have a hack to prevent the viewport to move when focusing an input, however this code was targeting the textarea node through a global selector which is working fine on iOS as we only show one composer at a time but was failing on iPad as we show both channel and thread on the same screen. As a result `document.querySelector(".chat-composer__input")` was always targeting the first textarea on the screen which was the channel's composer, making it impossible to focus the thread's one.
This commit makes it so that when the user has unread threads
for a channel we show a blue dot in the sidebar (or channel index
for mobile/drawer).
This blue dot is slightly different from the channel unread messages:
1. It will only show if the new thread messages were created since
the user last viewed the channel
2. It will be cleared when the user views the channel, but the threads
are still considered unread because we want the user to click into
the thread list to view them
This necessitates a change to the current user serializer to also
include the unread thread overview, which is all unread threads
across all channels and their last reply date + time.