diff --git a/app/assets/javascripts/discourse/app/controllers/discovery/topics.js b/app/assets/javascripts/discourse/app/controllers/discovery/topics.js
index 2b2f5e1c71e..d5c13c8451d 100644
--- a/app/assets/javascripts/discourse/app/controllers/discovery/topics.js
+++ b/app/assets/javascripts/discourse/app/controllers/discovery/topics.js
@@ -1,5 +1,6 @@
import { alias, empty, equal, gt, not, readOnly } from "@ember/object/computed";
import BulkTopicSelection from "discourse/mixins/bulk-topic-selection";
+import DismissTopics from "discourse/mixins/dismiss-topics";
import DiscoveryController from "discourse/controllers/discovery";
import I18n from "I18n";
import Topic from "discourse/models/topic";
@@ -56,6 +57,32 @@ const controllerOpts = {
return this._isFilterPage(filter, "new") && topicsLength > 0;
},
+ callResetNew(dismissPosts = false, dismissTopics = false, untrack = false) {
+ const tracked =
+ (this.router.currentRoute.queryParams["f"] ||
+ this.router.currentRoute.queryParams["filter"]) === "tracked";
+
+ let topicIds = this.selected
+ ? this.selected.map((topic) => topic.id)
+ : null;
+
+ Topic.resetNew(this.category, !this.noSubcategories, {
+ tracked,
+ topicIds,
+ dismissPosts,
+ dismissTopics,
+ untrack,
+ }).then((result) => {
+ if (result.topic_ids) {
+ this.topicTrackingState.removeTopics(result.topic_ids);
+ }
+ this.send(
+ "refresh",
+ tracked ? { skipResettingParams: ["filter", "f"] } : {}
+ );
+ });
+ },
+
// Show newly inserted topics
@action
showInserted(event) {
@@ -114,26 +141,6 @@ const controllerOpts = {
}
});
},
-
- resetNew() {
- const tracked =
- (this.router.currentRoute.queryParams["f"] ||
- this.router.currentRoute.queryParams["filter"]) === "tracked";
-
- let topicIds = this.selected
- ? this.selected.map((topic) => topic.id)
- : null;
-
- Topic.resetNew(this.category, !this.noSubcategories, {
- tracked,
- topicIds,
- }).then(() =>
- this.send(
- "refresh",
- tracked ? { skipResettingParams: ["filter", "f"] } : {}
- )
- );
- },
},
afterRefresh(filter, list, listModel = list) {
@@ -213,4 +220,8 @@ const controllerOpts = {
},
};
-export default DiscoveryController.extend(controllerOpts, BulkTopicSelection);
+export default DiscoveryController.extend(
+ controllerOpts,
+ BulkTopicSelection,
+ DismissTopics
+);
diff --git a/app/assets/javascripts/discourse/app/controllers/dismiss-new.js b/app/assets/javascripts/discourse/app/controllers/dismiss-new.js
new file mode 100644
index 00000000000..3b847e8bc19
--- /dev/null
+++ b/app/assets/javascripts/discourse/app/controllers/dismiss-new.js
@@ -0,0 +1,13 @@
+import Controller from "@ember/controller";
+import ModalFunctionality from "discourse/mixins/modal-functionality";
+import { action } from "@ember/object";
+
+export default class DismissNewController extends Controller.extend(
+ ModalFunctionality
+) {
+ @action
+ dismiss() {
+ this.dismissCallback();
+ this.send("closeModal");
+ }
+}
diff --git a/app/assets/javascripts/discourse/app/controllers/tag-show.js b/app/assets/javascripts/discourse/app/controllers/tag-show.js
index 48a3964fec9..d1737593b3d 100644
--- a/app/assets/javascripts/discourse/app/controllers/tag-show.js
+++ b/app/assets/javascripts/discourse/app/controllers/tag-show.js
@@ -2,6 +2,7 @@ import DiscoverySortableController from "discourse/controllers/discovery-sortabl
import { inject as controller } from "@ember/controller";
import discourseComputed, { observes } from "discourse-common/utils/decorators";
import BulkTopicSelection from "discourse/mixins/bulk-topic-selection";
+import DismissTopics from "discourse/mixins/dismiss-topics";
import FilterModeMixin from "discourse/mixins/filter-mode";
import I18n from "I18n";
import NavItem from "discourse/models/nav-item";
@@ -13,6 +14,7 @@ import { inject as service } from "@ember/service";
export default DiscoverySortableController.extend(
BulkTopicSelection,
+ DismissTopics,
FilterModeMixin,
{
application: controller(),
@@ -91,8 +93,7 @@ export default DiscoverySortableController.extend(
return this._isFilterPage(filter, "new") && topicsLength > 0;
},
- @action
- resetNew() {
+ callResetNew(dismissPosts = false, dismissTopics = false, untrack = false) {
const tracked =
(this.router.currentRoute.queryParams["f"] ||
this.router.currentRoute.queryParams["filter"]) === "tracked";
@@ -103,9 +104,15 @@ export default DiscoverySortableController.extend(
tracked,
tag: this.tag,
topicIds,
- }).then(() =>
- this.refresh(tracked ? { skipResettingParams: ["filter", "f"] } : {})
- );
+ dismissPosts,
+ dismissTopics,
+ untrack,
+ }).then((result) => {
+ if (result.topic_ids) {
+ this.topicTrackingState.removeTopics(result.topic_ids);
+ }
+ this.refresh(tracked ? { skipResettingParams: ["filter", "f"] } : {});
+ });
},
@action
diff --git a/app/assets/javascripts/discourse/app/mixins/dismiss-topics.js b/app/assets/javascripts/discourse/app/mixins/dismiss-topics.js
new file mode 100644
index 00000000000..00c5a7620a3
--- /dev/null
+++ b/app/assets/javascripts/discourse/app/mixins/dismiss-topics.js
@@ -0,0 +1,30 @@
+import Mixin from "@ember/object/mixin";
+import User from "discourse/models/user";
+import showModal from "discourse/lib/show-modal";
+import I18n from "I18n";
+
+export default Mixin.create({
+ actions: {
+ resetNew() {
+ const user = User.current();
+ if (!user.new_new_view_enabled) {
+ return this.callResetNew();
+ }
+ const controller = showModal("dismiss-new", {
+ model: {
+ dismissTopics: true,
+ dismissPosts: true,
+ },
+ titleTranslated: I18n.t("topics.bulk.dismiss_new_modal.title"),
+ });
+
+ controller.set("dismissCallback", () => {
+ this.callResetNew(
+ controller.model.dismissPosts,
+ controller.model.dismissTopics,
+ controller.model.untrack
+ );
+ });
+ },
+ },
+});
diff --git a/app/assets/javascripts/discourse/app/models/topic.js b/app/assets/javascripts/discourse/app/models/topic.js
index 9d4d22c4983..2c66fdb759b 100644
--- a/app/assets/javascripts/discourse/app/models/topic.js
+++ b/app/assets/javascripts/discourse/app/models/topic.js
@@ -851,6 +851,18 @@ Topic.reopenClass({
data.topic_ids = topicIds;
}
+ if (opts.dismissPosts) {
+ data.dismiss_posts = opts.dismissPosts;
+ }
+
+ if (opts.dismissTopics) {
+ data.dismiss_topics = opts.dismissTopics;
+ }
+
+ if (opts.untrack) {
+ data.untrack = opts.untrack;
+ }
+
return ajax("/topics/reset-new", { type: "PUT", data });
},
diff --git a/app/assets/javascripts/discourse/app/templates/modal/dismiss-new.hbs b/app/assets/javascripts/discourse/app/templates/modal/dismiss-new.hbs
new file mode 100644
index 00000000000..4d40ce221d8
--- /dev/null
+++ b/app/assets/javascripts/discourse/app/templates/modal/dismiss-new.hbs
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/controllers/topics_controller.rb b/app/controllers/topics_controller.rb
index e8cfbea0998..314bcc7f939 100644
--- a/app/controllers/topics_controller.rb
+++ b/app/controllers/topics_controller.rb
@@ -1059,7 +1059,20 @@ class TopicsController < ApplicationController
elsif params[:tag_id].present?
Topic.joins(:tags).where(tags: { name: params[:tag_id] })
else
- new_results = TopicQuery.new(current_user).new_results(limit: false)
+ new_results =
+ if current_user.new_new_view_enabled?
+ if (params[:dismiss_topics] && params[:dismiss_posts])
+ TopicQuery.new(current_user).new_and_unread_results(limit: false)
+ elsif params[:dismiss_topics]
+ TopicQuery.new(current_user).new_results(limit: false)
+ elsif params[:dismiss_posts]
+ TopicQuery.new(current_user).unread_results(limit: false)
+ else
+ Topic.none
+ end
+ else
+ TopicQuery.new(current_user).new_results(limit: false)
+ end
if params[:tracked].to_s == "true"
TopicQuery.tracked_filter(new_results, current_user.id)
else
@@ -1077,11 +1090,30 @@ class TopicsController < ApplicationController
topic_scope = topic_scope.where(id: topic_ids)
end
- dismissed_topic_ids =
- TopicsBulkAction.new(current_user, topic_scope.pluck(:id), type: "dismiss_topics").perform!
- TopicTrackingState.publish_dismiss_new(current_user.id, topic_ids: dismissed_topic_ids)
+ dismissed_topic_ids = []
+ dismissed_post_topic_ids = []
+ if !current_user.new_new_view_enabled? || params[:dismiss_topics]
+ dismissed_topic_ids =
+ TopicsBulkAction.new(current_user, topic_scope.pluck(:id), type: "dismiss_topics").perform!
+ TopicTrackingState.publish_dismiss_new(current_user.id, topic_ids: dismissed_topic_ids)
+ end
- render body: nil
+ if params[:dismiss_posts]
+ if params[:untrack]
+ dismissed_post_topic_ids =
+ TopicsBulkAction.new(
+ current_user,
+ topic_scope.pluck(:id),
+ type: "change_notification_level",
+ notification_level_id: NotificationLevels.topic_levels[:regular],
+ ).perform!
+ else
+ dismissed_post_topic_ids =
+ TopicsBulkAction.new(current_user, topic_scope.pluck(:id), type: "dismiss_posts").perform!
+ end
+ end
+
+ render_json_dump topic_ids: dismissed_topic_ids.concat(dismissed_post_topic_ids).uniq
end
def convert_topic
diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml
index 939e47ffe9e..2964e59e4d0 100644
--- a/config/locales/client.en.yml
+++ b/config/locales/client.en.yml
@@ -2866,6 +2866,11 @@ en:
dismiss_tooltip: "Dismiss just new posts or stop tracking topics"
also_dismiss_topics: "Stop tracking these topics so they never show up as unread for me again"
dismiss_new: "Dismiss New"
+ dismiss_new_modal:
+ title: "Dismiss new"
+ topics: "Dismiss new topics"
+ posts: "Dismiss new posts"
+ untrack: "Stop tracking these topics so they stop appearing in my new list"
dismiss_new_with_selected:
one: "Dismiss New (%{count})"
other: "Dismiss New (%{count})"
diff --git a/spec/requests/topics_controller_spec.rb b/spec/requests/topics_controller_spec.rb
index 0548fbf74ee..745cd75a448 100644
--- a/spec/requests/topics_controller_spec.rb
+++ b/spec/requests/topics_controller_spec.rb
@@ -4061,6 +4061,168 @@ RSpec.describe TopicsController do
end
end
end
+
+ describe "new and unread" do
+ fab!(:group) { Fabricate(:group) }
+ fab!(:new_topic) { Fabricate(:topic) }
+ fab!(:unread_topic) { Fabricate(:topic, highest_post_number: 3) }
+ fab!(:topic_user) do
+ Fabricate(
+ :topic_user,
+ topic: unread_topic,
+ user: user,
+ notification_level: NotificationLevels.topic_levels[:tracking],
+ last_read_post_number: 1,
+ )
+ end
+
+ before do
+ create_post(topic: unread_topic)
+ create_post(topic: unread_topic)
+ user.groups << group
+ SiteSetting.experimental_new_new_view_groups = group.id
+ sign_in(user)
+ end
+
+ it "dismisses new topics" do
+ put "/topics/reset-new.json"
+ topics = TopicQuery.new(user).new_and_unread_results(limit: false)
+ expect(topics).to eq([unread_topic, new_topic])
+ expect(response.status).to eq(200)
+ expect(response.parsed_body["topic_ids"]).to eq([])
+
+ put "/topics/reset-new.json", params: { dismiss_topics: true }
+ expect(response.status).to eq(200)
+ expect(response.parsed_body["topic_ids"]).to eq([new_topic.id])
+
+ topics = TopicQuery.new(user).new_and_unread_results(limit: false)
+ expect(topics).to eq([unread_topic])
+ expect(DismissedTopicUser.where(user: user).count).to eq(1)
+ expect(DismissedTopicUser.where(user: user).first.topic_id).to eq(new_topic.id)
+ expect(topic_user.reload.notification_level).to eq(
+ NotificationLevels.topic_levels[:tracking],
+ )
+ end
+
+ it "dismisses unread topics" do
+ put "/topics/reset-new.json"
+ expect(response.status).to eq(200)
+ expect(response.parsed_body["topic_ids"]).to eq([])
+ topics = TopicQuery.new(user).new_and_unread_results(limit: false)
+ expect(topics).to eq([unread_topic, new_topic])
+
+ put "/topics/reset-new.json", params: { dismiss_posts: true }
+ expect(response.status).to eq(200)
+ expect(response.parsed_body["topic_ids"]).to eq([unread_topic.id])
+
+ topics = TopicQuery.new(user).new_and_unread_results(limit: false)
+ expect(topics).to eq([new_topic])
+ expect(DismissedTopicUser.count).to eq(0)
+ expect(topic_user.reload.notification_level).to eq(
+ NotificationLevels.topic_levels[:tracking],
+ )
+ end
+
+ it "untrack topics" do
+ expect(topic_user.notification_level).to eq(NotificationLevels.topic_levels[:tracking])
+ put "/topics/reset-new.json", params: { dismiss_posts: true, untrack: true }
+ expect(response.status).to eq(200)
+ expect(response.parsed_body["topic_ids"]).to eq([unread_topic.id])
+
+ expect(topic_user.reload.notification_level).to eq(
+ NotificationLevels.topic_levels[:regular],
+ )
+ end
+
+ it "dismisses new topics, unread posts and untrack" do
+ put "/topics/reset-new.json",
+ params: {
+ dismiss_topics: true,
+ dismiss_posts: true,
+ untrack: true,
+ }
+ expect(response.status).to eq(200)
+ expect(response.parsed_body["topic_ids"]).to eq([new_topic.id, unread_topic.id])
+
+ topics = TopicQuery.new(user).new_and_unread_results(limit: false)
+ expect(topics).to be_empty
+ expect(DismissedTopicUser.where(user: user).count).to eq(1)
+ expect(DismissedTopicUser.where(user: user).first.topic_id).to eq(new_topic.id)
+
+ expect(user.topic_users.map(&:notification_level).uniq).to eq(
+ [NotificationLevels.topic_levels[:regular]],
+ )
+ end
+
+ context "when category" do
+ fab!(:category) { Fabricate(:category) }
+ fab!(:new_topic_2) { Fabricate(:topic, category: category) }
+ fab!(:unread_topic_2) { Fabricate(:topic, category: category, highest_post_number: 3) }
+ fab!(:topic_user) do
+ Fabricate(
+ :topic_user,
+ topic: unread_topic_2,
+ user: user,
+ notification_level: NotificationLevels.topic_levels[:tracking],
+ last_read_post_number: 1,
+ )
+ end
+
+ it "dismisses new topics, unread posts and untrack for specific category" do
+ topics = TopicQuery.new(user).new_and_unread_results(limit: false)
+ expect(topics).to match_array([new_topic, new_topic_2, unread_topic, unread_topic_2])
+
+ put "/topics/reset-new.json",
+ params: {
+ dismiss_topics: true,
+ dismiss_posts: true,
+ untrack: true,
+ category_id: category.id,
+ }
+ expect(response.status).to eq(200)
+ expect(response.parsed_body["topic_ids"]).to eq([new_topic_2.id, unread_topic_2.id])
+
+ topics = TopicQuery.new(user).new_and_unread_results(limit: false)
+ expect(topics).to match_array([new_topic, unread_topic])
+ end
+ end
+
+ context "when tag" do
+ fab!(:tag) { Fabricate(:tag) }
+ fab!(:new_topic_2) { Fabricate(:topic) }
+ fab!(:unread_topic_2) { Fabricate(:topic, highest_post_number: 3) }
+ fab!(:topic_user) do
+ Fabricate(
+ :topic_user,
+ topic: unread_topic_2,
+ user: user,
+ notification_level: NotificationLevels.topic_levels[:tracking],
+ last_read_post_number: 1,
+ )
+ end
+ fab!(:topic_tag) { Fabricate(:topic_tag, topic: new_topic_2, tag: tag) }
+ fab!(:topic_tag_2) { Fabricate(:topic_tag, topic: unread_topic_2, tag: tag) }
+
+ it "dismisses new topics, unread posts and untrack for specific tag" do
+ topics = TopicQuery.new(user).new_and_unread_results(limit: false)
+ expect(topics).to match_array([new_topic, new_topic_2, unread_topic, unread_topic_2])
+
+ put "/topics/reset-new.json",
+ params: {
+ dismiss_topics: true,
+ dismiss_posts: true,
+ untrack: true,
+ tag_id: tag.name,
+ }
+
+ expect(response.status).to eq(200)
+ expect(response.parsed_body["topic_ids"]).to eq([new_topic_2.id, unread_topic_2.id])
+
+ topics = TopicQuery.new(user).new_and_unread_results(limit: false)
+ expect(topics).to match_array([new_topic, unread_topic])
+ end
+ end
+ end
end
describe "#feature_stats" do
diff --git a/spec/system/dismiss_topics_spec.rb b/spec/system/dismiss_topics_spec.rb
new file mode 100644
index 00000000000..c28c6bc75dc
--- /dev/null
+++ b/spec/system/dismiss_topics_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+describe "Filtering topics", type: :system, js: true do
+ fab!(:user) { Fabricate(:user) }
+ let(:topic_list) { PageObjects::Components::TopicList.new }
+ let(:dismiss_new_modal) { PageObjects::Modals::DismissNew.new }
+ fab!(:group) { Fabricate(:group).tap { |g| g.add(user) } }
+ fab!(:topic) { Fabricate(:topic) }
+
+ before { SiteSetting.experimental_new_new_view_groups = group.id }
+
+ it "displays confirmation modal with preselected options" do
+ sign_in(user)
+
+ visit("/new")
+
+ expect(topic_list).to have_topic(topic)
+ find(".dismiss-read").click
+ expect(dismiss_new_modal).to have_dismiss_topics_checked
+ expect(dismiss_new_modal).to have_dismiss_posts_checked
+ expect(dismiss_new_modal).to have_untrack_unchecked
+ end
+end
diff --git a/spec/system/page_objects/modals/dismiss_new.rb b/spec/system/page_objects/modals/dismiss_new.rb
new file mode 100644
index 00000000000..52ae68142bd
--- /dev/null
+++ b/spec/system/page_objects/modals/dismiss_new.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+module PageObjects
+ module Modals
+ class DismissNew < PageObjects::Modals::Base
+ def has_dismiss_topics_checked?
+ find(".dismiss-topics label").has_checked_field?
+ end
+
+ def has_dismiss_posts_checked?
+ find(".dismiss-posts label").has_checked_field?
+ end
+
+ def has_untrack_unchecked?
+ find(".untrack label").has_no_checked_field?
+ end
+ end
+ end
+end