FEATURE: new "notification level when assigned" user preference (#626)

This adds a new user preference allowed users to control how the
notification level of the topic they're assigned to changes.

Internal ref - t/141162
This commit is contained in:
Régis Hanol 2025-01-16 17:59:16 +01:00 committed by GitHub
parent 654f197003
commit 8c52b9a31c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 187 additions and 11 deletions

View File

@ -1,3 +1,4 @@
< 3.4.0.beta4-dev: 654f197003f9cdf1926b07137fc2214b21c91a79
< 3.4.0.beta3-dev: 6472f4593e1a4abbb457288db012ddb10f0b16f5
< 3.4.0.beta1-dev: fe725251c1b248c349c38c96432e892c668822c6
< 3.3.0.beta2-dev: b796ae3fcc89b48cf777de5ee3a4c21aada9271e

View File

@ -0,0 +1,52 @@
import Component from "@glimmer/component";
import { service } from "@ember/service";
import { i18n } from "discourse-i18n";
import ComboBox from "select-kit/components/combo-box";
export default class NotificationLevelWhenAssigned extends Component {
@service siteSettings;
constructor(owner, args) {
super(...arguments);
if (this.siteSettings.assign_enabled) {
args.outletArgs.customAttrNames.push("notification_level_when_assigned");
}
}
get notificationLevelsWhenAssigned() {
// The order matches the "notification level when replying" user preference
return [
{
name: i18n("user.notification_level_when_assigned.watch_topic"),
value: "watch_topic",
},
{
name: i18n("user.notification_level_when_assigned.track_topic"),
value: "track_topic",
},
{
name: i18n("user.notification_level_when_assigned.do_nothing"),
value: "do_nothing",
},
];
}
<template>
{{#if this.siteSettings.assign_enabled}}
<div
class="controls controls-dropdown"
data-setting-name="user-notification-level-when-assigned"
>
<label>{{i18n "user.notification_level_when_assigned.label"}}</label>
<ComboBox
@content={{this.notificationLevelsWhenAssigned}}
@value={{@outletArgs.model.user_option.notification_level_when_assigned}}
@valueProperty="value"
@onChange={{action
(mut @outletArgs.model.user_option.notification_level_when_assigned)
}}
/>
</div>
{{/if}}
</template>
}

View File

@ -860,6 +860,8 @@ export default {
api.addUserSearchOption("assignableGroups");
api.addSaveableUserOptionField("notification_level_when_assigned");
api.addBulkActionButton({
id: "assign-topics",
label: "topics.bulk.assign",

View File

@ -100,6 +100,11 @@ en:
assignable_levels:
title: "Who can assign this group"
user:
notification_level_when_assigned:
label: "When assigned"
watch_topic: "Watch topic"
track_topic: "Track topic"
do_nothing: "Do nothing"
messages:
assigned_title: "Assigned (%{count})"
assigned: "Assigned"

View File

@ -0,0 +1,6 @@
# frozen_string_literal: true
class AddNotificationLevelWhenAssignedUserOption < ActiveRecord::Migration[7.2]
def change
add_column :user_options, :notification_level_when_assigned, :integer, null: false, default: 3 # watch topic
end
end

View File

@ -329,17 +329,19 @@ class ::Assigner
publish_assignment(assignment, assign_to, note, status)
if assignment.assigned_to_user?
if !TopicUser.exists?(
user_id: assign_to.id,
topic_id: topic.id,
notification_level: TopicUser.notification_levels[:watching],
)
TopicUser.change(
assign_to.id,
topic.id,
notification_level: TopicUser.notification_levels[:watching],
notifications_reason_id: TopicUser.notification_reasons[:plugin_changed],
)
if !assign_to.user_option.do_nothing_when_assigned?
notification_level =
if assign_to.user_option.track_topic_when_assigned?
TopicUser.notification_levels[:tracking]
else
TopicUser.notification_levels[:watching]
end
topic_user = TopicUser.find_by(user_id: assign_to.id, topic:)
if !topic_user || topic_user.notification_level < notification_level
notifications_reason_id = TopicUser.notification_reasons[:plugin_changed]
TopicUser.change(assign_to.id, topic.id, notification_level:, notifications_reason_id:)
end
end
if SiteSetting.assign_mailer == AssignMailer.levels[:always] ||
@ -506,6 +508,7 @@ class ::Assigner
@assigned_by,
text,
bump: false,
auto_track: false,
post_type: SiteSetting.assigns_public ? Post.types[:small_action] : Post.types[:whisper],
action_code: action_code,
custom_fields: custom_fields,

View File

@ -0,0 +1,13 @@
# frozen_string_literal: true
module DiscourseAssign
module UserOptionExtension
extend ActiveSupport::Concern
prepended do
enum :notification_level_when_assigned,
{ do_nothing: 1, track_topic: 2, watch_topic: 3 },
suffix: "when_assigned"
end
end
end

View File

@ -22,6 +22,8 @@ require_relative "lib/discourse_assign/engine"
require_relative "lib/validators/assign_statuses_validator"
after_initialize do
UserUpdater::OPTION_ATTR.push(:notification_level_when_assigned)
reloadable_patch do |plugin|
Group.prepend(DiscourseAssign::GroupExtension)
ListController.prepend(DiscourseAssign::ListControllerExtension)
@ -29,6 +31,15 @@ after_initialize do
Topic.prepend(DiscourseAssign::TopicExtension)
WebHook.prepend(DiscourseAssign::WebHookExtension)
Notification.prepend(DiscourseAssign::NotificationExtension)
UserOption.prepend(DiscourseAssign::UserOptionExtension)
end
add_to_serializer(:user_option, :notification_level_when_assigned) do
object.notification_level_when_assigned
end
add_to_serializer(:current_user_option, :notification_level_when_assigned) do
object.notification_level_when_assigned
end
register_group_param(:assignable_level)

View File

@ -43,6 +43,46 @@ RSpec.describe Assigner do
)
end
describe "when user watchs topic when assigned" do
before { moderator.user_option.watch_topic_when_assigned! }
it "respects 'when assigned' user preference" do
expect(TopicUser.find_by(user: moderator)).to be(nil)
assigner.assign(moderator)
expect(TopicUser.find_by(user: moderator).notification_level).to eq(
TopicUser.notification_levels[:watching],
)
end
end
describe "when user tracks topic when assigned" do
before { moderator.user_option.track_topic_when_assigned! }
it "respects 'when assigned' user preference" do
expect(TopicUser.find_by(user: moderator)).to be(nil)
assigner.assign(moderator)
expect(TopicUser.find_by(user: moderator).notification_level).to eq(
TopicUser.notification_levels[:tracking],
)
end
end
describe "when user wants to do nothing when assigned" do
before { moderator.user_option.do_nothing_when_assigned! }
it "respects 'when assigned' user preference" do
expect(TopicUser.find_by(user: moderator)).to be(nil)
assigner.assign(moderator)
expect(TopicUser.find_by(user: moderator)).to be(nil)
end
end
it "deletes notification for original assignee when reassigning" do
Jobs.run_immediately!

View File

@ -0,0 +1,43 @@
# frozen_string_literal: true
describe "Assign | User Preferences", type: :system, js: true do
fab!(:user)
let(:selector) { "[data-setting-name='user-notification-level-when-assigned'] .combobox" }
before { sign_in(user) }
describe "when discourse-assign is disabled" do
before { SiteSetting.assign_enabled = false }
it "does not show the 'when assigned' tracking user preference" do
visit "/my/preferences/tracking"
expect(page).not_to have_css(selector)
end
end
describe "when discourse-assign is enabled" do
before { SiteSetting.assign_enabled = true }
let(:when_assigned) { PageObjects::Components::SelectKit.new(selector) }
it "shows the 'when assigned' tracking user preference" do
visit "/my/preferences/tracking"
expect(when_assigned).to have_selected_value("watch_topic")
end
it "supports changing the 'when assigned' tracking user preference" do
visit "/my/preferences/tracking"
when_assigned.expand
when_assigned.select_row_by_value("track_topic")
page.find("button.save-changes").click
page.refresh
expect(when_assigned).to have_selected_value("track_topic")
end
end
end