Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 4 commits into from
Jan 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .discourse-compatibility
Original file line number Diff line number Diff line change
@@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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");
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When the plugin is enabled, we add this user option to the list of options that are saved in the /my/preferences/tracking user preferences page.

}
}

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>
}
Original file line number Diff line number Diff line change
Expand Up @@ -860,6 +860,8 @@ export default {

api.addUserSearchOption("assignableGroups");

api.addSaveableUserOptionField("notification_level_when_assigned");
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to "globally" allow this user option to be "saveable" when save a user preference's page.


api.addBulkActionButton({
id: "assign-topics",
label: "topics.bulk.assign",
Expand Down
5 changes: 5 additions & 0 deletions config/locales/client.en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keep the default of "watching" a topic on assign.

end
end
25 changes: 14 additions & 11 deletions lib/assigner.rb
Original file line number Diff line number Diff line change
Expand Up @@ -329,17 +329,19 @@ def assign(
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] ||
Expand Down Expand Up @@ -506,6 +508,7 @@ def add_small_action_post(action_code, assign_to, text)
@assigned_by,
text,
bump: false,
auto_track: false,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need this to prevent the "small action" / "whisper" post that is automatically created when assigning a topic from changing the notification level because the user "replied".

post_type: SiteSetting.assigns_public ? Post.types[:small_action] : Post.types[:whisper],
action_code: action_code,
custom_fields: custom_fields,
Expand Down
13 changes: 13 additions & 0 deletions lib/discourse_assign/user_option_extension.rb
Original file line number Diff line number Diff line change
@@ -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
11 changes: 11 additions & 0 deletions plugin.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,24 @@ module ::DiscourseAssign
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)
Post.prepend(DiscourseAssign::PostExtension)
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)
Expand Down
40 changes: 40 additions & 0 deletions spec/lib/assigner_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,46 @@
)
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!

Expand Down
43 changes: 43 additions & 0 deletions spec/system/user_preferences_spec.rb
Original file line number Diff line number Diff line change
@@ -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
Loading