mirror of
https://github.com/discourse/discourse.git
synced 2025-06-04 04:14:38 +08:00
DEV: Remove the legacy widget post menu code (#31211)
https://meta.discourse.org/t/341014
This commit is contained in:
@ -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
|
||||
|
@ -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}}
|
||||
|
@ -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()
|
||||
),
|
||||
};
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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",
|
||||
|
@ -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}}
|
||||
/>`
|
||||
);
|
@ -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;
|
||||
result.push(
|
||||
this.attach("glimmer-post-menu", {
|
||||
const filteredRepliesView = this.siteSettings.enable_filtered_replies_view;
|
||||
result.push(
|
||||
// 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",
|
||||
|
@ -3,85 +3,76 @@ 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");
|
||||
|
||||
test("accessibility of the likes list below the post", async function (assert) {
|
||||
await visit("/t/internationalization-localization/280");
|
||||
assert
|
||||
.dom("#post_2 button.like-count")
|
||||
.hasAria("pressed", "false", "show likes button isn't pressed");
|
||||
|
||||
assert
|
||||
.dom("#post_2 button.like-count")
|
||||
.hasAria("pressed", "false", "show likes button isn't pressed");
|
||||
await click("#post_2 button.like-count");
|
||||
assert
|
||||
.dom("#post_2 button.like-count")
|
||||
.hasAria("pressed", "true", "show likes button is now pressed");
|
||||
|
||||
await click("#post_2 button.like-count");
|
||||
assert
|
||||
.dom("#post_2 button.like-count")
|
||||
.hasAria("pressed", "true", "show likes button is now pressed");
|
||||
assert
|
||||
.dom("#post_2 .small-user-list.who-liked .small-user-list-content")
|
||||
.hasAttribute("role", "list", "likes container has list role");
|
||||
|
||||
assert
|
||||
.dom("#post_2 .small-user-list.who-liked .small-user-list-content")
|
||||
.hasAttribute("role", "list", "likes container has list role");
|
||||
assert
|
||||
.dom("#post_2 .small-user-list.who-liked a.trigger-user-card")
|
||||
.exists("avatars are rendered");
|
||||
|
||||
assert
|
||||
.dom("#post_2 .small-user-list.who-liked a.trigger-user-card")
|
||||
.exists("avatars are rendered");
|
||||
assert
|
||||
.dom("#post_2 .small-user-list.who-liked a.trigger-user-card")
|
||||
.hasAria("hidden", "false", "avatars are not aria-hidden");
|
||||
assert
|
||||
.dom("#post_2 .small-user-list.who-liked a.trigger-user-card")
|
||||
.hasAttribute("role", "listitem", "avatars have listitem role");
|
||||
});
|
||||
|
||||
assert
|
||||
.dom("#post_2 .small-user-list.who-liked a.trigger-user-card")
|
||||
.hasAria("hidden", "false", "avatars are not aria-hidden");
|
||||
assert
|
||||
.dom("#post_2 .small-user-list.who-liked a.trigger-user-card")
|
||||
.hasAttribute("role", "listitem", "avatars have listitem role");
|
||||
});
|
||||
test("accessibility of the embedded replies below the post", async function (assert) {
|
||||
await visit("/t/internationalization-localization/280");
|
||||
|
||||
test("accessibility of the embedded replies below the post", async function (assert) {
|
||||
await visit("/t/internationalization-localization/280");
|
||||
assert
|
||||
.dom("#post_1 button.show-replies")
|
||||
.hasAria("pressed", "false", "show replies button isn't pressed");
|
||||
assert
|
||||
.dom("#post_1 button.show-replies")
|
||||
.hasAria(
|
||||
"label",
|
||||
i18n("post.sr_expand_replies", { count: 1 }),
|
||||
"show replies button has aria-label"
|
||||
);
|
||||
|
||||
assert
|
||||
.dom("#post_1 button.show-replies")
|
||||
.hasAria("pressed", "false", "show replies button isn't pressed");
|
||||
assert
|
||||
.dom("#post_1 button.show-replies")
|
||||
.hasAria(
|
||||
"label",
|
||||
i18n("post.sr_expand_replies", { count: 1 }),
|
||||
"show replies button has aria-label"
|
||||
);
|
||||
await click("#post_1 button.show-replies");
|
||||
assert
|
||||
.dom("#post_1 button.show-replies")
|
||||
.hasAria("pressed", "true", "show replies button is now pressed");
|
||||
|
||||
await click("#post_1 button.show-replies");
|
||||
assert
|
||||
.dom("#post_1 button.show-replies")
|
||||
.hasAria("pressed", "true", "show replies button is now pressed");
|
||||
// const replies = Array.from(queryAll("#post_1 .embedded-posts .reply"));
|
||||
assert
|
||||
.dom("#post_1 .embedded-posts .reply")
|
||||
.exists({ count: 1 }, "replies are rendered");
|
||||
|
||||
// const replies = Array.from(queryAll("#post_1 .embedded-posts .reply"));
|
||||
assert
|
||||
.dom("#post_1 .embedded-posts .reply")
|
||||
.exists({ count: 1 }, "replies are rendered");
|
||||
|
||||
assert
|
||||
.dom("#post_1 .embedded-posts .reply")
|
||||
.hasAttribute("role", "region", "replies have region role");
|
||||
assert.dom("#post_1 .embedded-posts .reply").hasAria(
|
||||
"label",
|
||||
i18n("post.sr_embedded_reply_description", {
|
||||
post_number: 1,
|
||||
username: "somebody",
|
||||
}),
|
||||
"replies have aria-label"
|
||||
);
|
||||
assert
|
||||
.dom("#post_1 .embedded-posts .btn.collapse-up")
|
||||
.hasAria(
|
||||
"label",
|
||||
i18n("post.sr_collapse_replies"),
|
||||
"collapse button has aria-label"
|
||||
);
|
||||
});
|
||||
}
|
||||
);
|
||||
assert
|
||||
.dom("#post_1 .embedded-posts .reply")
|
||||
.hasAttribute("role", "region", "replies have region role");
|
||||
assert.dom("#post_1 .embedded-posts .reply").hasAria(
|
||||
"label",
|
||||
i18n("post.sr_embedded_reply_description", {
|
||||
post_number: 1,
|
||||
username: "somebody",
|
||||
}),
|
||||
"replies have aria-label"
|
||||
);
|
||||
assert
|
||||
.dom("#post_1 .embedded-posts .btn.collapse-up")
|
||||
.hasAria(
|
||||
"label",
|
||||
i18n("post.sr_collapse_replies"),
|
||||
"collapse button has aria-label"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@ -19,254 +19,248 @@ 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})`,
|
||||
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,
|
||||
acceptance(
|
||||
`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_stream_mode: postStreamMode,
|
||||
});
|
||||
needs.pretender((server, helper) => {
|
||||
server.get("/c/2/visible_groups.json", () =>
|
||||
helper.response(200, {
|
||||
groups: [],
|
||||
})
|
||||
);
|
||||
|
||||
server.get("/c/feature/find_by_slug.json", () => {
|
||||
return helper.response(200, CategoryFixtures["/c/1/show.json"]);
|
||||
});
|
||||
needs.pretender((server, helper) => {
|
||||
server.get("/c/2/visible_groups.json", () =>
|
||||
helper.response(200, {
|
||||
groups: [],
|
||||
})
|
||||
server.put("/posts/398/wiki", () => {
|
||||
return helper.response({});
|
||||
});
|
||||
});
|
||||
|
||||
test("Reply as new topic", async function (assert) {
|
||||
await visit("/t/internationalization-localization/280");
|
||||
await click("button.share:nth-of-type(1)");
|
||||
await click("button.new-topic");
|
||||
|
||||
assert.dom(".d-editor-input").exists("the composer input is visible");
|
||||
|
||||
assert
|
||||
.dom(".d-editor-input")
|
||||
.hasValue(
|
||||
`Continuing the discussion from [Internationalization / localization](${window.location.origin}/t/internationalization-localization/280):\n\n`,
|
||||
"fills composer with the ring string"
|
||||
);
|
||||
assert.strictEqual(
|
||||
selectKit(".category-chooser").header().value(),
|
||||
"2",
|
||||
"fills category selector with the right category"
|
||||
);
|
||||
});
|
||||
|
||||
test("Reply as new message", async function (assert) {
|
||||
await visit("/t/pm-for-testing/12");
|
||||
await click("button.share:nth-of-type(1)");
|
||||
await click("button.new-topic");
|
||||
|
||||
assert.dom(".d-editor-input").exists("the composer input is visible");
|
||||
|
||||
assert
|
||||
.dom(".d-editor-input")
|
||||
.hasValue(
|
||||
`Continuing the discussion from [PM for testing](${window.location.origin}/t/pm-for-testing/12):\n\n`,
|
||||
"fills composer with the ring string"
|
||||
);
|
||||
|
||||
server.get("/c/feature/find_by_slug.json", () => {
|
||||
return helper.response(200, CategoryFixtures["/c/1/show.json"]);
|
||||
});
|
||||
server.put("/posts/398/wiki", () => {
|
||||
return helper.response({});
|
||||
});
|
||||
});
|
||||
const privateMessageUsers = selectKit("#private-message-users");
|
||||
assert.strictEqual(
|
||||
privateMessageUsers.header().value(),
|
||||
"someguy,test,Group",
|
||||
"fills up the composer correctly"
|
||||
);
|
||||
});
|
||||
|
||||
test("Reply as new topic", async function (assert) {
|
||||
await visit("/t/internationalization-localization/280");
|
||||
await click("button.share:nth-of-type(1)");
|
||||
await click("button.new-topic");
|
||||
test("Share Modal", async function (assert) {
|
||||
await visit("/t/internationalization-localization/280");
|
||||
await click(".topic-post:first-child button.share");
|
||||
|
||||
assert.dom(".d-editor-input").exists("the composer input is visible");
|
||||
assert.dom(".share-topic-modal").exists("shows the share modal");
|
||||
});
|
||||
|
||||
assert
|
||||
.dom(".d-editor-input")
|
||||
.hasValue(
|
||||
`Continuing the discussion from [Internationalization / localization](${window.location.origin}/t/internationalization-localization/280):\n\n`,
|
||||
"fills composer with the ring string"
|
||||
);
|
||||
assert.strictEqual(
|
||||
selectKit(".category-chooser").header().value(),
|
||||
"2",
|
||||
"fills category selector with the right category"
|
||||
test("Copy Link Button", async function (assert) {
|
||||
await visit("/t/internationalization-localization/280");
|
||||
await click(
|
||||
".topic-post:first-child button.post-action-menu__copy-link"
|
||||
);
|
||||
|
||||
assert
|
||||
.dom(".post-action-menu__copy-link-checkmark")
|
||||
.exists("shows the Link Copied! message");
|
||||
});
|
||||
|
||||
test("Showing and hiding the edit controls", async function (assert) {
|
||||
await visit("/t/internationalization-localization/280");
|
||||
|
||||
await click("#topic-title .d-icon-pencil");
|
||||
|
||||
assert.dom("#edit-title").exists("shows the editing controls");
|
||||
assert
|
||||
.dom(".title-wrapper .remove-featured-link")
|
||||
.doesNotExist("link to remove featured link is not shown");
|
||||
|
||||
await fillIn("#edit-title", "this is the new title");
|
||||
await click("#topic-title .cancel-edit");
|
||||
assert.dom("#edit-title").doesNotExist("hides the editing controls");
|
||||
});
|
||||
|
||||
test("Updating the topic title and category", async function (assert) {
|
||||
const categoryChooser = selectKit(".title-wrapper .category-chooser");
|
||||
|
||||
await visit("/t/internationalization-localization/280");
|
||||
|
||||
await click("#topic-title .d-icon-pencil");
|
||||
await fillIn("#edit-title", "this is the new title");
|
||||
await categoryChooser.expand();
|
||||
await categoryChooser.selectRowByValue(4);
|
||||
await click("#topic-title .submit-edit");
|
||||
|
||||
assert
|
||||
.dom("#topic-title .badge-category")
|
||||
.hasText("faq", "displays the new category");
|
||||
assert
|
||||
.dom(".fancy-title")
|
||||
.hasText("this is the new title", "displays the new title");
|
||||
});
|
||||
|
||||
test("Marking a topic as wiki", async function (assert) {
|
||||
await visit("/t/internationalization-localization/280");
|
||||
|
||||
assert.dom("a.wiki").doesNotExist("does not show the wiki icon");
|
||||
|
||||
await click(".topic-post:nth-of-type(1) button.show-more-actions");
|
||||
await click(".topic-post:nth-of-type(1) button.show-post-admin-menu");
|
||||
await click(".btn.wiki");
|
||||
|
||||
assert.dom("button.wiki").exists("shows the wiki icon");
|
||||
});
|
||||
|
||||
test("Visit topic routes", async function (assert) {
|
||||
await visit("/t/12");
|
||||
|
||||
assert
|
||||
.dom(".fancy-title")
|
||||
.hasText("PM for testing", "routes to the right topic");
|
||||
|
||||
await visit("/t/280/20");
|
||||
|
||||
assert
|
||||
.dom(".fancy-title")
|
||||
.hasText(
|
||||
"Internationalization / localization",
|
||||
"routes to the right topic"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
test("Reply as new message", async function (assert) {
|
||||
await visit("/t/pm-for-testing/12");
|
||||
await click("button.share:nth-of-type(1)");
|
||||
await click("button.new-topic");
|
||||
test("Updating the topic title with emojis", async function (assert) {
|
||||
await visit("/t/internationalization-localization/280");
|
||||
await click("#topic-title .d-icon-pencil");
|
||||
|
||||
assert.dom(".d-editor-input").exists("the composer input is visible");
|
||||
await fillIn("#edit-title", "emojis title :bike: :blonde_woman:t6:");
|
||||
|
||||
assert
|
||||
.dom(".d-editor-input")
|
||||
.hasValue(
|
||||
`Continuing the discussion from [PM for testing](${window.location.origin}/t/pm-for-testing/12):\n\n`,
|
||||
"fills composer with the ring string"
|
||||
);
|
||||
await click("#topic-title .submit-edit");
|
||||
|
||||
const privateMessageUsers = selectKit("#private-message-users");
|
||||
assert.strictEqual(
|
||||
privateMessageUsers.header().value(),
|
||||
"someguy,test,Group",
|
||||
"fills up the composer correctly"
|
||||
assert
|
||||
.dom(".fancy-title")
|
||||
.includesHtml("bike.png", "displays the new title with emojis");
|
||||
});
|
||||
|
||||
test("Updating the topic title with unicode emojis", async function (assert) {
|
||||
await visit("/t/internationalization-localization/280");
|
||||
await click("#topic-title .d-icon-pencil");
|
||||
|
||||
await fillIn("#edit-title", "emojis title 👨🌾🙏");
|
||||
|
||||
await click("#topic-title .submit-edit");
|
||||
|
||||
assert
|
||||
.dom(".fancy-title")
|
||||
.includesHtml("man_farmer.png", "displays the new title with emojis");
|
||||
});
|
||||
|
||||
test("Updating the topic title with unicode emojis without whitespace", async function (assert) {
|
||||
this.siteSettings.enable_inline_emoji_translation = true;
|
||||
await visit("/t/internationalization-localization/280");
|
||||
await click("#topic-title .d-icon-pencil");
|
||||
|
||||
await fillIn("#edit-title", "Test🙂Title");
|
||||
|
||||
await click("#topic-title .submit-edit");
|
||||
|
||||
assert
|
||||
.dom(".fancy-title")
|
||||
.includesHtml(
|
||||
"slightly_smiling_face.png",
|
||||
"displays the new title with emojis"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
test("Share Modal", async function (assert) {
|
||||
await visit("/t/internationalization-localization/280");
|
||||
await click(".topic-post:first-child button.share");
|
||||
test("Suggested topics", async function (assert) {
|
||||
await visit("/t/internationalization-localization/280");
|
||||
|
||||
assert.dom(".share-topic-modal").exists("shows the share modal");
|
||||
});
|
||||
assert
|
||||
.dom("#suggested-topics-title")
|
||||
.hasText(i18n("suggested_topics.title"));
|
||||
});
|
||||
|
||||
test("Copy Link Button", async function (assert) {
|
||||
await visit("/t/internationalization-localization/280");
|
||||
await click(
|
||||
".topic-post:first-child button.post-action-menu__copy-link"
|
||||
);
|
||||
test("Deleting a topic", async function (assert) {
|
||||
this.siteSettings.min_topic_views_for_delete_confirm = 10000;
|
||||
await visit("/t/internationalization-localization/280");
|
||||
await click(".topic-post:nth-of-type(1) button.show-more-actions");
|
||||
await click(".topic-post:nth-of-type(1) button.delete");
|
||||
await click(".toggle-admin-menu");
|
||||
assert.dom(".topic-admin-recover").exists("shows the recover button");
|
||||
});
|
||||
|
||||
assert
|
||||
.dom(".post-action-menu__copy-link-checkmark")
|
||||
.exists("shows the Link Copied! message");
|
||||
});
|
||||
test("Deleting a popular topic displays confirmation modal", async function (assert) {
|
||||
this.siteSettings.min_topic_views_for_delete_confirm = 10;
|
||||
await visit("/t/internationalization-localization/280");
|
||||
await click(".topic-post:nth-of-type(1) button.show-more-actions");
|
||||
await click(".topic-post:nth-of-type(1) button.delete");
|
||||
assert
|
||||
.dom(".delete-topic-confirm-modal")
|
||||
.exists("shows the delete confirmation modal");
|
||||
|
||||
test("Showing and hiding the edit controls", async function (assert) {
|
||||
await visit("/t/internationalization-localization/280");
|
||||
await click(".delete-topic-confirm-modal .btn-primary");
|
||||
assert
|
||||
.dom(".delete-topic-confirm-modal")
|
||||
.doesNotExist("hides the delete confirmation modal");
|
||||
await click(".topic-post:nth-of-type(1) button.delete");
|
||||
await click(".delete-topic-confirm-modal .btn-danger");
|
||||
await click(".toggle-admin-menu");
|
||||
assert.dom(".topic-admin-recover").exists("shows the recover button");
|
||||
});
|
||||
|
||||
await click("#topic-title .d-icon-pencil");
|
||||
test("Group category moderator posts", async function (assert) {
|
||||
await visit("/t/topic-for-group-moderators/2480");
|
||||
|
||||
assert.dom("#edit-title").exists("shows the editing controls");
|
||||
assert
|
||||
.dom(".title-wrapper .remove-featured-link")
|
||||
.doesNotExist("link to remove featured link is not shown");
|
||||
assert.dom(".category-moderator").exists("has a class applied");
|
||||
assert.dom(".d-icon-shield-halved").exists("shows an icon");
|
||||
});
|
||||
|
||||
await fillIn("#edit-title", "this is the new title");
|
||||
await click("#topic-title .cancel-edit");
|
||||
assert.dom("#edit-title").doesNotExist("hides the editing controls");
|
||||
});
|
||||
test("Suspended user posts", async function (assert) {
|
||||
await visit("/t/topic-from-suspended-user/54077");
|
||||
|
||||
test("Updating the topic title and category", async function (assert) {
|
||||
const categoryChooser = selectKit(".title-wrapper .category-chooser");
|
||||
|
||||
await visit("/t/internationalization-localization/280");
|
||||
|
||||
await click("#topic-title .d-icon-pencil");
|
||||
await fillIn("#edit-title", "this is the new title");
|
||||
await categoryChooser.expand();
|
||||
await categoryChooser.selectRowByValue(4);
|
||||
await click("#topic-title .submit-edit");
|
||||
|
||||
assert
|
||||
.dom("#topic-title .badge-category")
|
||||
.hasText("faq", "displays the new category");
|
||||
assert
|
||||
.dom(".fancy-title")
|
||||
.hasText("this is the new title", "displays the new title");
|
||||
});
|
||||
|
||||
test("Marking a topic as wiki", async function (assert) {
|
||||
await visit("/t/internationalization-localization/280");
|
||||
|
||||
assert.dom("a.wiki").doesNotExist("does not show the wiki icon");
|
||||
|
||||
await click(".topic-post:nth-of-type(1) button.show-more-actions");
|
||||
await click(".topic-post:nth-of-type(1) button.show-post-admin-menu");
|
||||
await click(".btn.wiki");
|
||||
|
||||
assert.dom("button.wiki").exists("shows the wiki icon");
|
||||
});
|
||||
|
||||
test("Visit topic routes", async function (assert) {
|
||||
await visit("/t/12");
|
||||
|
||||
assert
|
||||
.dom(".fancy-title")
|
||||
.hasText("PM for testing", "routes to the right topic");
|
||||
|
||||
await visit("/t/280/20");
|
||||
|
||||
assert
|
||||
.dom(".fancy-title")
|
||||
.hasText(
|
||||
"Internationalization / localization",
|
||||
"routes to the right topic"
|
||||
);
|
||||
});
|
||||
|
||||
test("Updating the topic title with emojis", async function (assert) {
|
||||
await visit("/t/internationalization-localization/280");
|
||||
await click("#topic-title .d-icon-pencil");
|
||||
|
||||
await fillIn("#edit-title", "emojis title :bike: :blonde_woman:t6:");
|
||||
|
||||
await click("#topic-title .submit-edit");
|
||||
|
||||
assert
|
||||
.dom(".fancy-title")
|
||||
.includesHtml("bike.png", "displays the new title with emojis");
|
||||
});
|
||||
|
||||
test("Updating the topic title with unicode emojis", async function (assert) {
|
||||
await visit("/t/internationalization-localization/280");
|
||||
await click("#topic-title .d-icon-pencil");
|
||||
|
||||
await fillIn("#edit-title", "emojis title 👨🌾🙏");
|
||||
|
||||
await click("#topic-title .submit-edit");
|
||||
|
||||
assert
|
||||
.dom(".fancy-title")
|
||||
.includesHtml(
|
||||
"man_farmer.png",
|
||||
"displays the new title with emojis"
|
||||
);
|
||||
});
|
||||
|
||||
test("Updating the topic title with unicode emojis without whitespace", async function (assert) {
|
||||
this.siteSettings.enable_inline_emoji_translation = true;
|
||||
await visit("/t/internationalization-localization/280");
|
||||
await click("#topic-title .d-icon-pencil");
|
||||
|
||||
await fillIn("#edit-title", "Test🙂Title");
|
||||
|
||||
await click("#topic-title .submit-edit");
|
||||
|
||||
assert
|
||||
.dom(".fancy-title")
|
||||
.includesHtml(
|
||||
"slightly_smiling_face.png",
|
||||
"displays the new title with emojis"
|
||||
);
|
||||
});
|
||||
|
||||
test("Suggested topics", async function (assert) {
|
||||
await visit("/t/internationalization-localization/280");
|
||||
|
||||
assert
|
||||
.dom("#suggested-topics-title")
|
||||
.hasText(i18n("suggested_topics.title"));
|
||||
});
|
||||
|
||||
test("Deleting a topic", async function (assert) {
|
||||
this.siteSettings.min_topic_views_for_delete_confirm = 10000;
|
||||
await visit("/t/internationalization-localization/280");
|
||||
await click(".topic-post:nth-of-type(1) button.show-more-actions");
|
||||
await click(".topic-post:nth-of-type(1) button.delete");
|
||||
await click(".toggle-admin-menu");
|
||||
assert.dom(".topic-admin-recover").exists("shows the recover button");
|
||||
});
|
||||
|
||||
test("Deleting a popular topic displays confirmation modal", async function (assert) {
|
||||
this.siteSettings.min_topic_views_for_delete_confirm = 10;
|
||||
await visit("/t/internationalization-localization/280");
|
||||
await click(".topic-post:nth-of-type(1) button.show-more-actions");
|
||||
await click(".topic-post:nth-of-type(1) button.delete");
|
||||
assert
|
||||
.dom(".delete-topic-confirm-modal")
|
||||
.exists("shows the delete confirmation modal");
|
||||
|
||||
await click(".delete-topic-confirm-modal .btn-primary");
|
||||
assert
|
||||
.dom(".delete-topic-confirm-modal")
|
||||
.doesNotExist("hides the delete confirmation modal");
|
||||
await click(".topic-post:nth-of-type(1) button.delete");
|
||||
await click(".delete-topic-confirm-modal .btn-danger");
|
||||
await click(".toggle-admin-menu");
|
||||
assert.dom(".topic-admin-recover").exists("shows the recover button");
|
||||
});
|
||||
|
||||
test("Group category moderator posts", async function (assert) {
|
||||
await visit("/t/topic-for-group-moderators/2480");
|
||||
|
||||
assert.dom(".category-moderator").exists("has a class applied");
|
||||
assert.dom(".d-icon-shield-halved").exists("shows an icon");
|
||||
});
|
||||
|
||||
test("Suspended user posts", async function (assert) {
|
||||
await visit("/t/topic-from-suspended-user/54077");
|
||||
|
||||
assert
|
||||
.dom(".topic-post.user-suspended > #post_1")
|
||||
.exists("has a class applied");
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
assert
|
||||
.dom(".topic-post.user-suspended > #post_1")
|
||||
.exists("has a class applied");
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
acceptance(
|
||||
`Topic featured links (glimmer_post_stream_mode = ${postStreamMode})`,
|
||||
|
@ -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) {
|
||||
needs.user();
|
||||
needs.site({ user_tips: { post_menu: 3 } });
|
||||
needs.settings({
|
||||
glimmer_post_menu_mode: postMenuMode,
|
||||
});
|
||||
acceptance(`User Tips - post_menu`, function (needs) {
|
||||
needs.user();
|
||||
needs.site({ user_tips: { post_menu: 3 } });
|
||||
|
||||
test("Shows post menu user tip", async function (assert) {
|
||||
this.siteSettings.enable_user_tips = true;
|
||||
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"));
|
||||
});
|
||||
}
|
||||
);
|
||||
await visit("/t/internationalization-localization/280");
|
||||
assert.dom(".user-tip__title").hasText(i18n("user_tips.post_menu.title"));
|
||||
});
|
||||
});
|
||||
|
||||
acceptance("User Tips - topic_notification_levels", function (needs) {
|
||||
|
@ -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();
|
||||
|
@ -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");
|
||||
|
@ -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");
|
||||
});
|
||||
});
|
@ -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,218 +20,211 @@ function postStreamTest(name, attrs) {
|
||||
}
|
||||
|
||||
["enabled", "disabled"].forEach((postStreamMode) => {
|
||||
["enabled", "disabled"].forEach((postMenuMode) => {
|
||||
let lastTransformedPost = null;
|
||||
let lastTransformedPost = null;
|
||||
|
||||
module(
|
||||
`Integration | Component | Widget | post-stream (glimmer_post_stream_mode = ${postStreamMode}) (glimmer_post_menu_mode = ${postMenuMode})`,
|
||||
function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
module(
|
||||
`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.beforeEach(function () {
|
||||
this.siteSettings.glimmer_post_stream_mode = postStreamMode;
|
||||
});
|
||||
|
||||
hooks.afterEach(function () {
|
||||
resetPostMenuExtraButtons();
|
||||
});
|
||||
const CustomPostMenuButton = <template>
|
||||
<DButton
|
||||
class="hot-coffee"
|
||||
...attributes
|
||||
@icon="mug-saucer"
|
||||
@title="coffee.title"
|
||||
/>
|
||||
</template>;
|
||||
|
||||
const CustomPostMenuButton = <template>
|
||||
<DButton
|
||||
class="hot-coffee"
|
||||
...attributes
|
||||
@icon="mug-saucer"
|
||||
@title="coffee.title"
|
||||
/>
|
||||
</template>;
|
||||
postStreamTest("extensibility", {
|
||||
posts() {
|
||||
withPluginApi("1.34.0", (api) => {
|
||||
api.registerValueTransformer(
|
||||
"post-menu-buttons",
|
||||
({ value: dag, context: { post, firstButtonKey } }) => {
|
||||
dag.add("coffee", CustomPostMenuButton, {
|
||||
before: firstButtonKey,
|
||||
});
|
||||
|
||||
postStreamTest("extensibility", {
|
||||
posts() {
|
||||
withPluginApi("1.34.0", (api) => {
|
||||
api.registerValueTransformer(
|
||||
"post-menu-buttons",
|
||||
({ value: dag, context: { post, firstButtonKey } }) => {
|
||||
dag.add("coffee", CustomPostMenuButton, {
|
||||
before: firstButtonKey,
|
||||
});
|
||||
|
||||
// value transformers shouldn't have side effects
|
||||
// we are only doing it below for testing purposes. Do not use strategies like this in the app code
|
||||
lastTransformedPost = post;
|
||||
}
|
||||
);
|
||||
|
||||
withSilencedDeprecations(
|
||||
"discourse.post-menu-widget-overrides",
|
||||
() => {
|
||||
api.addPostMenuButton("coffee", (transformedPost) => {
|
||||
lastTransformedPost = transformedPost;
|
||||
|
||||
return {
|
||||
action: "drinkCoffee",
|
||||
icon: "mug-saucer",
|
||||
className: "hot-coffee",
|
||||
title: "coffee.title",
|
||||
position: "first",
|
||||
};
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
const store = getOwner(this).lookup("service:store");
|
||||
const topic = store.createRecord("topic");
|
||||
topic.set("details.created_by", { id: 123 });
|
||||
topic.set("id", 1234);
|
||||
|
||||
return [
|
||||
store.createRecord("post", {
|
||||
topic,
|
||||
id: 1,
|
||||
post_number: 1,
|
||||
user_id: 123,
|
||||
primary_group_name: "trout",
|
||||
avatar_template: "/images/avatar.png",
|
||||
}),
|
||||
];
|
||||
},
|
||||
|
||||
test(assert) {
|
||||
assert.dom(".post-stream").exists({ count: 1 });
|
||||
assert.dom(".topic-post").exists({ count: 1 }, "renders all posts");
|
||||
assert
|
||||
.dom(".topic-post:nth-of-type(1) button.hot-coffee")
|
||||
.exists("it transforms posts");
|
||||
assert.strictEqual(
|
||||
lastTransformedPost.topic.id,
|
||||
1234,
|
||||
"it also transforms the topic"
|
||||
// value transformers shouldn't have side effects
|
||||
// we are only doing it below for testing purposes. Do not use strategies like this in the app code
|
||||
lastTransformedPost = post;
|
||||
}
|
||||
);
|
||||
assert
|
||||
.dom(".actions .hot-coffee")
|
||||
.exists({ count: 1 }, "has the extended button");
|
||||
},
|
||||
});
|
||||
|
||||
postStreamTest("basics", {
|
||||
posts() {
|
||||
const site = getOwner(this).lookup("service:site");
|
||||
const store = getOwner(this).lookup("service:store");
|
||||
const topic = store.createRecord("topic");
|
||||
topic.set("details.created_by", { id: 123 });
|
||||
withSilencedDeprecations(
|
||||
"discourse.post-menu-widget-overrides",
|
||||
() => {
|
||||
api.addPostMenuButton("coffee", (transformedPost) => {
|
||||
lastTransformedPost = transformedPost;
|
||||
|
||||
return [
|
||||
store.createRecord("post", {
|
||||
topic,
|
||||
id: 1,
|
||||
post_number: 1,
|
||||
username: "eviltrout",
|
||||
user_id: 123,
|
||||
primary_group_name: "trout",
|
||||
avatar_template: "/images/avatar.png",
|
||||
}),
|
||||
store.createRecord("post", {
|
||||
topic,
|
||||
id: 2,
|
||||
post_number: 2,
|
||||
post_type: site.get("post_types.moderator_action"),
|
||||
}),
|
||||
store.createRecord("post", {
|
||||
topic,
|
||||
id: 3,
|
||||
post_number: 3,
|
||||
hidden: true,
|
||||
}),
|
||||
store.createRecord("post", {
|
||||
topic,
|
||||
id: 4,
|
||||
post_number: 4,
|
||||
post_type: site.get("post_types.whisper"),
|
||||
}),
|
||||
store.createRecord("post", {
|
||||
topic,
|
||||
id: 5,
|
||||
post_number: 5,
|
||||
wiki: true,
|
||||
via_email: true,
|
||||
}),
|
||||
store.createRecord("post", {
|
||||
topic,
|
||||
id: 6,
|
||||
post_number: 6,
|
||||
via_email: true,
|
||||
is_auto_generated: true,
|
||||
}),
|
||||
];
|
||||
},
|
||||
return {
|
||||
action: "drinkCoffee",
|
||||
icon: "mug-saucer",
|
||||
className: "hot-coffee",
|
||||
title: "coffee.title",
|
||||
position: "first",
|
||||
};
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
async test(assert) {
|
||||
assert.dom(".post-stream").exists({ count: 1 });
|
||||
assert.dom(".topic-post").exists({ count: 6 }, "renders all posts");
|
||||
const store = getOwner(this).lookup("service:store");
|
||||
const topic = store.createRecord("topic");
|
||||
topic.set("details.created_by", { id: 123 });
|
||||
topic.set("id", 1234);
|
||||
|
||||
// look for special class bindings
|
||||
assert
|
||||
.dom(".topic-post:nth-of-type(1).topic-owner")
|
||||
.exists({ count: 1 }, "applies the topic owner class");
|
||||
assert
|
||||
.dom(".topic-post:nth-of-type(1).group-trout")
|
||||
.exists({ count: 1 }, "applies the primary group class");
|
||||
assert
|
||||
.dom(".topic-post:nth-of-type(1).regular")
|
||||
.exists({ count: 1 }, "applies the regular class");
|
||||
assert
|
||||
.dom(".topic-post:nth-of-type(2).moderator")
|
||||
.exists({ count: 1 }, "applies the moderator class");
|
||||
assert
|
||||
.dom(".topic-post:nth-of-type(3).post-hidden")
|
||||
.exists({ count: 1 }, "applies the hidden class");
|
||||
assert
|
||||
.dom(".topic-post:nth-of-type(4).whisper")
|
||||
.exists({ count: 1 }, "applies the whisper class");
|
||||
assert
|
||||
.dom(".topic-post:nth-of-type(5).wiki")
|
||||
.exists({ count: 1 }, "applies the wiki class");
|
||||
return [
|
||||
store.createRecord("post", {
|
||||
topic,
|
||||
id: 1,
|
||||
post_number: 1,
|
||||
user_id: 123,
|
||||
primary_group_name: "trout",
|
||||
avatar_template: "/images/avatar.png",
|
||||
}),
|
||||
];
|
||||
},
|
||||
|
||||
// it renders an article for the body with appropriate attributes
|
||||
assert.dom("article#post_2").exists({ count: 1 });
|
||||
assert.dom('article[data-user-id="123"]').exists({ count: 1 });
|
||||
assert.dom('article[data-post-id="3"]').exists({ count: 1 });
|
||||
assert.dom("article#post_5.via-email").exists({ count: 1 });
|
||||
assert.dom("article#post_6.is-auto-generated").exists({ count: 1 });
|
||||
test(assert) {
|
||||
assert.dom(".post-stream").exists({ count: 1 });
|
||||
assert.dom(".topic-post").exists({ count: 1 }, "renders all posts");
|
||||
assert
|
||||
.dom(".topic-post:nth-of-type(1) button.hot-coffee")
|
||||
.exists("it transforms posts");
|
||||
assert.strictEqual(
|
||||
lastTransformedPost.topic.id,
|
||||
1234,
|
||||
"it also transforms the topic"
|
||||
);
|
||||
assert
|
||||
.dom(".actions .hot-coffee")
|
||||
.exists({ count: 1 }, "has the extended button");
|
||||
},
|
||||
});
|
||||
|
||||
assert
|
||||
.dom("article:nth-of-type(1) .main-avatar")
|
||||
.exists({ count: 1 }, "renders the main avatar");
|
||||
},
|
||||
});
|
||||
postStreamTest("basics", {
|
||||
posts() {
|
||||
const site = getOwner(this).lookup("service:site");
|
||||
const store = getOwner(this).lookup("service:store");
|
||||
const topic = store.createRecord("topic");
|
||||
topic.set("details.created_by", { id: 123 });
|
||||
|
||||
postStreamTest("deleted posts", {
|
||||
posts() {
|
||||
const store = getOwner(this).lookup("service:store");
|
||||
const topic = store.createRecord("topic");
|
||||
topic.set("details.created_by", { id: 123 });
|
||||
return [
|
||||
store.createRecord("post", {
|
||||
topic,
|
||||
id: 1,
|
||||
post_number: 1,
|
||||
username: "eviltrout",
|
||||
user_id: 123,
|
||||
primary_group_name: "trout",
|
||||
avatar_template: "/images/avatar.png",
|
||||
}),
|
||||
store.createRecord("post", {
|
||||
topic,
|
||||
id: 2,
|
||||
post_number: 2,
|
||||
post_type: site.get("post_types.moderator_action"),
|
||||
}),
|
||||
store.createRecord("post", {
|
||||
topic,
|
||||
id: 3,
|
||||
post_number: 3,
|
||||
hidden: true,
|
||||
}),
|
||||
store.createRecord("post", {
|
||||
topic,
|
||||
id: 4,
|
||||
post_number: 4,
|
||||
post_type: site.get("post_types.whisper"),
|
||||
}),
|
||||
store.createRecord("post", {
|
||||
topic,
|
||||
id: 5,
|
||||
post_number: 5,
|
||||
wiki: true,
|
||||
via_email: true,
|
||||
}),
|
||||
store.createRecord("post", {
|
||||
topic,
|
||||
id: 6,
|
||||
post_number: 6,
|
||||
via_email: true,
|
||||
is_auto_generated: true,
|
||||
}),
|
||||
];
|
||||
},
|
||||
|
||||
return [
|
||||
store.createRecord("post", {
|
||||
topic,
|
||||
id: 1,
|
||||
post_number: 1,
|
||||
deleted_at: new Date().toString(),
|
||||
}),
|
||||
];
|
||||
},
|
||||
async test(assert) {
|
||||
assert.dom(".post-stream").exists({ count: 1 });
|
||||
assert.dom(".topic-post").exists({ count: 6 }, "renders all posts");
|
||||
|
||||
test(assert) {
|
||||
assert
|
||||
.dom(".topic-post.deleted")
|
||||
.exists({ count: 1 }, "applies the deleted class");
|
||||
assert
|
||||
.dom(".deleted-user-avatar")
|
||||
.exists({ count: 1 }, "has the trash avatar");
|
||||
},
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
// look for special class bindings
|
||||
assert
|
||||
.dom(".topic-post:nth-of-type(1).topic-owner")
|
||||
.exists({ count: 1 }, "applies the topic owner class");
|
||||
assert
|
||||
.dom(".topic-post:nth-of-type(1).group-trout")
|
||||
.exists({ count: 1 }, "applies the primary group class");
|
||||
assert
|
||||
.dom(".topic-post:nth-of-type(1).regular")
|
||||
.exists({ count: 1 }, "applies the regular class");
|
||||
assert
|
||||
.dom(".topic-post:nth-of-type(2).moderator")
|
||||
.exists({ count: 1 }, "applies the moderator class");
|
||||
assert
|
||||
.dom(".topic-post:nth-of-type(3).post-hidden")
|
||||
.exists({ count: 1 }, "applies the hidden class");
|
||||
assert
|
||||
.dom(".topic-post:nth-of-type(4).whisper")
|
||||
.exists({ count: 1 }, "applies the whisper class");
|
||||
assert
|
||||
.dom(".topic-post:nth-of-type(5).wiki")
|
||||
.exists({ count: 1 }, "applies the wiki class");
|
||||
|
||||
// it renders an article for the body with appropriate attributes
|
||||
assert.dom("article#post_2").exists({ count: 1 });
|
||||
assert.dom('article[data-user-id="123"]').exists({ count: 1 });
|
||||
assert.dom('article[data-post-id="3"]').exists({ count: 1 });
|
||||
assert.dom("article#post_5.via-email").exists({ count: 1 });
|
||||
assert.dom("article#post_6.is-auto-generated").exists({ count: 1 });
|
||||
|
||||
assert
|
||||
.dom("article:nth-of-type(1) .main-avatar")
|
||||
.exists({ count: 1 }, "renders the main avatar");
|
||||
},
|
||||
});
|
||||
|
||||
postStreamTest("deleted posts", {
|
||||
posts() {
|
||||
const store = getOwner(this).lookup("service:store");
|
||||
const topic = store.createRecord("topic");
|
||||
topic.set("details.created_by", { id: 123 });
|
||||
|
||||
return [
|
||||
store.createRecord("post", {
|
||||
topic,
|
||||
id: 1,
|
||||
post_number: 1,
|
||||
deleted_at: new Date().toString(),
|
||||
}),
|
||||
];
|
||||
},
|
||||
|
||||
test(assert) {
|
||||
assert
|
||||
.dom(".topic-post.deleted")
|
||||
.exists({ count: 1 }, "applies the deleted class");
|
||||
assert
|
||||
.dom(".deleted-user-avatar")
|
||||
.exists({ count: 1 }, "has the trash avatar");
|
||||
},
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -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 = "";
|
||||
|
@ -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;
|
||||
|
@ -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>."
|
||||
|
@ -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
|
||||
|
@ -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
|
@ -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
|
||||
|
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user