From d57bea4de36cb2c936b8bacfb998a6925a1ea672 Mon Sep 17 00:00:00 2001 From: Arpit Jalan Date: Tue, 9 Aug 2022 21:52:39 +0530 Subject: [PATCH] FEATURE: add welcome topic cta banner (#17821) --- .../app/components/welcome-topic-banner.js | 25 ++++++++++ .../app/controllers/discovery/topics.js | 7 +++ .../components/welcome-topic-banner.hbs | 9 ++++ .../app/templates/discovery/topics.hbs | 4 ++ .../acceptance/welcome-topic-banner-test.js | 17 +++++++ .../stylesheets/desktop/topic-list.scss | 19 ++++++++ app/models/site.rb | 6 +++ app/models/user.rb | 8 ++++ app/serializers/site_serializer.rb | 7 ++- config/locales/client.en.yml | 5 ++ config/site_settings.yml | 1 + lib/wizard.rb | 8 +--- spec/models/site_spec.rb | 46 +++++++++++++++++++ .../api/schemas/json/site_response.json | 3 ++ spec/serializers/site_serializer_spec.rb | 12 +++++ 15 files changed, 169 insertions(+), 8 deletions(-) create mode 100644 app/assets/javascripts/discourse/app/components/welcome-topic-banner.js create mode 100644 app/assets/javascripts/discourse/app/templates/components/welcome-topic-banner.hbs create mode 100644 app/assets/javascripts/discourse/tests/acceptance/welcome-topic-banner-test.js diff --git a/app/assets/javascripts/discourse/app/components/welcome-topic-banner.js b/app/assets/javascripts/discourse/app/components/welcome-topic-banner.js new file mode 100644 index 00000000000..e8778b4f84f --- /dev/null +++ b/app/assets/javascripts/discourse/app/components/welcome-topic-banner.js @@ -0,0 +1,25 @@ +import GlimmerComponent from "discourse/components/glimmer"; +import { action } from "@ember/object"; +import { getOwner } from "discourse-common/lib/get-owner"; +import Topic from "discourse/models/topic"; +import Composer from "discourse/models/composer"; + +export default class WelcomeTopicBanner extends GlimmerComponent { + @action + editWelcomeTopic() { + const topicController = getOwner(this).lookup("controller:topic"); + + Topic.find(this.siteSettings.welcome_topic_id, {}).then((topic) => { + this.store + .createRecord("topic", { + id: topic.id, + slug: topic.slug, + }) + .postStream.loadPostByPostNumber(1) + .then((post) => { + post.topic.set("draft_key", Composer.EDIT); + topicController.send("editPost", post); + }); + }); + } +} diff --git a/app/assets/javascripts/discourse/app/controllers/discovery/topics.js b/app/assets/javascripts/discourse/app/controllers/discovery/topics.js index 0897026ddd6..a0037f4da57 100644 --- a/app/assets/javascripts/discourse/app/controllers/discovery/topics.js +++ b/app/assets/javascripts/discourse/app/controllers/discovery/topics.js @@ -24,6 +24,13 @@ const controllerOpts = { showTopicPostBadges: not("new"), redirectedReason: alias("currentUser.redirected_to_top.reason"), + @discourseComputed("model.filter", "site.show_welcome_topic_banner") + showEditWelcomeTopicBanner(filter, showWelcomeTopicBanner) { + return ( + this.currentUser?.staff && filter === "latest" && showWelcomeTopicBanner + ); + }, + expandGloballyPinned: false, expandAllPinned: false, diff --git a/app/assets/javascripts/discourse/app/templates/components/welcome-topic-banner.hbs b/app/assets/javascripts/discourse/app/templates/components/welcome-topic-banner.hbs new file mode 100644 index 00000000000..f5a577a8663 --- /dev/null +++ b/app/assets/javascripts/discourse/app/templates/components/welcome-topic-banner.hbs @@ -0,0 +1,9 @@ +
+
+

{{i18n "welcome_topic_banner.title"}}

+

{{i18n "welcome_topic_banner.description"}}

+
+
+ +
+
diff --git a/app/assets/javascripts/discourse/app/templates/discovery/topics.hbs b/app/assets/javascripts/discourse/app/templates/discovery/topics.hbs index 8e6196607ef..7deb8d1edc0 100644 --- a/app/assets/javascripts/discourse/app/templates/discovery/topics.hbs +++ b/app/assets/javascripts/discourse/app/templates/discovery/topics.hbs @@ -2,6 +2,10 @@
{{this.redirectedReason}}
{{/if}} +{{#if this.showEditWelcomeTopicBanner}} + +{{/if}} + {{#if this.model.sharedDrafts}} diff --git a/app/assets/javascripts/discourse/tests/acceptance/welcome-topic-banner-test.js b/app/assets/javascripts/discourse/tests/acceptance/welcome-topic-banner-test.js new file mode 100644 index 00000000000..2e7ee79a01a --- /dev/null +++ b/app/assets/javascripts/discourse/tests/acceptance/welcome-topic-banner-test.js @@ -0,0 +1,17 @@ +import { acceptance, exists } from "discourse/tests/helpers/qunit-helpers"; +import { test } from "qunit"; +import { visit } from "@ember/test-helpers"; + +acceptance("Welcome Topic Banner", function (needs) { + needs.user(); + needs.site({ show_welcome_topic_banner: true }); + + test("Navigation", async function (assert) { + await visit("/"); + assert.ok(exists(".welcome-topic-banner"), "has the welcome topic banner"); + assert.ok( + exists("button.welcome-topic-cta"), + "has the welcome topic edit button" + ); + }); +}); diff --git a/app/assets/stylesheets/desktop/topic-list.scss b/app/assets/stylesheets/desktop/topic-list.scss index 3a71168c548..b986c462ba1 100644 --- a/app/assets/stylesheets/desktop/topic-list.scss +++ b/app/assets/stylesheets/desktop/topic-list.scss @@ -252,3 +252,22 @@ } } } + +.welcome-topic-banner { + border: 1px solid var(--primary-low); + box-shadow: 0px 0px 6px 0px rgba(0, 0, 0, 0.12); + padding: 10px; + display: flex; + flex-direction: row; + + .col-text { + width: 70%; + } + .col-button { + width: 30%; + display: flex; + align-self: center; + justify-content: flex-end; + padding-right: 30px; + } +} diff --git a/app/models/site.rb b/app/models/site.rb index f04b25bd766..8a23bf1e044 100644 --- a/app/models/site.rb +++ b/app/models/site.rb @@ -203,4 +203,10 @@ class Site MessageBus.publish(SITE_JSON_CHANNEL, '') end + def self.show_welcome_topic_banner?(guardian) + return false unless guardian.is_admin? + return false unless guardian.user.id == User.first_login_admin_id + + Post.find_by("topic_id = :topic_id AND post_number = 1 AND version = 1 AND created_at > :created_at", topic_id: SiteSetting.welcome_topic_id, created_at: 1.month.ago).present? + end end diff --git a/app/models/user.rb b/app/models/user.rb index c828313239d..2811029705b 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1878,6 +1878,14 @@ class User < ActiveRecord::Base end end + def self.first_login_admin_id + User.where(admin: true) + .human_users + .joins(:user_auth_tokens) + .order('user_auth_tokens.created_at') + .pluck_first(:id) + end + private def stat diff --git a/app/serializers/site_serializer.rb b/app/serializers/site_serializer.rb index b7abf74c3f5..aa68f845f02 100644 --- a/app/serializers/site_serializer.rb +++ b/app/serializers/site_serializer.rb @@ -34,7 +34,8 @@ class SiteSerializer < ApplicationSerializer :watched_words_link, :categories, :markdown_additional_options, - :displayed_about_plugin_stat_groups + :displayed_about_plugin_stat_groups, + :show_welcome_topic_banner ) has_many :archetypes, embed: :objects, serializer: ArchetypeSerializer @@ -213,6 +214,10 @@ class SiteSerializer < ApplicationSerializer About.displayed_plugin_stat_groups end + def show_welcome_topic_banner + Site.show_welcome_topic_banner?(scope) + end + private def ordered_flags(flags) diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 932ae33d3be..2ad39b15d43 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -4136,6 +4136,11 @@ en: one: "%{count} draft" other: "%{count} drafts" + welcome_topic_banner: + title: "Create your Welcome Topic" + description: 'Your welcome topic is the first thing new arrivals will read. Think of it as your one paragraph "elevator pitch" or "mission statement"' + button_title: "Start Editing" + # This section is exported to the javascript for i18n in the admin section admin_js: type_to_filter: "type to filter..." diff --git a/config/site_settings.yml b/config/site_settings.yml index 293edff401b..434516e01fc 100644 --- a/config/site_settings.yml +++ b/config/site_settings.yml @@ -2325,6 +2325,7 @@ uncategorized: welcome_topic_id: default: -1 hidden: true + client: true lounge_welcome_topic_id: default: -1 hidden: true diff --git a/lib/wizard.rb b/lib/wizard.rb index c58d652e48b..534f361660c 100644 --- a/lib/wizard.rb +++ b/lib/wizard.rb @@ -106,13 +106,7 @@ class Wizard return false end - first_admin_id = User.where(admin: true) - .human_users - .joins(:user_auth_tokens) - .order('user_auth_tokens.created_at') - .pluck_first(:id) - - if @user&.id && first_admin_id == @user.id + if @user&.id && User.first_login_admin_id == @user.id !Wizard::Builder.new(@user).build.completed? else false diff --git a/spec/models/site_spec.rb b/spec/models/site_spec.rb index 612642704f2..0b264ec78e5 100644 --- a/spec/models/site_spec.rb +++ b/spec/models/site_spec.rb @@ -183,4 +183,50 @@ RSpec.describe Site do expect(data["auth_providers"].map { |a| a["name"] }).to contain_exactly('facebook', 'twitter') end + describe ".show_welcome_topic_banner?" do + it "returns false when the user is not admin" do + first_post = Fabricate(:post, created_at: 25.days.ago) + SiteSetting.welcome_topic_id = first_post.topic.id + + expect(Site.show_welcome_topic_banner?(Guardian.new(Fabricate(:user)))).to eq(false) + end + + it "returns false when the user is not first admin who logs in" do + first_post = Fabricate(:post, created_at: 25.days.ago) + SiteSetting.welcome_topic_id = first_post.topic.id + + expect(Site.show_welcome_topic_banner?(Guardian.new(Fabricate(:admin)))).to eq(false) + end + + it "returns true when welcome topic is less than month old" do + admin = Fabricate(:admin) + UserAuthToken.generate!(user_id: admin.id) + + first_post = Fabricate(:post, created_at: 25.days.ago) + SiteSetting.welcome_topic_id = first_post.topic.id + + expect(Site.show_welcome_topic_banner?(Guardian.new(admin))).to eq(true) + end + + it "returns false when welcome topic is more than month old" do + admin = Fabricate(:admin) + UserAuthToken.generate!(user_id: admin.id) + + first_post = Fabricate(:post, created_at: 35.days.ago) + SiteSetting.welcome_topic_id = first_post.topic.id + + expect(Site.show_welcome_topic_banner?(Guardian.new(admin))).to eq(false) + end + + it "returns false when welcome topic has been edited" do + admin = Fabricate(:admin) + UserAuthToken.generate!(user_id: admin.id) + + first_post = Fabricate(:post, version: 2, created_at: 25.days.ago) + SiteSetting.welcome_topic_id = first_post.topic.id + + expect(Site.show_welcome_topic_banner?(Guardian.new(admin))).to eq(false) + end + end + end diff --git a/spec/requests/api/schemas/json/site_response.json b/spec/requests/api/schemas/json/site_response.json index a07f3c9afe1..5703ede25fd 100644 --- a/spec/requests/api/schemas/json/site_response.json +++ b/spec/requests/api/schemas/json/site_response.json @@ -685,6 +685,9 @@ ] } }, + "show_welcome_topic_banner": { + "type": "boolean" + }, "archetypes": { "type": "array", "items": diff --git a/spec/serializers/site_serializer_spec.rb b/spec/serializers/site_serializer_spec.rb index d49df518d2e..f815b5afa07 100644 --- a/spec/serializers/site_serializer_spec.rb +++ b/spec/serializers/site_serializer_spec.rb @@ -112,4 +112,16 @@ RSpec.describe SiteSerializer do serialized = described_class.new(Site.new(admin_guardian), scope: admin_guardian, root: false).as_json expect(serialized[:shared_drafts_category_id]).to eq(nil) end + + it 'includes show_welcome_topic_banner' do + admin = Fabricate(:admin) + admin_guardian = Guardian.new(admin) + UserAuthToken.generate!(user_id: admin.id) + + first_post = Fabricate(:post, created_at: 25.days.ago) + SiteSetting.welcome_topic_id = first_post.topic.id + + serialized = described_class.new(Site.new(admin_guardian), scope: admin_guardian, root: false).as_json + expect(serialized[:show_welcome_topic_banner]).to eq(true) + end end