Skip to content

FEATURE: Theme-owned color palettes #32795

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 16 commits into
base: main
Choose a base branch
from

Conversation

OsamaSayegh
Copy link
Member

This PR replaces the color palette dropdown on the theme page with a color palette editor that enables editing the theme's color palette directly from the theme page. With this change, a theme's color palette is strongly tied to its theme and can't be linked to other themes and it can't be selected by users without using the theme as well.

All of the changes are behind a feature flag. To enable it, turn on the use_overhauled_theme_color_palette setting.

Screenshot:

@OsamaSayegh OsamaSayegh force-pushed the feature/theme-owned-palettes branch 4 times, most recently from 768923f to 53295b1 Compare May 19, 2025 09:48
@OsamaSayegh OsamaSayegh force-pushed the feature/theme-owned-palettes branch from f903ffa to 3169855 Compare May 29, 2025 12:54
@github-actions github-actions bot added the i18n PRs which update English locale files or i18n related code label May 29, 2025
@OsamaSayegh OsamaSayegh force-pushed the feature/theme-owned-palettes branch 4 times, most recently from 839286d to 069de0f Compare May 30, 2025 15:57
@OsamaSayegh OsamaSayegh force-pushed the feature/theme-owned-palettes branch from 508ee6c to 6867ede Compare June 2, 2025 17:00
@OsamaSayegh OsamaSayegh force-pushed the feature/theme-owned-palettes branch from d3ff343 to 01f7913 Compare June 3, 2025 01:01
Comment on lines +87 to +93
this.toasts.error({
data: {
message: i18n(
"admin.config_areas.color_palettes.invalid_color_length"
),
},
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personally, I feel like using a toast here is not the right UI pattern. Normally, input validation errors are shown right below the input so that it is clear which input the error is shown for.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to make it clear, I don't think this should be a blocking comment but something worth thinking about.

cc @ellaestigoy

if (event.keyCode === 13) {
event.preventDefault();

if (currentValue.length !== 6 && currentValue.length !== 3) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of having to manually validate this, I think we can just rely on the minLength and maxLength attributes on the input to restrict the length?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, it has limitation that toast will show only when admin hit enter (13 keyCode). But I can click outside and will never know that something is wrong.

Demo

Screen.Recording.2025-06-03.at.4.17.31.pm.mov

When I am out of focus and value is invalid, should we display error as well?

Comment on lines +122 to +139
@action
onTextPaste(event) {
const content = (event.clipboardData || window.clipboardData).getData(
"text"
);

if (!this.isValidHex(content)) {
event.preventDefault();
this.toasts.error({
data: {
message: i18n(
"admin.config_areas.color_palettes.invalid_color_length"
),
},
});
return;
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having to handle all these events manually feels odd to me. In my mind, the pattern should be that each input tracks its own "value" and reacts accordingly when the value of its input changes.

data: JSON.stringify({ colors }),
contentType: "application/json",
});
this.owned_color_palette = paletteData;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm curious why we need to set this attribute as it doesn't seem to be used anywhere.

import { service } from "@ember/service";
import { i18n } from "discourse-i18n";

export default class AdminCustomizeThemesShowColorsRoute extends Route {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to take note to add a page title whenever we add a new route.

import { scrollTop } from "discourse/lib/scroll-top";
import { i18n } from "discourse-i18n";

export default class AdminCustomizeThemesShowIndexRoute extends Route {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, we have a missing page title.

import Service from "@ember/service";
import { TrackedSet } from "@ember-compat/tracked-built-ins";

export default class ColorPaletteChangeTracker extends Service {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if this qualifies as an Ember service which is an Ember object that lives for the duration of the application. In the case of this usage here, I think defining the tracked properties on the controller is a better fit?

Comment on lines +1045 to +1048
ThemeColorScheme.insert_all(
[{ theme_id: self.id, color_scheme_id: copy.id }],
unique_by: :index_theme_color_schemes_on_theme_id,
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just curious, why do we need to use insert_all here when we can just use create! and let the database handle the uniqueness validation.

Comment on lines +1051 to +1053
# race condition, a palette has already been associated with this theme
copy.destroy!
self.reload.owned_color_palette
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you expand on how this race condition can happen?

Comment on lines +1055 to +1059
palette.colors.each do |color|
copy_color = color.dup
copy_color.color_scheme_id = copy.id
copy_color.save!
end
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like a good opportunity to use insert_all since we are trying to insert multiple rows.

end

def include_base_palette?
object.color_scheme_id.blank? && object.owned_color_palette.blank?
Copy link
Contributor

@tgxworld tgxworld Jun 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't seem to see owned_color_palette being eager loaded anywhere so this will result in an N+1 queries problem when this serializer is used to render a list of themes.

if (event.keyCode === 13) {
event.preventDefault();

if (currentValue.length !== 6 && currentValue.length !== 3) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, it has limitation that toast will show only when admin hit enter (13 keyCode). But I can click outside and will never know that something is wrong.

Demo

Screen.Recording.2025-06-03.at.4.17.31.pm.mov

When I am out of focus and value is invalid, should we display error as well?

this.colorPaletteChangeTracker.dirtyColorsCount > 0 &&
transition.intent.name !== "adminCustomizeThemes.show.index"
) {
transition.abort();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed a bug when I played with it. When I change color and try to leave the page, I see a warning. However, if I frist click settings, warning is not showing. Recorded that

Screen.Recording.2025-06-03.at.4.33.58.pm.mov

@lis2
Copy link
Contributor

lis2 commented Jun 3, 2025

Wow, that is huge amount of work! I played with it and it feels much better than the previous version 🎊

I found and recorded 2 bugs, but because it is hidden behind feature flag, I am ok to merge it like that and improve in next iterations. However, I think that Tgx's comments are valid and important.

Finally, I feel that we should not remove feature flag until we solve "revert to original color". But this is problem for another day and let's first close this monster :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
i18n PRs which update English locale files or i18n related code
Development

Successfully merging this pull request may close these issues.

5 participants