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 tagging a user in composer, the autocomplete div has a fixed width, causing longer names and usernames to get cut off. This change allows the div to expand up until a max-width of 600px on desktop.
The Digital Services Act requires a checkbox for any user who's flagging a post as illegal to confirm that they are flagging in good faith. This PR adds that.
* Remove unnecessary properties
* Use rem unit
* Add a drop shadow
* Make the "box shadow" slightly more subtle
* Use --d-border-radius
* Don't scale the "box shadow" on hover
* Scale down the on-hover size
* Make the button slightly larger
* Respect prefers-reduced-motion
* Use white rather than --secondary-or-primary
* Don't animate on-hover on mobile
If you upload a portrait video or just a video that doesn't fit in the
normal video dimensions we want it to have a black background instead of
trying to render parts of the placeholder image as the video background.
This change removes the placeholder image for the video background when
the play button is clicked and replaces it with an all black background.
The video placeholder play button is white, so on a video placeholder
that is also white it is very hard to see where the play button is, so
this change adds a dark grey transparent background to the play button
so that it stands out. This is similar to how we have done the
play/pause button on animated gifs.
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.
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).
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.
post action feedback is the mechanism in which we provide visual feedback
to the user when a post action is clicked, in cases where the action is a
background (hidden to user) for example: copying text to the clipboard
Core uses this to share post links, but other plugins (for example: AI) use
this to share post transcripts via the clipboard.
This adds a proper plugin API to consume this functionality
`addPostMenuButton` can provide a builder that specified a function as the action.
This function will be called with an object that has both the current post and a method for showing feedback.
This new navbar component is used for every navbar in chat, full page or drawer, and any screen.
This commit also uses this opportunity to correctly decouple drawer-routes from full page routes. This will avoid having this kind of properties in components: `@includeHeader={{false}}`. The header is now defined in the parent template using a navbar. Each route has now its own template wrapped in a div of the name of the route, eg: `<div class="c-routes-threads">..</div>`.
The navbar API:
```gjs
<Navbar as |navbar|>
<navbar.BackButton />
<navbar.Title @title="Foo" />
<navbar.ChannelTitle @channel={{@channel}} />
<navbar.Actions as |action|>
<action.CloseThreadButton />
</navbar.Actions>
</navbar>
```
The full list of components is listed in `plugins/chat/assets/javascripts/discourse/components/navbar/index.gjs` and `plugins/chat/assets/javascripts/discourse/components/navbar/actions.gjs`.
Visually the header is not changing much, only in drawer mode the background has been removed.
This commit also introduces a `<List />` component to facilitate rendering lists in chat plugin.
Float-kit elements (menus/tooltips) are positioned where they should be by setting an inline `left` property in JavaScript when they're rendered. For some reasons, we also set `left: 0` on float-kit elements here:
25d9927785/app/assets/stylesheets/common/float-kit/d-menu.scss (L11-L15)
This property is overridden by the inline property that the library sets in JavaScript. However, in RTL mode, all of our scss files are flipped where everything left becomes right and vice versa. In this case, the `left: 0` property in the scss file above becomes `right: 0`.
This results in a conflict specific to RTL mode where both the `left` and `right` properties are defined on the same absolute-positioned element; the `right` property will always be set to 0 because it comes from the (flipped) scss file above, and the inline `left` property will be set to some px amount determined in JavaScript.
The `right` property will take precedence over the inline `left` property due to the page being right-to-left (source: https://developer.mozilla.org/en-US/docs/Web/CSS/right#description) and this causes float-kit elements to incorrectly always stick to the right.
This commit removes the `left: 0` property altogether for float-kit elements from our scss files. It's not clear from git history why the property was added, and removing it doesn't seem to cause any issues.
Meta topic: https://meta.discourse.org/t/positioning-issues-with-rtl-locales-after-recent-updates/280220?u=osama
In modern hljs, languages should be targetted with `lang-` prefixes. These selectors haven't worked in Discourse for a long time, so let's drop them to reduce confusion
This commit ports the feature by @chapoi that was
previously a theme component in core.
A new post_menu button, copyLink, is added and used
as the default instead of share.
copyLink, on desktop, will copy the link of the post
to the user's clipboard and show a nice 'lil animation.
On mobile the native share menu will be shown.
If site owners want the old behaviour back, they just
need to change the post_menu site setting to use
the share button instead of copyLink.
This commit refactors the Wizard component code in preparation for moving it to the 'static' directory for Embroider route-splitting. It also includes a number of general improvements and simplifications.
Extracted from https://github.com/discourse/discourse/pull/23678
Co-authored-by: Godfrey Chan <godfreykfc@gmail.com>
Why this change?
The tags modal loads more tags via infinite loading based on when the last tag in the
given page appears in the viewport for the user. When it comes in to
view, a request is then triggered to fetch additional tags. To ensure
that we are only loading a single page of tags each time the modal is
opened, we previously set a max height on the modal's body to ensure
that the last tag which appears in the modal will be outside of the view
port in the initial load. However, this has regressed recently due to
unknown reasons and resulted in multiple pages of tags being loaded
immediately from the server as the modal's height was not restricted.
This regression was caught by an existing test but was unfortunately
determined as flaky.
What does this change do?
This change restores the max height on the edit navigation menu tags
modal on dekstop.
Followup e37fb3042d
* Automatically remove the prefix `Discourse ` from all the plugin titles to avoid repetition
* Remove the :discourse_dev: icon from the author. Consider a "By Discourse" with no labels as official
* We add a `label` metadata to plugin.rb
* Only plugins made by us in `discourse` and `discourse-org` GitHub organizations will show these in the list
* Make the plugin author font size a little smaller
* Make the commit sha look like a link so it's more obvious it goes to the code
Also I added some validation and truncation for plugin metadata
parsing since currently you can put absolutely anything in there
and it will show on the plugin list.
Admin can add tag description up to 1000 characters.
Full description is displayed on tag page, however on topic list it is truncated to 80 characters.
In the past, our loading spinner implementation used Ember's loading substate. That meant that, when the site setting was toggled, there would be fundamental changes in the routing behavior.
This commit simplifies things so that the (non-default) loading spinner implementation is purely a styling thing, and behaves exactly the same as the spinner which appears under the 'slider' configuration when loading takes too long.
This does involve a slight UX change. Now, the entire page will be replaced by a loading spinner instead of just the relevant `{{outlet}}`. We strongly recommend sites use the new default 'slider' behavior.
* Remove checkmark for official plugins
* Add author for plugin, which is By Discourse for all discourse
and discourse-org github plugins
* Link to meta topic instead of github repo
* Add experimental flag for plugin metadata and show this as a
badge on the plugin list if present
---------
Co-authored-by: chapoi <101828855+chapoi@users.noreply.github.com>
This commit makes it so the fullscreen code modal grows
to fit its content, and doesn't show horizontal scrollbars
unless the entire screen is filled by the modal already.
The code syntax highlighting and copy buttons were also
broken in fullscreen because of modal changes over time.
Recently, we disabled the option to reorder links directly from the sidebar. Instead, user has to go to edit modal.
https://github.com/discourse/discourse/pull/24188
However, move cursor was left, which is misleading.
This PR refactors the following:
* leaving all the CSS applied to the old `modal-body` classes in their respective files
* made new clean styling for `.d-modal` and refactored the template to use the new BEM classes
* `inner-`, `middle-`, `outer-` container classes are gone and replaced with simplified `wrapper` and `container` classes
* use standardised max-sizes with modifiers `-large` and `-max`
* lighter backdrop,
* min-width to prevent puny modals
* other styling changes regarding padding, close button,…
* pulled out all modal overrides into a general `modal-overrides` file + cleanup of outdated CSS
* pulled out login and create account modal styling into their own file, cause it's such a big override
* removed old general login.scss file for mobile & desktop
* only kept some remainders I don't want to touch in `app/assets/stylesheets/common/base/login.scss`
We ask users to confirm their session if they are making a sensitive
action, such as adding/updating second factors or passkeys. This
commit adds the ability to confirm sessions with passkeys as an option
to the password confirmation.
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
* UX: separate invite-signup styling
* UX: invite page centering
* remove old invites-show css
* UX: invite signup page – mobile
* remove class references in general file
* add styling for instructions
Switches to using a dialog to confirm a session (i.e. sudo mode for
account changes where we want to be extra sure the current user is who
they say they are) to match what we do with passkeys.
This change removes the background image (which is the small version of the uploaded image) from the lightbox backdrop.
Now a solid color (dark grey) is used for the backdrop so we can distinguish between the lightbox's head, body and footer.
Since we don't have icons or access to the JS that transforms
hashtag icon placeholders into their proper icons and colours
on embed and publish pages, we need to at least show _something_
and make sure the hashtags are not totally broken on these pages.
NOTE: Most of this is experimental and will be removed at a later
time, which is why things like translations have not been added.
The new /admin-revamp UI uses a sidebar for admin nav. This initial
step adds a script to generate a map of all the current admin nav
into a format the sidebar to read. Then, people can experiment
with different changes to this structure.
The structure can then be edited from `/admin-revamp/config/sidebar-experiment`,
and it is saved to local storage so people can visually experiment with different ways
of showing the admin sidebar links.
Files in `/assets/*` are given digests by sprockets, and we don't have any infrastructure for accessing those URLs in SCSS files. Instead, we should put this image with other similar images in the `public/images` directory, and then use the `absolute-image-url` helper so that it correctly uses the CDN where available.
plugin/theme-breaking changes:
1. `controller:create-account` is gone (use `component:modal/create-account` in modifyClass, **if** absolutely necessary)
2. `create-account-body` css class is gone (target `.d-modal.create-account` or any of the inner classes: `.modal-outer-container`, `.modal-middle-container`, `.modal-inner-container`, or `.modal-body`)
1. Removes accidental bold from `text` and `multiselect` labels/placeholders
2. Adds the animated label/placeholder combo to `multiselect`
3. Makes the `multiselect` placeholder lighter to match other fields
4. Makes the `dropdown` values darker to match other fields
5. Removes the extra 5px spacing before `confirmation` fields
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.
- 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.
This commit adds a new admin UI under the route `/admin-revamp`, which is
only accessible if the user is in a group defined by the new `enable_experimental_admin_ui_groups` site setting. It
also adds a special `admin` sidebar panel that is shown instead of the `main`
forum one when the admin is in this area.
![image](https://github.com/discourse/discourse/assets/920448/fa0f25e1-e178-4d94-aa5f-472fd3efd787)
We also add an "Admin Revamp" sidebar link to the community section, which
will only appear if the user is in the setting group:
![image](https://github.com/discourse/discourse/assets/920448/ec05ca8b-5a54-442b-ba89-6af35695c104)
Within this there are subroutes defined like `/admin-revamp/config/:area`,
these areas could contain any UI imaginable, this is just laying down an
initial idea of the structure and how the sidebar will work. Sidebar links are
currently hardcoded.
Some other changes:
* Changed the `main` and `chat` panels sidebar panel keys to use exported const values for reuse
* Allowed custom sidebar sections to hide their headers with the `hideSectionHeader` option
* Add a `groupSettingArray` setting on `this.siteSettings` in JS, which accepts a group site setting name
and splits it by `|` then converts the items in the array to integers, similar to the `_map` magic for ruby
group site settings
* Adds a `hidden` option for sidebar panels which prevents them from showing in separated mode and prevents
the switch button from being shown
---------
Co-authored-by: Krzysztof Kotlarek <kotlarek.krzysztof@gmail.com>
* UX: add static confetti bacgkround image on wizard steps
* DEV: slow down speed animation for confetti
* DEV: compress image file size
* UX: use an image that has transparent background
* DEV: use correct image file name
- don't try to guess the name of the manager (too many options)
- improve error message when registration is not allowed
- output error in console when registration fails
- minor fix to rename dialog layout
- hides action buttons in DiscourseHub (because adding passkeys there is not possible)
- adds acceptance test to ensure action buttons are hidden for admins seeing another user's profile
This commit introduces a new endpoint to search categories and uses it
instead of the categories map that is preloaded using SiteSerializer.
This feature is enabled only when the hidden site setting
lazy_load_categories is enabled and should be used only on sites with
many categories.
PERF: improve touch, swipe, panning performance on mobile menus
---
* stop event propagation on swipe events: other touch events were stealing a huge amount of time here. Stop event
propagation when handling pan events.
* animate with [web animations api](https://developer.mozilla.org/en-US/docs/Web/API/Web_Animations_API/Using_the_Web_Animations_API)
* prefer translate3d to hint for gpu rendering.
* query document for elements only on start move event, not on subsequent move
events
* remove unused calculations for directioned velocity and distance: all swipe/pan elements function in x/y direction only.
* re-implement scroll locking behavior.
re-implemented scroll lock behavior
---
With stop event propagation, we need to re-implement scroll locking on menu swipes.
Previously, this was using onTouchMove which was costly.
We may now use styling with overflow-y:hidden to lock scroll behavior.
overflow:hidden on html/body elements is now supported by iOS as of 2022
https://bugs.webkit.org/show_bug.cgi?id=153852https://bugs.webkit.org/show_bug.cgi?id=220908
UX: improve swipe
---
Some improvements to get gestures and swipes feeling a little more polished.
This focuses on end gesture, and how we transfer it to a css animation to
complete a menu open/close action.
Multitouch: events may pan, scroll, and zoom - especially on iOS safari.
Cancelling the swipe event allows for a more pleasant zooming experience.
* ease-out on menus opening, linear on close
* calculate animation duration for opening and closing,
attempt to better transfer user swipe velocity to css animation.
* more timely close/open and cleanup from calculated animation timing.
* add animation to closing menus on cloak tap
* correctly animate menus with ease-in and ease-out
* add swipe cancel event on multitouch event
DEV
---
* lean on promises
js animations api gives us promises to listen to. Update test waiters
to use waitForPromise from @ember/test-waiters instead of reigster/unregister.
* convert swipe mixin to its own class.
Convert swipe callbacks to custom events on the element.
Move shared functions for max animation time and close logic to
new shared class.
swipe-events lib uses custom events to trigger callbacks, rather than assuming
implemented hard coded function from the mixin's base class. Custom events are
triggered from the bound element as swipestart, swipeend, swipe
Add shared convenience functions for swipe events so they can be more easily
shared.
A client receives an initial swipe event and can check some state to see if it
wants to handle the swipe event and if it doesn't, calling
`event.preventDefault();` will prevent `swipe` and `swipeend` events from firing
until another distinct swipestart event is fired. Swipe events will auto-cancel on multitouch.
The scroll lock has also exposed as its own utility class.
Adds UI elements for registering a passkey and logging in with it. The feature is still in an early stage, interested parties that want to try it can use the `experimental_passkeys` site setting (via Rails console).
See PR for more details.
---------
Co-authored-by: Joffrey JAFFEUX <j.jaffeux@gmail.com>
This commit adds a new Revise... action that can be taken
for queued post reviewables. This will open a modal where
the user can select a Reason from a preconfigured list
(or by choosing Other..., a custom reason) and provide feedback
to the user about their post.
The post will be rejected still, but a PM will also be sent to
the user so they have an opportunity to improve their post when
they resubmit it.
Preloading just metadata is not always respected by browsers, and
sometimes the whole video will be downloaded. This switches to using a
placeholder image for the video and only loads the video when the play
button is clicked.
This fixes an issue where, on a textarea with a lot of text, the cursor
would jump when adding a new line. See video in PR for a repro.
This is a Chrome bug with scroll anchoring.
Refs: https://bugs.chromium.org/p/chromium/issues/detail?id=997266
The fix here disables `overflow-anchor` on the composer textarea. There
should be no side effects to this change, as scroll anchoring is likely
not needed for the composer textarea element.
Currently, if you set an integer site setting in the admin interface and include thousands separators, you will silently configure the wrong value.
This PR replaces TextField inputs for integer site settings with NumberField. It also cleans the numeric input of any non-digits in the backend in case any separators make it through.
- 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 addresses spacing between focused & the topic header, by cancelling out the spacing added between the first table row while still having space between the tabs "Suggested" & "Related"
* UX: selected indicator more topics spacing
In relation to the selected indicator, this PR addresses a vertical and horizontal issue with the indicator touching the table header and its left alignment to be more consistent with the regular topic list feed.
* UX: border for pill navigation on mobile
Makes UI more consistent desktop -> mobile, by adding a border to the bottom of the Suggested/Related.
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>
Previously we were using 'mouseup', which meant that if you started the click inside, and then dragged to outside the modal, it would still close. This kind of dragging action is common when selecting text, and having it close the modal can be very frustrating.
Simply switching to a 'click' listener doesn't totally solve the problem, because when a click event involves dragging from one element to another, the browser will fire the event on "the most specific ancestor element that contained both elements". For modals, the most specific common ancestor was still the `modal-middle-container`, which would cause the modal to close.
Therefore, this commit sets the modal containers to have `pointer-events: none`, and sets up the click listener on the `.modal-backdrop` element, which is **adjacent** to the modal in the DOM. That means that click events fired on any ancestors of the modal will not accidentally trigger closure.
This is an aesthetic change. Currently, if one of the scores involved in the reviewable score explanation is negative, we display it as: + -value. This changes that.
I also made an attempt at converting the component into GJS format. This is done as a separate commit.
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>
This commit fixes an issue from 2ecc8291e8
where the user sees an ugly plain #hashtag when sending a chat
message. Now, we add a basic placeholder that looks like the
cooked hashtag with a grey square, which is then filled in
once the "sent" message bus event for the message comes back,
and we do decorateCooked on the message to fill in the proper
hashtag details.
Follow-up to #23199 in which we moved the "delete user" options under the relevant action menu for flagged post. This change does the same, but to queued posts.
* UX: update styling for related/suggested
This PR addresses state issues for icons of the Related & Suggested buttons, as well as well as fixes alignment issues for folding phones / tablets, wider mobile devices by moving styling to the desktop scss file; also replaces border with box-shadow.
* UX: update styling for related/suggested
This PR fixes styling for previous related/suggested changes' positioning being off for topics and updates the active icon color by removing a line that changed its active color.
This PR updates the styling for the related & suggested topics to distract less from the Reply buttons and clearly indicate its usage as tabs, also referred to as pills.
This PR changes how we track which lists are available for a topic and how we decide which is the active one. The new approach centralizes everything in the service, and exposes functions for adding/removing a list, which each calls via `did-insert/will-destroy` modifiers.
It makes it much easier to track and update state when navigated to another topic or PM, ensuring things get updated correctly.
71ff3417 removed the mobile-specific template for discovery/topics. It was noted that this would introduce an additional `<div class='show-more'>` wrapper around the new topic indicator on mobile, but I didn't realise that it would cause positioning problems on mobile.
This commit moves the desktop-specific CSS into the desktop SCSS file so that it does not apply to mobile.
* Minor style adjustments
* Removes "all" count because it's redundant to the count on New
* Updates generic class names with -- modifier to follow BEM and help avoid class name collisions
* Hides the toggle when bulk select is enabled (the UI ends up being too busy)
This PR adds a new toggle to switch the (new) /new list between showing topics with new replies (a.k.a unread topics), new topics, or everything mixed together.
This commit moves the calendar date and time picker shown in
the local dates modal into a core component that can be reused
in other places. Also add system specs to make sure there isn't
any breakages with this feature, and a section to the styleguide.
This fixes:
- a regression from 30c152c, where navigating to a topic's last reply
via keyboard would lose track of the topic when returning to the topic
list
- an issue where if a topic's last post is a small post, navigating to it
via keyboard would not focus the post
Co-authored-by: David Taylor <david@taylorhq.com>
Adds a padding-bottom to the wrapper to avoid cutting the message on the mobile app and sets a max-width to align with the timeline on the desktop.
Fixes a bug on mobile where we updated the preference, but the user had a single list.
This PR updates how we display related and suggested topics on mobile and desktop. It adds a new `PluginOutlet` specifically designed for adding new topic lists, which automatically work if following the same conventions as the ones inside `<MoreTopics />`.
While we display lists side by side on desktop, we only display one in mobile. You can switch to another one by clicking on the nav pills, and we'll automatically save your preference for next time.
provide the ability to edit theme settings in the json editor, and also copy them as a text file so they can be pasted into another instance.
Reference: /t/65023
Fixes issue with scrolling background when lightbox is opened on mobile.
Since we rely on swiping for navigating lightbox galleries on mobile, we want to disable document scrolling.
Why this change?
When setting up the `IntersectionObserver`, we did not account for the
top margin and padding causing no intersection event to fire when the
last tag is load into view. This commits fixes the problem by setting a
bottom margin using the `rootMargin` option when setting up the
`IntersectionObserver`.
This commit also improves the test coverage surrounding the loading of
more tags.
Recently we started giving admins a notice in the advice panel when their translations have become outdated due to changes in core. However, we didn't include any additional information.
This PR adds more information about the outdated translation inside the site text edit page, together with an option to dismiss the warning.
* UX: Disclose AI model used and add animation to placeholder
* Move text into hbs template
DTooltip (weirdly) attaches to a sibling element, so we need something else to be rendered inside the RenderGlimmer wrapper div
---------
Co-authored-by: David Taylor <david@taylorhq.com>
Define new concept of panels in sidebar. Panels are wrappers around sidebar sections. In the future, it allows creating full focus mode by switching between panels.
A new API method called addSidebarPanel was added. Default main panel is already registered and by default all API sections are mounted to main.
* FEATURE: Inline topic summary. Cached version accessible to everyone.
Anons and non-members of the `custom_summarization_allowed_groups_map` groups can see cached summaries for any accessible topic. After the first 12 hours and if the posts to summarize have changed, allowed users clicking on the button will automatically re-generate it.
* Ensure chat summaries work and prevent model hallucinations when there are no messages.
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 brings the functionality from https://github.com/discourse/discourse-loading-slider into Discourse core. Default behaviour remains the same - the new slider mode can be enabled using the new 'page_loading_indicator' site setting.
What is the problem?
Before this change, we were relying on the `/tags` endpoint which
returned all the tags that are visible to a give user on the site leading to potential performance problems.
The attribute keys of the response also changes based on the `tags_listed_by_group` site setting.
What is the fix?
This commit fixes the problems listed above by creating a dedicate `#list` action in the
`TagsController` to handle the listing of the tags in the edit
navigation menu tags modal. This is because the `TagsController#index`
action was created specifically for the `/tags` route and the response
body does not really map well to what we need. The `TagsController#list`
action added here is also much safer since the response is paginated and
we avoid loading a whole bunch of tags upfront.
Why this change?
A new component based API for modals was introduced in
b3a23bd9d6. This commit moves the edit
navigation menu tags and categories modal to the new API.
- 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 is the first of a number of PRs aimed at helping admins manage their translation overrides. It simply adds a list of available interpolation keys below the input field when editing an override.
It also includes custom interpolation key.
Why this change?
We want the position of the filters to remain fixed when scrolling
through the list of categories or tags. Otherwise, the user has to
scroll all the way back to othe top in order to access the filters when
the list of categories or tags is large.
What does this change do?
This change adds a dropdown filter that allows a user to filter by
selected or unselected categories/tags in the edit navigation menu
modal.
For the categories modal, parent categories that do not match the
dropdown filter will be displayed as disabled since those parent
categories need to be displayed to maintain the hieracy of the child
child categories.
Why this change?
There was alot of duplication between the edit navigation menu tags/categories modal which
was making it hard to introduce new changes as the work had to be
duplicated into multiple places.
This commit mainly extracts the duplicated code into common components
such that it is easier to make styling changes across both modals.
To export chat messages, go to `/admin/plugins/chat` and click the Create export
button in the _Export chat messages_ section. You'll receive a direct message
when the export is finished.
Currently, this exports all messages from the last 6 months, but not more than
10000 messages.
This exports all chat messages, including messages from private channels and
users' direct conversations. This also exports messages that were deleted.
What does this change do?
This change adds the deselect all and reset to defaults buttons to the
edit navigation menu tags modal. The deselect all button when
clicked deselects all the selected tags in the modal. If the user
saves with no tags selected, the user's tags section in the
navigation menu will be set to the site's top tags.
The reset to defaults button is only shown when the
`default_navigation_menu_tags` site setting has been configured.
When clicked, the user's tags section in the navigation menu is
automatically set to the tags defined by the
`default_navigation_menu_tags` site setting.
What does this change do?
This commit adds an input filter to filter through the tag checkboxes in the
modal to edit tags that are shown in the user's navigation menu. The
filtering is a simple matching of the given filter term against the
names of the tags.
What does this change do?
This change is a first pass for adding a modal used to edit tags that appears in
the navigation menu. As the feature is being worked on in phases, it is
currently hidden behind the `new_edit_sidebar_categories_tags_interface_groups` site setting.
The following features will be worked on in future commits:
1. Input filter to filter through the tgas
2. Button to reset tag selection to default navigation menu tags site
settings
3. Button to deselect all current selection
What does this commit do?
Prior to this change, a long category name on mobile would cause a
horizontal scroll to appear because the category name was overflowing
its element. This commit changes it such that the category name will
wrap around instead.
What does this change do?
This change adds the deselect all and reset to defaults buttons to the
edit navigation menu categories modal. The deselect all button when
click deselects all the selected categories in the modal. If the user
saves with no categories selected, the user's categories section in the
navigation menu will be set to the site's top categories.
The reset to defaults button is only shown when the
`default_navigation_menu_categories` site setting has been configured.
When clicked, the user's categories section in the navigation menu is
automatically set to the categories defined by the
`default_navigation_menu_categories` site setting.
# Top level view
This PR is the first version of converting the search menu and its logic from (deprecated) widgets to glimmer components. The changes are hidden behind a group based feature flag. This will give us the ability to test the new implementation in a production setting before fully committing to the new search menu.
# What has changed
The majority of the logic from the widget implementation has been updated to fit within the context of a glimmer component, but it has not fundamentally changed. Instead of having a single widget - [search-menu.js](https://github.com/discourse/discourse/blob/main/app/assets/javascripts/discourse/app/widgets/search-menu.js) - that built the bulk of the search menu logic, we split the logic into (20+) bite size components. This greatly increases the readability and makes extending a component in the search menu much more straightforward.
That being said, certain pieces needed to be rewritten from scratch as they did not translate from widget -> glimmer, or there was a general code upgraded needed. There are a few of these changes worth noting:
### Search Service
**Search Term** -> In the widget implementation we had a overly complex way of managing the current search term. We tracked the search term across multiple different states (`term`, `opts.term`, `searchData.term`) causing headaches. This PR introduces a single source of truth:
```js
this.search.activeGlobalSearchTerm
```
This tracked value is available anywhere the `search` service is injected. In the case the search term should be needs to be updated you can call
```js
this.search.activeGlobalSearchTerm = "foo"
```
**event listeners** -> In the widget implementation we defined event listeners **only** on the search input to handle things such as
- keyboard navigation / shortcuts
- closing the search menu
- performing a search with "enter"
Having this in one place caused a lot of bloat in our logic as we had to handle multiple different cases in one location. Do _x_ if it is this element, but do _y_ if it is another. This PR updates the event listeners to be attached to individual components, allowing for a more fine tuned set of actions per element. To not duplicate logic across multiple components, we have condensed shared logic to actions on the search service to be reused. For example - `this.search.handleArrowUpOrDown` - to handle keyboard navigation.
### Search Context
We have unique logic based on the current search context (topic / tag / category / user / etc). This context is set within a models route file. We have updated the search service with a tracked value `searchContext` that can be utilized and updated from any component where the search service is injected.
```js
# before
this.searchService.set("searchContext", user.searchContext);
# after
this.searchService.searchContext = user.searchContext;
```
# Views
<img width="434" alt="Screenshot 2023-06-15 at 11 01 01 AM" src="https://github.com/discourse/discourse/assets/50783505/ef57e8e6-4e7b-4ba0-a770-8f2ed6310569">
<img width="418" alt="Screenshot 2023-06-15 at 11 04 11 AM" src="https://github.com/discourse/discourse/assets/50783505/2c1e0b38-d12c-4339-a1d5-04f0c1932b08">
<img width="413" alt="Screenshot 2023-06-15 at 11 04 34 AM" src="https://github.com/discourse/discourse/assets/50783505/b871d164-88cb-405e-9b78-d326a6f63686">
<img width="419" alt="Screenshot 2023-06-15 at 11 07 51 AM" src="https://github.com/discourse/discourse/assets/50783505/c7309a19-f541-47f4-94ef-10fa65658d8c">
<img width="424" alt="Screenshot 2023-06-15 at 11 04 48 AM" src="https://github.com/discourse/discourse/assets/50783505/f3dba06e-b029-431c-b3d0-36727b9e6dce">
<img width="415" alt="Screenshot 2023-06-15 at 11 08 57 AM" src="https://github.com/discourse/discourse/assets/50783505/ad4e7250-040c-4d06-bf06-99652f4c7b7c">
- FIX: improves reactions and thread indicator touch event on mobile
These "buttons" are located inside a scroll list which makes them very specific. The general idea is to ensure these events are passive and are not bubbling to the parent.
- DEV: moves state on top level message node
- FIX: ensures popover arrow has the correct border
- FIX: makes a message expanded by default
- FIX applies the same ios scroll fix on thread and channel
- UI: better active/hover state for thread indicator
- UI: attempts to follow more closely our BEM naming scheme
- FIX: reduces bottom padding on message with thread indicator and user info hidden
- UI: add padding for first message in thread
- FIX: prevents actions backdrop to open thread
- UI: makes thread indicator resizable
What this change?
Previous solution relied on CSS to hide the header which is first
wasteful since we're still rendering the header and second makes it
untestable. If we don't want the header to show, we should avoid
rendering it in the first place.
* FEATURE: Content custom summarization strategies.
This PR establishes a pattern for plugins to register alternative ways of summarizing content by extending a class that defines an interface.
Core controls which strategy we'll use and who has access to it through the `summarization_strategy` and `custom_summarization_allowed_groups`. It also defines the UI for summarizing topics.
Other plugins can access this summarization mechanism and implement their features, removing cross-plugin customizations, as it currently happens between chat and the discourse-ai plugin.
* Group membership validation and rate limiting
* Work with objects instead of classes
* Port summarization feature from discourse-ai to chat
* Rename available summaries to 'Top Replies' and 'Summary'
Improves the layout of most grids in posts, by using `object-fit: cover` for most images. This allows images to better fill up the space, without changing their aspect ratio.
* move the chat unread indicator to top to match the profile avatar indicator
* add white border to profile avatar indicator (badge notification) to match chat indicator and userstatus styling
* change `.urgent` to BEM
* congregate all styling into mixin
* update chat index to use mixin
* update thread indicator to use mixin
* update header indicator to use mixin
---------
Co-authored-by: Joffrey JAFFEUX <j.jaffeux@gmail.com>
Co-authored-by: Martin Brennan <martin@discourse.org>