This patch allows using an AR relation as a model in services without
fetching associated records. It will just check if the relation is empty
or not. In the former case, the execution will stop at that point, as
expected.
This commit introduces a new frontend API to add custom items to the "Site activity" section in the new /about page. The new API is called `addAboutPageActivity` and it works along side the `register_stat` serve-side API which serializes the data that the frontend API consumes. More details of how the two APIs work together is in the JSDoc comment above the API function definition.
Internal topic: t/128545/9.
This commit improves the hilight-ing of mentions in posts and chat messages.
- `@here` and `@all` will generate a `<a class="mention --wide">`
- bots will generate a `<a class="mention --bot">`
- current user will generate a `<a class="mention --current">`
To achieve this change the following value transformer has been added: "mentions-class". It will be run in posts and chat messages after the mention is rendered.
A bug were bots were not considered in mentioned users has also been fixed as part of this PR.
Adds a new statistics (hidden from the UI, but available via the API) that tracks daily participating users.
A user is considered as "participating" if they have
- Reacted to a post
- Replied to a topic
- Created a new topic
- Created a new PM
- Sent a chat message
- Reacted to a chat message
Internal ref - t/131013
* FIX: Remove chat default channel being applied to mobile chat and drawer
* DEV: removing chat_default_channel_id setting
* DEV: add migration to remove chat default channel id
* DEV: remove default_channel_validator and tests
* DEV: rename chat preferred mobile index to chat preferred index
* UX: change routing to be consistent with mobile
* DEV: change migration file to use script
* UX: show footer only if more than one option is available
* UX: Remove desktopView only checks for chat
* DEV: Remove unused imports
* UX: Update chat footer checks and Add rerouting to chat drawer
* UX: Add margin to chat row in desktop and update chat drawer logic
* UX: Change chat in desktop to use flexbox
* UX: Add drawer actions to chat navbar
* DEV: Update page object with new chat css classes
removed `.open-browse-page-btn` usage in 7bd65006d7
* DEV: rename `browse/open` in chat url to `channels`
* UX: Adjust css for when in threads mode
* DEV: change css class name in no_sidebar_spec.rb
* DEV: rename tests to be more descriptive with the action they are testing
update chat template to not rely on `:has`
* DEV: update test and add method to chat page object
* DEV: update no_sidebar_spec for chat changes
* DEV: remove tests from navigation_spec that no longer apply
* DEV: revert typo in test
* DEV: change url path for mobile chat in test specs
* DEV: Add check for when is desktop in rerouting
* UX: Removed footer from desktop.
Made `hasThreads` and `hasDirectMessages` methods in chat-drawer public
* UX: remove sidebar on desktop full page if dm list is empty
* DEV: Address review comments
* DEV: Adjust reroute logic for chat browse
remove unused code
* UX: Adjust rerouting to go to browse.open
* UX: Change rerouting to be more consistent
Add chat_default_channel_id routing
* UX: Update rerouting configuration for chat routes
* DEV: Update tests with the new chat behavior
* DEV: revert changes made in tests and bring back toggle for drawer
* DEV: revert classes in page objects
* DEV: Add tests to new chat navigation behavior
remove unused stylesheets
revert deleted lines in tests
update concat class logic in chat dm template
* DEV: update css on test
The message grace edit window (10 seconds) was too short after freezing time, possibly causing the test to fail occasionally if the record is not updated within 5 seconds.
This commit ensures the browse page can be loaded in the drawer and doesn’t force full page mode.
Other notable changes of this commit:
- be consistent about wrapping each full page route with "c-routes.--route-name" and each drawer container with "c-drawer-routes.--route-name"
- move browse channels into its own component, it was before in the template of the channels browse
This change allows us to distinguish between regular user generated chat messages and those created via the Chat SDK.
A new created_by_sdk boolean column is added to the Chat Messages table. When this value is true, we will not include the message in the user summary email that is sent to users.
Prior to this fix we would show the message after a round trip to the server. If you had a too long message error, at this point your input would be empty and we would show an error in chat. It's important to have this server side safety net, but we can have a better UX by showing an error on the frontend before sending the message, that way you can correct your message before sending it and not lose it.
Follow up to #27631 to account for group mentions in channels.
We only want to show mentions for groups that are currently mentionable and those that the current user belongs to.
* DEV: Upgrade Rails to 7.1
* FIX: Remove references to `Rails.logger.chained`
`Rails.logger.chained` was provided by Logster before Rails 7.1
introduced their broadcast logger. Now all the loggers are added to
`Rails.logger.broadcasts`.
Some code in our initializers was still using `chained` instead of
`broadcasts`.
* DEV: Make parameters optional to all FakeLogger methods
* FIX: Set `override_level` on Logster loggers (#27519)
A followup to f595d599dd
* FIX: Don’t duplicate Rack response
---------
Co-authored-by: Jarek Radosz <jradosz@gmail.com>
* DEV: Upgrade Rails to 7.1
* FIX: Remove references to `Rails.logger.chained`
`Rails.logger.chained` was provided by Logster before Rails 7.1
introduced their broadcast logger. Now all the loggers are added to
`Rails.logger.broadcasts`.
Some code in our initializers was still using `chained` instead of
`broadcasts`.
* DEV: Make parameters optional to all FakeLogger methods
* FIX: Set `override_level` on Logster loggers (#27519)
A followup to f595d599dd
* FIX: Don’t duplicate Rack response
---------
Co-authored-by: Jarek Radosz <jradosz@gmail.com>
* Revert "FIX: Set `override_level` on Logster loggers (#27519)"
This reverts commit c1b0488c54.
* Revert "DEV: Make parameters optional to all FakeLogger methods"
This reverts commit 3318dad7b4.
* Revert "FIX: Remove references to `Rails.logger.chained`"
This reverts commit f595d599dd.
* Revert "DEV: Upgrade Rails to 7.1"
This reverts commit 081b00391e.
This change replaces the chat drawer tabs with new drawer routes for channels, direct messages and threads.
The main objective is to improve navigation within drawer, now that we have separation of chat sections in drawer.
The order of chat direct message groups can sometimes place usernames in an unexpected order, this change tests multiple combinations of usernames and accepts them no matter what order they are in.
In 4e7a75a7ec, we moved to a single admin plugin page and added a few fields to the "plugin serializer" but we already had a proper route with the correct serializers to properly load channels.
This fixes it by removing the "add_to_serializer" calls and changed the calls to "/admin/plugins/chat.json" to the proper "/admin/plugins/chat/hooks.json" route.
Meta - https://meta.discourse.org/t/names-are-missing-from-list-when-creating-new-chat-channel-webhooks/308481
When chat is enabled, there's a scheduled job that runs every 5 minutes to check whether we need to send a "chat summary" email to users with unread chat messages or mentions.
On Discourse with a large number of users, the query used wasn't optimal and sometimes taking minutes. Which isn't good when the query is called every 5 minutes 😬
This PR reworks the query in `Chat::Mailer.send_unread_mentions_summary`.
Instead of starting from the `users` table, it starts from the `user_chat_channel_memberships` table which is the main piece tying everything together.
The new query is mostly similar to the previous one, with some bug fixes (like ensuring the user has `allow_private_messages` enabled for direct messages) and is also slightly simpler since it doesn't keep track of the `memberships_with_unread_messages` anymore. That part has been moved to the `user_notifications.chat_summary` email method.
The `UserEmailExtension` has been deleted since that was using to N+1 update the `user_chat_channel_memberships.last_unread_mention_when_emailed_it`(quite a mouthful 😛) but that's now done directly in the `user_notifications.chat_summary` email method.
The "plat de résistance" of that PR - the `user_notifications.chat_summary` method has been re-worked for improved performances 🚀
Instead of doing everything in one query, it does 4 tiny ones.
- One to retrieve the list of unread mentions (@something) in "category" channels
- One to retrieve the list of unread messages in "direct message" channels (aka. 1-1 and group discussions)
- One to load all the chat messages for each "category" channels from the last unread mention
- One to load all the chat messages for each "direct message" channels from the last unread message
All the specs for both `Chat::Mailer` and `UserNotification.chat_summary` have been rewriten for easier comprehension and faster execution (mostly by not using chat services which makes the specs go 10x slower...)
Internal ref - t/129848
This change allows chat drawer users to edit channel settings and members without leaving drawer mode. If a channel is open within chat drawer and the user clicks the Channel name, it will load channel settings within the drawer.
This change allows the correct number of members to be added when creating a group direct message, based on the site setting chat_max_direct_message_users.
Previously we counted the current user within the max user limit and therefore the count was off by 1.
Before this fix we could only list messages of a thread if it was part of a `threading_enabled` channel or if the thread was set to `force`.
Due to our design of also using a thread id when this is just a chain of replies so we can switch from threading enabled to disabled at any time, we will allow `Chat:: ListChannelThreadMessages` to list the messages of any thread, the only important requirements are:
- having a thread id
- being able to access this thread
To allow this, this commit simply removes the check on `threading_enabled` or `force`.
A few follup changes after changing to the chat footer split for drawer:
* Fixing a bug that stretched the unread indicator on mobile
* Minor style changes in hover/focus behaviour for chat drawer
* Repositioning of unread indicator so it has more space at the top of the footer
* Using the `c-unread-indicator` mixin
Adds a placeholder image + CTA in chat, for empty channel and DM lists.
On desktop with drawer mode, we split chat into tabs (like mobile).
---------
Co-authored-by: Joffrey JAFFEUX <j.jaffeux@gmail.com>
Co-authored-by: David Battersby <info@davidbattersby.com>
Co-authored-by: Régis Hanol <regis@hanol.fr>
Prior to this fix we had too logic to detect if a user is active or not:
- idle codepath on the frontend
- online user ids on the backend
The frontend solution is not very reliable, and both solution are just trying to be too smart. Making a lot of people questioning why they receive a notification sometimes and sometimes not. This commit removes all this logic and replaces it with a much more simpler logic:
- you can't receive notifications for channel you are actually watching
- we won't play a sound more than once every 3seconds
When users click a link that points to an existing group chat, we should reopen that chat instead of creating a new group chat so users can more easily continue ongoing conversations.
Prior to this fix we wouldn't intercept it, and we also wouldn't handle it, which in result would cause us to handle as a full page interaction and open the full page chat even if you were in drawer mode.
When a user had the chat option "Show activity indicator in header" set to "all new messages", and they would get a reply to a thread they're part of, the chat icon in the header would not show the unread bubble indicator.
In order to fix this, the `ChatHeaderIconUnreadIndicator` component will now `showUnreadIndicator` whenever there is either one unread public channel or there are unread threads.
I only added a system spec for this very specific path because I don't want to slow down the whole suite to test for all the various combination of the `chat_header_indicator_preference` values.
Internal ref - t/128874
... wasn't properly escaped so it would should html entities (like `'` instead of the apostrophe `'`).
I checked all the other places we show an excerpt and this was the only one that was missing the call to `htmlSafe` -> `replaceEmoji`.
Internal ref - t/128877
When you reply to a chat message, we [always create a thread][1]. But when the channel we're in doesn't have threading enabled, the reply is _technically_ not a thread.
This changes the `in_thread?` method to check for both the presence of a `thread_id` and to ensure that the channel has `threading_enabled`.
Internal ref - t/128103/3
[1]: e6e3eaf472/plugins/chat/app/services/chat/create_message.rb (L110-L115)
Whenever you get a bookmark notification, a mention notification, or click on a bookmark on a message in a long thread, we should ensure we always highlight and show the proper message.
Before this fix, we would correctly load the thread, but would always start at the bottom.
Internal ref. t/128103
For both `chat_allowed_groups` and `chat_message_flag_allowed_groups`,
this commit removes the `is_staff?` guardian check, and instead
adds both `moderators` and `admins` auto groups as `mandatory_values`
to those settings, as part of an ongoing effort to do this for
group-based setting values.
We were missing two `getURL` calls.
The test is now written for subfolder but it's good enough. If it's working for subfolder, it's working for non subfolders, the opposite being false.
Prior to this commit, only system users had this pass.
Another significant change of the PR, is to make membership of a channel the angular stone of the permission check to create/update/stop streaming a message. The idea being, if you are a member of a channel already we don't need to check if you can join it AGAIN.
We also have `Chat::AutoRemove::HandleCategoryUpdated` which will deal with permissions change so it's simpler and less prone to error to consider the membership as the only source of truth.
There's no point checking if a user can join a channel if they are already part of it. This case was frequent when using `enforce_membership: true` for custom bots for example.
This case had not been tested end to end as `Discourse.track_events` was not working when wrapping `send_message`. Because of this lack of end to end test, a regression has been created when renaming the expected context properties. This commit fixes the regression and write a slightly convulted, but effective end to end test.
This commit introduces several enhancements to the ChatSDK module, aiming to improve the functionality and usability of chat thread interactions. Here's what has been changed and added:
1. **New Method: `first_messages`:**
- Added a method to retrieve the first set of messages from a specified chat thread.
- This method is particularly useful for fetching initial messages when entering a chat thread.
- Parameters include `thread_id`, `guardian`, and an optional `page_size` which defaults to 10.
- Usage example added to demonstrate fetching the first 15 messages from a thread.
2. **New Method: `last_messages`:**
- Added a method to retrieve the last set of messages from a specified chat thread.
- This method supports reverse pagination, where the user may want to see the most recent messages first.
- Similar to `first_messages`, it accepts `thread_id`, `guardian`, and an optional `page_size` parameter, defaulting to 10.
- Usage example provided to illustrate fetching the last 20 messages from a thread.
The TextCleaner step has been moved from chat message’s validation to create_message/update_message services. It allows us to easily tweak part of its behavior depending on the needs.
For example we will now disable strip_whitespaces by default when streaming messages as we want to keep newlines and spaces at the end of the message.
This change encourages users to title their threads to make it easier for other users to join in on conversations that matter to them.
The creator of the chat thread will receive a toast notification prompting them to add a thread title when on mobile and the thread has at least 5 sent replies.
That could cause flakeyness in specs depending in which timing message bus would arrive and it's not necessary as it should be updated with `handleThreadOriginalMessageUpdate`.
Follow up to #26712 to account for older threads that don't have a persisted excerpt, as this was previously generated on every page load.
This change allows us to build the excerpt on the fly when none exists, fixing the issue of missing message excerpts for thread previews (within channel) and thread lists (on mobile/desktop).
This change moves the chat message excerpt into a new database column (string) on the chat_messages table.
As part of this change, we will now set the excerpt within the `Chat::CreateMessage` service, and update it within the `Chat::UpdateMessage` service.
This commit will now allow us to track read position in a thread and returns to this position when you open the thread.
Note this commit is also extracting the following components to make it possible:
- `<ChatMessagesScroller />`
- `<ChatMessagesContainer />`
The `UpdateUserThreadLastRead` has been updated to allow this.
Various refactorings have also been done to the code and specs to improve the support of last read.
This is to enable :array type attributes for Contract
attributes in services, this is a followup to the move
of services from chat to core here:
cab178a405
Co-authored-by: Joffrey JAFFEUX <j.jaffeux@gmail.com>
When selecting messages to move to a new channel, if any of the selected messages is the original message of a thread, the entire thread, including all its replies, will be moved to the destination channel
The fix is to actually wait for the bottom arrow to show before appending a new message, otherwise sometimes it goes too fast, and we create a new message while the scroll has not ended yet, making the arrow not visible yet.
This commit also uses this opportunity to move from `50.times.map {}` to `Fabricate.times(50, ...)` in this spec file.
Why this change?
`expect(page.title).to starts_with("...")` does not rely on capybara
waiters. This commit switches us to use `have_title` instead which will
rely on Capybara waiters.
The expected behavior when receiving a message is the following:
- if user is at the bottom of the screen, scroll and append message
- if user is not at the bottom of the screen, don't scroll, show arrow and don't append message
Why this change?
When the site setting for chat_max_direct_message_users is set to 1, it is expected that users can have a 1:1 chat with other users. However, since the current user is counting as 1 user it makes starting a new chat impossible.
This change hands this validation off to DirectMessageChannel::MaxUsersExcessPolicy which handles the count correctly by filtering out the current user.
This enables the following in Discourse AI
```
plugin.register_modifier(:chat_allowed_bot_user_ids) do |user_ids, guardian|
if guardian.user
mentionables = AiPersona.mentionables(user: guardian.user)
allowed_bot_ids = mentionables.map { |mentionable| mentionable[:user_id] }
user_ids.concat(allowed_bot_ids)
end
user_ids
end
```
some bots that are id < 0 need to be discoverable in search otherwise people can not talk to them.
---------
Co-authored-by: Joffrey JAFFEUX <j.jaffeux@gmail.com>
We were incorrectly using `return` in a block which was causing exceptions at runtime. These exceptions were not causing much issues as they are in defer block.
While working on writing a test for this specific case, I noticed that our `upsert_custom_fields` function was using rails `update_all` which is not updating the `updated_at` timestamp. This commit also fixes it and adds a test for it.
The issue:
When the current user disables chat from within user preferences, the chat button still appears when clicking another user’s profile picture to open the user card. This is also the case when the current user has chat enabled but the target user has disabled chat.
After this change:
- when a user disables chat in preferences, the chat button should not be displayed when opening a user card or visiting profiles of other users.
- when chat is enabled in preferences but another user disables chat, the chat button should not appear on their user card or profile
This commit fixes an issue where the following happens:
1. You open /admin as a member of the admin_sidebar_enabled_groups
1. You then click the chat icon in the header when you prefer to have
drawer open, or if you just minimise chat into drawer after it opens
fullscreen
1. You lose the admin sidebar panel, and are reset instead to the main
panel
Also included is a bit of refactoring to make it so the forcing of
admin sidebar state is in one place.
Prior to this change we would pre-load all the user channels which making initial page load slower. This change will make them be loaded right after initial load. In the past this was not possible as the channels would have to be loaded on each page transition. However since about a year, we made the channels to be cached on the frontend and no other request will be needed.
I have decided for now to not show a loading state in the sidebar as I think it would be noise, but we can reconsider this later.
Note given we don't have the channels loaded at first certain things where harder to accomplish. The biggest UX change of this commit is that we removed all the complex logic of computing the best channel to display when you load /chat. We will now store the id of the last channel you visited and will use this id to decide which channel to show.
This commit makes it so the site settings filter controls and
the list of settings input editors themselves can be used elsewhere
in the admin UI outside of /admin/site_settings
This allows us to provide more targeted groups of settings in different
UI areas where it makes sense to provide them, such as on plugin pages.
You could open a single page for a plugin where you can see information
about that plugin, change settings, and configure it with custom UIs
in the one place.
In future we will do this in "config areas" for other parts of the
admin UI.