The complexity of the situation is that we don't want to load faker into production by default but fabricators and styleguide are available on production.
This is made possible through app/assets/javascripts/discourse/app/lib/load-faker.js which contains a function to ensure faker is loaded asynchronously (loadFaker) and another function to access the loaded faker (getLoadedFaker).
Note 1: this commit also refactors fabricators to have access to context and use faker where possible
Note 2: this commit moves automation to admin bundle
---------
Co-authored-by: David Taylor <david@taylorhq.com>
When selecting messages to move to a new channel, if any of the selected messages is the original message of a thread, the entire thread, including all its replies, will be moved to the destination channel
Adds the new quick menu for bookmarking. When you bookmark
a post (chat message behaviour will come later) we show this new quick
menu and bookmark the item straight away.
You can then choose a reminder quick option, or choose Custom... to open
the old modal. If you click on an existing bookmark, we show the same quick menu
but with Edit and Delete options.
A later PR will introduce a new bookmark modal, but for now we
are using the old modal for Edit and Custom... options.
The fix is to actually wait for the bottom arrow to show before appending a new message, otherwise sometimes it goes too fast, and we create a new message while the scroll has not ended yet, making the arrow not visible yet.
This commit also uses this opportunity to move from `50.times.map {}` to `Fabricate.times(50, ...)` in this spec file.
Some of the properties, like 'categoriesById', 'parentCategory' and
'subcategories', were updated manually when categories were loaded.
This was not ideal because it required a lot of code to keep the
objects in sync and some of the properties were not updated correctly.
Why this change?
`expect(page.title).to starts_with("...")` does not rely on capybara
waiters. This commit switches us to use `have_title` instead which will
rely on Capybara waiters.
We need to scroll lock textareas when the keyboard is visible, otherwise they might become unusable if another element is body scroll locked on the page (eg: channels messages).
Note this commit is also slightly simplifying the code.
The expected behavior when receiving a message is the following:
- if user is at the bottom of the screen, scroll and append message
- if user is not at the bottom of the screen, don't scroll, show arrow and don't append message
Why this change?
When the site setting for chat_max_direct_message_users is set to 1, it is expected that users can have a 1:1 chat with other users. However, since the current user is counting as 1 user it makes starting a new chat impossible.
This change hands this validation off to DirectMessageChannel::MaxUsersExcessPolicy which handles the count correctly by filtering out the current user.
This commit is making the following changes:
- replaces `mobile-keyboard` initializer and `chat-vh` with a new template-less component: `d-vh`
- ensures body scroll lock is released when page/tab focus changes
- correctly locks body on chat channels and chat threads when composer is focused
- removes `bodyScrollFix` as we now use body scroll lock
- `onViewportResize` has been debounced to ensure it's not a bad performance vector
- adds a reverse option do body scroll lock, this is made to support reversed scroll areas (like chat channels and threads)
---------
Co-authored-by: Penar Musaraj <pmusaraj@gmail.com>
This enables the following in Discourse AI
```
plugin.register_modifier(:chat_allowed_bot_user_ids) do |user_ids, guardian|
if guardian.user
mentionables = AiPersona.mentionables(user: guardian.user)
allowed_bot_ids = mentionables.map { |mentionable| mentionable[:user_id] }
user_ids.concat(allowed_bot_ids)
end
user_ids
end
```
some bots that are id < 0 need to be discoverable in search otherwise people can not talk to them.
---------
Co-authored-by: Joffrey JAFFEUX <j.jaffeux@gmail.com>
- Converts all header buttons to use `<DButton`
- Updates `<DButton` to render `<a href=` tags when `@href` is passed (previously it was rendering a `<button`, and then using JS to route when clicked)
We were incorrectly using `return` in a block which was causing exceptions at runtime. These exceptions were not causing much issues as they are in defer block.
While working on writing a test for this specific case, I noticed that our `upsert_custom_fields` function was using rails `update_all` which is not updating the `updated_at` timestamp. This commit also fixes it and adds a test for it.
The issue:
When the current user disables chat from within user preferences, the chat button still appears when clicking another user’s profile picture to open the user card. This is also the case when the current user has chat enabled but the target user has disabled chat.
After this change:
- when a user disables chat in preferences, the chat button should not be displayed when opening a user card or visiting profiles of other users.
- when chat is enabled in preferences but another user disables chat, the chat button should not appear on their user card or profile
When adding the new "illegal" flag option, we missed adding the translation to the chat plugin, so when flagging a chat message (rather than a post) you'd see [en.chat.flags.illegal]. This PR fixes that.
This commit fixes an issue where the following happens:
1. You open /admin as a member of the admin_sidebar_enabled_groups
1. You then click the chat icon in the header when you prefer to have
drawer open, or if you just minimise chat into drawer after it opens
fullscreen
1. You lose the admin sidebar panel, and are reset instead to the main
panel
Also included is a bit of refactoring to make it so the forcing of
admin sidebar state is in one place.
Prior to this change we would pre-load all the user channels which making initial page load slower. This change will make them be loaded right after initial load. In the past this was not possible as the channels would have to be loaded on each page transition. However since about a year, we made the channels to be cached on the frontend and no other request will be needed.
I have decided for now to not show a loading state in the sidebar as I think it would be noise, but we can reconsider this later.
Note given we don't have the channels loaded at first certain things where harder to accomplish. The biggest UX change of this commit is that we removed all the complex logic of computing the best channel to display when you load /chat. We will now store the id of the last channel you visited and will use this id to decide which channel to show.
This commit makes it so the site settings filter controls and
the list of settings input editors themselves can be used elsewhere
in the admin UI outside of /admin/site_settings
This allows us to provide more targeted groups of settings in different
UI areas where it makes sense to provide them, such as on plugin pages.
You could open a single page for a plugin where you can see information
about that plugin, change settings, and configure it with custom UIs
in the one place.
In future we will do this in "config areas" for other parts of the
admin UI.
With the new admin sidebar restructure, we have a link to "Installed plugins". We would like to ensure that when the admin is searching for a plugin name like "akismet" or "automation" this link will be visible. Also when entering the plugins page, related plugins should be highlighted.
This commit adds new plugin show routes (`/admin/plugins/:plugin_id`) as we move
towards every plugin having a consistent UI/landing page.
As part of this, we are introducing a consistent way for plugins
to show an inner sidebar in their config page, via a new plugin
API `register_admin_config_nav_routes`
This accepts an array of links with a label/text, and an
ember route. Once this commit is merged we can start the process
of conforming other plugins to follow this pattern, as well
as supporting a single-page version of this for simpler plugins
that don't require an inner sidebar.
Part of /t/122841 internally
**TL;DR:** Refactor autocomplete to use async markdown parsing for code block detection.
Previously, the `inCodeBlock` function in `discourse/app/lib/utilities.js` used regular expressions to determine if a given position in the text was inside a code block. This approach had some limitations and could lead to incorrect behavior in certain edge cases.
This commit refactors `inCodeBlock` to use a more robust algorithm that leverages Discourse's markdown parsing library.
The new approach works as follows:
1. Check if the text contains any code block markers using a regular expression.
If not, return `false` since the cursor can't be in a code block.
1. If potential code blocks exist, find a unique marker character that doesn't appear in the text.
1. Insert the unique marker character into the text at the cursor position.
1. Parse the modified text using Discourse's markdown parser, which converts the markdown into a tree of tokens.
1. Traverse the token tree to find the token that contains the unique marker character.
1. Check if the token's type is one of the types representing code blocks ("code_inline", "code_block", or "fence").
If so, return `true`, indicating that the cursor is inside a code block.
Otherwise, return `false`.
This algorithm provides a more accurate way to determine the cursor's position in relation to code blocks, accounting for the various ways code blocks can be represented in markdown.
To accommodate this change, the autocomplete `triggerRule` option is now an async function.
The autocomplete logic in `composer-editor.js`, `d-editor.js`, and `hashtag-autocomplete.js` has been updated to handle the async nature of `inCodeBlock`.
Additionally, many of the tests have been refactored to handle async behavior. The test helpers now simulate typing and autocomplete selection in a more realistic, step-by-step manner. This should make the tests more robust and reflective of real-world usage.
This is a significant refactor that touches multiple parts of the codebase, but it should lead to more accurate and reliable autocomplete behavior, especially when dealing with code blocks in the editor.
> Written by an 🤖 LLM. Edited by a 🧑💻 human.
On mobile, when viewing the My Threads area, each thread will show:
- The avatar of the last responder in the thread, overlaid with the chat thread symbol to visually distinguish this area from DMs.
- Either the thread title, where applicable, or the first message of the thread, truncated to fit on one line.
- The channel where the thread originated.
- The last message sent in the thread, truncated to fit on one line.
- When the last message was sent in the thread.
---------
Co-authored-by: David Battersby <info@davidbattersby.com>
- The thread preview is now a regular link and can be right clicked
- left gutter date, and regular date of a thread message will not correctly link to the thread's message
Previously services would let you define a high level default `def default_actions_for_service; end` which would define various handlers like `on_success`, after months of usage we consider the cons are superior to the pros here.
Two mains cons:
- people would often not understand where the handling was coming from
- it's easy to miss a case when you write your specs
Forcing a thread will work even in channel which don't have `threading_enabled` or in direct message channels.
For now this feature is only available through the `ChatSDK`:
```ruby
ChatSDK::Message.create(in_reply_to_id: 1, guardian: guardian, raw: "foo bar baz", channel_id: 2, force_thread: true)
```
Prior to this fix if a user had started to reply to a message without actually sending a message, the thread would still be created and we would end up listing it in the threads list of a channel.
This commit also improves adds thread and thread_replies_count to the 4th parameter of the chat_message_created event.
* UX: chat message creator scss cleanup + design tweak to username display
* add user status with live updates to modal
* show user status description in modal
* add tests for user status
* UX: add user-status styling to chat message creator
---------
Co-authored-by: David Battersby <info@davidbattersby.com>
Co-authored-by: Joffrey JAFFEUX <j.jaffeux@gmail.com>
Prior to this fix we were checking if user was not part of a group which allows to chat, but we were not checking if this user was part of groups who can use direct messages.
Prior to this fix clicking <kbd>x</kdb> on a channel row would effectively leave the channel on server side, but it wouldn't disappear from the screen before a page refresh.
With the adjustments of `btn-transparent` in https://github.com/discourse/discourse/pull/24666, there are more buttons that could use this class instead of `btn-flat`. This mostly relates to `x` close buttons, but also includes composer and chat toggles.
The primary difference between these styles is that `btn-transparent` never has a background, where `btn-flat` may have a hover or focus background.
When we send a bookmark reminder, there is an option to delete
the underlying bookmark. The Notification record stays around.
However, if you want to filter your notifications user menu
to only bookmark-based notifications, we were not showing unread
bookmark notifications for deleted bookmarks.
This commit fixes the issue _going forward_ by adding the
bookmarkable_id and bookmarkable_type to the Notification data,
so we can look up the underlying Post/Topic/Chat::Message
for a deleted bookmark and check user access in this way. Then,
it doesn't matter if the bookmark was deleted.
`chat_preferred_mobile_index` allows to set the preferred default tab when loading chat on mobile.
Current choices are:
- channels
- direct_messages
- my_threads
Users can hide their public profile and presence information by checking
“Hide my public profile and presence features” on the
`u/{username}/preferences/interface` page. In that case, we also don't
want to return user status from the server.
This work has been started in https://github.com/discourse/discourse/pull/23946.
The current PR fixes all the remaining places in Core.
Note that the actual fix is quite simple – a5802f484d.
But we had a fair amount of duplication in the code responsible for
the user status serialization, so I had to dry that up first. The refactoring
as well as adding some additional tests is the main part of this PR.
```ruby
ChatSDK::Message.start_stream(message_id: 1, guardian: guardian)
ChatSDK::Message.stream(raw: "foo", message_id: 1, guardian: guardian)
ChatSDK::Message.stream(raw: "bar", message_id: 1, guardian: guardian)
ChatSDK::Message.stop_stream(message_id: 1, guardian: guardian)
```
Generally speaking only admins or owners of the message can interact with a message. Also note, Streaming to an existing message with a different user won't change the initial user of the message.
Prior to this fix, if the last message of a thread had been made by a deleted user it would cause an exception as we would have no user to display, this commit uses a solution we have been using at other places: the null pattern, through the use of `Chat::NullUser.new`.
Plugins can now register this modifier:
```ruby
register_modifier(:chat_can_create_direct_message_channel) do |user, target_users|
# your logic which should return true or false
end
```
Prior to this fix the scroll was ignored when clicking the arrow bottom which would prevent the call to update last read. This fix manually calls update last read in this case and adds a test for it.
In safe mode plugins are not loaded, so the plugin admin
routes are not loaded. This was causing errors in the
admin sidebar because we are trying to show links to the plugin
admin routes.
This fixes the issue by just not adding the plugin links if
we are in safe mode.
If a user had `123456789` as username, it could be passed to the query as a number and the query would fail as it expects a string.
Also applies the same fix to groups.
Why this change?
We noticed that running `LOAD_PLUGINS=1 rspec --seed=38855 plugins/chat/spec/system/chat_new_message_spec.rb` locally
results in the system tests randomly failing. When we inspected the
request logs closely, we noticed that a `/presence/get` request from a
previous rspec example was being processed when a new rspec example is
already being run. We know it was from the previous rspec example
because inspecting the auth token showed the request using the auth
token of a user from the previous example. However, when a request using
an auth token from a previous example is used it ends up logging out the
same user on the server side because the user id in the cookie is the same
due to the use of `fab!`.
I did some research and there is apparently no way to wait until all
inflight requests by the browser has completed through capybara or
selenium. Therefore, we will add an identifier by attaching a cookie to all non-xhr requests so that
xhr requests which are triggered subsequently will contain the cookie in the request.
In the `BlockRequestsMiddleware` middleware, we will then reject any
requests when the value of the identifier in the cookie does not match the current rspec's example
location.
To see the problem locally, change `Auth::DefaultCurrentUserProvider.find_v1_auth_cookie` to the following:
```
def self.find_v1_auth_cookie(env)
return env[DECRYPTED_AUTH_COOKIE] if env.key?(DECRYPTED_AUTH_COOKIE)
env[DECRYPTED_AUTH_COOKIE] = begin
request = ActionDispatch::Request.new(env)
cookie = request.cookies[TOKEN_COOKIE]
# don't even initialize a cookie jar if we don't have a cookie at all
if cookie&.valid_encoding? && cookie.present?
puts "#{env["REQUEST_PATH"]} #{request.cookie_jar.encrypted[TOKEN_COOKIE]&.with_indifferent_access}"
request.cookie_jar.encrypted[TOKEN_COOKIE]&.with_indifferent_access
end
end
end
```
After which run the following command: `LOAD_PLUGINS=1 rspec --format documentation --seed=38855 plugins/chat/spec/system/chat_new_message_spec.rb`
It takes a few tries but the last spec should fail and you should see something like this:
```
assets/chunk.c16f6ba8b6824baa47ac.d41d8cd9.js {"token"=>"37d995a4b65395d3b343ec70fff915b4", "user_id"=>3382, "username"=>"bruce0", "trust_level"=>1, "issued_at"=>1708591735}
/assets/chunk.050148142e1d2dc992dd.d41d8cd9.js {"token"=>"37d995a4b65395d3b343ec70fff915b4", "user_id"=>3382, "username"=>"bruce0", "trust_level"=>1, "issued_at"=>1708591735}
/chat/api/channels/527/messages {"token"=>"37d995a4b65395d3b343ec70fff915b4", "user_id"=>3382, "username"=>"bruce0", "trust_level"=>1, "issued_at"=>1708591735}
/uploads/default/test_0/optimized/1X/_129430568242d1b7f853bb13ebea28b3f6af4e7_2_512x512.png {"token"=>"37d995a4b65395d3b343ec70fff915b4", "user_id"=>3382, "username"=>"bruce0", "trust_level"=>1, "issued_at"=>1708591735}
redirects to existing chat channel
redirects to chat channel if recipients param is missing (PENDING: Temporarily skipped with xit)
with multiple users
/favicon.ico {"token"=>"9a75c114c4d3401509a23d240f0a46d4", "user_id"=>3382, "username"=>"bruce0", "trust_level"=>1, "issued_at"=>1708591736}
/chat/new-message {"token"=>"9a75c114c4d3401509a23d240f0a46d4", "user_id"=>3382, "username"=>"bruce0", "trust_level"=>1, "issued_at"=>1708591736}
/presence/get {"token"=>"37d995a4b65395d3b343ec70fff915b4", "user_id"=>3382, "username"=>"bruce0", "trust_level"=>1, "issued_at"=>1708591735}
```
Note how the `/presence/get` request is using a token from the previous example.
Co-authored-by: David Taylor <david@taylorhq.com>
We have separated and combined modes for sidebar panels.
Separated means the panels show only their own sections,
combined means sections from all panels are shown.
The admin sidebar only shows its own panels, so it must set
the mode to separated; however when we navigate to chat or
home we must revert to the initial mode setttings.
When hiding/showing the sidebar, as is the case on mobile
and using the toggle in the top left on desktop, we delete
and recreate the ember component on the page. This causes
the `sections` for each sidebar panel to get re-evaluated
every time.
For the admin sidebar, this means that we were constantly
re-adding the plugin links to the sidebar, causing duplication.
This can be fixed by just adding @cached to the getter for
sections.
This feature adds the functionality to start a new chat directly from the URL using query params.
The format is: /chat/new-message?recipients=buford,jona
The initial version of this feature allows for the following:
- Open an existing direct message channel with a single user
- Create a new direct message channel with a single user (and auto redirect)
- Create or open a channel with multiple users (and auto redirect)
- Redirects to chat home if the recipients param is missing
This commit introduces the possibility to stream messages. To allow plugins to use streaming this commit also ships a `ChatSDK` library to allow to interact with few parts of discourse chat.
```ruby
ChatSDK::Message.create_with_stream(raw: "test") do |helper|
5.times do |i|
is_streaming = helper.stream(raw: "more #{i}")
next if !is_streaming
sleep 2
end
end
```
This commit also introduces all the frontend parts:
- messages can now be marked as streaming
- when streaming their content will be updated when a new content is appended
- a special UI will be showing (a blinking indicator)
- a cancel button allows the user to stop the streaming, when cancelled `helper.stream(...)` will return `false`, and the plugin can decide exit early
Affects the following settings:
delete_all_posts_and_topics_allowed_groups
experimental_new_new_view_groups
enable_experimental_admin_ui_groups
custom_summarization_allowed_groups
pm_tags_allowed_for_groups
chat_allowed_groups
direct_message_enabled_groups
chat_message_flag_allowed_groups
This turns off client: true for these group-based settings,
because there is no guarantee that the current user gets all
their group memberships serialized to the client. Better to check
server-side first.
Why this change?
The tests are consistently flaky and failing with the following error:
```
CapybaraTimeoutExtension::CapybaraTimedOut:
This spec passed, but capybara waited for the full wait duration (10s) at least once. This will slow down the test suite. Beware of negating the result of selenium's RSpec matchers.
```
In certain cases, chat channels may have empty slugs, it happens when:
1. The `slug_generation_method` setting is set to `None`
2. `slug_generation_method` is set to `ASCII` and a channel with
a Unicode name and an empty slug is created (in this case, the code
that creates channels tries to generate a slug and fallbacks to an empty slug)
At the moment, we have a unique index on the `chat_channels.slug` column
which leads to errors when creating several channels with empty slugs
(Discourse is able to create one such channel, but when trying to create
the second one fails because of the unique constraint). This PR fixes that
by adding a `where` condition to the index. Slugs still have to be unique,
but now many channels may have empty slugs.
This fix is similar to the one we made to the category slugs – 7ba914f1e1.
This commit was created with a combination of the ember-native-class-codemod and manual cleanup.
User-status-related functionality was previously encapsulated in its own `User.reopen` call, which is essentially an 'inline mixin'. This commit refactors it into a utility class, with an instance accessible on `User#statusManager`
The service `Chat::CreateMessage` will now accept `context_post_ids` and `context_topic_id` as params. These values represent the topic which might be visible when sending a message (for now, this is only possible when using the drawer).
The `DiscourseEvent` `chat_message_created` will now have the following signature:
```ruby
on(:chat_message_created) do | message, channel, user, meta|
p meta[:context][:post_ids]
end
```
Channels can include emojis in front of the channel title which causes problems when sorting.
Using the channel slug is a more reliable way to sort and avoid these kind of issues.
This change will sort channels by activity on mobile, with preference to those with urgent or unread messages.
Channels with mentions will appear first, followed by channels with unread messages, then finally everything else sorted by the channel title (alphabetically).
This commit includes several changes to make hashtags work when "lazy
load categories" is enabled. The previous hashtag implementation use the
category colors CSS variables, but these are not defined when the site
setting is enabled because categories are no longer preloaded.
This commit implements two fundamental changes:
1. load colors together with the other hashtag information
2. load cooked hashtag data asynchronously
The first change is implemented by adding "colors" to the HashtagItem
model. It is a list because two colors are returned for subcategories:
the color of the parent category and subcategory.
The second change is implemented on the server-side in a new route
/hashtags/by-ids and on the client side by loading previously unseen
hashtags, generating the CSS on the fly and injecting it into the page.
There have been minimal changes outside of these two fundamental ones,
but a refactoring will be coming soon to reuse as much of the code
and maybe favor use of `style` rather than injecting CSS into the page,
which can lead to page rerenders and indefinite grow of the styles.
When we show the links to installed plugins in the admin
sidebar (for plugins that have custom admin routes) we were
previously only doing this if you opened /admin, not if you
navigated there from the main forum. We should just always
preload this data if the user is admin.
This commit also changes `admin_sidebar_enabled_groups` to
not be sent to the client as part of ongoing efforts to
not check groups on the client, since not all a user's groups
may be serialized.
The leave channel button is cut off when accessing the channel settings page on mobile.
This change adds additional padding to the bottom of the channel settings page when accessing via iPad/PWA/Hub.
Chat mobile has separate routes for channels and direct messages. However on desktop we want to prevent these routes from being accessible as they aren't intended to be used by chat in full-page or drawer mode on desktop.
We had two issues which were present for a long time I think:
- one that impacts both core discourse and chat. We were not setting top on the header when `footer-nav-ipad` was present, meaning that you could make it scroll under if you try to scroll up by putting your finger on the discourse header
- one that impacted only chat. It's also present in core, but in core it's not a probem because we don't have a fixed height div. The body height was higher than the screen which would cause a second scrollbar to appear and would slightly break layout, if you scroll on this scrollbar (body).
* UX: increase font-size of last message + decrease emoji size
* UX: decrease size of username emoji
* UX: Mobile chat index cleanup
* UX: decrease size of chat-channel-title in thead list
Chat should follow the same convention as topics, where invalid mentions
are not styled the same as valid mentions. This PR makes chat use such styling.
I'm following the same pattern that we use for invalid mentions in posts –
9bd6523581/app/assets/stylesheets/common/base/topic-post.scss (L1285-L1288)
This way it'll be easier to dry it up if we decide to do that at some point.
We've changed access settings to be group membership based rather than based on the TL value directly. We kept both conditions here while we updated any plugins and themes. It should now be safe to remove.
This commit adds a link to the original message of a thread, this link will:
- load the channel message and highlight it while keeping thread panel open on desktop
- open the channel and highlight the message in mobile (and close thread panel, as mobile never shows channel and thread in the same view)
Co-authored-by: chapoi <101828855+chapoi@users.noreply.github.com>
Since https://github.com/discourse/discourse/pull/25501 this behavior was broken. This PR attempts to fix it by being more fine grain.
Also note that this PR is moving `footer-nav-ipad` and `footer-nav-visible` to the `html` element and not the `body`. It makes more sense as we are already adding most of other global state class like `keyboard-visible` to the `html` element.
Tested on:
- chrome desktop
- safari ios - iphone
- PWA ios - iphone
- PWA ios - ipad
- DiscourseHub iphone
This commit sets a default of 0px for `--footer-nav-height` and set it only when `body.footer-nav-visible` allowing us to safely use `--footer-nav-height` wherever it will be needed if set.
We usually don't enforce foreign key relationships on the database level.
Because of that, occasionally it's possible to see a chat message that
references to a non-existent chat_channel or user. MessagesExporter
failed in such case before, this PR fixes that.
When reaching the top of a thread, the full thread title will be displayed if it was too long to fit.
It works in mobile, drawer mode, and fullscreen.
---------
Co-authored-by: Joffrey JAFFEUX <j.jaffeux@gmail.com>
For performance reasons we don't automatically add fabricated users to trust level auto-groups. However, when explicitly passing a trust level to the fabricator, in 99% of cases it means that trust level is relevant for the test, and we need the groups.
This change makes it so that when a trust level is explicitly passed to the fabricator, the auto-groups are refreshed. There's no longer a need to also pass refresh_auto_groups: true, which means clearer tests, fewer mistakes, and less confusion.
This change adds notification badges to the new footer tabs on mobile chat, to help users easily find areas where there’s new activity to review.
When on mobile chat:
- Show a badge on the DMs footer when there is unread activity in DMs.
- Show a badge on the Channels footer tab when there is unread channel activity.
- Show a badge on the Threads footer tab when there is unread activity in a followed thread.
- Notification badges should be removed once the unread activity is viewed.
Additionally this change will:
- Show green notification badges for channel mentions or DMs
- Show blue notification badges for unread messages in channels or threads
Co-authored-by: chapoi <101828855+chapoi@users.noreply.github.com>
The value [Yesterday] was a fixed string which couldn't be translated. Also removes nextWeek/nextDay which make no sense for dates which are always supposed to be in the past.