From ac272c041eb1b773a3e6beb503527389ac23d052 Mon Sep 17 00:00:00 2001 From: Bianca Nenciu Date: Tue, 15 Nov 2022 17:36:08 +0200 Subject: [PATCH] FEATURE: Add user tips for post and topic features (#18964) * DEV: Add utility to hide all user tips * DEV: Add UserTip Glimmer component * DEV: Add tests for existing user tips * FEATURE: Add user tip for post menu * FEATURE: Add user tip for topic notification level * FEATURE: Add user tip for suggested topics * FEATURE: Hide new popups for existing users --- .../app/components/topic-footer-buttons.js | 6 ++ .../discourse/app/components/user-tip.hbs | 1 + .../discourse/app/components/user-tip.js | 35 +++++++ .../discourse/app/controllers/topic.js | 4 + .../discourse/app/lib/user-tips.js | 10 ++ .../javascripts/discourse/app/models/user.js | 9 +- .../templates/components/suggested-topics.hbs | 2 + .../components/topic-footer-buttons.hbs | 4 + .../discourse/app/widgets/header.js | 3 +- .../discourse/app/widgets/post-menu.js | 6 ++ .../javascripts/discourse/app/widgets/post.js | 35 +++++++ .../discourse/app/widgets/topic-timeline.js | 1 + .../tests/acceptance/user-tips-test.js | 96 +++++++++++++++++++ .../stylesheets/common/base/user-tips.scss | 6 ++ app/models/user.rb | 3 + config/locales/client.en.yml | 12 +++ ...ide_user_tips_3_to_5_for_existing_users.rb | 11 +++ 17 files changed, 240 insertions(+), 4 deletions(-) create mode 100644 app/assets/javascripts/discourse/app/components/user-tip.hbs create mode 100644 app/assets/javascripts/discourse/app/components/user-tip.js create mode 100644 app/assets/javascripts/discourse/tests/acceptance/user-tips-test.js create mode 100644 db/migrate/20221114215902_hide_user_tips_3_to_5_for_existing_users.rb diff --git a/app/assets/javascripts/discourse/app/components/topic-footer-buttons.js b/app/assets/javascripts/discourse/app/components/topic-footer-buttons.js index e65a85aeb8e..5f91ecca8e2 100644 --- a/app/assets/javascripts/discourse/app/components/topic-footer-buttons.js +++ b/app/assets/javascripts/discourse/app/components/topic-footer-buttons.js @@ -2,6 +2,7 @@ import { alias, or } from "@ember/object/computed"; import { computed } from "@ember/object"; import Component from "@ember/component"; import discourseComputed from "discourse-common/utils/decorators"; +import { NotificationLevels } from "discourse/lib/notification-levels"; import { getTopicFooterButtons } from "discourse/lib/register-topic-footer-button"; import { getTopicFooterDropdowns } from "discourse/lib/register-topic-footer-dropdown"; @@ -46,6 +47,11 @@ export default Component.extend({ return !isPM || this.canSendPms; }, + @discourseComputed("topic.details.notification_level") + showNotificationUserTip(notificationLevel) { + return notificationLevel >= NotificationLevels.TRACKING; + }, + canSendPms: alias("currentUser.can_send_private_messages"), canInviteTo: alias("topic.details.can_invite_to"), diff --git a/app/assets/javascripts/discourse/app/components/user-tip.hbs b/app/assets/javascripts/discourse/app/components/user-tip.hbs new file mode 100644 index 00000000000..dc4b3e55d50 --- /dev/null +++ b/app/assets/javascripts/discourse/app/components/user-tip.hbs @@ -0,0 +1 @@ + diff --git a/app/assets/javascripts/discourse/app/components/user-tip.js b/app/assets/javascripts/discourse/app/components/user-tip.js new file mode 100644 index 00000000000..d85656f898d --- /dev/null +++ b/app/assets/javascripts/discourse/app/components/user-tip.js @@ -0,0 +1,35 @@ +import { action } from "@ember/object"; +import { inject as service } from "@ember/service"; +import Component from "@glimmer/component"; +import { hideUserTip } from "discourse/lib/user-tips"; +import I18n from "I18n"; + +export default class UserTip extends Component { + @service currentUser; + + @action + showUserTip(element) { + if (!this.currentUser) { + return; + } + + const { id, selector, content, placement } = this.args; + this.currentUser.showUserTip({ + id, + + titleText: I18n.t(`user_tips.${id}.title`), + contentText: content || I18n.t(`user_tips.${id}.content`), + + reference: selector + ? element.parentElement.querySelector(selector) || element.parentElement + : element, + appendTo: element.parentElement, + + placement: placement || "top", + }); + } + + willDestroy() { + hideUserTip(this.args.id); + } +} diff --git a/app/assets/javascripts/discourse/app/controllers/topic.js b/app/assets/javascripts/discourse/app/controllers/topic.js index 7e473e321cd..56a5b6ba4b8 100644 --- a/app/assets/javascripts/discourse/app/controllers/topic.js +++ b/app/assets/javascripts/discourse/app/controllers/topic.js @@ -611,6 +611,10 @@ export default Controller.extend(bufferedProperty("model"), { // Post related methods replyToPost(post) { + if (this.currentUser) { + this.currentUser.hideUserTipForever("post_menu"); + } + const composerController = this.composer; const topic = post ? post.get("topic") : this.model; const quoteState = this.quoteState; diff --git a/app/assets/javascripts/discourse/app/lib/user-tips.js b/app/assets/javascripts/discourse/app/lib/user-tips.js index 3eefac3bdea..df27654a90e 100644 --- a/app/assets/javascripts/discourse/app/lib/user-tips.js +++ b/app/assets/javascripts/discourse/app/lib/user-tips.js @@ -30,6 +30,7 @@ export function showUserTip(options) { arrow: iconHTML("tippy-rounded-arrow"), placement: options.placement, + appendTo: options.appendTo, // It often happens for the reference element to be rerendered. In this // case, tippy must be rerendered too. Having an animation means that the @@ -77,6 +78,15 @@ export function hideUserTip(userTipId) { instance.destroy(); } delete instances[userTipId]; + + const index = queue.findIndex((userTip) => userTip.id === userTipId); + if (index > -1) { + queue.splice(index, 1); + } +} + +export function hideAllUserTips() { + Object.keys(instances).forEach(hideUserTip); } function addToQueue(options) { diff --git a/app/assets/javascripts/discourse/app/models/user.js b/app/assets/javascripts/discourse/app/models/user.js index 89ae1f2fbd7..c12ea209f89 100644 --- a/app/assets/javascripts/discourse/app/models/user.js +++ b/app/assets/javascripts/discourse/app/models/user.js @@ -44,6 +44,7 @@ import { cancel } from "@ember/runloop"; import discourseLater from "discourse-common/lib/later"; import { isTesting } from "discourse-common/config/environment"; import { + hideAllUserTips, hideUserTip, showNextUserTip, showUserTip, @@ -1101,8 +1102,10 @@ const User = RestModel.extend({ } if (!userTips[options.id]) { - // eslint-disable-next-line no-console - console.warn("Cannot show user tip with type =", options.id); + if (!isTesting()) { + // eslint-disable-next-line no-console + console.warn("Cannot show user tip with type =", options.id); + } return; } @@ -1142,7 +1145,7 @@ const User = RestModel.extend({ seenUserTips.push(userTips[userTipId]); } } else { - Object.keys(userTips).forEach(hideUserTip); + hideAllUserTips(); seenUserTips = [-1]; } diff --git a/app/assets/javascripts/discourse/app/templates/components/suggested-topics.hbs b/app/assets/javascripts/discourse/app/templates/components/suggested-topics.hbs index 77ad43678b3..0725c3d6edc 100644 --- a/app/assets/javascripts/discourse/app/templates/components/suggested-topics.hbs +++ b/app/assets/javascripts/discourse/app/templates/components/suggested-topics.hbs @@ -1,4 +1,6 @@