Commit Graph
100 Commits
Author SHA1 Message Date
Joffrey JAFFEUX 875966cc9e FIX: ensures border and shadow of header is visible (#24847) 2023-12-12 14:18:36 +01:00
Joffrey JAFFEUX fca67117ee FIX: incorrect spacing with my threads on ios (#24843)
- sticky doesn't work well with overflow: hidden parents. These overflows were used to hide other issues which shouldn't exist anyways. If it causes issues we should fix the root cause.

- our `--header-offset` is changing a lot on safari while scrolling, sometimes with very unexpected value like: negative or very high value, which causes the navbar to appear at unexpected positions for few ms, this commit is using the value of the header on insert and not changing it after, it shouldn't cause any issue.
2023-12-12 11:32:36 +01:00
Joffrey JAFFEUX dacb06842e FIX: allow to click on thread indicator (#24821)
We can also now control tabindex, and other html attributes, which allows to lick on the thread indicator but not tab to it for example.

By default the tabindex will be 0.
2023-12-11 16:35:29 +01:00
Joffrey JAFFEUX a7a4aaa6b9 FIX: ensures side panel is closed (#24822)
When navigating to /chat/threads we were not closing the side panel. It would not show as the route doesn't support it, but after if we would open a channel from the sidebar it would open the channel with an empty opened sidepanel .
2023-12-11 16:35:18 +01:00
Joffrey JAFFEUXandchapoi 2e25e95ce1 UI: ensures emojis are correctly aligned in reactions (#24814)
Co-authored-by: chapoi <101828855+chapoi@users.noreply.github.com>
2023-12-11 14:29:16 +01:00
Joffrey JAFFEUX 54724f7c09 UX: allow users to click thread title to open it (#24816) 2023-12-11 13:54:00 +01:00
Joffrey JAFFEUX 09277bc543 FEATURE: my threads page (#24771)
This commit adds a new "My threads" link in sidebar and drawer. This link will open the "/chat/threads" page which contains all threads where the current user is a member. It's ordered by activity (unread and then last message created).

Moreover, the threads list of a channel page is now showing every threads of a channel, and not just the ones where you are a member.
2023-12-11 07:38:07 +01:00
Joffrey JAFFEUX c5aa6b5e16 FIX: correctly update replies_count on chat_threads (#24711)
The previous query would look at the existing messages, count them, and update the associated thread.

But, if for some reason messages were **ALL** deleted without updating the `replies_count`, then the query wouldn't find any message, and wouldn't update any thread's `replies_count`.
2023-12-05 13:46:58 +01:00
Joffrey JAFFEUX 094d597ec8 FIX: increments message version when processed (#24713)
Incrementing version, ensures the message will re-play decorators.

Also removes a debugging line which had been forgotten.
2023-12-05 12:34:19 +01:00
Joffrey JAFFEUX c6ee647b12 DEV: skip failing spec du to refactoring modal for now (#24712) 2023-12-05 11:47:39 +01:00
Joffrey JAFFEUX c805bcc7ba DEV: adds a chat-join-channel-button outlet (#24698)
This outlet allows to redefine the button displayed when asking the user to join a channel.

The following outletArgs are sent to the outlet:

```
onJoinChannel
channel
icon
title
label
disabled
```
2023-12-04 18:11:33 +01:00
Joffrey JAFFEUX 3ed1528327 FIX: failing spec in sidebar tags (#24682)
The modal is now larger and more tags will be shown by default, we need more tags to correctly test the scroll behavior.
2023-12-04 12:46:53 +01:00
Joffrey JAFFEUX bcca1692c6 FIX: disables tooltip on desktop message menu’s reaction (#24650) 2023-11-30 19:41:28 +01:00
Joffrey JAFFEUX a7a7d8e41e DEV: correctly uses link to message endpoint in spec (#24652)
- correctly uses link to message endpoint in spec
- correctly uses "chat with"
2023-11-30 19:33:41 +01:00
Joffrey JAFFEUX 4fa1b0e978 FIX: show leave channel notice only on group channels (#24653) 2023-11-30 19:24:14 +01:00
Joffrey JAFFEUX a1ae9f6ab0 FIX: correctly show "chat with" and not "chat in" for users (#24651)
Prior to this fix direct message would always show "Chat in ..." when hovering the channel even if you were hovering a direct message channel with another user (or yourself).

We will now correctly show:

- `Chat in ...` for group channels
- `Chat with ...` for direct message channels
2023-11-30 19:02:19 +01:00
Joffrey JAFFEUX a1690e0401 DEV: removes dead code (message_link) (#24648)
Linking to a message ID is now handled by the frontend router: `chat.channel.near-message`
2023-11-30 11:13:37 +01:00
Joffrey JAFFEUX 0b65aa8b77 DEV: ensures we have error message before message check (#24647)
Previously the spec could be flakey as the long message could show on the screen while we await for processing. Now we will first check to have the error message on screen, at this point the erroneous message should never be visible.
2023-11-30 10:55:05 +01:00
Joffrey JAFFEUX 384a8b17a1 FIX: leaving a group channel should destroy membership (#24631)
In other kind of channels we will only unfollow but for group channels we don't want people to keep appearing in members list.

This commit also creates appropriate services:
- `Chat::LeaveChannel`
- `Chat::UnfollowChannel`

And dedicated endpoint for unfollow: `DELETE /chat/api/channels/:id/memberships/me/follows`
2023-11-29 17:48:14 +01:00
Joffrey JAFFEUX ee5bdb3436 DEV: refactor flag message (#24604)
- Uses a chat service: `Chat::FlatMessage`
- Moves logic inside chat api controllers
- Create a javascript chat api helper: `chatApi.flagMessage(...)`
2023-11-28 18:24:09 +01:00
Joffrey JAFFEUX 2befff5101 FIX: nullifies target message id when not readable (#24540)
This bug was very reproducible when your last read was a message you didn't read and an admin would delete it. When coming back to the channel you would get a not found, in this case we will now reset last read and present you the last message of the channel.

We could be more fancy and  try to detect the next readable message but that would be more code and complexity for such a rare case.
2023-11-24 11:46:00 +01:00
Joffrey JAFFEUX d667c22171 FIX: prevents input to reset at wrong moment (#24536)
Before this fix we would reset the input two times:

- right before sending message
- and after it's been sent

The second one is actually not necessary, and more over with the server delay the user could have started typing a new message and that would clear it.
2023-11-24 10:59:48 +10:00
Joffrey JAFFEUX 57584c38c0 FIX: correctly uses private_email site setting in chat (#24528)
Chat will now check for the state of `SiteSetting.private_email` when sending the summary, when enabled, the mail will not display user information, channel information other than the ID and no message information, only the count of messages.
2023-11-23 15:54:22 +01:00
Joffrey JAFFEUX 17033d46c3 DEV: cooks messages synchronously (#24510)
Mentions and other post processing (like images) are still done asynchronously in the background. This should ensure reloading a channel while the message has not been processed yet doesn’t renders a blank message.

As a followup, we could probably simplify the staged message logic, given we have the new cooked on send.
2023-11-22 13:00:23 +01:00
Joffrey JAFFEUX 906caa63d7 FEATURE: implements drafts for threads (#24483)
This commit implements drafts for threads by adding a new `thread_id` column to `chat_drafts` table. This column is used to create draft keys on the frontend which are a compound key of the channel and the thread. If the draft is only for the channel, the key will be `c-${channelId}`, if for a thread: `c-${channelId}:t-${threadId}`.

This commit also moves the draft holder from the service to the channel or thread model. The current draft can now always be accessed by doing: `channel.draft` or `thread.draft`.

Other notable changes of this commit:
- moves ChatChannel to gjs
- moves ChatThread to gjs
2023-11-22 11:54:23 +01:00
Joffrey JAFFEUX 60535e8f3c FIX: relies on mention mixin for size (#24503)
This commit fixes a regression where non highlighted mentions have an incorrect size.
2023-11-21 23:14:09 +01:00
Joffrey JAFFEUX 2b7ecee06e FIX: correctly highlights message on reply click (#24431) 2023-11-17 18:05:38 +01:00
Joffrey JAFFEUX ee2e1e04a8 UX: shorter copy link confirmation (#24380) 2023-11-15 11:30:27 +01:00
Joffrey JAFFEUX 016e91380c FIX: correct online indicator for non interactive (#24364)
When introducing non interactive user avatar, the `chat-user-avatar__container` div has been omitted, which prevented the css to correctly apply.
2023-11-14 11:46:50 +01:00
Joffrey JAFFEUX b1cff6de6a UX: shows editing title only on settings page (#24330) 2023-11-14 11:34:54 +01:00
Joffrey JAFFEUX a0769f6f43 UX: improves search of message creator
- correctly show unread indicator
- handles disabled user correctly
- do not show new-group-chat when filtering
2023-11-13 22:03:47 +01:00
Joffrey JAFFEUX 1dad379412 DEV: allows to wrap wizard checkbox with outlet (#24334) 2023-11-13 14:02:14 -05:00
Joffrey JAFFEUX 7f3f682f67 DEV: removes dead code (#24345) 2023-11-13 15:18:15 +01:00
Joffrey JAFFEUX 208491fa00 DEV: uses alphanumeric to avoid flakey specs (#24344)
Faker text with quotes or thread dots for example would cause issues in tests due to cooking.
2023-11-13 14:54:26 +01:00
Joffrey JAFFEUX ab832cc865 FEATURE: introduces group channels (#24288)
Group channels will allow users to create channels with a name and invite people. It's possible to add people even after creation of the channel. Removing users is not yet possible but will be added in the near future.

Technically a group channel is `direct_message_channel` with a group attribute set to true on its direct message (chatable). This model might evolve in the future but offers much flexibility for now without having to rely on a complex migration.

The commit essentially consists of:
- a migration to set existing direct message channels with more than 2 users to a group
- a new message creator which allows to search, add members, and create groups
- a new `AddUsersToChannel` service
- a modified `SearchChatable` service
2023-11-10 11:29:28 +01:00
Joffrey JAFFEUX dcaa719363 FIX: correctly handle subscriptions (#24270)
Subscriptions manager have been a pain since the beginning, one of the problem is that thread and channels behave mostly the same but with various small difference which I expect to increase over time.

Trying to use subclasses for this case has proven to be a mistake, this commit now uses a class for each case (channel, thread) which for now contains a lot of duplication, which might be reduced in the future but has the merit to make reasoning about each case very simple.

This refactor is fixing a bug introduced in https://github.com/discourse/discourse/commit/90efdd7f9d74677f0f1a0eb7ccbe55f2b3cefa11 which was causing the wrong channel to be unsubscribed, this shouldn't be possible anymore. We had tests for this which were disabled due to flakeyness, I will consider re-enabling them in the future.

Other notes:
- notices had been added to the subscriptions manager service, they have been moved into their own dedicated service: `ChatChannelNoticesManager`
- the `(each model)` trick used in `<ChatChannel />` since https://github.com/discourse/discourse/commit/90efdd7f9d74677f0f1a0eb7ccbe55f2b3cefa11 to ensure atomicity has been applied to `<ChatThread />` too
2023-11-07 16:37:42 +01:00
Joffrey JAFFEUX 039d060832 DEV: improves reliability of delete/restore/update specs (#24265) 2023-11-07 11:34:35 +01:00
Joffrey JAFFEUX 2797e0478e FIX: target chat-message-text only on secondary action (#24255)
This is not necessary for other cases and was breaking specs. This commit also avoids using chat service when not necessary.
2023-11-07 00:45:13 +01:00
Joffrey JAFFEUX 2bd4cf69d0 FIX: targeting chat-message-text is more reliable (#24245) 2023-11-07 00:05:53 +01:00
Joffrey JAFFEUX 90efdd7f9d PERF: cook message in background (#24227)
This commit starts from a simple observation: cooking messages on the hot path can be slow. Especially with a lot of mentions.

To move cooking from the hot path, this commit has made the following changes:

- updating cooked, inserting mentions and notifying user of new mentions has been moved inside the `process_message` job. It happens right after the `Chat::MessageProcessor` run, which is where the cooking happens.
- the similar existing code in `rebake!` has also been moved to rely on the `process_message`job only
- refactored `create_mentions` and `update_mentions` into one single `upsert_mentions` which can be called invariably
- allows services to decide if their job is ran inline or later. It avoids to need to know you have to use `Jobs.run_immediately!` in this case, in tests it will be inline per default
- made various frontend changes to make the chat-channel component lifecycle clearer. we had to handle `did-update @channel` which was super awkward and creating bugs with listeners which the changes of the PR made clear in failing specs
- adds a new `-processed` (and `-not-processed`) class on the chat message, this is made to have a good lifecyle hook in system specs
2023-11-06 15:45:30 +01:00
Joffrey JAFFEUX 4859340b2d FIX: correctly display escaped thread titles (#24159)
Prior to this fix, titles with a quote `'` for example, would be rendered as: `&#x27`
2023-10-30 21:06:31 +01:00
Joffrey JAFFEUX db880d8ed7 DEV: adds a :chat_thread_created trigger (#24133)
Usage:

```ruby
DiscourseEvent.on(:chat_thread_created) do |thread|
end
```
2023-10-27 10:27:34 +02:00
Joffrey JAFFEUX ce801e3ff0 UX: improves support of window resize in chat (#24115)
- correctly respects min-width/height defined in css
- removes fixed width/height when resizing window
- reduces the min width of the side panel from 250px to 150px
2023-10-26 20:26:59 +02:00
Joffrey JAFFEUX becad4d7d7 FIX: do not check if inside cooked early (#24105)
We already do this check inside `selectionChanged` and this was preventing us to correctly set `isSelecting` to true. This was causing issues when starting your selection from outside cooked.
2023-10-25 22:50:32 +02:00
Joffrey JAFFEUX 619d709d54 FIX: clear state if selected text is empty (#24102)
This would cause a loop and some very fast flashing of the selection in safari when scrolling the selection until the bottom of the page.
2023-10-25 22:15:30 +02:00
Joffrey JAFFEUX 334be4eac7 UX: improve quote/edit bar's rendering (#24097)
This change allows for a faster and smoother experience. It's also less noisy because the buttons will not be shown while selecting text.
2023-10-25 12:51:47 -04:00
Joffrey JAFFEUX 0e37ceeeb9 FIX: improves reliability of post-text-selection bar (#24093)
The main change made is to use `pointerdown` and `touchstart` for detecting click outside in `FloatKit`, the problem of using `click` is that it will trigger on `mouseup` which is not working well with `FloatKit` shown using `mousedown` (when we change selection with the `mousedown` for example) as the release will be interpreted as a click outside and close the menu. To solve this issue the previous code in `post-text-selection` was going through various hacks for detecting state of mouse which are not always very reliable.

The second fix is to exit earlier when selection didn't change.

This has been tested on chrome/firefox and safari (mobile) and seems to work reliably.

<!-- 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. -->
2023-10-25 14:34:53 +02:00
Joffrey JAFFEUX 5fec841c19 FIX: ensures users can open channel invites (#24067)
We were incorrectly generating URLs with message id even when it was not provided, resulting in a route ending with "undefined", which was causing an error.

This commit also uses this opportunity to:
- move `invite_users` into a proper controller inside the API namespace
- refactors the code into a service: `Chat::InviteUsersToChannel`
2023-10-24 18:51:33 +02:00
Joffrey JAFFEUX 043b4a4187 FIX: replaces data-tooltip usage by <DTooltip /> (#24062)
As much as possible I would like us to avoid having to go the with a global event listener on click/mouseover. For now I have removed all cases of `data-tooltip`, if we clearly identify a use case of a global event listener we might reconsider this.

The following changes are also included:
- by default tooltips won't attempt to focus first focusable element anymore
- tooltip will now use `cursor: pointer` by default
- a new service has been introduced: `InternalTooltip` which is responsible to track the current instance displayed by a `<DTooltip />`. Portal elements when replaced are not properly cleaned and I couldn't figure out a way to have a proper hook to ensure the previous `DTooltipInstance` is properly set as not expanded; this problem was very visible when using a tooltip as interactive and hovering another tooltip, which would replace the interactive tooltip as not closed.
2023-10-23 21:09:02 +02:00
Joffrey JAFFEUX 552cf56afe DEV: toasts improvements (#24046)
- more subtle animation when showing a toast
- resumes auto close when removing the mouse from the toast
- correctly follows reduced motion
- uses output with role status as element: https://web.dev/articles/building/a-toast-component
- shows toasts inside a section element
- prevents toast to all have the same width
- fixes a bug on mobile where we would limit the width and the close button wouldn't show correctly aligned

I would prefer to have tests for this, but the conjunction of css/animations and our helper changing `discourseLater` to 0 in tests is making it quite challenging for a rather low value. We have system specs using  toasts ensuring they show when they should.
2023-10-23 15:23:10 +02:00
Joffrey JAFFEUX 7c057878e2 DEV: skips two flakey specs (#24044) 2023-10-22 18:50:04 +02:00
Joffrey JAFFEUXandDavid Taylor bc9558550d DEV: replace registerUnbound usage with default exports (#23802)
`registerUnbound` was present for legacy reasons when using helpers in raw-hbs and has been replaced by `registerRawHelper`.

For new helpers used only in classic ember template, exporting a default function from `helpers/*.js` is recommended.

This change also means that all existing helpers will be available to import in `gjs` files.

Co-authored-by: David Taylor <david@taylorhq.com>
2023-10-19 14:28:25 +01:00
Joffrey JAFFEUXandPenar Musaraj d1ef6ab99f FIX: do not serialize status when profile is hidden (#23946)
Users can decide to hide their profile and presence. It seems more correct to also not return the status in this case.

Co-authored-by: Penar Musaraj <pmusaraj@gmail.com>
2023-10-16 17:43:39 +02:00
Joffrey JAFFEUX b6d9aa5a4c DEV: simplify reply to message smoke spec (#23928)
We now create threads on reply so the refresh check is not really necessary as there's nothing special about it anymore. We don't refresh every pages in other tests to check they still work.

Hopefully these changes will prevent few flakeys too.
2023-10-13 11:16:26 +02:00
Joffrey JAFFEUX c1abf8b35c UX: improves reminder setting text (#23918)
The setting will change from "%{count} days" to "Chat settings have been set to retain channel messages for %{count} day."

This commit also:
- migrates `chat-retention-reminder` to gjs
- adds a "type" property to `chat-retention-reminder-text` to allow use a long or short text depending on where it's used.
2023-10-13 07:55:47 +02:00
Joffrey JAFFEUX b122e69ed3 DEV: flakey members list spec (#23917)
No repro but, it's possible that we were at the limit of triggering the scroll, this should ensure we correctly trigger the scroll more.
2023-10-12 22:32:03 +02:00
Joffrey JAFFEUX b77b0ee1c8 UX: opens thread on channel with unread threads (#23361)
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.
2023-10-11 12:19:30 +02:00
Joffrey JAFFEUX f25388428e FIX: ensures threads list button is not showing (#23895)
We were incorrectly showing it when the the panel is opened.
2023-10-11 11:46:00 +02:00
Joffrey JAFFEUX a39ff830e8 UX: makes avatar non interactive in thread participants list (#23847)
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
2023-10-09 21:12:50 +02:00
Joffrey JAFFEUX 245e9f30a4 FIX: minor chat styleguide fixes (#23849)
- makes message take full width
- allows to remove the thread state
- removes non-existing component (mention warning)
2023-10-09 18:25:57 +02:00
Joffrey JAFFEUX f13f2fecfe FIX: correctly reset active message on destroy (#23844)
It should prevent any message action to still show after a transition while hovering a message (eg: link to channel settings from a message).
2023-10-09 15:16:42 +02:00
Joffrey JAFFEUX 2ac2c846bc FIX: uses correct label on mobile view for members (#23845) 2023-10-09 15:15:42 +02:00
Joffrey JAFFEUX 42801c950f UI: redesigned settings/members (#23804)
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
2023-10-09 14:11:16 +02:00
Joffrey JAFFEUX cae282029e DEV: prevents flakey spec with dots (#23843)
Faker can generate test containing `...` which will get converted to `…` by `PrettyText`, it means that we can't use the input to check the output. This commit simply normalise the generated text to ensure this part of the input is not modified.
2023-10-09 12:23:48 +02:00
Joffrey JAFFEUX c970cbeac4 DEV: ensures thread panel has been opened (#23836)
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.
2023-10-09 09:30:18 +02:00
Joffrey JAFFEUX dfe840f71d FIX: properly create a mention when followed by dots (#23829)
At the moment writing a mention similar to `@bob...hi` would have resulted in chat trying to find a user named `bob...hi` which would fail.

This was due to the `replacements` rule not being present in the rules used to cook chat messages.

We are still missing few default rules like: normalize, smartquotes, text_join, ... which don't seem to be necessary but could be added if we found a reason for. More info at: https://github.com/markdown-it/markdown-it/blob/e476f78bc3ea3576beb61bdc94322d0a6b2d85cc/lib/parser_core.js
2023-10-09 08:30:18 +02:00
Joffrey JAFFEUX b4c4f01b84 DEV: skip mobile copy link spec (#23805)
Most likely on CI sometimes it makes a press instead of a long press because there's a thread preview underneath.
2023-10-05 21:50:19 +02:00
Joffrey JAFFEUX 9e5fc2e817 FIX: correctly checks user activation on use (#23793)
We can't have it computed only at start of the application as the state wouldn't most likely be false at this point in time.
2023-10-05 11:24:01 +02:00
Joffrey JAFFEUX 6cd4b8de6d FIX: long press chat message test failure (#23791)
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_activation
https://developer.mozilla.org/en-US/docs/Web/Security/User_activation
https://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. -->
2023-10-05 10:16:13 +02:00
Joffrey JAFFEUX a1aedc9ce1 DEV: removes dead code (#23772) 2023-10-04 20:02:57 +02:00
Joffrey JAFFEUX 35d2426b42 FIX: ensures close arg from d-menu/:content works (#23773) 2023-10-04 19:58:17 +02:00
Joffrey JAFFEUX 8447928840 DEV: enforces system user membership (#23771)
System user will automatically adds itself to the channel when trying to send a message.
2023-10-04 17:03:23 +02:00
Joffrey JAFFEUX 08df8fc1d1 UX: enhances chat copy features (#23770)
- 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. -->
2023-10-04 16:14:37 +02:00
Joffrey JAFFEUX edc843e942 UX: removes username from bookmark notification (#23760) 2023-10-03 17:44:12 +02:00
Joffrey JAFFEUX a441f07908 FIX: correctly clear the quote state (#23705)
Prior to this fix clicking outside text and reseting the selection wouldn't clear the quote state, which would cause a click on "reply" or "create" to start the composer with the quote state.

This commit attempts to simplify this behaviour by not mutating quote state while the menu is opened. The quote state will now only be cleared when the menu is closed.

No tests have ever been written for this complex and subtle behavior (both `mousedown` and `selectionchange` events can trigger the final `selectionChanged` codepath which prevents us to for example stop the event when clicking quote as it will still change the selection even if we can prevent the `mousedown`. Ideally a huge part of this code should be rewritten to be easier to test, this commit only attempt to fix a regression introduced when using FloatKit to position the menu.
2023-09-28 19:37:11 +02:00
Joffrey JAFFEUX db558dc3fc FIX: correctly sort channels with null last message (#23672)
As per https://github.com/discourse/discourse/commit/92839dc722057fcd8dcc2504bb89598423392744 lastMessage won't be null when a message is deleted. This would cause sorting issues when messages from a direct message channels are deleted as we would try to sort on last message and thought it would exist when actually it would be a null message.

I might rethink this design to not return any last_message in this case soon as checking on ID feels dirty and prone to error, so only fixing the issue for now.

This commit is also using `@cached` to avoid replaying the sort logic on each access.
2023-09-26 20:36:51 +02:00
2a10ea0e3f DEV: FloatKit (#23650)
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>
2023-09-26 13:39:52 +02:00
Joffrey JAFFEUX e3e94aec95 FIX: ensures we reuse existing thread if existing (#23618)
This is a special case to ensure that if two users start a thread at the same time, we won't attempt to create a second thread.
2023-09-26 10:15:16 +02:00
Joffrey JAFFEUX 92839dc722 FIX: ensures an empty last message won't cause errors (#23647)
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.
2023-09-25 12:43:04 +02:00
Joffrey JAFFEUX c8fff19b99 DEV: removes the notion of staged thread (#23612)
While very fast and powerful staged threads forces a lot of gymnastic and edge cases. This patch adds a new service `Chat::CreateThread` and uses it to create a thread unconditionally when a user replies to a message in a threading enabled channel. If the user actually doesn’t send a message we will have a thread with no messages which has no important impact and could even be periodically cleaned if necessary.

Note that this commit also moves message actions to .gjs as it was the original goal of this PR to correctly check for staged thread to show the menu or not.
2023-09-15 18:09:45 +02:00
Joffrey JAFFEUXandchapoi ae27beb01a UI: improves remove channel animation (#23585)
Co-authored-by: chapoi <101828855+chapoi@users.noreply.github.com>
2023-09-14 18:48:29 +02:00
Joffrey JAFFEUX ef0a049b87 FIX: scope animated-images-pause to topic-post (#23561)
This was causing issues on chat which is not properly supporting this for now.
2023-09-13 16:20:06 +02:00
Joffrey JAFFEUX 85fddf58bc Revert "DEV: FloatKit (#23541)" (#23549)
This reverts commits

0623ac684a
408e71e437
a32fa3b947

User tips were running into some issues.
2023-09-12 13:55:12 -04:00
Joffrey JAFFEUX 408e71e437 FIX: tooltips can be over the header (#23548)
As a result they need a high z-index
2023-09-12 19:17:35 +02:00
Joffrey JAFFEUX a32fa3b947 FIX: cancel post toolbar on click outside (#23546)
On `mousedown` if the click is outside a cooked element cancel the `mousedown`/`mouseup` sequence and only rely on the `selectionchange` event.

This change ensures a click on avatar for example will work, even if user is doing a rather slow click (meaning: the mousedown has been hold for more than 100ms).
2023-09-12 18:49:06 +02:00
0623ac684a DEV: FloatKit (#23541)
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>
2023-09-12 15:50:26 +02:00
Joffrey JAFFEUX b8cc1072cc Revert "DEV: FloatKit (#23312)" (#23540)
This reverts commit abcdd8d367.
2023-09-12 15:37:16 +02:00
abcdd8d367 DEV: FloatKit (#23312)
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>
2023-09-12 15:06:51 +02:00
Joffrey JAFFEUX 898c75a05c FIX: ensures swipe works with scroll (#23508)
- do not prevent the event (it was a violation anyways as the touchstart is passive)
- waits for at least 25px horizontal move before showing the remove action, it avoids showing the remove while scrolling and moving a little bit horizontally
2023-09-11 15:39:54 +02:00
Joffrey JAFFEUXandchapoi b8d5f951f6 UX: implements swipe on row channel (#23436)
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>
2023-09-11 14:51:13 +02:00
Joffrey JAFFEUX 7bcf934765 FIX: ensures automation can send chat message (#23478)
It's been broken in https://github.com/discourse/discourse/commit/243793ec6ea41a9bf53dc6b9585ba9239dfc4c85. Sadly it's not very practical to write cross plugins tests.
2023-09-08 16:37:05 +02:00
Joffrey JAFFEUX 4f8d52bbcb UX: hides header's unread indicator on full page (#23370)
The unread(s) will still show in the sidebar, outside of chat and when in drawer mode. This is to prevent the confusion to show an unread count for chat on a button which is going to take the user out of chat.
2023-09-02 12:06:40 +02:00
Joffrey JAFFEUX ed35ae4dcd FIX: closes chat emoji picker on body scroll (#23362)
Prior to this fix we would scroll the emoji picker with the body of the page in drawer mode.

With this fix scrolling inside the drawer or the emoji picker will scroll the drawer or the emoji picker, but, scrolling body will close the chat emoji picker.
2023-09-01 09:17:48 +02:00
Joffrey JAFFEUX f1d8cd529e Revert "Revert "PERF: Cache each theme field value once (#23192)" (#23354)" (#23356)
This reverts commit 9821ca9413.
2023-08-31 14:12:03 -05:00
Joffrey JAFFEUX 24d46fd981 Revert "DEV: Improve strategy for identifying ember-cli JS chunks (#23336)" (#23355)
This reverts commit 2c58d456dd.
2023-08-31 19:28:48 +02:00
Joffrey JAFFEUX 9821ca9413 Revert "PERF: Cache each theme field value once (#23192)" (#23354)
This reverts commit 82a56334a3.
2023-08-31 19:04:43 +02:00
Joffrey JAFFEUX d4322a69db UX: hides original message user in thread participants (#23350)
Usage:

```hbs
<Chat::Thread::Participants
  @thread={{@thread}}
  @includeOriginalMessageUser={{false}}
/>
```
2023-08-31 14:46:37 +02:00
Joffrey JAFFEUXandMark VanLandingham 4ee0f4e5ba FIX: ensures we update cached model last message bus id (#23271)
Channels and threads are cached as much as possible, as a result the `last_message_bus_id` can become stalled.

It was for example exhibited with the following actions:
- open a channel (A)
- send a message
- navigate to another channel (B)
- come back to channel (A), and you would actually get all the messages replayed since you opened (A) for the first time as the `last_message_bus_id` would only refresh on a full page reload

This was technically not causing known bugs ATM, but was probably the source of few hard to repro bugs and would for sure cause issues in the future.

Co-authored-by: Mark VanLandingham <markvanlan@gmail.com>
2023-08-25 19:17:48 +02:00
Joffrey JAFFEUX 311b28d485 FIX: incorrect chat message reaction text (#23260)
Prior to this fix the text would be incorrect when the current user reacted and number of reactions was above 2.

This commit fixes the bug and also makes the following changes:
- separates text computation in a standalone lib to make it easier to test
- increases the number of displayed usernames in reaction text (from 5 to 15)
- adds a full test suite for this new `getReactionText` function
- fixes a bug in reaction fabricator which would prevent to change the count to zero
2023-08-25 15:20:56 +02:00
Joffrey JAFFEUX c5ac500181 UX: minor tweaks to thread list item (#23259)
- drop @
- prevents +X  (participants) to show on next line
- few spacing/fonts adjustments

Note that this commit is also stripping links from chat excerpts.
2023-08-25 11:20:03 +02:00