When visiting a channel which has unread threads, we will now open the threads list panel.
Note that:
mobile
linking to message
linking to a thread
Won't open the threads list.
It was slightly surprising to have a user card show when click on a thread item list.
More over this commit does:
- moves chat/user-avatar to chat-user-avatar and converts it to gjs
- moves chat/thread/participants to chat-thread-participants
- rewrite the `toggleCheckIfPossible` modifier to only be applied when selecting messages, it prevents the click event to collide with the click of avatars in regular messages
This PR is a first step towards private groups. It redesigns settings/members area of a channel and also drops the "about" page which is now mixed into settings.
This commit is also:
- introducing chat-form, a small DSL to create forms, ideally I would want something in core for this
- introducing a DToggleSwitch page object component to simplify testing toggles
- migrating various components to gjs
I don't have a repro of this ATM, but I suspect that ensuring the panel has been opened before moving to next tests could make this test more resilient.
Why this change?
Back in May 17 2023 along with the release of Discourse 3.1, we announced
on meta that the legacy hamburger dropdown navigation menu is
deprecated and will be dropped in Discourse 3.2. This is the link to the announcement
on meta: https://meta.discourse.org/t/removing-the-legacy-hamburger-navigation-menu-option/265274
## What does this change do?
This change removes the `legacy` option from the `navigation_menu` site
setting and migrates existing sites on the `legacy` option to the
`header dropdown` option.
All references to the `legacy` option in code and tests have been
removed as well.
This commit brings two fixes.
- increase the delay to trigger the action menu
- check of user activation before using vibrate:
https://developer.mozilla.org/en-US/docs/Glossary/Sticky_activationhttps://developer.mozilla.org/en-US/docs/Web/Security/User_activationhttps://developer.mozilla.org/en-US/docs/Web/API/UserActivation/hasBeenActive
> Sticky activation is a window state that indicates a user has pressed a button, moved a mouse, used a menu, or performed some other user interaction. It is not reset after it has been set initially (unlike transient activation).
> APIs that require sticky activation (not exhaustive):
> - Navigator.vibrate()
> - VirtualKeyboard.show()
> - Autoplay of Media and Web Audio APIs (in particular for AudioContexts).
Before this fix, we could end up with this error in the console in tests:
> Blocked call to navigator.vibrate because user hasn't tapped on the frame or any embedded
<!-- 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. -->
- Allows to copy quotes from mobile
- Allows to copy text of a message from mobile
- Allows to select messages by clicking on it when selection has started
Note this commit is also now using toasts to show a confirmation of copy, and refactors system specs helpers concerning secondary actions.
<!-- 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. -->
This PR introduces three new concepts to Discourse codebase through an addon called "FloatKit":
- menu
- tooltip
- toast
## Tooltips
### Component
Simple cases can be express with an API similar to DButton:
```hbs
<DTooltip
@Label={{i18n "foo.bar"}}
@ICON="check"
@content="Something"
/>
```
More complex cases can use blocks:
```hbs
<DTooltip>
<:trigger>
{{d-icon "check"}}
<span>{{i18n "foo.bar"}}</span>
</:trigger>
<:content>
Something
</:content>
</DTooltip>
```
### Service
You can manually show a tooltip using the `tooltip` service:
```javascript
const tooltipInstance = await this.tooltip.show(
document.querySelector(".my-span"),
options
)
// and later manual close or destroy it
tooltipInstance.close();
tooltipInstance.destroy();
// you can also just close any open tooltip through the service
this.tooltip.close();
```
The service also allows you to register event listeners on a trigger, it removes the need for you to manage open/close of a tooltip started through the service:
```javascript
const tooltipInstance = this.tooltip.register(
document.querySelector(".my-span"),
options
)
// when done you can destroy the instance to remove the listeners
tooltipInstance.destroy();
```
Note that the service also allows you to use a custom component as content which will receive `@data` and `@close` as args:
```javascript
const tooltipInstance = await this.tooltip.show(
document.querySelector(".my-span"),
{
component: MyComponent,
data: { foo: 1 }
}
)
```
## Menus
Menus are very similar to tooltips and provide the same kind of APIs:
### Component
```hbs
<DMenu @ICON="plus" @Label={{i18n "foo.bar"}}>
<ul>
<li>Foo</li>
<li>Bat</li>
<li>Baz</li>
</ul>
</DMenu>
```
They also support blocks:
```hbs
<DMenu>
<:trigger>
{{d-icon "plus"}}
<span>{{i18n "foo.bar"}}</span>
</:trigger>
<:content>
<ul>
<li>Foo</li>
<li>Bat</li>
<li>Baz</li>
</ul>
</:content>
</DMenu>
```
### Service
You can manually show a menu using the `menu` service:
```javascript
const menuInstance = await this.menu.show(
document.querySelector(".my-span"),
options
)
// and later manual close or destroy it
menuInstance.close();
menuInstance.destroy();
// you can also just close any open tooltip through the service
this.menu.close();
```
The service also allows you to register event listeners on a trigger, it removes the need for you to manage open/close of a tooltip started through the service:
```javascript
const menuInstance = this.menu.register(
document.querySelector(".my-span"),
options
)
// when done you can destroy the instance to remove the listeners
menuInstance.destroy();
```
Note that the service also allows you to use a custom component as content which will receive `@data` and `@close` as args:
```javascript
const menuInstance = await this.menu.show(
document.querySelector(".my-span"),
{
component: MyComponent,
data: { foo: 1 }
}
)
```
## Toasts
Interacting with toasts is made only through the `toasts` service.
A default component is provided (DDefaultToast) and can be used through dedicated service methods:
- this.toasts.success({ ... });
- this.toasts.warning({ ... });
- this.toasts.info({ ... });
- this.toasts.error({ ... });
- this.toasts.default({ ... });
```javascript
this.toasts.success({
data: {
title: "Foo",
message: "Bar",
actions: [
{
label: "Ok",
class: "btn-primary",
action: (componentArgs) => {
// eslint-disable-next-line no-alert
alert("Closing toast:" + componentArgs.data.title);
componentArgs.close();
},
}
]
},
});
```
You can also provide your own component:
```javascript
this.toasts.show(MyComponent, {
autoClose: false,
class: "foo",
data: { baz: 1 },
})
```
Co-authored-by: Martin Brennan <mjrbrennan@gmail.com>
Co-authored-by: Isaac Janzen <50783505+janzenisaac@users.noreply.github.com>
Co-authored-by: David Taylor <david@taylorhq.com>
Co-authored-by: Jarek Radosz <jradosz@gmail.com>
Why this change?
Before this change, we were doing a partial match when checking for
existence. This is a source of flaky tests because a chat message with
text `this is a message` will match any substring like `message` or `a`.
What does this change do?
This change removes the partial match and instead opts for the
`exact_text` option instead.
This would cause an error when deleting the original message of a thread, due to the non existing `last_message`. This fix is implemented using the null pattern.
Note this commit is also using this opportunity to unify naming of null objects, `Chat::DeletedUser` becomes `Chat::NullUser`, it feels better to have a name describing what is the object, instead of a name describing why this object has to be used, which can change depending on cases.
Second iteration of https://github.com/discourse/discourse/pull/23312 with a fix for embroider not resolving an export file using .gjs extension.
---
This PR introduces three new concepts to Discourse codebase through an addon called "FloatKit":
- menu
- tooltip
- toast
## Tooltips
### Component
Simple cases can be express with an API similar to DButton:
```hbs
<DTooltip
@label={{i18n "foo.bar"}}
@icon="check"
@content="Something"
/>
```
More complex cases can use blocks:
```hbs
<DTooltip>
<:trigger>
{{d-icon "check"}}
<span>{{i18n "foo.bar"}}</span>
</:trigger>
<:content>
Something
</:content>
</DTooltip>
```
### Service
You can manually show a tooltip using the `tooltip` service:
```javascript
const tooltipInstance = await this.tooltip.show(
document.querySelector(".my-span"),
options
)
// and later manual close or destroy it
tooltipInstance.close();
tooltipInstance.destroy();
// you can also just close any open tooltip through the service
this.tooltip.close();
```
The service also allows you to register event listeners on a trigger, it removes the need for you to manage open/close of a tooltip started through the service:
```javascript
const tooltipInstance = this.tooltip.register(
document.querySelector(".my-span"),
options
)
// when done you can destroy the instance to remove the listeners
tooltipInstance.destroy();
```
Note that the service also allows you to use a custom component as content which will receive `@data` and `@close` as args:
```javascript
const tooltipInstance = await this.tooltip.show(
document.querySelector(".my-span"),
{
component: MyComponent,
data: { foo: 1 }
}
)
```
## Menus
Menus are very similar to tooltips and provide the same kind of APIs:
### Component
```hbs
<DMenu @icon="plus" @label={{i18n "foo.bar"}}>
<ul>
<li>Foo</li>
<li>Bat</li>
<li>Baz</li>
</ul>
</DMenu>
```
They also support blocks:
```hbs
<DMenu>
<:trigger>
{{d-icon "plus"}}
<span>{{i18n "foo.bar"}}</span>
</:trigger>
<:content>
<ul>
<li>Foo</li>
<li>Bat</li>
<li>Baz</li>
</ul>
</:content>
</DMenu>
```
### Service
You can manually show a menu using the `menu` service:
```javascript
const menuInstance = await this.menu.show(
document.querySelector(".my-span"),
options
)
// and later manual close or destroy it
menuInstance.close();
menuInstance.destroy();
// you can also just close any open tooltip through the service
this.menu.close();
```
The service also allows you to register event listeners on a trigger, it removes the need for you to manage open/close of a tooltip started through the service:
```javascript
const menuInstance = this.menu.register(
document.querySelector(".my-span"),
options
)
// when done you can destroy the instance to remove the listeners
menuInstance.destroy();
```
Note that the service also allows you to use a custom component as content which will receive `@data` and `@close` as args:
```javascript
const menuInstance = await this.menu.show(
document.querySelector(".my-span"),
{
component: MyComponent,
data: { foo: 1 }
}
)
```
## Toasts
Interacting with toasts is made only through the `toasts` service.
A default component is provided (DDefaultToast) and can be used through dedicated service methods:
- this.toasts.success({ ... });
- this.toasts.warning({ ... });
- this.toasts.info({ ... });
- this.toasts.error({ ... });
- this.toasts.default({ ... });
```javascript
this.toasts.success({
data: {
title: "Foo",
message: "Bar",
actions: [
{
label: "Ok",
class: "btn-primary",
action: (componentArgs) => {
// eslint-disable-next-line no-alert
alert("Closing toast:" + componentArgs.data.title);
componentArgs.close();
},
}
]
},
});
```
You can also provide your own component:
```javascript
this.toasts.show(MyComponent, {
autoClose: false,
class: "foo",
data: { baz: 1 },
})
```
Co-authored-by: Martin Brennan <mjrbrennan@gmail.com>
Co-authored-by: Isaac Janzen <50783505+janzenisaac@users.noreply.github.com>
Co-authored-by: David Taylor <david@taylorhq.com>
Co-authored-by: Jarek Radosz <jradosz@gmail.com>
This PR introduces three new UI elements to Discourse codebase through an addon called "FloatKit":
- menu
- tooltip
- toast
Simple cases can be express with an API similar to DButton:
```hbs
<DTooltip
@label={{i18n "foo.bar"}}
@icon="check"
@content="Something"
/>
```
More complex cases can use blocks:
```hbs
<DTooltip>
<:trigger>
{{d-icon "check"}}
<span>{{i18n "foo.bar"}}</span>
</:trigger>
<:content>
Something
</:content>
</DTooltip>
```
You can manually show a tooltip using the `tooltip` service:
```javascript
const tooltipInstance = await this.tooltip.show(
document.querySelector(".my-span"),
options
)
// and later manually close or destroy it
tooltipInstance.close();
tooltipInstance.destroy();
// you can also just close any open tooltip through the service
this.tooltip.close();
```
The service also allows you to register event listeners on a trigger, it removes the need for you to manage open/close of a tooltip started through the service:
```javascript
const tooltipInstance = this.tooltip.register(
document.querySelector(".my-span"),
options
)
// when done you can destroy the instance to remove the listeners
tooltipInstance.destroy();
```
Note that the service also allows you to use a custom component as content which will receive `@data` and `@close` as args:
```javascript
const tooltipInstance = await this.tooltip.show(
document.querySelector(".my-span"),
{
component: MyComponent,
data: { foo: 1 }
}
)
```
Menus are very similar to tooltips and provide the same kind of APIs:
```hbs
<DMenu @icon="plus" @label={{i18n "foo.bar"}}>
<ul>
<li>Foo</li>
<li>Bat</li>
<li>Baz</li>
</ul>
</DMenu>
```
They also support blocks:
```hbs
<DMenu>
<:trigger>
{{d-icon "plus"}}
<span>{{i18n "foo.bar"}}</span>
</:trigger>
<:content>
<ul>
<li>Foo</li>
<li>Bat</li>
<li>Baz</li>
</ul>
</:content>
</DMenu>
```
You can manually show a menu using the `menu` service:
```javascript
const menuInstance = await this.menu.show(
document.querySelector(".my-span"),
options
)
// and later manually close or destroy it
menuInstance.close();
menuInstance.destroy();
// you can also just close any open tooltip through the service
this.menu.close();
```
The service also allows you to register event listeners on a trigger, it removes the need for you to manage open/close of a tooltip started through the service:
```javascript
const menuInstance = this.menu.register(
document.querySelector(".my-span"),
options
)
// when done you can destroy the instance to remove the listeners
menuInstance.destroy();
```
Note that the service also allows you to use a custom component as content which will receive `@data` and `@close` as args:
```javascript
const menuInstance = await this.menu.show(
document.querySelector(".my-span"),
{
component: MyComponent,
data: { foo: 1 }
}
)
```
Interacting with toasts is made only through the `toasts` service.
A default component is provided (DDefaultToast) and can be used through dedicated service methods:
- this.toasts.success({ ... });
- this.toasts.warning({ ... });
- this.toasts.info({ ... });
- this.toasts.error({ ... });
- this.toasts.default({ ... });
```javascript
this.toasts.success({
data: {
title: "Foo",
message: "Bar",
actions: [
{
label: "Ok",
class: "btn-primary",
action: (componentArgs) => {
// eslint-disable-next-line no-alert
alert("Closing toast:" + componentArgs.data.title);
componentArgs.close();
},
}
]
},
});
```
You can also provide your own component:
```javascript
this.toasts.show(MyComponent, {
autoClose: false,
class: "foo",
data: { baz: 1 },
})
```
Co-authored-by: Martin Brennan <mjrbrennan@gmail.com>
Co-authored-by: Isaac Janzen <50783505+janzenisaac@users.noreply.github.com>
Co-authored-by: David Taylor <david@taylorhq.com>
Co-authored-by: Jarek Radosz <jradosz@gmail.com>
On mobile swiping a channel row will now show a "Remove" option. Holding this to the end will now remove this row from your list of followed direct message channels.
Co-authored-by: chapoi <101828855+chapoi@users.noreply.github.com>
This PR swaps out the custom pathway to publishing and rendering mention warnings after a message is sent.
ChatPublisher#publish_notice is used, and expanded. Now, instead of only accepting text_content as an argument, component and component_args are accepted and there is a renderer for these components.
Translations moved to server, as notices expect text to be passed in unless a component is rendered
The warnings are rendered at the top now, outside of the scope of the single message that sent it.
I entirely removed the jit_messages_spec b/c it's duplicate testing of other parts of the app. IMO we don't need a backend test for a feature, a component test for the feature AND a system test (that is slow and potentially even flakey due to timing issues with wait) to test the same thing. So jit_messages_spec is gone.
This is extracted from #22390.
This patch aims to ease the transition to the new message creation
service. (in progress in #22390) Indeed, the new service patch is
breaking some specs from `discourse-ai` and `discourse-templates`
because these plugins are using either `Chat::MessageCreator` or the
`chat_message` fabricator.
This patch addresses theses issues by normalizing how we create a chat
message in specs. To do so, the preferred way is to use
`Fabricate(:chat_message)` with a new `:use_service` option allowing to
call the service under the hood. While this patch will obviously call
`Chat::MessageCreator`, the new service patch will now be able to simply
change the call to `Chat::CreateMessage` without breaking any specs from
other plugins.
Another thing this patch does is to not create chat messages using the
service for specs that aren’t system ones, thus speeding the execution
time a bit in the process.
Tries to fix the composer upload spec by making the upload
slow enough to allow clicking the Cancel button, and improves
generally the API for CDP network changes.
- drop @
- prevents +X (participants) to show on next line
- few spacing/fonts adjustments
Note that this commit is also stripping links from chat excerpts.
It will now replies count and participants list. Also the title will be OM excerpt or user defined title, no more default "Thread" title. Lastly, the author of the last reply is also shown as prefix of it.
This could happen after you had already change the separation mode and would cause unexpected bugs.
This PR also adds more tests around using switch buttons with chat.
Prior to this fix we would test by visiting the tab which could create a false positive, as the tab could not be present but we could still access the tab, the implementation and tests have been changed to correctly ensure this.
This is also fixes the issue of chat composer warnings persisting across channels. Currently if you try to mention more groups than is allowed for example, a mention warning pops up. When you change channels the mention warning will not disappear even if there is no text in the composer.
This adds a reset function to the chat-composer-warnings-tracker.js, which is called when the channel is changed and the message is empty. In the event that the message is not empty we call captureMentions to check the loaded drafts' mentions.
This PR would be nicer if the post-send notice used the new chat notices API to publish the mention warnings but we would have to change the existing ones and I thought that would be too much change for this PR. It'd be a good followup though.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
Prior to this change you might end up in a loop where removing a channel would redirect you to this channel and as we auto-follow opened direct message channels, you could never unfollow this last direct message channel.
This commit attempts to fix an issue where we are ending
up with bad created_at date formats for last messages, which
is breaking the DM sort order and sometimes causing DM channels
to fall off the list, or show "Invalid date" on mobile.
I have not been able to consistently reproduce these issues
locally, however the serialzier for the channels index uses
MultiJSON.dump() and the Chat::Publisher uses .to_json, both of
which format created_at differently for messages.
The former is `2023-07-05T06:53:25.977Z` (iso8601).
The latter is `2023-07-14 03:59:22 UTC` (.to_s default).
Since we are doing comparison and sorting of these dates on the UI
we need consistent formatting for the JS Date parsers (and moment)
to deal with.
If the issue still occurs after this we can investigate further.
It could only occur on message created by the user itself and deleted while the user was looking at the channel.
It more generally fix the trash service which was not correctly setting the author of the delete.
`SiteSetting.enable_public_channels` allows site admin to decide if public channels are available at all. There's no distinction between admins or not as we expect admins to create private category channels if they want to limit usage.
Not sure how this was even working previously, since it's trying
to press the reply button on a thread original message, which doesn't
work, you need to click the indicator to open the thread.
Why this change?
The following test is flaky on our CI:
```
1) Navigation when sidebar is configured as the navigation menu when re-opening full page chat after navigating to a channel opens full page chat on correct channel
Failure/Error: measurement = Benchmark.measure { example.run }
expected "/" to equal "/chat/c/random-9/17"
```
The theory here is that system tests is running too fast that we're not
giving the href for the chat header icon a chance to update before
clicking on it. Therefore, we're adding an additional assertion to
assert that the link has the right href before clicking on it.
Initial migration and changes to models as well as
changing the following services to update last_message_id:
* Chat::MessageCreator
* Chat::RestoreMessage
* Chat::TrashMessage
The data migration will set the `last_message_id` for all existing
threads and channels in the database.
When we query the thread list as well as the channel,
we look at the last message ID for the following:
* Channel - Sorting DM channels, and channel metadata for the list of channels
* Thread - Last reply details for thread indicators and thread list
It is now safe to render the message excerpt as HTML since
it is no longer using text_entities: true in the server
PrettyText.excerpt call when creating the message excerpt
from the cooked HTML.
This will fix the issue of things like mentions showing
HTML code instead of the actual mention when replying,
and cannot be used to inject improper HTML like style tags
via XSS.
This commit makes sure we don't load all data into memory when doing CSV exports.
The most important change here made to the recently introduced export of chat
messages (3ea31f4). We were loading all data into memory in the first version, with
this commit it's not the case anymore.
Speaking of old exports. Some of them already use find_each, and it worked as
expected, without loading all data into memory. And it will proceed working as
expected after this commit.
In general, I made sure this change didn't break other CSV exports, first manually, and
then by writing system specs for them. Sadly, I haven't managed yet to make those
specs stable, they work fine locally, but flaky in GitHub actions, so I've disabled them
for now.
I'll be making more changes to the CSV exports code soon, those system specs will be
very helpful. I'll be running them locally, and I hope I'll manage to make them stable
while doing that work.
This implementation will need more work in the future. For simplification of tracking and other events (new thread, delete/restore OM...) we used the threads from `threadsManager` which makes pagination more complicated as we already have some results when we start.
Note this commit also simplify `Collection` to only have one `load` method which can be called repeatedly.
Why this change?
The specs are flaky on CI and we've unable to figure out why so we've
decided to skip them only on CI for now. The tests are still ran in our
internal build so we still have some protection in place.
Why is this change being made?
We've decided that the previous "community" section should look more
like a primary section that holds the most important navigation links
for the site and the word "community" doesn't quite fit that
description. Therefore, we've made the decision to drop the
section heading for the community section.
As part of removing the section heading, the following changes are made
as well:
1. Button to customize the section has been moved to the "footer" of the
"More..." section when `navigation_menu` site setting is set to `sidebar`.
When `navigation_menu` is set to `header dropdown`, a button to customize
the section is shown inline.
2. The section will no longer be collapsable.
3. The title of the section is no longer customisable as it is no longer
displayed. As a technical note, we have not dropped any previous
customisations of the section's title previously in case we have to
bring back the header in the future.
4. The new topic button that was previously present in the header has
been removed alongside the header. Admins can add a custom section
link to the `/new-topic` route if there would like to make it easier for
users to create a new topic in the sidebar.
This commit also standardize the naming pattern of modals: `<Chat::Modal::FooBar />` and changes css class accordingly.
Co-authored-by: David Taylor <david@taylorhq.com>
When the loading slider is enabled, the rendering of `application.hbs` is slightly delayed compared to the old 'spinner' strategy. This means that if a route tried to render a dialog during its `model()` hook, the dialog wrapper element would not be present and an error would occur.
This commit detects that situation and delays rendering the error until the next runloop iteration. If the element is still not found, we print a useful error to the console.
In the long term, we should ideally convert the dialog service to use a pure-ember rendering strategy instead of leaning on a11y-dialog. But for now, this workaround should resolve the problems identified by the chat system specs.
It's way more common to have presence enabled than disabled, so we should have been making it the default from start.
This commit also changes the namespace of `<ChatUserAvatar />` into `<Chat::UserAvatar />` and refactors tests.
When a user sends their first message in a thread we
automatically track the thread in the backend, but we
don't reflect this in the UI until the user re-opens
the thread. This commit fixes that by showing the new
tracking level in the UI.
Chat drawer was using the `DiscourseURL` hook `afterRouteComplete`. This hook suffer from a very poor implementation which makes it very unreliable:
```javascript
if (typeof opts.afterRouteComplete === "function") {
schedule("afterRender", opts.afterRouteComplete);
}
```
This commit attempts to return the promise from `handleURL` to directly use it and have a very reliable after transition hook.
In previous changes we prevented creating a channel to also make users follow the channel. We were forcing recipients to follow the channel on message sent but this was not including the creator of the message itself.
This commit fixes it and also write an end-to-end system spec to cover these cases. The message creator service is currently being rewritten and should correctly test and ensure this logic is present.
This commit also makes changes on the frontend to instantly follow a DM when you open it, this change prevents a green dot to appear for a split second when you send a message in a channel you were previously not following. Only recipients will see the green dot.
Followup to 802fb3b194
We should not hide the replies count if there is only 1 participant
for a thread, because this makes it look like the last reply is the
only reply.
This introduces a PLATFORM_KEY_MODIFIER const that
can be used both client and server side, to determine
whether we should be using the Meta or Ctrl key based
on whether the user is on Windows/Linux or Mac.
Why this change?
This change ensures that we scroll to the top of the message when
hovering over a message to ensure that the message actions container
that appears on hover is not hidden in the chat drawer when the content
of the chat message is long.
This commit includes several fixes and improvements to thread
original message handling:
1. When a thread's original message is deleted, the thread no longer
counts as unread for a user
2. When a thread original message is deleted and the user is looking
at the thread list, it will be removed from the list
3. When a thread original message is restored and the user is looking
at the thread list, it will be added back to the list if it was
previously loaded
In specific conditions (generally a small drawer, with a long message) it is possible to have the message’s actions menu to be displayed hover the drawer's header.
This is particularly hard to fix correctly using popper due to our positioning which is slightly at the limit of the container.
The proposed fix targets mostly the specs by ensuring the messages actions will be hidden before attempting to click any header's button.
Why this change?
In CI, we know we're clicking a link to a chat channel's threads list.
However, the threads list is not loaded and we want to add more
assertions here to try and figure out why. By asserting for the current
URL, we will at least know that the transition to the URL is successful.
- Presence needs to be explicitly set on the component now
- We were not checking and testing correctly the presence of the unread indicator in the menu
This commit replaces two existing screens:
- draft
- channel selection modal
Main features compared to existing solutions
- features are now combined, meaning you can for example create multi users DM
- it will show users with chat disabled
- it shows unread state
- hopefully a better look/feel
- lots of small details and fixes...
Other noticeable fixes
- starting a DM with a user, even from the user card and clicking <kbd>Chat</kbd> will not show a green dot for the target user (or even the channel) until a message is actually sent
- it should almost never do a full page reload anymore
---------
Co-authored-by: Martin Brennan <mjrbrennan@gmail.com>
Co-authored-by: Jordan Vidrine <30537603+jordanvidrine@users.noreply.github.com>
Co-authored-by: chapoi <101828855+chapoi@users.noreply.github.com>
Co-authored-by: Mark VanLandingham <markvanlan@gmail.com>
Why this change?
Chat system tests that opens the message actions on mobile have been
flaky on our CI. Those system test usually fails when the message
actions do not show up as expected causing subsequent actions to fail.
In the case of the `Reply to message - channel - mobile when the message has an existing thread replies to the existing thread`
system test, failure screenshot shows that we ended up navigating to the
thread instead of opening the message actions button. To understand why
this happens, we first need to understand that by default Capybara clicks
on the centre of an element. Also, we need to note that the HTML structure of
a chat message is like so:
```
<div class="chat-message-container">
<div class="chat-message">
<div class="chat-message-avatar" />
<div class="chat-message-content" />
<div class="chat-message-thread-indicator" />
</div>
</div>
```
Since `PageObjects::Pages::ChatChannel#expand_message_actions_mobile`
attempts to click on the `.chat-message-contaier`, there is a
possibility that the center of that element is the
`.chat-message-thread-indicator` element which would explain why we
navigated to the thread list instead of opening up the message actions.
This is possible because the content of the original chat message as
well as the message excerpt in the thread is randomly generated where the
length of the message and how the text wraps on mobile can affect the
height of the `.chat-message-content` element as thus its position in
the `.chat-message-container` element. In most cases, the middle of the
`.chat-message-container` happens to be the `.chat-message-content`
which is why this test "flakes" sometimes.
What is the solution?
Instead of clicking on the `.chat-message-container`, we be more
specific and click on the `.chat-message-content` element instead.
* DEV: Fix flaky thread nav spec
When we transitioned from the chat thread panel under some conditions
the request for the thread would come back and realise the component
was destroyed, which was trying to do a transition to the channel
itself.
Now we check for the previous route here too and transition to the
correct route.
* DEV: Fix chat transcript spec relying on animation
The on-animation-end modifier is not reliable in system specs
because it fires instantly (we have disabled capybara animations)
so the showCopySuccess boolean can be mutated back to false straight
away.
Better to have a separate boolean tracked with a data-attr that we
can reliably inspect in the system spec.
Why this change?
By ensuring the reset happens in an `ensure` code block, we ensure that
the code will always be run even if code fails or an error is raised.
This helps to prevent leaking custom network condition states and
improves the stability of our system tests.
- Inline mentions on posts
- Inline mentions on chat messages
- The user autocomplete for the composer
- The user autocomplete for chat
- The chat section of the sidebar
This will be used when we move the channel creation for DMs
to happen when we first send a message in a DM channel to avoid
a double-request. For now we can just have a new API endpoint
for creating this that the existing frontend code can use,
that uses the new service pattern.
This also uses the new policy pattern for services where the policy
can be defined in a class so a more dynamic reason for the policy
failing can be sent to the controller.
Co-authored-by: Loïc Guitaut <loic@discourse.org>
This has been flagged by our internal system as well and has been
failing on CI. Skip this for now to improve the stability of our system
test runs while we figure out why it is flaky.
Enabling/Disabling threading has been possible through command line until now. This commit introduces two new UIs:
- When creating a channel, it will be available once the category has been selected
- On the settings page of a channel for admins
Followup to c6b43ce68b
We can just use the rich excerpt everywhere since we know
we don't need text_entities -- that introduced security issues
just to fix a spec.
Introduced in cec68b3e2c,
this is flaky because if you click the back button before
the route is fully transitioned to the loaded thread,
we end up going to the history _before_ the thread list,
which ends up being the channel.
We need to make sure that everything is loaded for the
thread first, meaning the skeleton is not there.
Also exclude some noise from the capybara logs (image load failures)
* FEATURE: Sort thread list by unread threads first
This commit changes the thread list to show the threads that
have unread messages at the top of the list sorted by the
last reply date + time, then all other threads sorted by
last reply date + time.
This also fixes some issues by removing the last_reply
relationship on the thread, which did not work for complex
querying scenarios because its order would be discarded.
* FIX: Various fixes for thread list loading
* Use the channel.threadsManager and find the channel first rather
than use activeChannel in the threads manager, otherwise we may
be looking at differenct channels.
* Look at threadsManager directly instead of storing result for threads
list otherwise it can get out of sync because of replace: true in
other places we are loading threads into the store.
* Fix sorting for thread.last_reply, needed a resort.
When clicking back from a thread, we want to either go back to the
channel if the thread was opened from an indicator, or to the thread
list if we opened it from there. Since ember doesn't give a nice way
to get the previous route, we need to store this ourselves. We only
do this on mobile, on desktop we just follow existing behaviour.
Also implements a chat router history.
---------
Co-authored-by: Joffrey JAFFEUX <j.jaffeux@gmail.com>
Updates the interface for implementing summarization strategies and adds a cache layer to summarize topics once.
The cache stores the final summary and each chunk used to build it, which will be useful when we have to extend or rebuild it.