DEV: Remove the legacy widget post menu code (#31211)

https://meta.discourse.org/t/341014
This commit is contained in:
Sérgio Saquetim
2025-04-01 12:03:58 -03:00
committed by GitHub
parent 4fb2fae0ed
commit 083082f328
23 changed files with 1425 additions and 4471 deletions

View File

@ -615,7 +615,6 @@ export default class PostMenu extends Component {
{{! this.collapsed is included in the check below because "Show More" button can be overriden to be always visible }}
class={{concatClass
"post-controls"
"glimmer-post-menu"
(if
(and
(this.showMoreButton.shouldRender

View File

@ -43,7 +43,7 @@ export default class PostMenuRepliesButton extends Component {
<template>
<DButton
class="show-replies btn-icon-text"
class="post-action-menu__show-replies show-replies btn-icon-text"
...attributes
disabled={{this.disabled}}
@action={{@buttonActions.toggleReplies}}

View File

@ -1,31 +0,0 @@
import { withSilencedDeprecations } from "discourse/lib/deprecated";
import { withPluginApi } from "discourse/lib/plugin-api";
import PostBookmarkManager from "discourse/lib/post-bookmark-manager";
export default {
name: "discourse-bookmark-menu",
initialize(container) {
const currentUser = container.lookup("service:current-user");
withPluginApi("0.10.1", (api) => {
if (currentUser) {
withSilencedDeprecations("discourse.post-menu-widget-overrides", () => {
api.replacePostMenuButton("bookmark", {
name: "bookmark-menu-shim",
shouldRender: () => true,
buildAttrs: (widget) => {
return {
post: widget.findAncestorModel(),
bookmarkManager: new PostBookmarkManager(
container,
widget.findAncestorModel()
),
};
},
});
});
}
});
},
};

View File

@ -139,12 +139,6 @@ import { setNotificationsLimit } from "discourse/routes/user-notifications";
import { addComposerSaveErrorCallback } from "discourse/services/composer";
import { addPostClassesCallback } from "discourse/widgets/post";
import { addDecorator } from "discourse/widgets/post-cooked";
import {
addButton,
apiExtraButtons,
removeButton,
replaceButton,
} from "discourse/widgets/post-menu";
import {
addPostSmallActionClassesCallback,
addPostSmallActionIcon,
@ -199,24 +193,12 @@ const DEPRECATED_POST_STREAM_WIDGETS = [
"topic-post-visited-line",
];
const DEPRECATED_POST_MENU_WIDGETS = [
"post-menu",
"post-user-tip-shim",
"small-user-list",
];
const POST_STREAM_DEPRECATION_OPTIONS = {
since: "v3.5.0.beta1-dev",
id: "discourse.post-stream-widget-overrides",
// url: "", // TODO (glimmer-post-stream) uncomment when the topic is created on meta
};
const POST_MENU_DEPRECATION_OPTIONS = {
since: "v3.4.0.beta3-dev",
id: "discourse.post-menu-widget-overrides",
url: "https://meta.discourse.org/t/341014",
};
export const RAW_TOPIC_LIST_DEPRECATION_OPTIONS = {
since: "v3.4.0.beta4-dev",
id: "discourse.hbr-topic-list-overrides",
@ -942,59 +924,13 @@ class PluginApi {
}
/**
* Add a new button below a post with your plugin.
*
* The `callback` function will be called whenever the post menu is rendered,
* and if you return an object with the button details it will be rendered.
*
* Example:
*
* ```
* api.addPostMenuButton('coffee', () => {
* return {
* action: 'drinkCoffee',
* icon: 'mug-saucer',
* className: 'hot-coffee',
* title: 'coffee.title',
* position: 'first' // can be `first`, `last` or `second-last-hidden`
* };
* });
*
* ```
*
* action: may be a string or a function. If it is a string, a widget action
* will be triggered. If it is function, the function will be called.
*
* function will receive a single argument:
* {
* post:
* showFeedback:
* }
*
* showFeedback can be called to issue a visual feedback on button press.
* It gets a single argument with a localization key.
*
* Example:
*
* api.addPostMenuButton('coffee', () => {
* return {
* action: ({ post, showFeedback }) => {
* drinkCoffee(post);
* showFeedback('discourse_plugin.coffee.drink');
* },
* icon: 'mug-saucer',
* className: 'hot-coffee',
* }
* }
* Decommissioned API
**/
addPostMenuButton(name, callback) {
deprecated(
"`api.addPostMenuButton` has been deprecated. Use the value transformer `post-menu-buttons` instead.",
POST_MENU_DEPRECATION_OPTIONS
addPostMenuButton() {
// eslint-disable-next-line no-console
console.error(
"`api.addPostMenuButton`: This API was decommissioned. Use the value transformer `post-menu-buttons` instead."
);
apiExtraButtons[name] = callback;
addButton(name, callback);
}
/**
@ -1046,54 +982,23 @@ class PluginApi {
}
/**
* Remove existing button below a post with your plugin.
*
* Example:
*
* ```
* api.removePostMenuButton('like');
* ```
*
* ```
* api.removePostMenuButton('like', (attrs, state, siteSettings, settings, currentUser) => {
* if (attrs.post_number === 1) {
* return true;
* }
* });
* ```
* Decommissioned API
**/
removePostMenuButton(name, callback) {
deprecated(
"`api.removePostMenuButton` has been deprecated. Use the value transformer `post-menu-buttons` instead.",
POST_MENU_DEPRECATION_OPTIONS
removePostMenuButton() {
// eslint-disable-next-line no-console
console.error(
"`api.removePostMenuButton`: This API was decommissioned. Use the value transformer `post-menu-buttons` instead."
);
removeButton(name, callback);
}
/**
* Replace an existing button with a widget
*
* Example:
* ```
* api.replacePostMenuButton("like", {
* name: "widget-name",
* buildAttrs: (widget) => {
* return { post: widget.findAncestorModel() };
* },
* shouldRender: (widget) => {
* const post = widget.findAncestorModel();
* return post.id === 1
* }
* });
* Decommissioned API
**/
replacePostMenuButton(name, widget) {
deprecated(
"`api.replacePostMenuButton` has been deprecated. Use the value transformer `post-menu-buttons` instead.",
POST_MENU_DEPRECATION_OPTIONS
replacePostMenuButton() {
// eslint-disable-next-line no-console
console.error(
"`api.replacePostMenuButton`: This API was decommissioned. Use the value transformer `post-menu-buttons` instead."
);
replaceButton(name, widget);
}
/**
@ -3538,14 +3443,6 @@ class PluginApi {
`The \`${widgetName}\` widget has been deprecated and \`api.${override}\` is no longer a supported override.`,
POST_STREAM_DEPRECATION_OPTIONS
);
return;
}
if (DEPRECATED_POST_MENU_WIDGETS.includes(widgetName)) {
deprecated(
`The ${widgetName} widget has been deprecated and ${override} is no longer a supported override.`,
POST_MENU_DEPRECATION_OPTIONS
);
}
}
}

View File

@ -18,7 +18,6 @@ export const CRITICAL_DEPRECATIONS = [
"discourse.add-header-panel",
"discourse.header-widget-overrides",
"discourse.plugin-outlet-tag-name",
"discourse.post-menu-widget-overrides",
"discourse.add-flag-property",
"discourse.breadcrumbs.childCategories",
"discourse.breadcrumbs.firstCategory",

View File

@ -1,977 +0,0 @@
import { next } from "@ember/runloop";
import { hbs } from "ember-cli-htmlbars";
import { Promise } from "rsvp";
import { h } from "virtual-dom";
import AdminPostMenu from "discourse/components/admin-post-menu";
import DeleteTopicDisallowedModal from "discourse/components/modal/delete-topic-disallowed";
import { smallUserAttrs } from "discourse/components/small-user-list";
import { formattedReminderTime } from "discourse/lib/bookmark";
import discourseLater from "discourse/lib/later";
import { recentlyCopied, showAlert } from "discourse/lib/post-action-feedback";
import {
NO_REMINDER_ICON,
WITH_REMINDER_ICON,
} from "discourse/models/bookmark";
import RenderGlimmer, {
registerWidgetShim,
} from "discourse/widgets/render-glimmer";
import { applyDecorators, createWidget } from "discourse/widgets/widget";
import { i18n } from "discourse-i18n";
const LIKE_ACTION = 2;
const VIBRATE_DURATION = 5;
const _builders = {};
export let apiExtraButtons = {};
let _extraButtons = {};
let _buttonsToRemoveCallbacks = {};
let _buttonsToReplace = {};
export function addButton(name, builder) {
_extraButtons[name] = builder;
}
export function resetPostMenuExtraButtons() {
for (const key of Object.keys(apiExtraButtons)) {
delete apiExtraButtons[key];
}
_extraButtons = {};
_buttonsToRemoveCallbacks = {};
_buttonsToReplace = {};
}
export function removeButton(name, callback) {
// 🏌️
_buttonsToRemoveCallbacks[name] ??= [];
_buttonsToRemoveCallbacks[name].push(callback || (() => true));
}
export function replaceButton(name, replaceWith) {
_buttonsToReplace[name] = replaceWith;
}
function registerButton(name, builder) {
_builders[name] = builder;
}
export function buildButton(name, widget) {
let { attrs, state, siteSettings, settings, currentUser } = widget;
// Return early if the button is supposed to be removed via the plugin API
if (
_buttonsToRemoveCallbacks[name] &&
_buttonsToRemoveCallbacks[name].some((c) =>
c(attrs, state, siteSettings, settings, currentUser)
)
) {
return;
}
// Look for a button replacement, build and return widget attrs if present
let replacement = _buttonsToReplace[name];
if (replacement && replacement?.shouldRender(widget)) {
return {
replaced: true,
name: replacement.name,
attrs: replacement.buildAttrs(widget),
};
}
let builder = _builders[name];
if (builder) {
let button = builder(attrs, state, siteSettings, settings, currentUser);
if (button && !button.id) {
button.id = name;
}
return button;
}
}
registerButton("read-count", (attrs, state) => {
if (attrs.showReadIndicator) {
const count = attrs.readCount;
if (count > 0) {
let ariaPressed = (state?.readers?.length > 0).toString();
return {
action: "toggleWhoRead",
title: "post.controls.read_indicator",
className: "button-count read-indicator",
contents: count,
iconRight: true,
addContainer: false,
translatedAriaLabel: i18n("post.sr_post_read_count_button", {
count,
}),
ariaPressed,
};
}
}
});
registerButton("read", (attrs) => {
const readBySomeone = attrs.readCount > 0;
if (attrs.showReadIndicator && readBySomeone) {
return {
action: "toggleWhoRead",
title: "post.controls.read_indicator",
icon: "book-open-reader",
before: "read-count",
addContainer: false,
};
}
});
function likeCount(attrs, state) {
const count = attrs.likeCount;
if (count > 0) {
const title = attrs.liked
? count === 1
? "post.has_likes_title_only_you"
: "post.has_likes_title_you"
: "post.has_likes_title";
let icon = attrs.yours ? "d-liked" : "";
let addContainer = attrs.yours;
const additionalClass = attrs.yours ? "my-likes" : "regular-likes";
if (!attrs.showLike) {
icon = attrs.yours ? "d-liked" : "d-unliked";
addContainer = true;
}
let ariaPressed = (state?.likedUsers?.length > 0).toString();
return {
action: "toggleWhoLiked",
title,
className: `button-count like-count highlight-action ${additionalClass}`,
contents: count,
icon,
iconRight: true,
addContainer,
titleOptions: { count: attrs.liked ? count - 1 : count },
translatedAriaLabel: i18n("post.sr_post_like_count_button", { count }),
ariaPressed,
};
}
}
registerButton("like-count", likeCount);
registerButton(
"like",
(attrs, _state, _siteSettings, _settings, currentUser) => {
if (!attrs.showLike) {
return likeCount(attrs);
}
const className = attrs.liked ? "toggle-like has-like" : "toggle-like like";
const button = {
action: "like",
icon: attrs.liked ? "d-liked" : "d-unliked",
className,
before: "like-count",
data: {
"post-id": attrs.id,
},
};
// If the user has already liked the post and doesn't have permission
// to undo that operation, then indicate via the title that they've liked it
// and disable the button. Otherwise, set the title even if the user
// is anonymous (meaning they don't currently have permission to like);
// this is important for accessibility.
if (attrs.liked && !attrs.canToggleLike) {
button.title = "post.controls.has_liked";
} else {
button.title = attrs.liked
? "post.controls.undo_like"
: "post.controls.like";
}
if (currentUser && !attrs.canToggleLike) {
button.disabled = true;
}
return button;
}
);
registerButton("flag-count", (attrs) => {
let className = "button-count";
if (attrs.reviewableScorePendingCount > 0) {
className += " has-pending";
}
return {
className,
contents: h("span", attrs.reviewableScoreCount.toString()),
url: `/review/${attrs.reviewableId}`,
};
});
registerButton(
"flag",
(attrs, _state, siteSettings, _postMenuSettings, currentUser) => {
if (
attrs.reviewableId ||
(attrs.canFlag && !attrs.hidden) ||
(siteSettings.allow_all_users_to_flag_illegal_content && !currentUser)
) {
const button = {
action: "showFlags",
title: currentUser
? "post.controls.flag"
: "post.controls.anonymous_flag",
icon: "flag",
className: "create-flag",
};
if (attrs.reviewableId) {
button.before = "flag-count";
}
return button;
}
}
);
registerButton("edit", (attrs) => {
if (attrs.canEdit) {
return {
action: "editPost",
className: "edit",
title: "post.controls.edit",
icon: "pencil",
alwaysShowYours: true,
};
}
});
registerButton("reply-small", (attrs) => {
if (!attrs.canCreatePost) {
return;
}
const args = {
action: "replyToPost",
title: "post.controls.reply",
icon: "reply",
className: "reply",
translatedAriaLabel: i18n("post.sr_reply_to", {
post_number: attrs.post_number,
username: attrs.username,
}),
};
return args;
});
registerButton("wiki-edit", (attrs) => {
if (attrs.canEdit) {
const args = {
action: "editPost",
className: "edit create",
title: "post.controls.edit",
icon: "far-pen-to-square",
alwaysShowYours: true,
};
if (!attrs.mobileView) {
args.label = "post.controls.edit_action";
}
return args;
}
});
registerButton("replies", (attrs, state, siteSettings) => {
const count = attrs.replyCount;
if (!count) {
return;
}
let title;
let action = "toggleRepliesBelow";
let icon = state.repliesShown ? "chevron-up" : "chevron-down";
if (siteSettings.enable_filtered_replies_view) {
action = "toggleFilteredRepliesView";
icon = state.filteredRepliesShown ? "chevron-up" : "chevron-down";
title = state.filteredRepliesShown
? "post.view_all_posts"
: "post.filtered_replies_hint";
}
// Omit replies if the setting `suppress_reply_directly_below` is enabled
if (
count === 1 &&
attrs.replyDirectlyBelow &&
siteSettings.suppress_reply_directly_below
) {
return;
}
let ariaPressed, ariaExpanded;
if (!siteSettings.enable_filtered_replies_view) {
ariaPressed = state.repliesShown.toString();
ariaExpanded = state.repliesShown.toString();
}
return {
action,
icon,
className: "show-replies",
titleOptions: { count },
title,
labelOptions: { count },
label: attrs.mobileView ? "post.has_replies_count" : "post.has_replies",
iconRight: !siteSettings.enable_filtered_replies_view || attrs.mobileView,
disabled: !!attrs.deleted,
translatedAriaLabel: i18n("post.sr_expand_replies", { count }),
ariaExpanded,
ariaPressed,
ariaControls: `embedded-posts__bottom--${attrs.post_number}`,
};
});
registerButton("share", () => {
return {
action: "share",
icon: "d-post-share",
className: "share",
title: "post.controls.share",
};
});
registerButton("copyLink", () => {
return {
action: "copyLink",
icon: "d-post-share",
className: "post-action-menu__copy-link",
title: "post.controls.copy_title",
ariaLive: "polite",
};
});
registerButton("reply", (attrs, state, siteSettings, postMenuSettings) => {
const args = {
action: "replyToPost",
title: "post.controls.reply",
icon: "reply",
className: "reply create fade-out",
translatedAriaLabel: i18n("post.sr_reply_to", {
post_number: attrs.post_number,
username: attrs.username,
}),
};
if (!attrs.canCreatePost) {
return;
}
if (postMenuSettings.showReplyTitleOnMobile || !attrs.mobileView) {
args.label = "topic.reply.title";
}
return args;
});
registerButton(
"bookmark",
(attrs, _state, siteSettings, _settings, currentUser) => {
if (!attrs.canBookmark) {
return;
}
let classNames = ["bookmark"];
let title = "bookmarks.not_bookmarked";
let titleOptions = { name: "" };
if (attrs.bookmarked) {
classNames.push("bookmarked");
if (attrs.bookmarkReminderAt) {
classNames.push("with-reminder");
let formattedReminder = formattedReminderTime(
attrs.bookmarkReminderAt,
currentUser.user_option.timezone
);
title = "bookmarks.created_with_reminder";
titleOptions.date = formattedReminder;
} else {
title = "bookmarks.created";
}
if (attrs.bookmarkName) {
titleOptions.name = attrs.bookmarkName;
}
}
return {
id: attrs.bookmarked ? "unbookmark" : "bookmark",
action: "toggleBookmark",
title,
titleOptions,
className: classNames.join(" "),
icon: attrs.bookmarkReminderAt ? WITH_REMINDER_ICON : NO_REMINDER_ICON,
};
}
);
registerButton("admin", (attrs) => {
if (!attrs.canManage && !attrs.canWiki && !attrs.canEditStaffNotes) {
return;
}
return {
action: "openAdminMenu",
title: "post.controls.admin",
className: "show-post-admin-menu",
icon: "wrench",
sendActionEvent: true,
};
});
registerButton("delete", (attrs) => {
if (attrs.canRecoverTopic) {
return {
id: "recover_topic",
action: "recoverPost",
title: "topic.actions.recover",
icon: "arrow-rotate-left",
className: "recover",
};
} else if (attrs.canDeleteTopic) {
return {
id: "delete_topic",
action: "deletePost",
title: "post.controls.delete_topic",
icon: "trash-can",
className: "delete",
};
} else if (attrs.canRecover) {
return {
id: "recover",
action: "recoverPost",
title: "post.controls.undelete",
icon: "arrow-rotate-left",
className: "recover",
};
} else if (attrs.canDelete) {
return {
id: "delete",
action: "deletePost",
title: "post.controls.delete",
icon: "trash-can",
className: "delete",
};
} else if (attrs.showFlagDelete) {
return {
id: "delete_topic",
action: "showDeleteTopicModal",
title: "post.controls.delete_topic_disallowed",
icon: "trash-can",
className: "delete",
};
}
});
function _replaceButton(buttons, find, replace) {
const idx = buttons.indexOf(find);
if (idx !== -1) {
buttons[idx] = replace;
}
}
export default createWidget("post-menu", {
tagName: "section.post-menu-area.clearfix",
services: ["modal", "menu"],
settings: {
collapseButtons: true,
buttonType: "flat-button",
showReplyTitleOnMobile: false,
},
defaultState() {
return {
collapsed: true,
likedUsers: [],
readers: [],
};
},
buildKey: (attrs) => `post-menu-${attrs.id}`,
attachButton(name) {
let buttonAttrs = buildButton(name, this);
if (buttonAttrs?.component) {
return [
new RenderGlimmer(
this,
buttonAttrs.tagName,
hbs`<@data.component
@permanentlyDeletePost={{@data.permanentlyDeletePost}}
@lockPost={{@data.lockPost}}
@unlockPost={{@data.unlockPost}}
@grantBadge={{@data.grantBadge}}
@rebakePost={{@data.rebakePost}}
@toggleWiki={{@data.toggleWiki}}
@changePostOwner={{@data.changePostOwner}}
@changeNotice={{@data.changeNotice}}
@togglePostType={{@data.togglePostType}}
@unhidePost={{@data.unhidePost}}
@showPagePublish={{@data.showPagePublish}}
@post={{@data.post}}
@transformedPost={{@data.transformedPost}}
@scheduleRerender={{@data.scheduleRerender}}
/>`,
{
component: buttonAttrs.component,
transformedPost: this.attrs,
post: this.findAncestorModel(),
permanentlyDeletePost: () =>
this.sendWidgetAction("permanentlyDeletePost"),
lockPost: () => this.sendWidgetAction("lockPost"),
unlockPost: () => this.sendWidgetAction("unlockPost"),
grantBadge: () => this.sendWidgetAction("grantBadge"),
rebakePost: () => this.sendWidgetAction("rebakePost"),
toggleWiki: () => this.sendWidgetAction("toggleWiki"),
changePostOwner: () => this.sendWidgetAction("changePostOwner"),
changeNotice: () => this.sendWidgetAction("changeNotice"),
togglePostType: () => this.sendWidgetAction("togglePostType"),
scheduleRerender: () => this.scheduleRerender(),
}
),
];
}
// If the button is replaced via the plugin API, we need to render the
// replacement rather than a button
if (buttonAttrs?.replaced) {
return this.attach(buttonAttrs.name, buttonAttrs.attrs);
}
if (buttonAttrs) {
let button = this.attach(this.settings.buttonType, buttonAttrs);
if (buttonAttrs.before) {
let before = this.attachButton(buttonAttrs.before);
return h("div.double-button", [before, button]);
} else if (buttonAttrs.addContainer) {
return h("div.double-button", [button]);
}
return button;
}
},
menuItems() {
return this.siteSettings.post_menu.split("|").filter(Boolean);
},
html(attrs, state) {
const { currentUser, keyValueStore, siteSettings } = this;
const hiddenSetting = siteSettings.post_menu_hidden_items || "";
const hiddenButtons = hiddenSetting
.split("|")
.filter((s) => !attrs.bookmarked || s !== "bookmark");
if (currentUser && keyValueStore) {
const likedPostId = keyValueStore.getInt("likedPostId");
if (likedPostId === attrs.id) {
keyValueStore.remove("likedPostId");
next(() => this.sendWidgetAction("toggleLike"));
}
}
const allButtons = [];
let visibleButtons = [];
// filter menu items based on site settings
const orderedButtons = this.menuItems();
// If the post is a wiki, make Edit more prominent
if (attrs.wiki && attrs.canEdit) {
_replaceButton(orderedButtons, "edit", "reply-small");
_replaceButton(orderedButtons, "reply", "wiki-edit");
}
orderedButtons.forEach((i) => {
const button = this.attachButton(i, attrs);
if (button) {
allButtons.push(button);
if (
(attrs.yours && button.attrs && button.attrs.alwaysShowYours) ||
(attrs.reviewableId && i === "flag") ||
!hiddenButtons.includes(i)
) {
visibleButtons.push(button);
}
}
});
if (!this.settings.collapseButtons) {
visibleButtons = allButtons;
}
let hasShowMoreButton = false;
// Only show ellipsis if there is more than one button hidden
// if there are no more buttons, we are not collapsed
if (!state.collapsed || allButtons.length <= visibleButtons.length + 1) {
visibleButtons = allButtons;
if (state.collapsed) {
state.collapsed = false;
}
} else {
const showMore = this.attach("flat-button", {
action: "showMoreActions",
title: "show_more",
className: "show-more-actions",
icon: "ellipsis",
});
visibleButtons.splice(visibleButtons.length - 1, 0, showMore);
hasShowMoreButton = true;
}
Object.values(_extraButtons).forEach((builder) => {
let shouldAddButton = true;
if (_buttonsToRemoveCallbacks[name]) {
shouldAddButton = !_buttonsToRemoveCallbacks[name].some((c) =>
c(
attrs,
this.state,
this.siteSettings,
this.settings,
this.currentUser
)
);
}
if (shouldAddButton && builder) {
const buttonAttrs = builder(
attrs,
this.state,
this.siteSettings,
this.settings,
this.currentUser
);
if (buttonAttrs) {
const { position, beforeButton, afterButton } = buttonAttrs;
delete buttonAttrs.position;
let button;
if (typeof buttonAttrs.action === "function") {
const original = buttonAttrs.action;
const self = this;
buttonAttrs.action = async function (post) {
let showFeedback = null;
if (buttonAttrs.className) {
showFeedback = (messageKey) => {
showAlert(post.id, buttonAttrs.className, messageKey);
};
}
const postAttrs = {
post,
showFeedback,
};
if (
!buttonAttrs.className ||
!recentlyCopied(post.id, buttonAttrs.actionClass)
) {
self.sendWidgetAction(original, postAttrs);
}
};
}
button = this.attach(this.settings.buttonType, buttonAttrs);
const content = [];
if (beforeButton) {
content.push(beforeButton(h));
}
content.push(button);
if (afterButton) {
content.push(afterButton(h));
}
button = h("span.extra-buttons", content);
if (button) {
switch (position) {
case "first":
visibleButtons.unshift(button);
break;
case "second":
visibleButtons.splice(1, 0, button);
break;
case "second-last-hidden":
if (!state.collapsed) {
visibleButtons.splice(visibleButtons.length - 2, 0, button);
}
break;
default:
visibleButtons.push(button);
break;
}
}
}
}
});
const postControls = [];
const repliesButton = this.attachButton("replies", attrs);
if (repliesButton) {
postControls.push(repliesButton);
}
const extraPostControls = applyDecorators(
this,
"extra-post-controls",
attrs,
state
);
postControls.push(extraPostControls);
const extraControls = applyDecorators(this, "extra-controls", attrs, state);
const beforeExtraControls = applyDecorators(
this,
"before-extra-controls",
attrs,
state
);
const controlsButtons = [
...beforeExtraControls,
...visibleButtons,
...extraControls,
];
postControls.push(h("div.actions", controlsButtons));
const contents = [
h(
"nav.post-controls" +
(this.state.collapsed ? ".collapsed" : ".expanded") +
(siteSettings.enable_filtered_replies_view
? ".replies-button-visible"
: ""),
postControls
),
];
if (state.readers.length) {
const remaining = state.totalReaders - state.readers.length;
const description =
remaining > 0
? "post.actions.people.read_capped"
: "post.actions.people.read";
const count = remaining > 0 ? remaining : state.totalReaders;
contents.push(
this.attach("small-user-list", {
users: state.readers,
addSelf: false,
listClassName: "who-read",
description,
count,
isVisible: true,
})
);
}
if (state.likedUsers.length) {
const remaining = state.total - state.likedUsers.length;
const description =
remaining > 0
? "post.actions.people.like_capped"
: "post.actions.people.like";
const count = remaining > 0 ? remaining : state.total;
contents.push(
this.attach("small-user-list", {
users: state.likedUsers,
addSelf: attrs.liked && remaining === 0,
listClassName: "who-liked",
description,
count,
isVisible: true,
})
);
}
if (hasShowMoreButton) {
contents.push(this.attach("post-user-tip-shim"));
}
return contents;
},
openAdminMenu(event) {
this.menu.show(event.target, {
identifier: "admin-post-menu",
component: AdminPostMenu,
modalForMobile: true,
autofocus: true,
data: {
scheduleRerender: this.scheduleRerender.bind(this),
transformedPost: this.attrs,
post: this.findAncestorModel(),
permanentlyDeletePost: () =>
this.sendWidgetAction("permanentlyDeletePost"),
lockPost: () => this.sendWidgetAction("lockPost"),
unlockPost: () => this.sendWidgetAction("unlockPost"),
grantBadge: () => this.sendWidgetAction("grantBadge"),
rebakePost: () => this.sendWidgetAction("rebakePost"),
toggleWiki: () => this.sendWidgetAction("toggleWiki"),
changePostOwner: () => this.sendWidgetAction("changePostOwner"),
changeNotice: () => this.sendWidgetAction("changeNotice"),
togglePostType: () => this.sendWidgetAction("togglePostType"),
unhidePost: () => this.sendWidgetAction("unhidePost"),
showPagePublish: () => this.sendWidgetAction("showPagePublish"),
},
});
},
showDeleteTopicModal() {
this.modal.show(DeleteTopicDisallowedModal);
},
showMoreActions() {
this.state.collapsed = false;
const likesPromise = !this.state.likedUsers.length
? this.getWhoLiked()
: Promise.resolve();
return likesPromise.then(() => {
if (!this.state.readers.length && this.attrs.showReadIndicator) {
return this.getWhoRead();
}
});
},
like() {
const { attrs, currentUser, keyValueStore } = this;
if (!currentUser) {
keyValueStore &&
keyValueStore.set({ key: "likedPostId", value: attrs.id });
return this.sendWidgetAction("showLogin");
}
if (this.capabilities.userHasBeenActive && this.capabilities.canVibrate) {
navigator.vibrate(VIBRATE_DURATION);
}
if (attrs.liked) {
return this.sendWidgetAction("toggleLike");
}
const heart = document.querySelector(
`.toggle-like[data-post-id="${attrs.id}"] .d-icon`
);
heart.closest(".toggle-like").classList.add("has-like");
heart.classList.add("heart-animation");
return new Promise((resolve) => {
discourseLater(() => {
this.sendWidgetAction("toggleLike").then(() => resolve());
}, 400);
});
},
refreshLikes() {
if (this.state.likedUsers.length) {
return this.getWhoLiked();
}
},
refreshReaders() {
if (this.state.readers.length) {
return this.getWhoRead();
}
},
getWhoLiked() {
const { attrs, state } = this;
return this.store
.find("post-action-user", {
id: attrs.id,
post_action_type_id: LIKE_ACTION,
})
.then((users) => {
state.likedUsers = users.map(smallUserAttrs);
state.total = users.totalRows;
});
},
getWhoRead() {
const { attrs, state } = this;
return this.store.find("post-reader", { id: attrs.id }).then((users) => {
state.readers = users.map(smallUserAttrs);
state.totalReaders = users.totalRows;
});
},
toggleWhoLiked() {
const state = this.state;
if (state.likedUsers.length) {
state.likedUsers = [];
} else {
return this.getWhoLiked();
}
},
toggleWhoRead() {
const state = this.state;
if (this.state.readers.length) {
state.readers = [];
} else {
return this.getWhoRead();
}
},
});
// TODO (glimmer-post-menu): Once this widget is removed the `<section>...</section>` tag needs to be added to the PostMenu component
registerWidgetShim(
"glimmer-post-menu",
"section.post-menu-area.clearfix",
hbs`
<Post::Menu
@canCreatePost={{@data.canCreatePost}}
@filteredRepliesView={{@data.filteredRepliesView}}
@nextPost={{@data.nextPost}}
@post={{@data.post}}
@prevPost={{@data.prevPost}}
@repliesShown={{@data.repliesShown}}
@showReadIndicator={{@data.showReadIndicator}}
@changeNotice={{@data.changeNotice}}
@changePostOwner={{@data.changePostOwner}}
@copyLink={{@data.copyLink}}
@deletePost={{@data.deletePost}}
@editPost={{@data.editPost}}
@grantBadge={{@data.grantBadge}}
@lockPost={{@data.lockPost}}
@permanentlyDeletePost={{@data.permanentlyDeletePost}}
@rebakePost={{@data.rebakePost}}
@recoverPost={{@data.recoverPost}}
@replyToPost={{@data.replyToPost}}
@share={{@data.share}}
@showFlags={{@data.showFlags}}
@showLogin={{@data.showLogin}}
@showPagePublish={{@data.showPagePublish}}
@toggleLike={{@data.toggleLike}}
@togglePostType={{@data.togglePostType}}
@toggleReplies={{@data.toggleReplies}}
@toggleWiki={{@data.toggleWiki}}
@unhidePost={{@data.unhidePost}}
@unlockPost={{@data.unlockPost}}
/>`
);

View File

@ -6,7 +6,6 @@ import ShareTopicModal from "discourse/components/modal/share-topic";
import { dateNode } from "discourse/helpers/node";
import autoGroupFlairForUser from "discourse/lib/avatar-flair";
import { avatarUrl, translateSize } from "discourse/lib/avatar-utils";
import { registerDeprecationHandler } from "discourse/lib/deprecated";
import { isTesting } from "discourse/lib/environment";
import { relativeAgeMediumSpan } from "discourse/lib/formatter";
import getURL, { getAbsoluteURL, getURLWithCDN } from "discourse/lib/get-url";
@ -17,7 +16,6 @@ import {
prioritizeNameFallback,
prioritizeNameInUx,
} from "discourse/lib/settings";
import { consolePrefix } from "discourse/lib/source-identifier";
import { transformBasicPost } from "discourse/lib/transform-post";
import DiscourseURL from "discourse/lib/url";
import {
@ -46,19 +44,6 @@ function transformWithCallbacks(post, topicUrl, store) {
return transformed;
}
let postMenuWidgetExtensionsAdded = null;
let postMenuConsoleWarningLogged = false;
registerDeprecationHandler((_, opts) => {
if (opts?.id === "discourse.post-menu-widget-overrides") {
if (!postMenuWidgetExtensionsAdded) {
postMenuWidgetExtensionsAdded = new Set();
}
postMenuWidgetExtensionsAdded.add(consolePrefix().slice(1, -1));
}
});
export function avatarImg(wanted, attrs) {
const size = translateSize(wanted);
const url = avatarUrl(attrs.template, size);
@ -562,36 +547,45 @@ createWidget("post-contents", {
},
};
if (
this.siteSettings.glimmer_post_menu_mode === "enabled" ||
(this.siteSettings.glimmer_post_menu_mode === "auto" &&
!postMenuWidgetExtensionsAdded)
) {
if (!postMenuConsoleWarningLogged) {
postMenuConsoleWarningLogged = true;
if (!isTesting()) {
// eslint-disable-next-line no-console
console.log("✅ Using the new 'glimmer' post menu!");
}
if (postMenuWidgetExtensionsAdded) {
// eslint-disable-next-line no-console
console.warn(
[
"Using the new 'glimmer' post menu, even though there are themes and/or plugins using deprecated APIs (glimmer_post_menu_mode = enabled).\n" +
"The following plugins and/or themes are using deprecated APIs, their post menu customizations are broken and may cause your site to not work properly:",
...Array.from(postMenuWidgetExtensionsAdded).sort(),
// TODO (glimmer-post-menu): add link to meta topic here when the roadmap for the update is announced
].join("\n- ")
);
}
}
const filteredRepliesView =
this.siteSettings.enable_filtered_replies_view;
const filteredRepliesView = this.siteSettings.enable_filtered_replies_view;
result.push(
this.attach("glimmer-post-menu", {
// TODO (glimmer-post-stream):
// Once this widget shim is removed the `<section>...</section>` tag needs to be added to the PostMenu component
new RenderGlimmer(
this,
"section.post-menu-area.clearfix",
hbs`
<Post::Menu
@canCreatePost={{@data.canCreatePost}}
@filteredRepliesView={{@data.filteredRepliesView}}
@nextPost={{@data.nextPost}}
@post={{@data.post}}
@prevPost={{@data.prevPost}}
@repliesShown={{@data.repliesShown}}
@showReadIndicator={{@data.showReadIndicator}}
@changeNotice={{@data.changeNotice}}
@changePostOwner={{@data.changePostOwner}}
@copyLink={{@data.copyLink}}
@deletePost={{@data.deletePost}}
@editPost={{@data.editPost}}
@grantBadge={{@data.grantBadge}}
@lockPost={{@data.lockPost}}
@permanentlyDeletePost={{@data.permanentlyDeletePost}}
@rebakePost={{@data.rebakePost}}
@recoverPost={{@data.recoverPost}}
@replyToPost={{@data.replyToPost}}
@share={{@data.share}}
@showFlags={{@data.showFlags}}
@showLogin={{@data.showLogin}}
@showPagePublish={{@data.showPagePublish}}
@toggleLike={{@data.toggleLike}}
@togglePostType={{@data.togglePostType}}
@toggleReplies={{@data.toggleReplies}}
@toggleWiki={{@data.toggleWiki}}
@unhidePost={{@data.unhidePost}}
@unlockPost={{@data.unlockPost}}
/>`,
{
canCreatePost: attrs.canCreatePost,
filteredRepliesView,
nextPost: attrs.nextPost,
@ -626,27 +620,9 @@ createWidget("post-contents", {
toggleWiki: () => this.sendWidgetAction("toggleWiki"), // this action comes from the post stream
unhidePost: () => this.sendWidgetAction("unhidePost"), // this action comes from the post stream
unlockPost: () => this.sendWidgetAction("unlockPost"), // this action comes from the post stream
})
);
} else {
if (
this.siteSettings.glimmer_post_menu_mode !== "disabled" &&
postMenuWidgetExtensionsAdded &&
!postMenuConsoleWarningLogged
) {
postMenuConsoleWarningLogged = true;
// eslint-disable-next-line no-console
console.warn(
[
"Using the legacy 'widget' post menu because the following plugins and/or themes are using deprecated APIs:",
...Array.from(postMenuWidgetExtensionsAdded).sort(),
// TODO (glimmer-post-menu): add link to meta topic here when the roadmap for the update is announced
].join("\n- ")
);
}
result.push(this.attach("post-menu", attrs, extraState));
}
)
);
const repliesBelow = state.repliesBelow;
if (repliesBelow.length) {
@ -1204,7 +1180,7 @@ export default createWidget("post", {
},
});
// TODO (glimmer-post-menu): Once this widget is removed the `<section>...</section>` tag needs to be added to the PostMenu component
// TODO (glimmer-post-stream): Once this widget is removed the `<section>...</section>` tag needs to be added to the PostMenu component
registerWidgetShim(
"glimmer-post",
"div",

View File

@ -3,14 +3,7 @@ import { test } from "qunit";
import { acceptance } from "discourse/tests/helpers/qunit-helpers";
import { i18n } from "discourse-i18n";
["enabled", "disabled"].forEach((postMenuMode) => {
acceptance(
`Post controls (glimmer_post_menu_mode = ${postMenuMode})`,
function (needs) {
needs.settings({
glimmer_post_menu_mode: postMenuMode,
});
acceptance(`Post controls`, function () {
test("accessibility of the likes list below the post", async function (assert) {
await visit("/t/internationalization-localization/280");
@ -82,6 +75,4 @@ import { i18n } from "discourse-i18n";
"collapse button has aria-label"
);
});
}
);
});

View File

@ -19,15 +19,13 @@ import selectKit from "discourse/tests/helpers/select-kit-helper";
import { i18n } from "discourse-i18n";
["enabled", "disabled"].forEach((postStreamMode) => {
["enabled", "disabled"].forEach((postMenuMode) => {
acceptance(
`Topic (glimmer_post_stream_mode = ${postStreamMode}) (glimmer_post_menu_mode = ${postMenuMode})`,
`Topic (glimmer_post_stream_mode = ${postStreamMode})`,
function (needs) {
needs.user();
needs.settings({
post_menu:
"read|like|share|flag|edit|bookmark|delete|admin|reply|copyLink",
glimmer_post_menu_mode: postMenuMode,
glimmer_post_stream_mode: postStreamMode,
});
needs.pretender((server, helper) => {
@ -191,10 +189,7 @@ import { i18n } from "discourse-i18n";
assert
.dom(".fancy-title")
.includesHtml(
"man_farmer.png",
"displays the new title with emojis"
);
.includesHtml("man_farmer.png", "displays the new title with emojis");
});
test("Updating the topic title with unicode emojis without whitespace", async function (assert) {
@ -266,7 +261,6 @@ import { i18n } from "discourse-i18n";
});
}
);
});
acceptance(
`Topic featured links (glimmer_post_stream_mode = ${postStreamMode})`,

View File

@ -49,26 +49,16 @@ acceptance("User Tips - topic_timeline", function (needs) {
});
});
["enabled", "disabled"].forEach((postMenuMode) => {
acceptance(
`User Tips - post_menu (glimmer_post_menu_mode = ${postMenuMode})`,
function (needs) {
acceptance(`User Tips - post_menu`, function (needs) {
needs.user();
needs.site({ user_tips: { post_menu: 3 } });
needs.settings({
glimmer_post_menu_mode: postMenuMode,
});
test("Shows post menu user tip", async function (assert) {
this.siteSettings.enable_user_tips = true;
await visit("/t/internationalization-localization/280");
assert
.dom(".user-tip__title")
.hasText(i18n("user_tips.post_menu.title"));
assert.dom(".user-tip__title").hasText(i18n("user_tips.post_menu.title"));
});
}
);
});
acceptance("User Tips - topic_notification_levels", function (needs) {

View File

@ -102,7 +102,6 @@ import {
} from "discourse/tests/helpers/site-settings";
import { resetPostClassesCallback } from "discourse/widgets/post";
import { resetDecorators as resetPostCookedDecorators } from "discourse/widgets/post-cooked";
import { resetPostMenuExtraButtons } from "discourse/widgets/post-menu";
import { resetPostSmallActionClassesCallbacks } from "discourse/widgets/post-small-action";
import { resetDecorators } from "discourse/widgets/widget";
import I18n from "discourse-i18n";
@ -221,7 +220,6 @@ export function testCleanup(container, app) {
resetCardClickListenerSelector();
resetComposerCustomizations();
resetQuickSearchRandomTips();
resetPostMenuExtraButtons();
resetUserMenuProfileTabItems();
clearExtraKeyboardShortcutHelp();
clearDisabledDefaultKeyboardBindings();

View File

@ -51,7 +51,6 @@ module("Integration | Component | Post", function (hooks) {
hooks.beforeEach(function () {
this.siteSettings.glimmer_post_stream_mode = "enabled";
this.siteSettings.glimmer_post_menu_mode = "enabled";
this.siteSettings.post_menu_hidden_items = "";
this.store = getOwner(this).lookup("service:store");

View File

@ -1,238 +0,0 @@
import { click, render } from "@ember/test-helpers";
import { module, test } from "qunit";
import { h } from "virtual-dom";
import MountWidget from "discourse/components/mount-widget";
import { withSilencedDeprecations } from "discourse/lib/deprecated";
import { withPluginApi } from "discourse/lib/plugin-api";
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
import { resetPostMenuExtraButtons } from "discourse/widgets/post-menu";
import { createWidget } from "discourse/widgets/widget";
module("Integration | Component | Widget | post-menu", function (hooks) {
setupRenderingTest(hooks);
hooks.afterEach(function () {
resetPostMenuExtraButtons();
});
test("add extra button", async function (assert) {
const self = this;
this.set("args", {});
withPluginApi("0.14.0", (api) => {
withSilencedDeprecations("discourse.post-menu-widget-overrides", () => {
api.addPostMenuButton("coffee", () => {
return {
action: "drinkCoffee",
icon: "mug-saucer",
className: "hot-coffee",
title: "coffee.title",
position: "first",
};
});
});
});
await render(
<template>
<MountWidget @widget="post-menu" @args={{self.args}} />
</template>
);
assert
.dom(".actions .extra-buttons .hot-coffee")
.exists("renders extra button");
});
test("add extra button with feedback", async function (assert) {
const self = this;
this.set("args", {});
let testPost = null;
withPluginApi("0.14.0", (api) => {
withSilencedDeprecations("discourse.post-menu-widget-overrides", () => {
api.addPostMenuButton("coffee", () => {
return {
action: ({ post, showFeedback }) => {
testPost = post;
showFeedback("coffee.drink");
},
icon: "mug-saucer",
className: "hot-coffee",
title: "coffee.title",
position: "first",
actionParam: { id: 123 }, // hack for testing
};
});
});
});
await render(
<template>
<article data-post-id="123">
<MountWidget @widget="post-menu" @args={{self.args}} />
</article>
</template>
);
await click(".hot-coffee");
assert.strictEqual(testPost.id, 123, "callback was called with post");
assert.dom(".post-action-feedback-button").exists("renders feedback");
assert
.dom(".actions .extra-buttons .hot-coffee")
.exists("renders extra button");
});
test("removes button based on callback", async function (assert) {
const self = this;
this.set("args", { canCreatePost: true, canRemoveReply: true });
withPluginApi("0.14.0", (api) => {
withSilencedDeprecations("discourse.post-menu-widget-overrides", () => {
api.removePostMenuButton("reply", (attrs) => {
return attrs.canRemoveReply;
});
});
});
await render(
<template>
<MountWidget @widget="post-menu" @args={{self.args}} />
</template>
);
assert.dom(".actions .reply").doesNotExist("removes reply button");
});
test("does not remove button", async function (assert) {
const self = this;
this.set("args", { canCreatePost: true, canRemoveReply: false });
withPluginApi("0.14.0", (api) => {
withSilencedDeprecations("discourse.post-menu-widget-overrides", () => {
api.removePostMenuButton("reply", (attrs) => {
return attrs.canRemoveReply;
});
});
});
await render(
<template>
<MountWidget @widget="post-menu" @args={{self.args}} />
</template>
);
assert.dom(".actions .reply").exists("does not remove reply button");
});
test("removes button", async function (assert) {
const self = this;
this.set("args", { canCreatePost: true });
withPluginApi("0.14.0", (api) => {
withSilencedDeprecations("discourse.post-menu-widget-overrides", () => {
api.removePostMenuButton("reply");
});
});
await render(
<template>
<MountWidget @widget="post-menu" @args={{self.args}} />
</template>
);
assert.dom(".actions .reply").doesNotExist("removes reply button");
});
test("removes button when any callback evaluates to true", async function (assert) {
const self = this;
this.set("args", {});
withPluginApi("0.14.0", (api) => {
withSilencedDeprecations("discourse.post-menu-widget-overrides", () => {
api.removePostMenuButton("reply", () => true);
api.removePostMenuButton("reply", () => false);
});
});
await render(
<template>
<MountWidget @widget="post-menu" @args={{self.args}} />
</template>
);
assert.dom(".actions .reply").doesNotExist("removes reply button");
});
createWidget("post-menu-replacement", {
html(attrs) {
return h("h1.post-menu-replacement", {}, attrs.id);
},
});
test("buttons are replaced when shouldRender is true", async function (assert) {
const self = this;
this.set("args", { id: 1, canCreatePost: true });
withPluginApi("0.14.0", (api) => {
withSilencedDeprecations("discourse.post-menu-widget-overrides", () => {
api.replacePostMenuButton("reply", {
name: "post-menu-replacement",
buildAttrs: (widget) => {
return widget.attrs;
},
shouldRender: (widget) => widget.attrs.id === 1, // true!
});
});
});
await render(
<template>
<MountWidget @widget="post-menu" @args={{self.args}} />
</template>
);
assert.dom("h1.post-menu-replacement").exists("replacement is rendered");
assert
.dom(".actions .reply")
.doesNotExist("reply button is replaced button");
});
test("buttons are not replaced when shouldRender is false", async function (assert) {
const self = this;
this.set("args", { id: 1, canCreatePost: true, canRemoveReply: false });
withPluginApi("0.14.0", (api) => {
withSilencedDeprecations("discourse.post-menu-widget-overrides", () => {
api.replacePostMenuButton("reply", {
name: "post-menu-replacement",
buildAttrs: (widget) => {
return widget.attrs;
},
shouldRender: (widget) => widget.attrs.id === 102323948, // false!
});
});
});
await render(
<template>
<MountWidget @widget="post-menu" @args={{self.args}} />
</template>
);
assert
.dom("h1.post-menu-replacement")
.doesNotExist("replacement is not rendered");
assert.dom(".actions .reply").exists("reply button is present");
});
});

View File

@ -6,7 +6,6 @@ import DButton from "discourse/components/d-button";
import { withSilencedDeprecations } from "discourse/lib/deprecated";
import { withPluginApi } from "discourse/lib/plugin-api";
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
import { resetPostMenuExtraButtons } from "discourse/widgets/post-menu";
function postStreamTest(name, attrs) {
test(name, async function (assert) {
@ -21,23 +20,17 @@ function postStreamTest(name, attrs) {
}
["enabled", "disabled"].forEach((postStreamMode) => {
["enabled", "disabled"].forEach((postMenuMode) => {
let lastTransformedPost = null;
module(
`Integration | Component | Widget | post-stream (glimmer_post_stream_mode = ${postStreamMode}) (glimmer_post_menu_mode = ${postMenuMode})`,
`Integration | Component | Widget | post-stream (glimmer_post_stream_mode = ${postStreamMode})`,
function (hooks) {
setupRenderingTest(hooks);
hooks.beforeEach(function () {
this.siteSettings.glimmer_post_menu_mode = postMenuMode;
this.siteSettings.glimmer_post_stream_mode = postStreamMode;
});
hooks.afterEach(function () {
resetPostMenuExtraButtons();
});
const CustomPostMenuButton = <template>
<DButton
class="hot-coffee"
@ -234,5 +227,4 @@ function postStreamTest(name, attrs) {
});
}
);
});
});

View File

@ -9,7 +9,6 @@ module("Unit | Component | post-menu", function (hooks) {
setupRenderingTest(hooks);
hooks.beforeEach(function () {
this.siteSettings.glimmer_post_menu_mode = "enabled";
this.siteSettings.post_menu =
"read|like|copyLink|share|flag|edit|bookmark|delete|admin|reply";
this.siteSettings.post_menu_hidden_items = "";

View File

@ -370,7 +370,6 @@ nav.post-controls {
&.like {
// Like button with 0 likes
&.d-hover,
&:hover {
background: var(--love-low);
@ -387,7 +386,6 @@ nav.post-controls {
}
.discourse-no-touch & {
&.d-hover,
&:hover {
background: var(--primary-low);
@ -410,7 +408,6 @@ nav.post-controls {
// Like button when like count is present
.discourse-no-touch & {
&.d-hover,
&:hover {
background: var(--primary-low);
}
@ -419,7 +416,6 @@ nav.post-controls {
// Like count button
.discourse-no-touch & {
&.d-hover,
&:hover {
color: var(--primary);
}
@ -459,14 +455,7 @@ nav.post-controls {
border: none;
border-radius: var(--d-post-control-border-radius);
.d-icon {
// this avoids an issue where hovering off the icon
// removes the .d-hover class from the button prematurely
pointer-events: none;
}
.discourse-no-touch & {
&.d-hover,
&:hover,
&:focus-visible,
&:active {
@ -475,8 +464,6 @@ nav.post-controls {
color: var(--d-post-control-text-color--hover);
}
// TODO (glimmer-post-menu): Go over the the d-hover style and remove the unnecessary ones when glimmer-post-menu replaces the widget version
&.delete.d-hover,
&.delete:hover,
&.delete:active,
&.delete:focus {
@ -612,8 +599,6 @@ nav.post-controls {
}
}
// .d-icon.heart-animation is the widget animation while .toggle-like.heart-animation is the glimmer-post-menu's
.has-like .d-icon.heart-animation,
.toggle-like.heart-animation .d-icon {
@media (prefers-reduced-motion: no-preference) {
animation: heartBump 0.4s;

View File

@ -2758,8 +2758,6 @@ en:
default_navigation_menu_tags: "Selected tags will be displayed under Navigation Menu's Tags section by default."
experimental_new_new_view_groups: 'Enable a new topics list that combines unread and new topics and make the "Everything" link in the sidebar link to it.'
glimmer_topic_list_mode: "Control whether the new 'glimmer' topic-list implementation is used. 'auto' will enable automatically once all your themes and plugins are ready. See <a href='https://meta.discourse.org/t/343404'>the Meta topic</a> for more information."
glimmer_post_menu_mode: "Control whether the new 'glimmer' post menu implementation is used. 'auto' will enable automatically once all your themes and plugins are ready. This implementation is under active development, and is not intended for production use. Do not develop themes/plugins against it until the implementation is finalized and announced."
glimmer_post_menu_groups: "Enable the new 'glimmer' post menu implementation in 'auto' mode for the specified user groups. This implementation is under active development, and is not intended for production use. Do not develop themes/plugins against it until the implementation is finalized and announced."
glimmer_post_stream_mode: "Control whether the new 'glimmer' post stream implementation is used. 'auto' will enable automatically once all your themes and plugins are ready. This implementation is under active development, and is not intended for production use. Do not develop themes/plugins against it until the implementation is finalized and announced."
glimmer_post_stream_mode_auto_groups: "Enable the new 'glimmer' post menu implementation in 'auto' mode for the specified user groups. This implementation is under active development, and is not intended for production use. Do not develop themes/plugins against it until the implementation is finalized and announced."
experimental_form_templates: "Enable the form templates feature. Manage the templates at <a href='%{base_path}/admin/customize/form-templates'>Customize / Templates</a>."

View File

@ -3552,14 +3552,6 @@ experimental:
- auto
- enabled
default: enabled
glimmer_post_menu_mode:
client: true
type: enum
choices:
- disabled
- auto
- enabled
default: enabled
glimmer_post_stream_mode_auto_groups:
client: true
type: group_list

View File

@ -0,0 +1,13 @@
# frozen_string_literal: true
class RemoveGlimmerPostMenuModeSetting < ActiveRecord::Migration[7.2]
def up
execute <<~SQL
DELETE FROM site_settings
WHERE name = 'glimmer_post_menu_mode'
SQL
end
def down
raise ActiveRecord::IrreversibleMigration
end
end

View File

@ -330,36 +330,35 @@ module PageObjects
end
def selector_for_post_action_button(button)
# TODO (glimmer-post-menu): Replace the selector with the BEM format ones once the glimmer-post-menu replaces the widget post menu
case button
when :admin
".post-controls .show-post-admin-menu"
".post-controls .post-action-menu__admin"
when :bookmark
".post-controls .bookmark"
".post-controls .post-action-menu__bookmark"
when :copy_link, :copyLink
".post-controls .post-action-menu__copy-link"
when :delete
".post-controls .delete"
".post-controls .post-action-menu__delete"
when :edit
".post-controls .edit"
".post-controls .post-action-menu__edit"
when :flag
".post-controls .create-flag"
".post-controls .post-action-menu__flag"
when :like
".post-controls .toggle-like"
".post-controls .post-action-menu__like"
when :like_count
".post-controls .like-count"
".post-controls .post-action-menu__like-count"
when :read
".post-controls .read-indicator"
".post-controls .post-action-menu__read"
when :recover
".post-controls .recover"
".post-controls .post-action-menu__recover"
when :replies
".post-controls .show-replies"
".post-controls .post-action-menu__show-replies"
when :reply
".post-controls .reply"
".post-controls .post-action-menu__reply"
when :share
".post-controls .share"
".post-controls .post-action-menu__share"
when :show_more
".post-controls .show-more-actions"
".post-controls .post-action-menu__show-more"
else
raise "Unknown post menu button type: #{button}"
end

View File

@ -13,14 +13,11 @@ describe "Post menu", type: :system do
let(:login_page) { PageObjects::Pages::Login.new }
let(:modal) { PageObjects::Modals::Base.new }
%w[enabled disabled].each do |value|
before do
SiteSetting.glimmer_post_menu_mode = value
SiteSetting.post_menu = "like|copyLink|share|flag|edit|bookmark|delete|admin|reply"
SiteSetting.post_menu_hidden_items = "flag|bookmark|edit|delete|admin"
end
context "when the Glimmer post menu is #{value}" do
describe "general rendering" do
before { sign_in(admin) }
@ -737,6 +734,4 @@ describe "Post menu", type: :system do
expect(topic_page).to have_post_action_button(post, :admin)
end
end
end
end
end