Currently in services, the `contract` step is only used to define where
the contract will be called in the execution flow. Then, a `Contract`
class has to be defined with validations in it.
This patch allows the `contract` step to take a block containing
validations, attributes, etc. directly. No need to then open a
`Contract` class later in the service.
It also has a nice side effect, as it’s now easy to define multiples
contracts inside the same service. Before, we had the `class_name:`
option, but it wasn’t really useful as you had to redefine a complete
new contract class.
Now, when using a name for the contract other than `default`, a new
contract will be created automatically using the provided name.
Example:
```ruby
contract(:user) do
attribute :user_id, :integer
validates :user_id, presence: true
end
```
This will create a `UserContract` class and use it, also putting the
resulting contract in `context[:user_contract]`.
In 61c1d35f17 I added a
PluginOutlet to AdminPluginConfigPage. This was intended to be
used as a way to render actions buttons inside the header of
a plugin that has a custom admin UI page. This worked, but
since the outlet was generically named, as soon as one plugin
used it the button would show on all plugins.
This fixes the immediate issue by naming the outlet based
on the plugin, then having each plugin specify their own
outlet to render into. There may be a nicer way to do this,
but for now this stops the bleeding.
With the current implementation, a service step can be written as:
```ruby
def my_step(a_default_value: 2)
…
end
```
That’s a pattern we want to avoid as default values (if needed) should
be probably defined in a contract.
This patch makes a service raise an exception if a default value is
encountered.
This will help to enforce a consistent pattern for creating service
actions.
This patch also namespaces actions and policies, making everything
related to a service available directly in
`app/services/<concept-name>`, making things more consistent at that
level too.
This change adds full names to direct message channel titles when the following conditions are met:
- SiteSetting.enable_names = true
- SiteSetting.display_name_on_posts = true
- SiteSetting.prioritize_username_in_ux = false
If a user's full name is blank, it will fallback to their username in both 1-1 channels and Group DM channels.
- fetch models inside services
- validate `user_id` in contracts
- use policy objects
- extract more logic to actions
- write specs for services and action
On occasion we get an error popup on desktop due to the channel not being found.
This change means that we only check the cached channels in ChatChannelsManager for the matching channel id, but we skip doing manual lookup which results in ajax popup when it fails.
This commit converts the current chat plugin UI into the
new "show plugin" UI already followed by AI and Gamification.
In the process, I also:
* Made a dedicated /new route to create new webhooks
* Converted the webhook form to FormKit
* Made some fixes and improvements to the `AdminPluginConfigPage`, `AdminPageHeader`,
and `AdminPageSubheader` generic components, so more plugins can
adopt the UI guidelines too. This includes adding a header outlet so plugins
can add action buttons to the plugin show page header.
* Fixes the submit button loading state for FormKit (by Joffrey)
---------
Co-authored-by: Joffrey JAFFEUX <j.jaffeux@gmail.com>
Every time a desktop chat sound plays, there should be some visual cue as to why the sound was played in the first place.
This change follows the chat indicator preference:
- All New Messages - a blue dot is shown for all messages, so we attempt to play a sound every time
- Direct Messages, Mentions and Watched Threads - a green dot is shown for all urgent messages, so we attempt to play a sound for urgent chat notifications
- Only Mentions - only play chat sounds when user is mentioned
- Never - we never play chat sounds, as user wouldn’t know why the sound was played
Chat toggle relies on using a height: auto to collapse drawer, however in channel threads we should be only displaying threads when drawer is expanded. Displaying threads conditionally based on drawer toggle status fixes this.
This change increases the visibility of unread channels to make them stand out more in drawer mode (desktop).
When a channel is unread:
- it floats to the top;
- when multiple channels are unread, they are sorted alphabetically (equal to how it’s done on mobile)
- the unread indicator blue dot moves to directly right of the channel name
This patch removes the `with_service` helper from the code base.
Instead, we can pass a block with actions directly to the `.call` method
of a service.
This simplifies how to use services:
- use `.call` without a block to run the service and get its result
object.
- use `.call` with a block of actions to run the service and execute
arbitrary code depending on the service outcome.
It also means a service is now “self-contained” and can be used anywhere
without having to include a helper or whatever.
We need to start printing deprecation notices when the `show_in_ui` argument is used because it works only for the old about page which will be removed soon. For the new about page, we've introduced a new API `addAboutPageActivity` which is more flexible than a true/false argument on the server side.
Internal topic: t/136551.
Currently, categories support designating only 1 group as a moderation group on the category. This commit removes the one group limitation and makes it possible to designate multiple groups as mods on a category.
Internal topic: t/124648.
A new setting attribute is used to define the areas (separated by `|`).
In addition, endpoint `/admin/config/site_settings.json` accepts new `filter_area` data.
This change introduces a new thread notification level allowing users to get notified when someone replies to the thread.
Users who watch a thread will get a green notification on the chat icon and a user notification (blue). User notifications are consolidated based on thread id to prevent cluttering the original users notification area.
---------
Co-authored-by: Régis Hanol <regis@hanol.fr>
We're working to remove all decorators from object-literal-properties. This commit refactors all initializers which were using these decorators into classes, where decorators are allowed. Also adds cleanup logic to the local-dates initializer, which was previously missing.
Removes chat-specific changes from dfc947a97d, and adds `preventScroll: true` to prevent timing issues between the emoji picker being brought into the viewport and being focussed.
Now that `ActiveRecord` relations are properly handled in a `model`
step, putting this step back will allow the service to stops its
execution if there are no users to be removed without calling the pretty
big SQL query contained in the `CalculateMembershipsForRemoval` action.
When we use CTRL/CMD + K to search, on the server we run 4 queries:
- 1 for users
- 1 for groups
- 1 for category channels
- 1 for direct message channels
The server returns up to 10 results per type and the client concatenate all the results (in the order I described) and then takes the first 10 or so.
Let's say you've had 1:1s with john1, john2, and john3. Searching for “john” would return all the johns as “users”. It doesn’t make sense to also return them as 1:1 dms.
That’s what this fixes. When the search returns some users, we skip all 1:1s because we assume they will show up as the results of the “users” query.
We’ll always return matching direct messages with more than 2 users (aka. “group chats”). All 3 other queries (users, groups, and category channels) are unaffected.
Internal ref - t/136079
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.
This is a variation on bc3e8a9963cf9a64d114ec751c875025af169690, which was reverted due to issues on iOS. Safari's "in response to user action" check cannot follow the `runAfterFramePaint` chain of interaction -> requestAnimationFrame -> messageChannel, and so some sensitive browser APIs (e.g. clipboard, upload, etc.) were blocked.
This commit is similar, but uses `next()` instead of `runAfterFramePaint()`. The result seems the same, but doesn't have the same issue on iOS.
The chat-emoji-picker change was required to resolve a test failure. The emoji picker has never closed-on-scroll on desktop, so there is no user-facing change in behavior.
This is a follow-up to 671f40ce07 and
ed11ee9d05.
While the optimisations in the previous commits were sound, it did not
resolve the memory bloat we were seeing. It turns out that we call
`.blank?` on the model's result if the model has not been marked
optional. The problem with this is that if the model returns an
ActiveRecord relation, calling `.blank?` on the relation basically loads
everything into memory.
Therefore, this commit removes `users` as a model in the since it really isn't
a model but just a relation.
This is a follow up to ed11ee9d05.
In `Chat::AutoRemove::HandleCategoryUpdated`, we are currently loading
the related users record in batches and then handing it off to
`Chat::Action::CalculateMembershipsForRemoval.call`. However, we are
still seeing memory spike as a result of this.
This commit eliminates the allocation of `User` ActiveRecord objects until
absolutely necessary. `Chat::Action::CalculateMembershipsForRemoval.call` has been
updated to accept an ActiveRecord relation instead which allows us to
avoid the ActiveRecord allocations.
This change delays the notify watching job to allow time for message to be marked as seen within chat.
Without a slight delay the job fires straight away and often means that messages are seen on screen but the user also receives a notification due to Chat::Notifier.user_has_seen_message? returning false.
This commit seeks to reduce the memory footprint of `Chat::AutoRemove::HandleCategoryUpdated.call`
by optimizing the
`Chat::AutoRemove::HandleCategoryUpdated#remove_users_without_channel_permission` method which was
loading all the ActiveRecord users objects into memory at once. This
change updates the method call to load the ActiveRecord user objects in
batches instead.
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
This outlet is rendered before the content of the drawer. The `currentRouteName` is accessible in `outletArgs`.
Example:
```gjs
export default class Test extends Component {
<template>
{{#if (eq @outletArgs.currentRouteName "chat.browse")}}
the browse page
{{/if}}
</template>
}
```
* FIX: make the check better for drawer rerouter
* adds a redirect method to chat-drawer-routes
---------
Co-authored-by: Joffrey JAFFEUX <j.jaffeux@gmail.com>
* 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.
* `@ember/owner` instead of `@ember/application`
* `discourse-i18n` instead of `I18n`
* `{ service } from "@ember/service"` instead of `inject as service`
This is a performance optimisation to prevent the same route to keep reloading the same endpoint.
No tests as it's not changing behavior and is also quite complex to test efficiently.
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 the following sequence would cause an overflow:
- open a thread
- expand thread panel to maximum width
- close panel
- reduce window width
- open thread again
- 💥
The fix is now ensuring that we never use or set a width which would cause the main panel + side panel to be larger than the chat container. We also removed the service as it was overkill for this case and it's easier to have all the implementation at one place.
This commit also uses JS animation api to set the width of the panel.
<!-- NOTE: All pull requests should have tests (rspec in Ruby, qunit in JavaScript). If your code does not include test coverage, please include an explanation of why it was omitted. -->
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.
I am changing many of these to notes or resolving them as is,
most of these I have not actively worked on in years so someone
else can work on them when we get to these areas again.
We want to allow admins to make new required fields apply to existing users. In order for this to work we need to have a way to make those users fill up the fields on their next page load. This is very similar to how adding a 2FA requirement post-fact works. Users will be redirected to a page where they can fill up the remaining required fields, and until they do that they won't be able to do anything else.
* 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.
There is currently only one scenario when both the composer and a user card would be present at the same time:
if you have the composer open and then you click on something outside it that triggers a card. Which implies intent to see the card (unobstructed by the composer 😉)
The reverse doesn't happen because opening the composer would close an existing user card.
In theory there's also displaying a user card by clicking on a mention in composer's preview but that functionality is currently broken (and this PR is a prerequisite 😉)
---
I changed `.user-card, .group-card` to `.fk-d-menu[data-identifier="card"]` because that regressed when we moved user cards to float-kit – they are nested inside `.fk-d-menu` so its `z-index` is now important (effectively the cards had `z-index: z("dropdown")` instead of `z("usercard")`)
* remove default prop values where they're being set in constructor
* replace some `||` operators in constructors with `??` so the fallback boolean values are actually used
* remove an unused service injection (and sort the rest)
* remove unused prop
* inline an arg check
* remove an unnecessary `?.` operator
* sort element attributes
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 prevents explicitly declaring each route that should be intercepted for chat drawer mode.
In theory all chat drawer routes should be intercepted from the main chat routes file and therefore we would only need to add new drawer routes directly within chat-drawer-router.js.
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.